init studio admin backend

This commit is contained in:
2026-07-01 17:30:04 +09:00
commit 4437d9980d
144 changed files with 44964 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
generator authClient {
provider = "prisma-client-js"
output = "../node_modules/@prisma/auth-client"
binaryTargets = ["native", "debian-openssl-3.0.x", "debian-openssl-1.1.x"]
}
datasource db {
provider = "postgresql"
url = env("AUTH_DATABASE_URL")
}
enum UserRole {
ADMIN
USER
}
enum LicenseCode {
FREE
EDU
PRO
}
model User {
id String @id @default(uuid()) @db.Uuid
email String @unique
passwordHash String @map("password_hash")
name String?
role UserRole @default(USER)
isActive Boolean @default(true) @map("is_active")
lastLoginAt DateTime? @map("last_login_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
userLicenses UserLicense[]
refreshTokens RefreshToken[]
@@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") @db.Timestamptz(3)
revokedAt DateTime? @map("revoked_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("refresh_tokens")
}
model License {
id String @id @default(uuid()) @db.Uuid
code LicenseCode @unique
name String
description String?
defaultDurationDays Int? @default(365) @map("default_duration_days")
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
userLicenses UserLicense[]
@@index([isActive])
@@map("licenses")
}
model UserLicense {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
licenseId String @map("license_id") @db.Uuid
startsAt DateTime @default(now()) @map("starts_at") @db.Timestamptz(3)
expiresAt DateTime? @map("expires_at") @db.Timestamptz(3)
assignedById String? @map("assigned_by") @db.Uuid
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
license License @relation(fields: [licenseId], references: [id], onDelete: Restrict)
@@index([userId, startsAt])
@@index([licenseId])
@@map("user_licenses")
}
+86
View File
@@ -0,0 +1,86 @@
generator launcherClient {
provider = "prisma-client-js"
output = "../node_modules/@prisma/launcher-client"
binaryTargets = ["native", "debian-openssl-3.0.x", "debian-openssl-1.1.x"]
}
datasource db {
provider = "postgresql"
url = env("LAUNCHER_DATABASE_URL")
}
model LauncherNotice {
id String @id @default(uuid()) @db.Uuid
appKey String @default("Launcher") @map("app_key")
title String
body String
linkUrl String? @map("link_url")
isActive Boolean @default(true) @map("is_active")
startsAt DateTime? @map("starts_at") @db.Timestamptz(3)
endsAt DateTime? @map("ends_at") @db.Timestamptz(3)
priority Int @default(0)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([appKey, isActive, priority])
@@map("launcher_notices")
}
model ProvisionedDevice {
id String @id @default(uuid()) @db.Uuid
accountEmail String @map("account_email")
authUserId String? @map("auth_user_id") @db.Uuid
hostname String
tailscaleHostname String @map("tailscale_hostname")
sshUser String? @map("ssh_user")
workspaceRoot String? @map("workspace_root")
usageType String @default("personal") @map("usage_type")
os String?
arch String?
kernel String?
distro String?
device Json?
installedAt DateTime @default(now()) @map("installed_at") @db.Timestamptz(3)
lastProvisionedAt DateTime @default(now()) @map("last_provisioned_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@unique([accountEmail, tailscaleHostname])
@@index([accountEmail])
@@index([tailscaleHostname])
@@map("provisioned_devices")
}
model LauncherSshKey {
id String @id @default(uuid()) @db.Uuid
accountEmail String @map("account_email")
authUserId String? @map("auth_user_id") @db.Uuid
publicKey String @map("public_key")
publicKeyHash String @map("public_key_hash")
keyType String? @map("key_type")
label String?
lastProvisionedAt DateTime @default(now()) @map("last_provisioned_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@unique([accountEmail, publicKeyHash])
@@index([accountEmail])
@@map("launcher_ssh_keys")
}
model LauncherCommonSetting {
id String @id @default(uuid()) @db.Uuid
key String @unique
name String
type String
value String
description String?
isActive Boolean @default(true) @map("is_active")
isSecret Boolean @default(false) @map("is_secret")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([type, isActive])
@@map("launcher_common_settings")
}
@@ -0,0 +1,136 @@
-- CreateEnum
CREATE TYPE "AppStatus" AS ENUM ('ACTIVE', 'READY', 'DISABLED');
-- CreateEnum
CREATE TYPE "ComponentType" AS ENUM ('ELECTRON', 'CONTAINER', 'STATIC_WEB', 'DATABASE', 'MIGRATION');
-- CreateEnum
CREATE TYPE "JobStatus" AS ENUM ('PENDING', 'RUNNING', 'SUCCEEDED', 'FAILED', 'CANCELED');
-- CreateTable
CREATE TABLE "apps" (
"id" UUID NOT NULL,
"key" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"namespace" TEXT,
"status" "AppStatus" NOT NULL DEFAULT 'READY',
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "apps_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "app_components" (
"id" UUID NOT NULL,
"app_id" UUID NOT NULL,
"name" TEXT NOT NULL,
"type" "ComponentType" NOT NULL,
"repository" TEXT,
"branch" TEXT,
"build_target" TEXT,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "app_components_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "build_jobs" (
"id" UUID NOT NULL,
"component_id" UUID NOT NULL,
"branch" TEXT NOT NULL,
"commit_sha" TEXT,
"artifact" TEXT,
"image" TEXT,
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
"logs" TEXT,
"started_at" TIMESTAMPTZ(3),
"finished_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "build_jobs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "app_releases" (
"id" UUID NOT NULL,
"app_id" UUID NOT NULL,
"version" TEXT NOT NULL,
"branch" TEXT NOT NULL,
"commit_sha" TEXT,
"artifact" TEXT,
"image_tags" JSONB,
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "app_releases_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "deployment_jobs" (
"id" UUID NOT NULL,
"app_id" UUID NOT NULL,
"release_id" UUID,
"environment" TEXT NOT NULL,
"namespace" TEXT NOT NULL,
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
"requested_by" TEXT,
"logs" TEXT,
"started_at" TIMESTAMPTZ(3),
"finished_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "deployment_jobs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "notices" (
"id" UUID NOT NULL,
"scope" TEXT NOT NULL,
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
"audience" TEXT NOT NULL,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"starts_at" TIMESTAMPTZ(3),
"ends_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "notices_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "apps_key_key" ON "apps"("key");
-- CreateIndex
CREATE UNIQUE INDEX "app_components_app_id_name_key" ON "app_components"("app_id", "name");
-- CreateIndex
CREATE INDEX "build_jobs_component_id_created_at_idx" ON "build_jobs"("component_id", "created_at");
-- CreateIndex
CREATE UNIQUE INDEX "app_releases_app_id_version_key" ON "app_releases"("app_id", "version");
-- CreateIndex
CREATE INDEX "deployment_jobs_app_id_created_at_idx" ON "deployment_jobs"("app_id", "created_at");
-- CreateIndex
CREATE INDEX "notices_scope_is_active_idx" ON "notices"("scope", "is_active");
-- AddForeignKey
ALTER TABLE "app_components" ADD CONSTRAINT "app_components_app_id_fkey" FOREIGN KEY ("app_id") REFERENCES "apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "build_jobs" ADD CONSTRAINT "build_jobs_component_id_fkey" FOREIGN KEY ("component_id") REFERENCES "app_components"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "app_releases" ADD CONSTRAINT "app_releases_app_id_fkey" FOREIGN KEY ("app_id") REFERENCES "apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "deployment_jobs" ADD CONSTRAINT "deployment_jobs_app_id_fkey" FOREIGN KEY ("app_id") REFERENCES "apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "deployment_jobs" ADD CONSTRAINT "deployment_jobs_release_id_fkey" FOREIGN KEY ("release_id") REFERENCES "app_releases"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,320 @@
-- CreateEnum
CREATE TYPE "AdminRole" AS ENUM ('OWNER', 'ADMIN', 'OPERATOR', 'VIEWER');
-- CreateEnum
CREATE TYPE "NoticeStatus" AS ENUM ('DRAFT', 'SCHEDULED', 'PUBLISHED', 'ARCHIVED');
-- CreateEnum
CREATE TYPE "LicenseCode" AS ENUM ('FREE', 'EDUCATION', 'PROFESSIONAL');
-- CreateEnum
CREATE TYPE "RuntimeAction" AS ENUM ('RESTART', 'SCALE', 'LOGS', 'EVENTS', 'ROLLBACK', 'STOP');
-- DropIndex
DROP INDEX "notices_scope_is_active_idx";
-- AlterTable
ALTER TABLE "notices" ADD COLUMN "priority" INTEGER NOT NULL DEFAULT 0,
ADD COLUMN "status" "NoticeStatus" NOT NULL DEFAULT 'DRAFT';
-- CreateTable
CREATE TABLE "admins" (
"id" UUID NOT NULL,
"email" TEXT NOT NULL,
"password_hash" TEXT NOT NULL,
"name" TEXT NOT NULL,
"role" "AdminRole" NOT NULL DEFAULT 'ADMIN',
"is_active" BOOLEAN NOT NULL DEFAULT true,
"last_login_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "admins_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "admin_sessions" (
"id" UUID NOT NULL,
"admin_id" UUID NOT NULL,
"token_hash" TEXT NOT NULL,
"ip_address" TEXT,
"user_agent" TEXT,
"expires_at" TIMESTAMPTZ(3) NOT NULL,
"revoked_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "admin_sessions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "admin_audit_logs" (
"id" UUID NOT NULL,
"admin_id" UUID,
"action" TEXT NOT NULL,
"resource" TEXT NOT NULL,
"resource_id" TEXT,
"metadata" JSONB,
"ip_address" TEXT,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "admin_audit_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "launcher_versions" (
"id" UUID NOT NULL,
"version" TEXT NOT NULL,
"minimum_version" TEXT,
"artifact" TEXT NOT NULL,
"download_url" TEXT NOT NULL,
"rollout_percent" INTEGER NOT NULL DEFAULT 0,
"release_notes" TEXT,
"is_active" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "launcher_versions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "launcher_common_settings" (
"id" UUID NOT NULL,
"key" TEXT NOT NULL,
"name" TEXT NOT NULL,
"type" TEXT NOT NULL,
"value" TEXT NOT NULL,
"description" TEXT,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"is_secret" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "launcher_common_settings_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "launcher_app_links" (
"id" UUID NOT NULL,
"app_id" UUID NOT NULL,
"release_id" UUID,
"display_order" INTEGER NOT NULL DEFAULT 0,
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
"launch_url" TEXT,
"artifact" TEXT,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "launcher_app_links_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "license_plan_snapshots" (
"id" UUID NOT NULL,
"code" "LicenseCode" NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"default_duration_days" INTEGER,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"assigned_count" INTEGER NOT NULL DEFAULT 0,
"active_user_count" INTEGER NOT NULL DEFAULT 0,
"expiring_soon_count" INTEGER NOT NULL DEFAULT 0,
"synced_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "license_plan_snapshots_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_license_snapshots" (
"id" UUID NOT NULL,
"license_id" UUID NOT NULL,
"auth_user_id" TEXT,
"email" TEXT NOT NULL,
"name" TEXT,
"starts_at" TIMESTAMPTZ(3),
"expires_at" TIMESTAMPTZ(3),
"assigned_by" TEXT,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"synced_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_license_snapshots_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "runtime_control_jobs" (
"id" UUID NOT NULL,
"namespace" TEXT NOT NULL,
"kind" TEXT NOT NULL,
"name" TEXT NOT NULL,
"action" "RuntimeAction" NOT NULL,
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
"requested_by" TEXT,
"request" JSONB,
"result" JSONB,
"logs" TEXT,
"started_at" TIMESTAMPTZ(3),
"finished_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "runtime_control_jobs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "platform_connections" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"endpoint" TEXT NOT NULL,
"role" TEXT NOT NULL,
"status" TEXT NOT NULL,
"last_sync" TIMESTAMPTZ(3),
"metadata" JSONB,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "platform_connections_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "agent_devices" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"account" TEXT,
"status" TEXT NOT NULL,
"workspace" TEXT,
"ssh_key" TEXT,
"last_seen" TIMESTAMPTZ(3),
"metadata" JSONB,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "agent_devices_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "tailscale_nodes" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"ip" TEXT NOT NULL,
"role" TEXT,
"user" TEXT,
"routes" TEXT,
"status" TEXT NOT NULL,
"last_seen" TIMESTAMPTZ(3),
"metadata" JSONB,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "tailscale_nodes_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "tailscale_settings" (
"id" UUID NOT NULL,
"key" TEXT NOT NULL,
"name" TEXT NOT NULL,
"value" TEXT NOT NULL,
"description" TEXT,
"status" TEXT NOT NULL,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "tailscale_settings_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "admins_email_key" ON "admins"("email");
-- CreateIndex
CREATE INDEX "admins_is_active_role_idx" ON "admins"("is_active", "role");
-- CreateIndex
CREATE UNIQUE INDEX "admin_sessions_token_hash_key" ON "admin_sessions"("token_hash");
-- CreateIndex
CREATE INDEX "admin_sessions_admin_id_expires_at_idx" ON "admin_sessions"("admin_id", "expires_at");
-- CreateIndex
CREATE INDEX "admin_audit_logs_resource_created_at_idx" ON "admin_audit_logs"("resource", "created_at");
-- CreateIndex
CREATE INDEX "admin_audit_logs_admin_id_created_at_idx" ON "admin_audit_logs"("admin_id", "created_at");
-- CreateIndex
CREATE UNIQUE INDEX "launcher_versions_version_key" ON "launcher_versions"("version");
-- CreateIndex
CREATE INDEX "launcher_versions_is_active_created_at_idx" ON "launcher_versions"("is_active", "created_at");
-- CreateIndex
CREATE UNIQUE INDEX "launcher_common_settings_key_key" ON "launcher_common_settings"("key");
-- CreateIndex
CREATE INDEX "launcher_common_settings_type_is_active_idx" ON "launcher_common_settings"("type", "is_active");
-- CreateIndex
CREATE UNIQUE INDEX "launcher_app_links_app_id_key" ON "launcher_app_links"("app_id");
-- CreateIndex
CREATE INDEX "launcher_app_links_display_order_is_enabled_idx" ON "launcher_app_links"("display_order", "is_enabled");
-- CreateIndex
CREATE UNIQUE INDEX "license_plan_snapshots_code_key" ON "license_plan_snapshots"("code");
-- CreateIndex
CREATE INDEX "user_license_snapshots_email_idx" ON "user_license_snapshots"("email");
-- CreateIndex
CREATE INDEX "user_license_snapshots_license_id_is_active_idx" ON "user_license_snapshots"("license_id", "is_active");
-- CreateIndex
CREATE INDEX "runtime_control_jobs_namespace_kind_name_idx" ON "runtime_control_jobs"("namespace", "kind", "name");
-- CreateIndex
CREATE INDEX "runtime_control_jobs_created_at_idx" ON "runtime_control_jobs"("created_at");
-- CreateIndex
CREATE UNIQUE INDEX "platform_connections_name_key" ON "platform_connections"("name");
-- CreateIndex
CREATE INDEX "platform_connections_status_idx" ON "platform_connections"("status");
-- CreateIndex
CREATE UNIQUE INDEX "agent_devices_name_key" ON "agent_devices"("name");
-- CreateIndex
CREATE INDEX "agent_devices_status_last_seen_idx" ON "agent_devices"("status", "last_seen");
-- CreateIndex
CREATE UNIQUE INDEX "tailscale_nodes_name_key" ON "tailscale_nodes"("name");
-- CreateIndex
CREATE INDEX "tailscale_nodes_status_last_seen_idx" ON "tailscale_nodes"("status", "last_seen");
-- CreateIndex
CREATE UNIQUE INDEX "tailscale_settings_key_key" ON "tailscale_settings"("key");
-- CreateIndex
CREATE INDEX "tailscale_settings_status_idx" ON "tailscale_settings"("status");
-- CreateIndex
CREATE INDEX "notices_scope_status_idx" ON "notices"("scope", "status");
-- CreateIndex
CREATE INDEX "notices_is_active_priority_idx" ON "notices"("is_active", "priority");
-- AddForeignKey
ALTER TABLE "admin_sessions" ADD CONSTRAINT "admin_sessions_admin_id_fkey" FOREIGN KEY ("admin_id") REFERENCES "admins"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "admin_audit_logs" ADD CONSTRAINT "admin_audit_logs_admin_id_fkey" FOREIGN KEY ("admin_id") REFERENCES "admins"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "launcher_app_links" ADD CONSTRAINT "launcher_app_links_app_id_fkey" FOREIGN KEY ("app_id") REFERENCES "apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "launcher_app_links" ADD CONSTRAINT "launcher_app_links_release_id_fkey" FOREIGN KEY ("release_id") REFERENCES "app_releases"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_license_snapshots" ADD CONSTRAINT "user_license_snapshots_license_id_fkey" FOREIGN KEY ("license_id") REFERENCES "license_plan_snapshots"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1 @@
DROP TABLE IF EXISTS "agent_devices";
@@ -0,0 +1 @@
DROP TABLE IF EXISTS "notices";
@@ -0,0 +1,200 @@
COMMENT ON TABLE "admins" IS '스튜디오 어드민 콘솔 관리자 계정';
COMMENT ON COLUMN "admins"."id" IS '관리자 고유 ID';
COMMENT ON COLUMN "admins"."email" IS '관리자 로그인 이메일';
COMMENT ON COLUMN "admins"."password_hash" IS '관리자 비밀번호 해시';
COMMENT ON COLUMN "admins"."name" IS '관리자 표시 이름';
COMMENT ON COLUMN "admins"."role" IS '관리자 권한 역할';
COMMENT ON COLUMN "admins"."is_active" IS '관리자 계정 활성 여부';
COMMENT ON COLUMN "admins"."last_login_at" IS '마지막 로그인 시각';
COMMENT ON COLUMN "admins"."created_at" IS '생성 시각';
COMMENT ON COLUMN "admins"."updated_at" IS '수정 시각';
COMMENT ON TABLE "admin_sessions" IS '스튜디오 어드민 콘솔 관리자 로그인 세션';
COMMENT ON COLUMN "admin_sessions"."id" IS '관리자 세션 고유 ID';
COMMENT ON COLUMN "admin_sessions"."admin_id" IS '관리자 ID';
COMMENT ON COLUMN "admin_sessions"."token_hash" IS '세션 토큰 해시';
COMMENT ON COLUMN "admin_sessions"."ip_address" IS '접속 IP 주소';
COMMENT ON COLUMN "admin_sessions"."user_agent" IS '접속 브라우저 또는 클라이언트 정보';
COMMENT ON COLUMN "admin_sessions"."expires_at" IS '세션 만료 시각';
COMMENT ON COLUMN "admin_sessions"."revoked_at" IS '세션 폐기 시각';
COMMENT ON COLUMN "admin_sessions"."created_at" IS '생성 시각';
COMMENT ON TABLE "admin_audit_logs" IS '스튜디오 어드민 콘솔 관리자 작업 감사 로그';
COMMENT ON COLUMN "admin_audit_logs"."id" IS '감사 로그 고유 ID';
COMMENT ON COLUMN "admin_audit_logs"."admin_id" IS '작업을 수행한 관리자 ID';
COMMENT ON COLUMN "admin_audit_logs"."action" IS '수행 작업 코드';
COMMENT ON COLUMN "admin_audit_logs"."resource" IS '작업 대상 리소스 유형';
COMMENT ON COLUMN "admin_audit_logs"."resource_id" IS '작업 대상 리소스 ID';
COMMENT ON COLUMN "admin_audit_logs"."metadata" IS '작업 상세 메타데이터';
COMMENT ON COLUMN "admin_audit_logs"."ip_address" IS '작업 요청 IP 주소';
COMMENT ON COLUMN "admin_audit_logs"."created_at" IS '생성 시각';
COMMENT ON TABLE "apps" IS '스튜디오에서 관리하는 애플리케이션 그룹';
COMMENT ON COLUMN "apps"."id" IS '애플리케이션 고유 ID';
COMMENT ON COLUMN "apps"."key" IS '애플리케이션 식별 키';
COMMENT ON COLUMN "apps"."name" IS '애플리케이션 표시 이름';
COMMENT ON COLUMN "apps"."description" IS '애플리케이션 설명';
COMMENT ON COLUMN "apps"."namespace" IS '배포 대상 네임스페이스';
COMMENT ON COLUMN "apps"."status" IS '애플리케이션 관리 상태';
COMMENT ON COLUMN "apps"."created_at" IS '생성 시각';
COMMENT ON COLUMN "apps"."updated_at" IS '수정 시각';
COMMENT ON TABLE "app_components" IS '애플리케이션을 구성하는 프론트엔드, 백엔드, DB, 마이그레이션 등 구성요소';
COMMENT ON COLUMN "app_components"."id" IS '애플리케이션 구성요소 고유 ID';
COMMENT ON COLUMN "app_components"."app_id" IS '소속 애플리케이션 ID';
COMMENT ON COLUMN "app_components"."name" IS '구성요소 이름';
COMMENT ON COLUMN "app_components"."type" IS '구성요소 유형';
COMMENT ON COLUMN "app_components"."repository" IS '소스 저장소 주소 또는 식별자';
COMMENT ON COLUMN "app_components"."branch" IS '기본 브랜치';
COMMENT ON COLUMN "app_components"."build_target" IS '빌드 대상 또는 산출물 구분';
COMMENT ON COLUMN "app_components"."created_at" IS '생성 시각';
COMMENT ON COLUMN "app_components"."updated_at" IS '수정 시각';
COMMENT ON TABLE "build_jobs" IS '애플리케이션 구성요소 빌드 작업 이력';
COMMENT ON COLUMN "build_jobs"."id" IS '빌드 작업 고유 ID';
COMMENT ON COLUMN "build_jobs"."component_id" IS '빌드 대상 구성요소 ID';
COMMENT ON COLUMN "build_jobs"."branch" IS '빌드 대상 브랜치';
COMMENT ON COLUMN "build_jobs"."commit_sha" IS '빌드 대상 커밋 해시';
COMMENT ON COLUMN "build_jobs"."artifact" IS '빌드 산출물 위치 또는 식별자';
COMMENT ON COLUMN "build_jobs"."image" IS '빌드된 컨테이너 이미지 태그';
COMMENT ON COLUMN "build_jobs"."status" IS '빌드 작업 상태';
COMMENT ON COLUMN "build_jobs"."logs" IS '빌드 로그';
COMMENT ON COLUMN "build_jobs"."started_at" IS '빌드 시작 시각';
COMMENT ON COLUMN "build_jobs"."finished_at" IS '빌드 종료 시각';
COMMENT ON COLUMN "build_jobs"."created_at" IS '생성 시각';
COMMENT ON TABLE "app_releases" IS '애플리케이션 릴리즈 버전과 산출물 정보';
COMMENT ON COLUMN "app_releases"."id" IS '애플리케이션 릴리즈 고유 ID';
COMMENT ON COLUMN "app_releases"."app_id" IS '릴리즈 대상 애플리케이션 ID';
COMMENT ON COLUMN "app_releases"."version" IS '릴리즈 버전';
COMMENT ON COLUMN "app_releases"."branch" IS '릴리즈 기준 브랜치';
COMMENT ON COLUMN "app_releases"."commit_sha" IS '릴리즈 기준 커밋 해시';
COMMENT ON COLUMN "app_releases"."artifact" IS '릴리즈 산출물 위치 또는 식별자';
COMMENT ON COLUMN "app_releases"."image_tags" IS '릴리즈에 포함된 이미지 태그 목록';
COMMENT ON COLUMN "app_releases"."status" IS '릴리즈 생성 상태';
COMMENT ON COLUMN "app_releases"."created_at" IS '생성 시각';
COMMENT ON COLUMN "app_releases"."updated_at" IS '수정 시각';
COMMENT ON TABLE "deployment_jobs" IS '애플리케이션 릴리즈 배포 작업 이력';
COMMENT ON COLUMN "deployment_jobs"."id" IS '배포 작업 고유 ID';
COMMENT ON COLUMN "deployment_jobs"."app_id" IS '배포 대상 애플리케이션 ID';
COMMENT ON COLUMN "deployment_jobs"."release_id" IS '배포 대상 릴리즈 ID';
COMMENT ON COLUMN "deployment_jobs"."environment" IS '배포 환경';
COMMENT ON COLUMN "deployment_jobs"."namespace" IS '배포 대상 네임스페이스';
COMMENT ON COLUMN "deployment_jobs"."status" IS '배포 작업 상태';
COMMENT ON COLUMN "deployment_jobs"."requested_by" IS '배포 요청자';
COMMENT ON COLUMN "deployment_jobs"."logs" IS '배포 로그';
COMMENT ON COLUMN "deployment_jobs"."started_at" IS '배포 시작 시각';
COMMENT ON COLUMN "deployment_jobs"."finished_at" IS '배포 종료 시각';
COMMENT ON COLUMN "deployment_jobs"."created_at" IS '생성 시각';
COMMENT ON TABLE "launcher_versions" IS '스튜디오 메인 런처 버전 관리 정보';
COMMENT ON COLUMN "launcher_versions"."id" IS '런처 버전 고유 ID';
COMMENT ON COLUMN "launcher_versions"."version" IS '런처 버전';
COMMENT ON COLUMN "launcher_versions"."minimum_version" IS '최소 지원 런처 버전';
COMMENT ON COLUMN "launcher_versions"."artifact" IS '런처 빌드 산출물 위치 또는 식별자';
COMMENT ON COLUMN "launcher_versions"."download_url" IS '런처 다운로드 URL';
COMMENT ON COLUMN "launcher_versions"."rollout_percent" IS '점진 배포 비율';
COMMENT ON COLUMN "launcher_versions"."release_notes" IS '런처 릴리즈 노트';
COMMENT ON COLUMN "launcher_versions"."is_active" IS '활성 런처 버전 여부';
COMMENT ON COLUMN "launcher_versions"."created_at" IS '생성 시각';
COMMENT ON COLUMN "launcher_versions"."updated_at" IS '수정 시각';
COMMENT ON TABLE "launcher_common_settings" IS '런처 공통 연결 설정과 플랫폼 연동 설정';
COMMENT ON COLUMN "launcher_common_settings"."id" IS '공통 설정 고유 ID';
COMMENT ON COLUMN "launcher_common_settings"."key" IS '공통 설정 식별 키';
COMMENT ON COLUMN "launcher_common_settings"."name" IS '공통 설정 표시 이름';
COMMENT ON COLUMN "launcher_common_settings"."type" IS '공통 설정 유형';
COMMENT ON COLUMN "launcher_common_settings"."value" IS '공통 설정 값';
COMMENT ON COLUMN "launcher_common_settings"."description" IS '공통 설정 설명';
COMMENT ON COLUMN "launcher_common_settings"."is_active" IS '공통 설정 활성 여부';
COMMENT ON COLUMN "launcher_common_settings"."is_secret" IS '민감 정보 여부';
COMMENT ON COLUMN "launcher_common_settings"."created_at" IS '생성 시각';
COMMENT ON COLUMN "launcher_common_settings"."updated_at" IS '수정 시각';
COMMENT ON TABLE "launcher_app_links" IS '런처에 노출할 애플리케이션 연결 및 실행 설정';
COMMENT ON COLUMN "launcher_app_links"."id" IS '런처 앱 연결 고유 ID';
COMMENT ON COLUMN "launcher_app_links"."app_id" IS '연결된 애플리케이션 ID';
COMMENT ON COLUMN "launcher_app_links"."release_id" IS '런처 실행에 사용할 릴리즈 ID';
COMMENT ON COLUMN "launcher_app_links"."display_order" IS '런처 노출 순서';
COMMENT ON COLUMN "launcher_app_links"."is_enabled" IS '런처 노출 및 실행 활성 여부';
COMMENT ON COLUMN "launcher_app_links"."launch_url" IS '런처 실행 URL';
COMMENT ON COLUMN "launcher_app_links"."artifact" IS '런처가 내려받을 실행 산출물 위치 또는 식별자';
COMMENT ON COLUMN "launcher_app_links"."created_at" IS '생성 시각';
COMMENT ON COLUMN "launcher_app_links"."updated_at" IS '수정 시각';
COMMENT ON TABLE "license_plan_snapshots" IS '공통 인증 DB에서 동기화한 라이센스 플랜 스냅샷';
COMMENT ON COLUMN "license_plan_snapshots"."id" IS '라이센스 플랜 스냅샷 고유 ID';
COMMENT ON COLUMN "license_plan_snapshots"."code" IS '라이센스 플랜 코드';
COMMENT ON COLUMN "license_plan_snapshots"."name" IS '라이센스 플랜 표시 이름';
COMMENT ON COLUMN "license_plan_snapshots"."description" IS '라이센스 플랜 설명';
COMMENT ON COLUMN "license_plan_snapshots"."default_duration_days" IS '기본 라이센스 기간 일수';
COMMENT ON COLUMN "license_plan_snapshots"."is_active" IS '라이센스 플랜 활성 여부';
COMMENT ON COLUMN "license_plan_snapshots"."assigned_count" IS '전체 할당 수';
COMMENT ON COLUMN "license_plan_snapshots"."active_user_count" IS '활성 사용자 수';
COMMENT ON COLUMN "license_plan_snapshots"."expiring_soon_count" IS '만료 예정 사용자 수';
COMMENT ON COLUMN "license_plan_snapshots"."synced_at" IS '원본 DB 동기화 시각';
COMMENT ON COLUMN "license_plan_snapshots"."created_at" IS '생성 시각';
COMMENT ON COLUMN "license_plan_snapshots"."updated_at" IS '수정 시각';
COMMENT ON TABLE "user_license_snapshots" IS '공통 인증 DB에서 동기화한 사용자 라이센스 할당 스냅샷';
COMMENT ON COLUMN "user_license_snapshots"."id" IS '사용자 라이센스 스냅샷 고유 ID';
COMMENT ON COLUMN "user_license_snapshots"."license_id" IS '라이센스 플랜 스냅샷 ID';
COMMENT ON COLUMN "user_license_snapshots"."auth_user_id" IS '공통 인증 사용자 ID';
COMMENT ON COLUMN "user_license_snapshots"."email" IS '사용자 이메일';
COMMENT ON COLUMN "user_license_snapshots"."name" IS '사용자 이름';
COMMENT ON COLUMN "user_license_snapshots"."starts_at" IS '라이센스 시작 시각';
COMMENT ON COLUMN "user_license_snapshots"."expires_at" IS '라이센스 만료 시각';
COMMENT ON COLUMN "user_license_snapshots"."assigned_by" IS '라이센스 할당자';
COMMENT ON COLUMN "user_license_snapshots"."is_active" IS '사용자 라이센스 활성 여부';
COMMENT ON COLUMN "user_license_snapshots"."synced_at" IS '원본 DB 동기화 시각';
COMMENT ON COLUMN "user_license_snapshots"."created_at" IS '생성 시각';
COMMENT ON TABLE "runtime_control_jobs" IS '플랫폼 런타임 리소스 제어 작업 이력';
COMMENT ON COLUMN "runtime_control_jobs"."id" IS '런타임 제어 작업 고유 ID';
COMMENT ON COLUMN "runtime_control_jobs"."namespace" IS '제어 대상 네임스페이스';
COMMENT ON COLUMN "runtime_control_jobs"."kind" IS '제어 대상 리소스 종류';
COMMENT ON COLUMN "runtime_control_jobs"."name" IS '제어 대상 리소스 이름';
COMMENT ON COLUMN "runtime_control_jobs"."action" IS '제어 작업 유형';
COMMENT ON COLUMN "runtime_control_jobs"."status" IS '제어 작업 상태';
COMMENT ON COLUMN "runtime_control_jobs"."requested_by" IS '제어 요청자';
COMMENT ON COLUMN "runtime_control_jobs"."request" IS '제어 요청 페이로드';
COMMENT ON COLUMN "runtime_control_jobs"."result" IS '제어 결과 페이로드';
COMMENT ON COLUMN "runtime_control_jobs"."logs" IS '제어 작업 로그';
COMMENT ON COLUMN "runtime_control_jobs"."started_at" IS '제어 시작 시각';
COMMENT ON COLUMN "runtime_control_jobs"."finished_at" IS '제어 종료 시각';
COMMENT ON COLUMN "runtime_control_jobs"."created_at" IS '생성 시각';
COMMENT ON TABLE "platform_connections" IS '플랫폼 게이트웨이 및 외부 플랫폼 연결 상태';
COMMENT ON COLUMN "platform_connections"."id" IS '플랫폼 연결 고유 ID';
COMMENT ON COLUMN "platform_connections"."name" IS '플랫폼 연결 이름';
COMMENT ON COLUMN "platform_connections"."endpoint" IS '플랫폼 연결 엔드포인트';
COMMENT ON COLUMN "platform_connections"."role" IS '플랫폼 연결 역할';
COMMENT ON COLUMN "platform_connections"."status" IS '플랫폼 연결 상태';
COMMENT ON COLUMN "platform_connections"."last_sync" IS '마지막 동기화 시각';
COMMENT ON COLUMN "platform_connections"."metadata" IS '플랫폼 연결 메타데이터';
COMMENT ON COLUMN "platform_connections"."created_at" IS '생성 시각';
COMMENT ON COLUMN "platform_connections"."updated_at" IS '수정 시각';
COMMENT ON TABLE "tailscale_nodes" IS '테일스케일 노드 상태 스냅샷';
COMMENT ON COLUMN "tailscale_nodes"."id" IS '테일스케일 노드 고유 ID';
COMMENT ON COLUMN "tailscale_nodes"."name" IS '테일스케일 노드 이름';
COMMENT ON COLUMN "tailscale_nodes"."ip" IS '테일스케일 노드 IP';
COMMENT ON COLUMN "tailscale_nodes"."role" IS '테일스케일 노드 역할';
COMMENT ON COLUMN "tailscale_nodes"."user" IS '테일스케일 사용자';
COMMENT ON COLUMN "tailscale_nodes"."routes" IS '광고 또는 허용된 라우트';
COMMENT ON COLUMN "tailscale_nodes"."status" IS '테일스케일 노드 상태';
COMMENT ON COLUMN "tailscale_nodes"."last_seen" IS '마지막 확인 시각';
COMMENT ON COLUMN "tailscale_nodes"."metadata" IS '테일스케일 노드 메타데이터';
COMMENT ON COLUMN "tailscale_nodes"."created_at" IS '생성 시각';
COMMENT ON COLUMN "tailscale_nodes"."updated_at" IS '수정 시각';
COMMENT ON TABLE "tailscale_settings" IS '테일스케일 연동 설정';
COMMENT ON COLUMN "tailscale_settings"."id" IS '테일스케일 설정 고유 ID';
COMMENT ON COLUMN "tailscale_settings"."key" IS '테일스케일 설정 식별 키';
COMMENT ON COLUMN "tailscale_settings"."name" IS '테일스케일 설정 표시 이름';
COMMENT ON COLUMN "tailscale_settings"."value" IS '테일스케일 설정 값';
COMMENT ON COLUMN "tailscale_settings"."description" IS '테일스케일 설정 설명';
COMMENT ON COLUMN "tailscale_settings"."status" IS '테일스케일 설정 상태';
COMMENT ON COLUMN "tailscale_settings"."created_at" IS '생성 시각';
COMMENT ON COLUMN "tailscale_settings"."updated_at" IS '수정 시각';
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS "user_license_snapshots";
DROP TABLE IF EXISTS "license_plan_snapshots";
@@ -0,0 +1,8 @@
ALTER TABLE "launcher_versions"
ADD COLUMN "repository" TEXT,
ADD COLUMN "branch" TEXT,
ADD COLUMN "commit_sha" TEXT;
COMMENT ON COLUMN "launcher_versions"."repository" IS '런처 버전을 생성한 Gitea 저장소';
COMMENT ON COLUMN "launcher_versions"."branch" IS '런처 버전을 생성한 Gitea 브랜치';
COMMENT ON COLUMN "launcher_versions"."commit_sha" IS '런처 버전을 생성한 Gitea 커밋 해시';
@@ -0,0 +1,36 @@
CREATE TABLE "admin_ssh_keys" (
"id" UUID NOT NULL,
"admin_id" UUID NOT NULL,
"label" TEXT,
"key_type" TEXT NOT NULL,
"public_key" TEXT NOT NULL,
"public_key_hash" TEXT NOT NULL,
"fingerprint" TEXT NOT NULL,
"gitea_username" TEXT,
"gitea_key_id" INTEGER,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "admin_ssh_keys_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "admin_ssh_keys_public_key_hash_key" ON "admin_ssh_keys"("public_key_hash");
CREATE INDEX "admin_ssh_keys_admin_id_created_at_idx" ON "admin_ssh_keys"("admin_id", "created_at");
CREATE INDEX "admin_ssh_keys_gitea_username_idx" ON "admin_ssh_keys"("gitea_username");
ALTER TABLE "admin_ssh_keys"
ADD CONSTRAINT "admin_ssh_keys_admin_id_fkey"
FOREIGN KEY ("admin_id") REFERENCES "admins"("id") ON DELETE CASCADE ON UPDATE CASCADE;
COMMENT ON TABLE "admin_ssh_keys" IS '관리자 계정별 Gitea SSH 공개키 등록 정보';
COMMENT ON COLUMN "admin_ssh_keys"."id" IS '관리자 SSH 키 고유 ID';
COMMENT ON COLUMN "admin_ssh_keys"."admin_id" IS 'SSH 키를 소유한 관리자 ID';
COMMENT ON COLUMN "admin_ssh_keys"."label" IS 'SSH 키 표시명';
COMMENT ON COLUMN "admin_ssh_keys"."key_type" IS 'SSH 공개키 타입';
COMMENT ON COLUMN "admin_ssh_keys"."public_key" IS 'SSH 공개키 원문';
COMMENT ON COLUMN "admin_ssh_keys"."public_key_hash" IS 'SSH 공개키 중복 방지용 해시';
COMMENT ON COLUMN "admin_ssh_keys"."fingerprint" IS 'SSH 공개키 SHA256 fingerprint';
COMMENT ON COLUMN "admin_ssh_keys"."gitea_username" IS '키를 등록할 Gitea 사용자명';
COMMENT ON COLUMN "admin_ssh_keys"."gitea_key_id" IS 'Gitea에 등록된 SSH key ID';
COMMENT ON COLUMN "admin_ssh_keys"."created_at" IS '생성 시각';
COMMENT ON COLUMN "admin_ssh_keys"."updated_at" IS '수정 시각';
@@ -0,0 +1,65 @@
-- CreateTable
CREATE TABLE "runtime_env_profiles" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"app_id" UUID NOT NULL,
"name" TEXT NOT NULL,
"environment" TEXT NOT NULL,
"namespace" TEXT NOT NULL,
"is_active" BOOLEAN NOT NULL DEFAULT true,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "runtime_env_profiles_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "runtime_env_variables" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"profile_id" UUID NOT NULL,
"component_id" UUID,
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
"is_secret" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "runtime_env_variables_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "runtime_env_profiles_app_id_environment_name_key" ON "runtime_env_profiles"("app_id", "environment", "name");
-- CreateIndex
CREATE INDEX "runtime_env_profiles_app_id_environment_idx" ON "runtime_env_profiles"("app_id", "environment");
-- CreateIndex
CREATE UNIQUE INDEX "runtime_env_variables_profile_id_component_id_key_key" ON "runtime_env_variables"("profile_id", "component_id", "key");
-- CreateIndex
CREATE INDEX "runtime_env_variables_profile_id_is_secret_idx" ON "runtime_env_variables"("profile_id", "is_secret");
-- CreateIndex
CREATE INDEX "runtime_env_variables_component_id_idx" ON "runtime_env_variables"("component_id");
-- AddForeignKey
ALTER TABLE "runtime_env_profiles" ADD CONSTRAINT "runtime_env_profiles_app_id_fkey" FOREIGN KEY ("app_id") REFERENCES "apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "runtime_env_variables" ADD CONSTRAINT "runtime_env_variables_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "runtime_env_profiles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "runtime_env_variables" ADD CONSTRAINT "runtime_env_variables_component_id_fkey" FOREIGN KEY ("component_id") REFERENCES "app_components"("id") ON DELETE SET NULL ON UPDATE CASCADE;
COMMENT ON TABLE "runtime_env_profiles" IS '앱별 런타임 환경 프로파일';
COMMENT ON COLUMN "runtime_env_profiles"."app_id" IS '앱 ID';
COMMENT ON COLUMN "runtime_env_profiles"."name" IS '프로파일명';
COMMENT ON COLUMN "runtime_env_profiles"."environment" IS '배포 환경';
COMMENT ON COLUMN "runtime_env_profiles"."namespace" IS '적용 namespace';
COMMENT ON COLUMN "runtime_env_profiles"."is_active" IS '활성 여부';
COMMENT ON TABLE "runtime_env_variables" IS '런타임 환경변수 및 시크릿';
COMMENT ON COLUMN "runtime_env_variables"."profile_id" IS '런타임 환경 프로파일 ID';
COMMENT ON COLUMN "runtime_env_variables"."component_id" IS '적용 구성요소 ID';
COMMENT ON COLUMN "runtime_env_variables"."key" IS '환경변수 키';
COMMENT ON COLUMN "runtime_env_variables"."value" IS '환경변수 값';
COMMENT ON COLUMN "runtime_env_variables"."is_secret" IS 'Secret 적용 여부';
@@ -0,0 +1 @@
ALTER TYPE "RuntimeAction" ADD VALUE IF NOT EXISTS 'MIGRATION';
@@ -0,0 +1,38 @@
CREATE TABLE "launcher_build_jobs" (
"id" UUID NOT NULL,
"target" TEXT NOT NULL DEFAULT 'launcher-client',
"repository" TEXT,
"branch" TEXT NOT NULL DEFAULT 'release',
"commit_sha" TEXT,
"version" TEXT NOT NULL,
"artifact" TEXT NOT NULL,
"upload_path" TEXT NOT NULL,
"download_url" TEXT NOT NULL,
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
"logs" TEXT,
"started_at" TIMESTAMPTZ(3),
"finished_at" TIMESTAMPTZ(3),
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "launcher_build_jobs_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "launcher_build_jobs_status_created_at_idx" ON "launcher_build_jobs"("status", "created_at");
COMMENT ON TABLE "launcher_build_jobs" IS '런처 설치 파일 빌드 작업 이력';
COMMENT ON COLUMN "launcher_build_jobs"."id" IS '런처 빌드 작업 고유 ID';
COMMENT ON COLUMN "launcher_build_jobs"."target" IS '빌드 대상';
COMMENT ON COLUMN "launcher_build_jobs"."repository" IS '빌드할 소스 저장소';
COMMENT ON COLUMN "launcher_build_jobs"."branch" IS '빌드 기준 브랜치';
COMMENT ON COLUMN "launcher_build_jobs"."commit_sha" IS '빌드 기준 커밋 해시';
COMMENT ON COLUMN "launcher_build_jobs"."version" IS '런처 버전';
COMMENT ON COLUMN "launcher_build_jobs"."artifact" IS '생성할 산출물 파일명';
COMMENT ON COLUMN "launcher_build_jobs"."upload_path" IS '산출물 업로드 디렉터리';
COMMENT ON COLUMN "launcher_build_jobs"."download_url" IS '런처 다운로드 URL';
COMMENT ON COLUMN "launcher_build_jobs"."status" IS '빌드 작업 상태';
COMMENT ON COLUMN "launcher_build_jobs"."logs" IS '빌드 작업 로그';
COMMENT ON COLUMN "launcher_build_jobs"."started_at" IS '빌드 시작 시각';
COMMENT ON COLUMN "launcher_build_jobs"."finished_at" IS '빌드 종료 시각';
COMMENT ON COLUMN "launcher_build_jobs"."created_at" IS '생성 시각';
COMMENT ON COLUMN "launcher_build_jobs"."updated_at" IS '수정 시각';
@@ -0,0 +1,23 @@
ALTER TABLE "app_components" ADD COLUMN "port" INTEGER;
ALTER TABLE "app_components" ADD COLUMN "target_port" INTEGER;
CREATE TABLE "runtime_ingress_rules" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"profile_id" UUID NOT NULL,
"component_id" UUID NOT NULL,
"host" TEXT NOT NULL,
"path" TEXT NOT NULL DEFAULT '/',
"service_port" INTEGER NOT NULL,
"tls_enabled" BOOLEAN NOT NULL DEFAULT false,
"tls_secret_name" TEXT,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "runtime_ingress_rules_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "runtime_ingress_rules_profile_id_host_path_key" ON "runtime_ingress_rules"("profile_id", "host", "path");
CREATE INDEX "runtime_ingress_rules_profile_id_idx" ON "runtime_ingress_rules"("profile_id");
CREATE INDEX "runtime_ingress_rules_component_id_idx" ON "runtime_ingress_rules"("component_id");
ALTER TABLE "runtime_ingress_rules" ADD CONSTRAINT "runtime_ingress_rules_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "runtime_env_profiles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "runtime_ingress_rules" ADD CONSTRAINT "runtime_ingress_rules_component_id_fkey" FOREIGN KEY ("component_id") REFERENCES "app_components"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,20 @@
ALTER TABLE "app_components"
ADD COLUMN "image_source" TEXT DEFAULT 'SOURCE',
ADD COLUMN "external_image" TEXT,
ADD COLUMN "workload_type" TEXT DEFAULT 'Deployment',
ADD COLUMN "replicas" INTEGER DEFAULT 1,
ADD COLUMN "service_type" TEXT DEFAULT 'ClusterIP',
ADD COLUMN "storage_enabled" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "storage_size" TEXT,
ADD COLUMN "storage_mount_path" TEXT,
ADD COLUMN "storage_retain" BOOLEAN NOT NULL DEFAULT true;
COMMENT ON COLUMN "app_components"."image_source" IS '구성요소 이미지 공급 방식. SOURCE, EXTERNAL, DATABASE 중 하나';
COMMENT ON COLUMN "app_components"."external_image" IS '외부 이미지 사용 시 이미지 주소';
COMMENT ON COLUMN "app_components"."workload_type" IS '실행 워크로드 유형. Deployment, StatefulSet, Job';
COMMENT ON COLUMN "app_components"."replicas" IS '기본 복제 수';
COMMENT ON COLUMN "app_components"."service_type" IS '서비스 유형. ClusterIP, NodePort 등';
COMMENT ON COLUMN "app_components"."storage_enabled" IS '스토리지 사용 여부';
COMMENT ON COLUMN "app_components"."storage_size" IS 'PVC 요청 용량';
COMMENT ON COLUMN "app_components"."storage_mount_path" IS '컨테이너 마운트 경로';
COMMENT ON COLUMN "app_components"."storage_retain" IS '워크로드 삭제 시 PVC 보존 여부';
@@ -0,0 +1,4 @@
ALTER TABLE "app_components"
ADD COLUMN "ports" JSONB;
COMMENT ON COLUMN "app_components"."ports" IS '구성요소 서비스/컨테이너 포트 목록. [{name, port, targetPort, protocol}]';
+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 = "postgresql"
+379
View File
@@ -0,0 +1,379 @@
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-3.0.x", "debian-openssl-1.1.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum AppStatus {
ACTIVE
READY
DISABLED
}
enum ComponentType {
ELECTRON
CONTAINER
STATIC_WEB
DATABASE
MIGRATION
}
enum JobStatus {
PENDING
RUNNING
SUCCEEDED
FAILED
CANCELED
}
enum AdminRole {
OWNER
ADMIN
OPERATOR
VIEWER
}
enum RuntimeAction {
MIGRATION
RESTART
SCALE
LOGS
EVENTS
ROLLBACK
STOP
}
model AdminRoleOption {
id String @id @default(uuid()) @db.Uuid
code AdminRole @unique
name String
description String?
sortOrder Int @default(0) @map("sort_order")
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([isActive, sortOrder])
@@map("admin_roles")
}
model Admin {
id String @id @default(uuid()) @db.Uuid
email String @unique
passwordHash String @map("password_hash")
name String
role AdminRole @default(ADMIN)
isActive Boolean @default(true) @map("is_active")
lastLoginAt DateTime? @map("last_login_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
sessions AdminSession[]
auditLogs AdminAuditLog[]
sshKeys AdminSshKey[]
@@index([isActive, role])
@@map("admins")
}
model AdminSshKey {
id String @id @default(uuid()) @db.Uuid
adminId String @map("admin_id") @db.Uuid
label String?
keyType String @map("key_type")
publicKey String @map("public_key")
publicKeyHash String @unique @map("public_key_hash")
fingerprint String
giteaUsername String? @map("gitea_username")
giteaKeyId Int? @map("gitea_key_id")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade)
@@index([adminId, createdAt])
@@index([giteaUsername])
@@map("admin_ssh_keys")
}
model AdminSession {
id String @id @default(uuid()) @db.Uuid
adminId String @map("admin_id") @db.Uuid
tokenHash String @unique @map("token_hash")
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
expiresAt DateTime @map("expires_at") @db.Timestamptz(3)
revokedAt DateTime? @map("revoked_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade)
@@index([adminId, expiresAt])
@@map("admin_sessions")
}
model AdminAuditLog {
id String @id @default(uuid()) @db.Uuid
adminId String? @map("admin_id") @db.Uuid
action String
resource String
resourceId String? @map("resource_id")
metadata Json?
ipAddress String? @map("ip_address")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
admin Admin? @relation(fields: [adminId], references: [id], onDelete: SetNull)
@@index([resource, createdAt])
@@index([adminId, createdAt])
@@map("admin_audit_logs")
}
model App {
id String @id @default(uuid()) @db.Uuid
key String @unique
name String
description String?
namespace String?
status AppStatus @default(READY)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
components AppComponent[]
releases AppRelease[]
deployments DeploymentJob[]
runtimeProfiles RuntimeEnvProfile[]
@@map("apps")
}
model AppComponent {
id String @id @default(uuid()) @db.Uuid
appId String @map("app_id") @db.Uuid
name String
type ComponentType
imageSource String? @default("SOURCE") @map("image_source")
externalImage String? @map("external_image")
workloadType String? @default("Deployment") @map("workload_type")
replicas Int? @default(1)
serviceType String? @default("ClusterIP") @map("service_type")
storageEnabled Boolean @default(false) @map("storage_enabled")
storageSize String? @map("storage_size")
storageMountPath String? @map("storage_mount_path")
storageRetain Boolean @default(true) @map("storage_retain")
repository String?
branch String?
buildTarget String? @map("build_target")
port Int?
targetPort Int? @map("target_port")
ports Json?
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
buildJobs BuildJob[]
runtimeVariables RuntimeEnvVariable[]
ingressRules RuntimeIngressRule[]
@@unique([appId, name])
@@map("app_components")
}
model BuildJob {
id String @id @default(uuid()) @db.Uuid
componentId String @map("component_id") @db.Uuid
branch String
commitSha String? @map("commit_sha")
artifact String?
image String?
status JobStatus @default(PENDING)
logs String?
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
component AppComponent @relation(fields: [componentId], references: [id], onDelete: Cascade)
@@index([componentId, createdAt])
@@map("build_jobs")
}
model AppRelease {
id String @id @default(uuid()) @db.Uuid
appId String @map("app_id") @db.Uuid
version String
branch String
commitSha String? @map("commit_sha")
artifact String?
imageTags Json? @map("image_tags")
status JobStatus @default(PENDING)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
deployments DeploymentJob[]
@@unique([appId, version])
@@map("app_releases")
}
model DeploymentJob {
id String @id @default(uuid()) @db.Uuid
appId String @map("app_id") @db.Uuid
releaseId String? @map("release_id") @db.Uuid
environment String
namespace String
status JobStatus @default(PENDING)
requestedBy String? @map("requested_by")
logs String?
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
release AppRelease? @relation(fields: [releaseId], references: [id], onDelete: SetNull)
@@index([appId, createdAt])
@@map("deployment_jobs")
}
model LauncherBuildJob {
id String @id @default(uuid()) @db.Uuid
target String @default("launcher-client")
repository String?
branch String @default("release")
commitSha String? @map("commit_sha")
version String
artifact String
uploadPath String @map("upload_path")
downloadUrl String @map("download_url")
status JobStatus @default(PENDING)
logs String?
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([status, createdAt])
@@map("launcher_build_jobs")
}
model RuntimeControlJob {
id String @id @default(uuid()) @db.Uuid
namespace String
kind String
name String
action RuntimeAction
status JobStatus @default(PENDING)
requestedBy String? @map("requested_by")
request Json?
result Json?
logs String?
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
@@index([namespace, kind, name])
@@index([createdAt])
@@map("runtime_control_jobs")
}
model RuntimeEnvProfile {
id String @id @default(uuid()) @db.Uuid
appId String @map("app_id") @db.Uuid
name String
environment String
namespace String
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
variables RuntimeEnvVariable[]
ingressRules RuntimeIngressRule[]
@@unique([appId, environment, name])
@@index([appId, environment])
@@map("runtime_env_profiles")
}
model RuntimeEnvVariable {
id String @id @default(uuid()) @db.Uuid
profileId String @map("profile_id") @db.Uuid
componentId String? @map("component_id") @db.Uuid
key String
value String
isSecret Boolean @default(false) @map("is_secret")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
profile RuntimeEnvProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
component AppComponent? @relation(fields: [componentId], references: [id], onDelete: SetNull)
@@unique([profileId, componentId, key])
@@index([profileId, isSecret])
@@index([componentId])
@@map("runtime_env_variables")
}
model RuntimeIngressRule {
id String @id @default(uuid()) @db.Uuid
profileId String @map("profile_id") @db.Uuid
componentId String @map("component_id") @db.Uuid
host String
path String @default("/")
servicePort Int @map("service_port")
tlsEnabled Boolean @default(false) @map("tls_enabled")
tlsSecretName String? @map("tls_secret_name")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
profile RuntimeEnvProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
component AppComponent @relation(fields: [componentId], references: [id], onDelete: Cascade)
@@unique([profileId, host, path])
@@index([profileId])
@@index([componentId])
@@map("runtime_ingress_rules")
}
model PlatformConnection {
id String @id @default(uuid()) @db.Uuid
name String @unique
endpoint String
role String
status String
lastSync DateTime? @map("last_sync") @db.Timestamptz(3)
metadata Json?
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([status])
@@map("platform_connections")
}
model TailscaleNode {
id String @id @default(uuid()) @db.Uuid
name String @unique
ip String
role String?
user String?
routes String?
status String
lastSeen DateTime? @map("last_seen") @db.Timestamptz(3)
metadata Json?
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([status, lastSeen])
@@map("tailscale_nodes")
}
model TailscaleSetting {
id String @id @default(uuid()) @db.Uuid
key String @unique
name String
value String
description String?
status String
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@index([status])
@@map("tailscale_settings")
}