Initial commit: digital reception monorepo (M1-M11 + demo extensions)

This commit is contained in:
2026-05-25 12:59:54 +05:00
commit b9f88194d9
182 changed files with 20578 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
{
"name": "@reception/db",
"version": "0.0.1",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"generate": "prisma generate",
"migrate": "prisma migrate dev",
"migrate:deploy": "prisma migrate deploy",
"migrate:reset": "prisma migrate reset --force",
"seed": "tsx prisma/seed.ts",
"studio": "prisma studio"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^5.22.0",
"bcrypt": "^5.1.1"
},
"devDependencies": {
"@reception/tsconfig": "workspace:*",
"@types/bcrypt": "^5.0.2",
"@types/node": "^22.9.0",
"prisma": "^5.22.0",
"tsx": "^4.19.2",
"typescript": "^5.6.3"
}
}
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "track_events" ADD COLUMN "face_bbox" JSONB;
@@ -0,0 +1,5 @@
-- Prisma migrate diff не умеет ADD VALUE для enum (известная багофича).
-- Пишем руками. ADD VALUE не работает в транзакции, поэтому каждый в своём statement.
ALTER TYPE "ZoneCode" ADD VALUE IF NOT EXISTS 'D';
ALTER TYPE "ZoneCode" ADD VALUE IF NOT EXISTS 'E';
@@ -0,0 +1,266 @@
-- CreateExtension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- CreateExtension
CREATE EXTENSION IF NOT EXISTS "vector";
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('MANAGER', 'SENIOR_ADMIN', 'SECURITY', 'SYSADMIN');
-- CreateEnum
CREATE TYPE "ConsentAction" AS ENUM ('GRANTED', 'REVOKED');
-- CreateEnum
CREATE TYPE "ZoneCode" AS ENUM ('A', 'B', 'C');
-- CreateEnum
CREATE TYPE "TrackStatus" AS ENUM ('UNMATCHED', 'MATCHED', 'ANONYMIZED');
-- CreateEnum
CREATE TYPE "TrackEventType" AS ENUM ('arrived', 'waiting', 'service_started', 'service_ended', 'left_without_service');
-- CreateEnum
CREATE TYPE "ConsentRevocationStatus" AS ENUM ('PENDING', 'DONE');
-- CreateTable
CREATE TABLE "users" (
"id" UUID NOT NULL,
"email" TEXT NOT NULL,
"full_name" TEXT NOT NULL,
"password_hash" TEXT NOT NULL,
"role" "Role" NOT NULL,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "refresh_tokens" (
"id" UUID NOT NULL,
"user_id" UUID NOT NULL,
"token_hash" TEXT NOT NULL,
"expires_at" TIMESTAMP(3) NOT NULL,
"revoked_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "refresh_tokens_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "patients" (
"id" UUID NOT NULL,
"polimed_patient_id" TEXT,
"full_name" TEXT,
"consent_received_at" TIMESTAMP(3),
"consent_revoked_at" TIMESTAMP(3),
"pending_deletion_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "patients_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "patient_consents" (
"id" UUID NOT NULL,
"patient_id" UUID NOT NULL,
"action" "ConsentAction" NOT NULL,
"paper_ref" TEXT,
"actor_user_id" UUID NOT NULL,
"occurred_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "patient_consents_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "zones" (
"id" UUID NOT NULL,
"code" "ZoneCode" NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "zones_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "cameras" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"rtsp_url" TEXT,
"zone_id" UUID NOT NULL,
CONSTRAINT "cameras_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "tracks" (
"id" UUID NOT NULL,
"patient_id" UUID,
"status" "TrackStatus" NOT NULL DEFAULT 'UNMATCHED',
"first_seen_at" TIMESTAMP(3) NOT NULL,
"last_seen_at" TIMESTAMP(3) NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "tracks_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "track_events" (
"id" UUID NOT NULL,
"track_id" UUID NOT NULL,
"type" "TrackEventType" NOT NULL,
"camera_id" UUID NOT NULL,
"zone_id" UUID NOT NULL,
"occurred_at" TIMESTAMP(3) NOT NULL,
"evidence_key" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "track_events_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "face_embeddings" (
"id" UUID NOT NULL,
"embedding" vector(512) NOT NULL,
"patient_id" UUID,
"track_id" UUID,
"camera_id" UUID NOT NULL,
"quality" DOUBLE PRECISION NOT NULL DEFAULT 0,
"captured_at" TIMESTAMP(3) NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "face_embeddings_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "visits" (
"id" UUID NOT NULL,
"patient_id" UUID NOT NULL,
"polimed_appointment_id" TEXT,
"arrived_at" TIMESTAMP(3) NOT NULL,
"service_started_at" TIMESTAMP(3),
"service_ended_at" TIMESTAMP(3),
"left_without_service" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "visits_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "biometry_access_log" (
"id" UUID NOT NULL,
"actor_user_id" UUID,
"subject_patient_id" UUID,
"action" TEXT NOT NULL,
"request_path" TEXT,
"occurred_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "biometry_access_log_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "consent_revocation_jobs" (
"id" UUID NOT NULL,
"patient_id" UUID NOT NULL,
"revoked_at" TIMESTAMP(3) NOT NULL,
"scheduled_for" TIMESTAMP(3) NOT NULL,
"status" "ConsentRevocationStatus" NOT NULL DEFAULT 'PENDING',
"completed_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "consent_revocation_jobs_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE UNIQUE INDEX "refresh_tokens_token_hash_key" ON "refresh_tokens"("token_hash");
-- CreateIndex
CREATE INDEX "refresh_tokens_user_id_idx" ON "refresh_tokens"("user_id");
-- CreateIndex
CREATE UNIQUE INDEX "patients_polimed_patient_id_key" ON "patients"("polimed_patient_id");
-- CreateIndex
CREATE INDEX "patient_consents_patient_id_idx" ON "patient_consents"("patient_id");
-- CreateIndex
CREATE UNIQUE INDEX "zones_code_key" ON "zones"("code");
-- CreateIndex
CREATE UNIQUE INDEX "cameras_name_key" ON "cameras"("name");
-- CreateIndex
CREATE INDEX "tracks_status_first_seen_at_idx" ON "tracks"("status", "first_seen_at");
-- CreateIndex
CREATE INDEX "tracks_patient_id_idx" ON "tracks"("patient_id");
-- CreateIndex
CREATE INDEX "track_events_track_id_occurred_at_idx" ON "track_events"("track_id", "occurred_at");
-- CreateIndex
CREATE INDEX "face_embeddings_track_id_idx" ON "face_embeddings"("track_id");
-- CreateIndex
CREATE INDEX "face_embeddings_patient_id_idx" ON "face_embeddings"("patient_id");
-- CreateIndex
CREATE INDEX "face_embeddings_captured_at_idx" ON "face_embeddings"("captured_at");
-- CreateIndex
CREATE INDEX "visits_patient_id_arrived_at_idx" ON "visits"("patient_id", "arrived_at");
-- CreateIndex
CREATE INDEX "biometry_access_log_occurred_at_idx" ON "biometry_access_log"("occurred_at");
-- CreateIndex
CREATE INDEX "biometry_access_log_subject_patient_id_idx" ON "biometry_access_log"("subject_patient_id");
-- CreateIndex
CREATE INDEX "consent_revocation_jobs_status_scheduled_for_idx" ON "consent_revocation_jobs"("status", "scheduled_for");
-- AddForeignKey
ALTER TABLE "refresh_tokens" ADD CONSTRAINT "refresh_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "patient_consents" ADD CONSTRAINT "patient_consents_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patients"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "patient_consents" ADD CONSTRAINT "patient_consents_actor_user_id_fkey" FOREIGN KEY ("actor_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "cameras" ADD CONSTRAINT "cameras_zone_id_fkey" FOREIGN KEY ("zone_id") REFERENCES "zones"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "tracks" ADD CONSTRAINT "tracks_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patients"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "track_events" ADD CONSTRAINT "track_events_track_id_fkey" FOREIGN KEY ("track_id") REFERENCES "tracks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "track_events" ADD CONSTRAINT "track_events_camera_id_fkey" FOREIGN KEY ("camera_id") REFERENCES "cameras"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "track_events" ADD CONSTRAINT "track_events_zone_id_fkey" FOREIGN KEY ("zone_id") REFERENCES "zones"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "face_embeddings" ADD CONSTRAINT "face_embeddings_track_id_fkey" FOREIGN KEY ("track_id") REFERENCES "tracks"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "face_embeddings" ADD CONSTRAINT "face_embeddings_camera_id_fkey" FOREIGN KEY ("camera_id") REFERENCES "cameras"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "visits" ADD CONSTRAINT "visits_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patients"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "biometry_access_log" ADD CONSTRAINT "biometry_access_log_actor_user_id_fkey" FOREIGN KEY ("actor_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "consent_revocation_jobs" ADD CONSTRAINT "consent_revocation_jobs_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patients"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+271
View File
@@ -0,0 +1,271 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
extensions = [vector, uuidOssp(map: "uuid-ossp")]
}
// =========================================================================
// AUTH / RBAC
// =========================================================================
enum Role {
MANAGER
SENIOR_ADMIN
SECURITY
SYSADMIN
}
model User {
id String @id @default(uuid()) @db.Uuid
email String @unique
fullName String @map("full_name")
passwordHash String @map("password_hash")
role Role
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
refreshTokens RefreshToken[]
consentActions PatientConsent[]
biometryAccessLog BiometryAccessLog[]
@@map("users")
}
model RefreshToken {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
tokenHash String @unique @map("token_hash")
expiresAt DateTime @map("expires_at")
revokedAt DateTime? @map("revoked_at")
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("refresh_tokens")
}
// =========================================================================
// DOMAIN: PATIENT / CONSENT
// =========================================================================
model Patient {
id String @id @default(uuid()) @db.Uuid
polimedPatientId String? @unique @map("polimed_patient_id")
fullName String? @map("full_name")
consentReceivedAt DateTime? @map("consent_received_at")
consentRevokedAt DateTime? @map("consent_revoked_at")
pendingDeletionAt DateTime? @map("pending_deletion_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
consents PatientConsent[]
tracks Track[]
visits Visit[]
consentRevocationJobs ConsentRevocationJob[]
@@map("patients")
}
enum ConsentAction {
GRANTED
REVOKED
}
model PatientConsent {
id String @id @default(uuid()) @db.Uuid
patientId String @map("patient_id") @db.Uuid
action ConsentAction
paperRef String? @map("paper_ref")
actorUserId String @map("actor_user_id") @db.Uuid
occurredAt DateTime @default(now()) @map("occurred_at")
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
actor User @relation(fields: [actorUserId], references: [id])
@@index([patientId])
@@map("patient_consents")
}
// =========================================================================
// CAMERAS / ZONES
// =========================================================================
enum ZoneCode {
A
B
C
D
E
}
model Zone {
id String @id @default(uuid()) @db.Uuid
code ZoneCode @unique
name String
cameras Camera[]
trackEvents TrackEvent[]
@@map("zones")
}
model Camera {
id String @id @default(uuid()) @db.Uuid
name String @unique
rtspUrl String? @map("rtsp_url")
zoneId String @map("zone_id") @db.Uuid
zone Zone @relation(fields: [zoneId], references: [id])
trackEvents TrackEvent[]
faceEmbeddings FaceEmbedding[]
@@map("cameras")
}
// =========================================================================
// TRACKS / EVENTS / EMBEDDINGS
// =========================================================================
enum TrackStatus {
UNMATCHED
MATCHED
ANONYMIZED
}
model Track {
id String @id @default(uuid()) @db.Uuid
patientId String? @map("patient_id") @db.Uuid
status TrackStatus @default(UNMATCHED)
firstSeenAt DateTime @map("first_seen_at")
lastSeenAt DateTime @map("last_seen_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
patient Patient? @relation(fields: [patientId], references: [id])
events TrackEvent[]
faceEmbeddings FaceEmbedding[]
@@index([status, firstSeenAt])
@@index([patientId])
@@map("tracks")
}
enum TrackEventType {
arrived
waiting
service_started
service_ended
left_without_service
}
model TrackEvent {
id String @id @default(uuid()) @db.Uuid
trackId String @map("track_id") @db.Uuid
type TrackEventType
cameraId String @map("camera_id") @db.Uuid
zoneId String @map("zone_id") @db.Uuid
occurredAt DateTime @map("occurred_at")
evidenceKey String? @map("evidence_key")
// [x1, y1, x2, y2] нормализовано 0..1 — bbox распознанного лица на кадре.
faceBbox Json? @map("face_bbox") @db.JsonB
createdAt DateTime @default(now()) @map("created_at")
track Track @relation(fields: [trackId], references: [id], onDelete: Cascade)
camera Camera @relation(fields: [cameraId], references: [id])
zone Zone @relation(fields: [zoneId], references: [id])
@@index([trackId, occurredAt])
@@map("track_events")
}
// 512-d face embedding (InsightFace buffalo_l).
// face-service пишет и читает напрямую через psycopg2 + pgvector.
model FaceEmbedding {
id String @id @default(uuid()) @db.Uuid
embedding Unsupported("vector(512)")
patientId String? @map("patient_id") @db.Uuid
trackId String? @map("track_id") @db.Uuid
cameraId String @map("camera_id") @db.Uuid
quality Float @default(0)
capturedAt DateTime @map("captured_at")
createdAt DateTime @default(now()) @map("created_at")
track Track? @relation(fields: [trackId], references: [id], onDelete: SetNull)
camera Camera @relation(fields: [cameraId], references: [id])
@@index([trackId])
@@index([patientId])
@@index([capturedAt])
@@map("face_embeddings")
}
// =========================================================================
// VISITS
// =========================================================================
model Visit {
id String @id @default(uuid()) @db.Uuid
patientId String @map("patient_id") @db.Uuid
polimedAppointmentId String? @map("polimed_appointment_id")
arrivedAt DateTime @map("arrived_at")
serviceStartedAt DateTime? @map("service_started_at")
serviceEndedAt DateTime? @map("service_ended_at")
leftWithoutService Boolean @default(false) @map("left_without_service")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
@@index([patientId, arrivedAt])
@@map("visits")
}
// =========================================================================
// AUDIT
// =========================================================================
model BiometryAccessLog {
id String @id @default(uuid()) @db.Uuid
actorUserId String? @map("actor_user_id") @db.Uuid
subjectPatientId String? @map("subject_patient_id") @db.Uuid
action String
requestPath String? @map("request_path")
occurredAt DateTime @default(now()) @map("occurred_at")
actor User? @relation(fields: [actorUserId], references: [id])
@@index([occurredAt])
@@index([subjectPatientId])
@@map("biometry_access_log")
}
// =========================================================================
// CONSENT REVOCATION JOBS (24h delete)
// =========================================================================
enum ConsentRevocationStatus {
PENDING
DONE
}
model ConsentRevocationJob {
id String @id @default(uuid()) @db.Uuid
patientId String @map("patient_id") @db.Uuid
revokedAt DateTime @map("revoked_at")
scheduledFor DateTime @map("scheduled_for")
status ConsentRevocationStatus @default(PENDING)
completedAt DateTime? @map("completed_at")
createdAt DateTime @default(now()) @map("created_at")
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
@@index([status, scheduledFor])
@@map("consent_revocation_jobs")
}
+110
View File
@@ -0,0 +1,110 @@
import { PrismaClient, Role, ZoneCode } from '@prisma/client';
import bcrypt from 'bcrypt';
const prisma = new PrismaClient();
const SEED_USERS: Array<{
email: string;
fullName: string;
role: Role;
passwordEnv: string;
passwordFallback: string;
}> = [
{
email: 'manager@local',
fullName: 'Иван Управляющий',
role: Role.MANAGER,
passwordEnv: 'SEED_PASSWORD_MANAGER',
passwordFallback: 'manager123',
},
{
email: 'senior@local',
fullName: 'Мария Старший Администратор',
role: Role.SENIOR_ADMIN,
passwordEnv: 'SEED_PASSWORD_SENIOR',
passwordFallback: 'senior123',
},
{
email: 'security@local',
fullName: 'Пётр Безопасность',
role: Role.SECURITY,
passwordEnv: 'SEED_PASSWORD_SECURITY',
passwordFallback: 'security123',
},
{
email: 'admin@local',
fullName: 'Анна Админ Системы',
role: Role.SYSADMIN,
passwordEnv: 'SEED_PASSWORD_SYSADMIN',
passwordFallback: 'admin123',
},
];
const SEED_ZONES: Array<{ code: ZoneCode; name: string }> = [
{ code: ZoneCode.A, name: 'Вход в клинику' },
{ code: ZoneCode.B, name: 'Коридор / зона ожидания' },
{ code: ZoneCode.C, name: 'Стойка рецепции' },
{ code: ZoneCode.D, name: 'Перед кабинетом врача' },
{ code: ZoneCode.E, name: 'В кабинете врача' },
];
const SEED_CAMERAS: Array<{ name: string; zoneCode: ZoneCode }> = [
{ name: 'cam-entrance', zoneCode: ZoneCode.A },
{ name: 'cam-corridor', zoneCode: ZoneCode.B },
// На рецепции 4 рабочих места — отдельная камера на каждое (С1…С4).
{ name: 'cam-reception-1', zoneCode: ZoneCode.C },
{ name: 'cam-reception-2', zoneCode: ZoneCode.C },
{ name: 'cam-reception-3', zoneCode: ZoneCode.C },
{ name: 'cam-reception-4', zoneCode: ZoneCode.C },
{ name: 'cam-doctor-waiting', zoneCode: ZoneCode.D },
{ name: 'cam-doctor-office', zoneCode: ZoneCode.E },
];
async function main() {
console.log('🌱 Seeding reception database...');
// Users
for (const u of SEED_USERS) {
const password = process.env[u.passwordEnv] ?? u.passwordFallback;
const passwordHash = await bcrypt.hash(password, 10);
await prisma.user.upsert({
where: { email: u.email },
update: { fullName: u.fullName, role: u.role, passwordHash, isActive: true },
create: { email: u.email, fullName: u.fullName, role: u.role, passwordHash, isActive: true },
});
console.log(` ✓ user ${u.email} (${u.role})`);
}
// Zones
for (const z of SEED_ZONES) {
await prisma.zone.upsert({
where: { code: z.code },
update: { name: z.name },
create: z,
});
console.log(` ✓ zone ${z.code}${z.name}`);
}
// Cameras (bound to zones)
for (const c of SEED_CAMERAS) {
const zone = await prisma.zone.findUniqueOrThrow({ where: { code: c.zoneCode } });
await prisma.camera.upsert({
where: { name: c.name },
update: { zoneId: zone.id },
create: { name: c.name, zoneId: zone.id },
});
console.log(` ✓ camera ${c.name} → zone ${c.zoneCode}`);
}
console.log('✅ Seed complete.');
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
+2
View File
@@ -0,0 +1,2 @@
export * from '@prisma/client';
export { PrismaClient } from '@prisma/client';
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "@reception/tsconfig/base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./",
"noEmit": false
},
"include": ["src/**/*.ts", "prisma/**/*.ts"]
}
+24
View File
@@ -0,0 +1,24 @@
/** Shared base ESLint config for the Reception monorepo. */
module.exports = {
root: false,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
],
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
env: {
node: true,
es2022: true,
},
rules: {
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
},
ignorePatterns: ['dist', 'build', '.next', 'node_modules', '*.config.js', '*.config.ts'],
};
+12
View File
@@ -0,0 +1,12 @@
{
"name": "@reception/eslint-config",
"version": "0.0.1",
"private": true,
"main": "index.js",
"dependencies": {
"@typescript-eslint/eslint-plugin": "^8.13.0",
"@typescript-eslint/parser": "^8.13.0",
"eslint": "^9.14.0",
"eslint-config-prettier": "^9.1.0"
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"sourceMap": true,
"noUncheckedIndexedAccess": true
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node",
"target": "ES2022",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"noUncheckedIndexedAccess": false
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "ES2022"],
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "preserve",
"allowJs": true,
"noEmit": true,
"incremental": true,
"plugins": [{ "name": "next" }]
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "@reception/tsconfig",
"version": "0.0.1",
"private": true,
"files": [
"base.json",
"nest.json",
"next.json"
]
}