initial: standalone repo from monorepo split

This commit is contained in:
kevin-asprec
2026-04-13 09:37:07 +08:00
commit 5382f3b4e5
87 changed files with 5278 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
{
"name": "@fiberops/admin-db",
"version": "0.1.0",
"private": true,
"main": "./generated/index.js",
"types": "./generated/index.d.ts",
"scripts": {
"build": "tsc",
"generate": "prisma generate",
"migrate:dev": "prisma migrate dev",
"migrate:deploy": "prisma migrate deploy",
"seed": "tsx prisma/seed.ts",
"studio": "prisma studio",
"lint": "tsc --noEmit",
"clean": "rm -rf dist"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.5.0"
},
"devDependencies": {
"prisma": "^6.5.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0"
}
}

View File

@@ -0,0 +1,135 @@
-- CreateTable
CREATE TABLE "super_admins" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "super_admins_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "admin_refresh_tokens" (
"id" TEXT NOT NULL,
"token" TEXT NOT NULL,
"adminId" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "admin_refresh_tokens_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "platform_audit_logs" (
"id" TEXT NOT NULL,
"adminId" TEXT NOT NULL,
"action" TEXT NOT NULL,
"target" TEXT,
"details" JSONB NOT NULL DEFAULT '{}',
"ipAddress" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "platform_audit_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "support_tickets" (
"id" TEXT NOT NULL,
"tenantId" TEXT NOT NULL,
"tenantName" TEXT NOT NULL,
"tenantSlug" TEXT NOT NULL,
"createdById" TEXT NOT NULL,
"createdByName" TEXT NOT NULL,
"subject" TEXT NOT NULL,
"description" TEXT NOT NULL,
"category" TEXT NOT NULL DEFAULT 'general',
"priority" TEXT NOT NULL DEFAULT 'normal',
"status" TEXT NOT NULL DEFAULT 'open',
"assignedToId" TEXT,
"resolvedAt" TIMESTAMP(3),
"closedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "support_tickets_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "support_ticket_comments" (
"id" TEXT NOT NULL,
"ticketId" TEXT NOT NULL,
"authorId" TEXT NOT NULL,
"authorName" TEXT NOT NULL,
"authorType" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "support_ticket_comments_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "super_admins_email_key" ON "super_admins"("email");
-- CreateIndex
CREATE UNIQUE INDEX "admin_refresh_tokens_token_key" ON "admin_refresh_tokens"("token");
-- CreateIndex
CREATE INDEX "admin_refresh_tokens_adminId_idx" ON "admin_refresh_tokens"("adminId");
-- CreateIndex
CREATE INDEX "admin_refresh_tokens_expiresAt_idx" ON "admin_refresh_tokens"("expiresAt");
-- CreateIndex
CREATE INDEX "platform_audit_logs_adminId_idx" ON "platform_audit_logs"("adminId");
-- CreateIndex
CREATE INDEX "platform_audit_logs_action_idx" ON "platform_audit_logs"("action");
-- CreateIndex
CREATE INDEX "platform_audit_logs_target_idx" ON "platform_audit_logs"("target");
-- CreateIndex
CREATE INDEX "platform_audit_logs_createdAt_idx" ON "platform_audit_logs"("createdAt");
-- CreateIndex
CREATE INDEX "support_tickets_tenantId_idx" ON "support_tickets"("tenantId");
-- CreateIndex
CREATE INDEX "support_tickets_status_idx" ON "support_tickets"("status");
-- CreateIndex
CREATE INDEX "support_tickets_category_idx" ON "support_tickets"("category");
-- CreateIndex
CREATE INDEX "support_tickets_priority_idx" ON "support_tickets"("priority");
-- CreateIndex
CREATE INDEX "support_tickets_assignedToId_idx" ON "support_tickets"("assignedToId");
-- CreateIndex
CREATE INDEX "support_tickets_createdAt_idx" ON "support_tickets"("createdAt");
-- CreateIndex
CREATE INDEX "support_ticket_comments_ticketId_idx" ON "support_ticket_comments"("ticketId");
-- CreateIndex
CREATE INDEX "support_ticket_comments_authorId_idx" ON "support_ticket_comments"("authorId");
-- AddForeignKey
ALTER TABLE "admin_refresh_tokens" ADD CONSTRAINT "admin_refresh_tokens_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "super_admins"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "platform_audit_logs" ADD CONSTRAINT "platform_audit_logs_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "super_admins"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "support_tickets" ADD CONSTRAINT "support_tickets_assignedToId_fkey" FOREIGN KEY ("assignedToId") REFERENCES "super_admins"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "support_ticket_comments" ADD CONSTRAINT "support_ticket_comments_ticketId_fkey" FOREIGN KEY ("ticketId") REFERENCES "support_tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "support_ticket_comments" ADD CONSTRAINT "support_ticket_comments_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "super_admins"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

View File

@@ -0,0 +1,133 @@
generator client {
provider = "prisma-client-js"
output = "../generated"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_ADMIN_URL")
}
// ─── Platform SuperAdmins ────────────────────────────
model SuperAdmin {
id String @id @default(uuid())
email String @unique
password String
firstName String
lastName String
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
refreshTokens AdminRefreshToken[]
auditLogs PlatformAuditLog[]
assignedTickets SupportTicket[] @relation("TicketAssignee")
ticketComments SupportTicketComment[]
@@map("super_admins")
}
model AdminRefreshToken {
id String @id @default(uuid())
token String @unique
adminId String
expiresAt DateTime
createdAt DateTime @default(now())
admin SuperAdmin @relation(fields: [adminId], references: [id], onDelete: Cascade)
@@index([adminId])
@@index([expiresAt])
@@map("admin_refresh_tokens")
}
// ─── Platform Audit Log ──────────────────────────────
model PlatformAuditLog {
id String @id @default(uuid())
adminId String
action String
target String?
details Json @default("{}")
ipAddress String?
createdAt DateTime @default(now())
admin SuperAdmin @relation(fields: [adminId], references: [id])
@@index([adminId])
@@index([action])
@@index([target])
@@index([createdAt])
@@map("platform_audit_logs")
}
// ─── Support Tickets (Tenant → Platform) ─────────────
model SupportTicket {
id String @id @default(uuid())
tenantId String
tenantName String
tenantSlug String
createdById String
createdByName String
subject String
description String
category String @default("general") // billing, technical, account, general, feature_request
priority String @default("normal") // low, normal, high, urgent
status String @default("open") // open, in_progress, waiting_tenant, resolved, closed
assignedToId String?
resolvedAt DateTime?
closedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
assignee SuperAdmin? @relation("TicketAssignee", fields: [assignedToId], references: [id])
comments SupportTicketComment[]
attachments SupportTicketAttachment[]
@@index([tenantId])
@@index([status])
@@index([category])
@@index([priority])
@@index([assignedToId])
@@index([createdAt])
@@map("support_tickets")
}
model SupportTicketComment {
id String @id @default(uuid())
ticketId String
authorId String
authorName String
authorType String // "super_admin" or "tenant_user"
content String
createdAt DateTime @default(now())
ticket SupportTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
author SuperAdmin? @relation(fields: [authorId], references: [id])
attachments SupportTicketAttachment[]
@@index([ticketId])
@@index([authorId])
@@map("support_ticket_comments")
}
model SupportTicketAttachment {
id String @id @default(uuid())
ticketId String
commentId String?
fileName String
originalName String
mimeType String
sizeBytes Int
uploadedBy String
createdAt DateTime @default(now())
ticket SupportTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
comment SupportTicketComment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
@@index([ticketId])
@@index([commentId])
@@map("support_ticket_attachments")
}

View File

@@ -0,0 +1,29 @@
import { PrismaClient } from '../generated';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
const password = await bcrypt.hash('SuperAdmin@123', 12);
const admin = await prisma.superAdmin.upsert({
where: { email: 'superadmin@fiberops.dev' },
update: {},
create: {
email: 'superadmin@fiberops.dev',
password,
firstName: 'Super',
lastName: 'Admin',
isActive: true,
},
});
console.log(`Seeded superadmin: ${admin.email}`);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());

View File

@@ -0,0 +1 @@
export { PrismaClient, Prisma } from '../generated';

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src", "prisma"]
}