Initial scaffold: full-stack RackMapper application
Complete project scaffold with working auth, REST API, Prisma/SQLite schema, Docker config, and React frontend for both Rack Planner and Service Mapper modules. Both server and client pass TypeScript strict mode with zero errors. Initial migration applied. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
103
prisma/migrations/20260322024601_init/migration.sql
Normal file
103
prisma/migrations/20260322024601_init/migration.sql
Normal file
@@ -0,0 +1,103 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Rack" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"totalU" INTEGER NOT NULL DEFAULT 42,
|
||||
"location" TEXT,
|
||||
"displayOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Module" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"rackId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"uPosition" INTEGER NOT NULL,
|
||||
"uSize" INTEGER NOT NULL DEFAULT 1,
|
||||
"manufacturer" TEXT,
|
||||
"model" TEXT,
|
||||
"ipAddress" TEXT,
|
||||
"notes" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Module_rackId_fkey" FOREIGN KEY ("rackId") REFERENCES "Rack" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Port" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"moduleId" TEXT NOT NULL,
|
||||
"portNumber" INTEGER NOT NULL,
|
||||
"label" TEXT,
|
||||
"portType" TEXT NOT NULL DEFAULT 'ETHERNET',
|
||||
"mode" TEXT NOT NULL DEFAULT 'ACCESS',
|
||||
"nativeVlan" INTEGER,
|
||||
"notes" TEXT,
|
||||
CONSTRAINT "Port_moduleId_fkey" FOREIGN KEY ("moduleId") REFERENCES "Module" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Vlan" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"vlanId" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"color" TEXT
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PortVlan" (
|
||||
"portId" TEXT NOT NULL,
|
||||
"vlanId" TEXT NOT NULL,
|
||||
"tagged" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
PRIMARY KEY ("portId", "vlanId"),
|
||||
CONSTRAINT "PortVlan_portId_fkey" FOREIGN KEY ("portId") REFERENCES "Port" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "PortVlan_vlanId_fkey" FOREIGN KEY ("vlanId") REFERENCES "Vlan" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ServiceMap" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ServiceNode" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"mapId" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"nodeType" TEXT NOT NULL,
|
||||
"positionX" REAL NOT NULL,
|
||||
"positionY" REAL NOT NULL,
|
||||
"metadata" TEXT,
|
||||
"color" TEXT,
|
||||
"icon" TEXT,
|
||||
"moduleId" TEXT,
|
||||
CONSTRAINT "ServiceNode_mapId_fkey" FOREIGN KEY ("mapId") REFERENCES "ServiceMap" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "ServiceNode_moduleId_fkey" FOREIGN KEY ("moduleId") REFERENCES "Module" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ServiceEdge" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"mapId" TEXT NOT NULL,
|
||||
"sourceId" TEXT NOT NULL,
|
||||
"targetId" TEXT NOT NULL,
|
||||
"label" TEXT,
|
||||
"edgeType" TEXT NOT NULL DEFAULT 'smoothstep',
|
||||
"animated" BOOLEAN NOT NULL DEFAULT false,
|
||||
"metadata" TEXT,
|
||||
CONSTRAINT "ServiceEdge_mapId_fkey" FOREIGN KEY ("mapId") REFERENCES "ServiceMap" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "ServiceEdge_sourceId_fkey" FOREIGN KEY ("sourceId") REFERENCES "ServiceNode" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "ServiceEdge_targetId_fkey" FOREIGN KEY ("targetId") REFERENCES "ServiceNode" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Vlan_vlanId_key" ON "Vlan"("vlanId");
|
||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
116
prisma/schema.prisma
Normal file
116
prisma/schema.prisma
Normal file
@@ -0,0 +1,116 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// NOTE: SQLite does not support Prisma enums.
|
||||
// All enum-like fields are stored as String with validation enforced in the service layer.
|
||||
// Valid values are documented in server/lib/constants.ts and client/src/types/index.ts
|
||||
|
||||
model Rack {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
totalU Int @default(42)
|
||||
location String?
|
||||
displayOrder Int @default(0)
|
||||
modules Module[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Module {
|
||||
id String @id @default(cuid())
|
||||
rackId String
|
||||
rack Rack @relation(fields: [rackId], references: [id], onDelete: Cascade)
|
||||
name String
|
||||
type String // ModuleType: SWITCH | AGGREGATE_SWITCH | MODEM | ROUTER | NAS | PDU | PATCH_PANEL | SERVER | FIREWALL | AP | BLANK | OTHER
|
||||
uPosition Int
|
||||
uSize Int @default(1)
|
||||
manufacturer String?
|
||||
model String?
|
||||
ipAddress String?
|
||||
notes String?
|
||||
ports Port[]
|
||||
serviceNodes ServiceNode[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Port {
|
||||
id String @id @default(cuid())
|
||||
moduleId String
|
||||
module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade)
|
||||
portNumber Int
|
||||
label String?
|
||||
portType String @default("ETHERNET") // PortType: ETHERNET | SFP | SFP_PLUS | QSFP | CONSOLE | UPLINK
|
||||
mode String @default("ACCESS") // VlanMode: ACCESS | TRUNK | HYBRID
|
||||
nativeVlan Int?
|
||||
vlans PortVlan[]
|
||||
notes String?
|
||||
}
|
||||
|
||||
model Vlan {
|
||||
id String @id @default(cuid())
|
||||
vlanId Int @unique
|
||||
name String
|
||||
description String?
|
||||
color String?
|
||||
ports PortVlan[]
|
||||
}
|
||||
|
||||
model PortVlan {
|
||||
portId String
|
||||
port Port @relation(fields: [portId], references: [id], onDelete: Cascade)
|
||||
vlanId String
|
||||
vlan Vlan @relation(fields: [vlanId], references: [id], onDelete: Cascade)
|
||||
tagged Boolean @default(false)
|
||||
|
||||
@@id([portId, vlanId])
|
||||
}
|
||||
|
||||
// --- Service Mapper ---
|
||||
|
||||
model ServiceMap {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String?
|
||||
nodes ServiceNode[]
|
||||
edges ServiceEdge[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model ServiceNode {
|
||||
id String @id @default(cuid())
|
||||
mapId String
|
||||
map ServiceMap @relation(fields: [mapId], references: [id], onDelete: Cascade)
|
||||
label String
|
||||
nodeType String // NodeType: SERVICE | DATABASE | API | DEVICE | EXTERNAL | USER | VLAN | FIREWALL | LOAD_BALANCER | NOTE
|
||||
positionX Float
|
||||
positionY Float
|
||||
metadata String?
|
||||
color String?
|
||||
icon String?
|
||||
moduleId String?
|
||||
module Module? @relation(fields: [moduleId], references: [id], onDelete: SetNull)
|
||||
sourceEdges ServiceEdge[] @relation("EdgeSource")
|
||||
targetEdges ServiceEdge[] @relation("EdgeTarget")
|
||||
}
|
||||
|
||||
model ServiceEdge {
|
||||
id String @id @default(cuid())
|
||||
mapId String
|
||||
map ServiceMap @relation(fields: [mapId], references: [id], onDelete: Cascade)
|
||||
sourceId String
|
||||
source ServiceNode @relation("EdgeSource", fields: [sourceId], references: [id], onDelete: Cascade)
|
||||
targetId String
|
||||
target ServiceNode @relation("EdgeTarget", fields: [targetId], references: [id], onDelete: Cascade)
|
||||
label String?
|
||||
edgeType String @default("smoothstep")
|
||||
animated Boolean @default(false)
|
||||
metadata String?
|
||||
}
|
||||
19
prisma/seed.ts
Normal file
19
prisma/seed.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
// No default seed data — all content is created by the user.
|
||||
// This script is safe to run multiple times (idempotent no-op).
|
||||
console.log('RackMapper database is ready. No seed data configured.');
|
||||
}
|
||||
|
||||
main()
|
||||
.then(async () => {
|
||||
await prisma.$disconnect();
|
||||
})
|
||||
.catch(async (e) => {
|
||||
console.error(e);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user