Full-stack engineer

DCIM: rack and power management for a live data center

In-house DCIM console for a hosting company: racks, devices, PDUs, network ports, and power or port wiring in one place. Management IPs and VLANs are first-class data, every mutation is role-gated, and Vitest + Playwright cover the logic, deployed as a Docker Compose stack.

Year
2026
Stack
TypeScriptReact 19DrizzleMariaDBExpressVitestPlaywrightDocker Compose

DCIM: rack and power management for a live data center

What it is

An in-house data center infrastructure management (DCIM) console for a hosting company. It tracks the physical layer and how it connects: data centers, racks, devices, PDUs, network ports, and which PDU socket feeds which PSU bay.

Every mutation is role-gated on the server and written to an audit log. Operators wire and re-wire hardware. Viewers audit what is wired where.

The stack is a React 19 + Vite console, an Express API, and Drizzle ORM on MariaDB 11, deployed as a Docker Compose stack behind nginx.

What runs where

Browser (React 19 console)
    │
    │  session auth (Better Auth)
    ▼
┌──────────────────────────────┐
│  nginx + certbot             │  TLS termination
└────────┬─────────────────────┘
         ▼
┌──────────────────────────────┐
│  Express API                 │
│  requireRole() ──────────────│── super_admin / operator / viewer
│  Drizzle ORM ────────────────│── typed SQL, migrations in CI
└────────┬─────────────────────┘
         ▼
┌──────────────────────────────┐
│  MariaDB 11                  │
└──────────────────────────────┘

Every connect and disconnect writes an audit row.

How it works

The power model

Power wiring is first-class data, not a diagram. Each PDU socket row carries connected_device_id and connected_psu_slot, so "what feeds this device" is a query instead of tribal knowledge.

Connecting a socket is one PUT /api/pdu-sockets/:socketId. The server rejects a slot that is already fed elsewhere, and the UI renders the 400 as an inline error on that socket's control.

Power Map: wire a rack from one page

The PDU detail table worked one PDU at a time. Wiring a full rack meant opening every PDU in turn. Power Map gives operators one page per rack: a deep-linkable rack selection (/power-map?rack=<id>, so browser back and shared links work), a summary bar with server-computed wired power, and one card per PDU.

Editing is optimistic, with debounced coalesced commits. A burst of changes becomes one request, and the row updates after the server confirms.

Network Map: wire a rack's ports

Power has Power Map; the network side works the same way. Network Map picks a rack from the URL (?rack=<id>), renders one card per switch or router, and wires servers to switch ports in place.

Each card stacks two views. SwitchFrontPanel draws the switch as an operator sees it: ports grouped in pairs with a wider gap every eight, colored green when connected or up, orange when disabled, gray otherwise. PortWiringTable does the wiring against the same ports.

A connection is a row in network_ports, so patching is data, not a diagram. Each port carries port_number, port_speed (10G default), connector_type (SFP28 default), status (up, down, disabled), cable_type, and mac_address. Wiring server port to switch port sets connected_port_id, a self-referencing foreign key, and the change lands in the audit log like every other mutation.

IPs and VLANs as queryable data

Every server and router carries management_ip and mac_address as schema columns, and the servers table is indexed on the IP, so "which device holds this address" is an index lookup.

The CSV import pipeline keeps that data honest at the boundary. Raw rack exports ship loose headers (ip, primary mac, vlan, port_sw); the field-mapping layer normalizes them into typed columns, so an IP validates as an IP and a VLAN lands as an integer, not a string.

Roles and audit

requireRole() gates each route server-side; the UI only mirrors it. Wiring changes stay behind the operator role. Viewers get read-only rendering with mutating controls omitted. Every connect and disconnect writes an audit row, so "who touched this rack, when" always has an answer.

Tests that don't need a running stack

Pure logic lives in server/lib/* and module-level functions, so aggregation and mapping code unit-tests without a DOM or a database. Playwright E2E covers the rest against a seeded MariaDB; the CI seed uses a dedicated password, so real credentials never enter the pipeline. pnpm lint runs tsc --noEmit and acts as the merge gatekeeper.

Why these choices

DecisionRationale
Drizzle over a heavier ORMTyped SQL without a query DSL to learn. Migrations run in CI before any test touches the schema
A MariaDB sidecar per test stageUnit and E2E suites get isolated databases. No shared state, no order dependence, torn down even on red
TanStack Query + ZustandQuery owns server state and invalidates on connect/disconnect. Zustand owns the small client-side bits
snake_case payloads mirrored in .d.tsOne type per payload on both sides of the wire. The compiler catches drift
Debounced, coalesced commitsOne PUT per editing burst instead of one per keystroke. Fewer round trips, cleaner audit trail
Self-referencing network_ports.connected_port_idA patch is one row, and "what is this port plugged into" is one lookup. No join table, no duplicated endpoint
console-kit primitivesPageHeader, Card, and Toolbar are shared across pages, so the console reads as one product

Where it stands

Feature-complete and handed to my senior. The app is in the migration & testing phase on the company's infrastructure.