josh e74bc99e94
build-and-push / build-and-push (push) Successful in 1m40s
Update README with feature overview, deploy instructions, and API reference
Expand the minimal README with sections for features, local run, tests,
Docker deploy via the Gitea registry, the Gitea Actions workflow, full
API table (now including interfaces and lookup-by-hardware-id), stack
choices, and project layout.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 17:09:02 -04:00

Infrastructure

A small internal tool for tracking servers. Look up a host by hardware ID, hostname, or asset ID and see where it lives (site / room / position), what kind of server it is, and how it's wired into the network.

Built as a single Node process that serves a vanilla-JS UI and a JSON API from one port. SQLite for storage. No auth — runs on a trusted internal network.

Features

  • One search box, three searchable fields. Single match jumps straight to the host page.
  • Per-host network interface tracking — name, MAC, IPv4, CIDR, link/duplex — with format-validating regex.
  • Lookup tables for sites, rooms, and server types, managed inline from the UI.
  • Same JSON API for browsers and scripts; schemas validated in, serialized out.

Run locally

Requires Node 22+ (uses the built-in node:sqlite and node:test).

npm install
npm start

UI: http://localhost:3000 · API: http://localhost:3000/api

First boot seeds one site, one room, and a handful of server types so the dropdowns aren't empty.

Test

npm test

21 tests run against an in-process Fastify instance backed by an in-memory SQLite database.

Deploy

Compose pulls the prebuilt image from the Gitea container registry:

docker compose up -d

This pulls gitea.thewrightserver.net/josh/infrastructure:latest and mounts a named volume at /app/data for the SQLite file. To grab a new build:

docker compose pull && docker compose up -d

CI

.gitea/workflows/build.yml runs on every push to main:

  1. Install deps and run the test suite.
  2. If green, log in to gitea.thewrightserver.net using the REGISTRY_TOKEN repo secret.
  3. Build the image and push two tags: :latest and :<commit-sha>.

Set REGISTRY_TOKEN under Repo → Settings → Actions → Secrets to a Personal Access Token with write:package scope.

API

Method Path Notes
GET /api/hosts?q=… Substring search across hardware_id, hostname, asset_id. Capped at 200.
GET /api/hosts/:id Fetch one by numeric ID.
GET /api/hosts/by-hardware-id/:hwid Fetch by hardware ID — what the detail page uses.
POST /api/hosts Create.
PUT /api/hosts/:id Replace.
DELETE /api/hosts/:id Hard delete (cascades to the host's interfaces).
GET /api/interfaces?host_id=N List interfaces for a host.
GET /api/interfaces/:id Fetch one.
POST /api/interfaces Create. Required: host_id, name. Optional: mac_address, ip_address, subnet, link_speed.
PUT /api/interfaces/:id Replace.
DELETE /api/interfaces/:id Hard delete.
* /api/sites[/:id] Site CRUD.
* /api/rooms[/:id] Room CRUD (?site_id= filter on GET).
* /api/server-types[/:id] Server-type CRUD.

Errors are uniform: { error, details? } with status 400 / 404 / 409 / 500.

Stack

  • Node 22 + node:sqlite + node:test — no better-sqlite3, no vitest
  • Fastify 5 with built-in JSON Schema validation
  • Vanilla HTML / CSS / JS — no framework, no bundler

Layout

src/
  server.js          Fastify bootstrap, /api mount, SPA fallback
  db.js              schema, prepared statements, query API
  schemas.js         JSON Schema definitions
  sqlite-errors.js   maps SQLite constraint violations to 400/409
  routes/            hosts, interfaces, sites, rooms, server-types
public/
  index.html  app.css  app.js
tests/
  *.test.js          one suite per resource
S
Description
A small internal tool for tracking servers
Readme 64 KiB
Languages
JavaScript 85%
CSS 10.2%
HTML 4.5%
Dockerfile 0.3%