Initial SuperAdmin API (NestJS + Prisma)

This commit is contained in:
SuperAdmin
2026-07-23 10:25:32 +03:30
commit 7fbc47766c
43 changed files with 12902 additions and 0 deletions
@@ -0,0 +1,75 @@
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL PRIMARY KEY,
"email" TEXT NOT NULL,
"password_hash" TEXT NOT NULL,
"name" TEXT NOT NULL,
"role" TEXT NOT NULL DEFAULT 'super_admin',
"is_active" BOOLEAN NOT NULL DEFAULT true,
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "tenants" (
"id" TEXT NOT NULL PRIMARY KEY,
"product" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"name_fa" TEXT NOT NULL,
"name_en" TEXT,
"domain" TEXT NOT NULL,
"domains" JSONB,
"avatar_url" TEXT,
"primary_color" TEXT,
"secondary_color" TEXT,
"background_color" TEXT,
"node_server" TEXT NOT NULL,
"node_ws" TEXT,
"app_url" TEXT,
"admin_url" TEXT,
"remote_api_token" TEXT,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"notes" TEXT,
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "audit_logs" (
"id" TEXT NOT NULL PRIMARY KEY,
"action" TEXT NOT NULL,
"entity_type" TEXT NOT NULL,
"entity_id" TEXT,
"tenant_id" TEXT,
"user_id" TEXT,
"before" JSONB,
"after" JSONB,
"meta" JSONB,
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "audit_logs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "audit_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE UNIQUE INDEX "tenants_slug_key" ON "tenants"("slug");
-- CreateIndex
CREATE UNIQUE INDEX "tenants_domain_key" ON "tenants"("domain");
-- CreateIndex
CREATE INDEX "tenants_product_idx" ON "tenants"("product");
-- CreateIndex
CREATE INDEX "tenants_is_active_idx" ON "tenants"("is_active");
-- CreateIndex
CREATE INDEX "audit_logs_tenant_id_idx" ON "audit_logs"("tenant_id");
-- CreateIndex
CREATE INDEX "audit_logs_user_id_idx" ON "audit_logs"("user_id");
-- CreateIndex
CREATE INDEX "audit_logs_created_at_idx" ON "audit_logs"("created_at");
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "tenants" ADD COLUMN "app_ui_config" JSONB;
+3
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 = "sqlite"
+84
View File
@@ -0,0 +1,84 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
// Local scaffold default: SQLite. For MySQL set provider = "mysql" and DATABASE_URL accordingly.
provider = "sqlite"
url = env("DATABASE_URL")
}
enum ProductType {
hamkar
khanegi
}
enum UserRole {
super_admin
viewer
}
model User {
id String @id @default(cuid())
email String @unique
passwordHash String @map("password_hash")
name String
role UserRole @default(super_admin)
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
auditLogs AuditLog[]
@@map("users")
}
model Tenant {
id String @id @default(cuid())
product ProductType
slug String @unique
nameFa String @map("name_fa")
nameEn String? @map("name_en")
domain String @unique
domains Json?
avatarUrl String? @map("avatar_url")
primaryColor String? @map("primary_color")
secondaryColor String? @map("secondary_color")
backgroundColor String? @map("background_color")
nodeServer String @map("node_server")
nodeWs String? @map("node_ws")
appUrl String? @map("app_url")
adminUrl String? @map("admin_url")
remoteApiToken String? @map("remote_api_token")
/// Full Khanegi UI JSON (theme, navigation, pages). Hamkar: unused for now.
appUiConfig Json? @map("app_ui_config")
isActive Boolean @default(true) @map("is_active")
notes String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
auditLogs AuditLog[]
@@index([product])
@@index([isActive])
@@map("tenants")
}
model AuditLog {
id String @id @default(cuid())
action String
entityType String @map("entity_type")
entityId String? @map("entity_id")
tenantId String? @map("tenant_id")
userId String? @map("user_id")
before Json?
after Json?
meta Json?
createdAt DateTime @default(now()) @map("created_at")
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@index([tenantId])
@@index([userId])
@@index([createdAt])
@@map("audit_logs")
}
+59
View File
@@ -0,0 +1,59 @@
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
const email = (process.env.SEED_ADMIN_EMAIL || 'admin@abshode.local').toLowerCase();
const password = process.env.SEED_ADMIN_PASSWORD || 'Admin@12345';
const name = process.env.SEED_ADMIN_NAME || 'Super Admin';
const passwordHash = await bcrypt.hash(password, 10);
const user = await prisma.user.upsert({
where: { email },
update: { passwordHash, name, isActive: true, role: 'super_admin' },
create: {
email,
passwordHash,
name,
role: 'super_admin',
isActive: true,
},
});
const sample = await prisma.tenant.upsert({
where: { slug: 'demo-hamkar' },
update: {},
create: {
product: 'hamkar',
slug: 'demo-hamkar',
nameFa: 'نسخه دمو همکار',
nameEn: 'Demo Hamkar',
domain: 'demo-hamkar.local',
avatarUrl: null,
primaryColor: '#1d4ed8',
secondaryColor: '#0f172a',
backgroundColor: '#ffffff',
nodeServer: 'https://demo-hamkar.local/server',
nodeWs: 'wss://demo-hamkar.local/prc/ws',
appUrl: 'https://demo-hamkar.local',
adminUrl: 'https://demo-hamkar.local/admin',
isActive: true,
notes: 'نمونه اولیه برای تست اسکفولد',
},
});
console.log('Seeded admin:', { id: user.id, email: user.email });
console.log('Seeded tenant:', { id: sample.id, slug: sample.slug });
console.log('Login with:', email, '/', password);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});