Compare commits
46 Commits
de98022e2c
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f67ee5719 | |||
| a6a4426249 | |||
| 8b7ec648cd | |||
| 8151ec094d | |||
| 63e85d5306 | |||
| 3945e42c53 | |||
| 53b4909df7 | |||
| a80e6eab20 | |||
| 1c240e895b | |||
| 03d065bccb | |||
| f4a748b5f3 | |||
| d922b873e4 | |||
| 78f3578098 | |||
| e811208773 | |||
| cd4bdbecd7 | |||
| e31a07f5d9 | |||
| 29abcca57e | |||
| 195a893bd1 | |||
| aac1d01526 | |||
| e8ea575182 | |||
| ada3309db4 | |||
| 6311f8a553 | |||
| 609c345739 | |||
| 3873ce3169 | |||
| dd09471267 | |||
| 3a79364905 | |||
| c46c176ca8 | |||
| fbefc08b85 | |||
| f142de8763 | |||
| b191dc9f03 | |||
| c1d424b13c | |||
| ca35c30f87 | |||
| dceb20b26a | |||
| 76cc887371 | |||
| b35c486ae9 | |||
| c2f834e92d | |||
| da30d88688 | |||
| eb79bddcc0 | |||
| beb9dda8c6 | |||
| 27bf41d2d3 | |||
| 5a2041ddd3 | |||
| fddf70c985 | |||
| 52d53b0462 | |||
| ec17bba92d | |||
| e74c296b7e | |||
| ce99890dc8 |
+6
-2
@@ -8,11 +8,15 @@ RUN npm run build
|
||||
FROM node:22-bookworm-slim AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
ARG DOCKER_VERSION=27.5.1
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates curl git docker.io binutils \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates curl git python3 make g++ \
|
||||
&& curl -fsSL "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" \
|
||||
| tar -xz -C /tmp \
|
||||
&& install -m 0755 /tmp/docker/docker /usr/local/bin/docker \
|
||||
&& curl -fsSL -o /usr/local/bin/kubectl "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl" \
|
||||
&& chmod 0755 /usr/local/bin/kubectl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
&& rm -rf /tmp/docker /var/lib/apt/lists/*
|
||||
COPY package*.json ./
|
||||
RUN npm ci --omit=dev
|
||||
COPY --from=builder /app/dist ./dist
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
container_name: atnmy-stdo-adm-v2-postgres
|
||||
environment:
|
||||
POSTGRES_DB: studio_admin
|
||||
POSTGRES_USER: studio_admin
|
||||
POSTGRES_PASSWORD: future1234!@
|
||||
ports:
|
||||
- "5436:5432"
|
||||
volumes:
|
||||
- atnmy-stdo-adm-v2-postgres:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U studio_admin -d studio_admin"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
|
||||
volumes:
|
||||
atnmy-stdo-adm-v2-postgres:
|
||||
@@ -33,6 +33,9 @@ model User {
|
||||
email String @unique
|
||||
passwordHash String @map("password_hash")
|
||||
name String?
|
||||
profileImageData Bytes? @map("profile_image_data")
|
||||
profileImageMime String? @map("profile_image_mime")
|
||||
profileImageUpdatedAt DateTime? @map("profile_image_updated_at") @db.Timestamptz(3)
|
||||
role UserRole @default(USER)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
lastLoginAt DateTime? @map("last_login_at") @db.Timestamptz(3)
|
||||
@@ -41,6 +44,7 @@ model User {
|
||||
userLicenses UserLicense[]
|
||||
refreshTokens RefreshToken[]
|
||||
affiliations UserAffiliation[]
|
||||
appRoles UserAppRole[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
@@ -68,11 +72,26 @@ model License {
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
userLicenses UserLicense[]
|
||||
appPermissions LicenseAppPermission[]
|
||||
|
||||
@@index([isActive])
|
||||
@@map("licenses")
|
||||
}
|
||||
|
||||
model LicenseAppPermission {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
licenseId String @map("license_id") @db.Uuid
|
||||
appKey String @map("app_key")
|
||||
canLaunch Boolean @default(true) @map("can_launch")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
license License @relation(fields: [licenseId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([licenseId, appKey])
|
||||
@@index([appKey])
|
||||
@@map("license_app_permissions")
|
||||
}
|
||||
|
||||
model UserLicense {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
@@ -90,17 +109,23 @@ model UserLicense {
|
||||
}
|
||||
|
||||
model Organization {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
type OrganizationType @default(OTHER)
|
||||
domain 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)
|
||||
affiliations UserAffiliation[]
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
type OrganizationType @default(OTHER)
|
||||
domain String?
|
||||
defaultLicenseCode String? @map("default_license_code")
|
||||
instructorInviteCode String @unique @map("instructor_invite_code")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdById String? @map("created_by_id") @db.Uuid
|
||||
createdByName String? @map("created_by_name")
|
||||
createdByEmail String? @map("created_by_email")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
affiliations UserAffiliation[]
|
||||
|
||||
@@unique([name, type])
|
||||
@@index([isActive, type])
|
||||
@@index([createdByEmail])
|
||||
@@map("organizations")
|
||||
}
|
||||
|
||||
@@ -122,3 +147,22 @@ model UserAffiliation {
|
||||
@@index([organizationId])
|
||||
@@map("user_affiliations")
|
||||
}
|
||||
|
||||
model UserAppRole {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
appKey String @map("app_key")
|
||||
roleCode String @map("role_code")
|
||||
assignedById String? @map("assigned_by") @db.Uuid
|
||||
assignedAt DateTime @default(now()) @map("assigned_at") @db.Timestamptz(3)
|
||||
startsAt DateTime? @map("starts_at") @db.Timestamptz(3)
|
||||
endsAt DateTime? @map("ends_at") @db.Timestamptz(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, appKey, roleCode])
|
||||
@@index([appKey, roleCode])
|
||||
@@index([userId, appKey])
|
||||
@@map("user_app_roles")
|
||||
}
|
||||
|
||||
@@ -19,13 +19,50 @@ model LauncherNotice {
|
||||
startsAt DateTime? @map("starts_at") @db.Timestamptz(3)
|
||||
endsAt DateTime? @map("ends_at") @db.Timestamptz(3)
|
||||
priority Int @default(0)
|
||||
createdById String? @map("created_by_id") @db.Uuid
|
||||
createdByName String? @map("created_by_name")
|
||||
createdByEmail String? @map("created_by_email")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([appKey, isActive, priority])
|
||||
@@index([createdByEmail])
|
||||
@@map("launcher_notices")
|
||||
}
|
||||
|
||||
model LauncherTerm {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
key String @unique
|
||||
body String
|
||||
isRequired Boolean @default(true) @map("is_required")
|
||||
createdById String? @map("created_by_id") @db.Uuid
|
||||
createdByName String? @map("created_by_name")
|
||||
createdByEmail String? @map("created_by_email")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([createdByEmail])
|
||||
@@map("launcher_terms")
|
||||
}
|
||||
|
||||
model LauncherManualDocument {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @map("app_key")
|
||||
title String
|
||||
body String
|
||||
isPublished Boolean @default(true) @map("is_published")
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
createdById String? @map("created_by_id") @db.Uuid
|
||||
createdByName String? @map("created_by_name")
|
||||
createdByEmail String? @map("created_by_email")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([appKey, isPublished, sortOrder])
|
||||
@@index([createdByEmail])
|
||||
@@map("launcher_manual_documents")
|
||||
}
|
||||
|
||||
model ProvisionedDevice {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
accountEmail String @map("account_email")
|
||||
@@ -40,6 +77,9 @@ model ProvisionedDevice {
|
||||
kernel String?
|
||||
distro String?
|
||||
device Json?
|
||||
agentVersion String? @map("agent_version")
|
||||
agentUpdatedAt DateTime? @map("agent_updated_at") @db.Timestamptz(3)
|
||||
agentLastSeenAt DateTime? @map("agent_last_seen_at") @db.Timestamptz(3)
|
||||
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)
|
||||
@@ -48,9 +88,32 @@ model ProvisionedDevice {
|
||||
@@unique([accountEmail, tailscaleHostname])
|
||||
@@index([accountEmail])
|
||||
@@index([tailscaleHostname])
|
||||
@@index([agentLastSeenAt])
|
||||
@@map("provisioned_devices")
|
||||
}
|
||||
|
||||
model DeviceAuthorization {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
deviceCodeHash String @unique @map("device_code_hash")
|
||||
userCode String @unique @map("user_code")
|
||||
status String @default("PENDING")
|
||||
authUserId String? @map("auth_user_id") @db.Uuid
|
||||
accountEmail String? @map("account_email")
|
||||
authUserName String? @map("auth_user_name")
|
||||
authUserRole String? @map("auth_user_role")
|
||||
authUserRoles Json? @map("auth_user_roles")
|
||||
device Json?
|
||||
expiresAt DateTime @map("expires_at") @db.Timestamptz(3)
|
||||
approvedAt DateTime? @map("approved_at") @db.Timestamptz(3)
|
||||
consumedAt DateTime? @map("consumed_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, expiresAt])
|
||||
@@index([authUserId, createdAt])
|
||||
@@map("device_authorizations")
|
||||
}
|
||||
|
||||
model LauncherSshKey {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
accountEmail String @map("account_email")
|
||||
@@ -69,7 +132,7 @@ model LauncherSshKey {
|
||||
}
|
||||
|
||||
|
||||
model LauncherCommonSetting {
|
||||
model LauncherSetting {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
key String @unique
|
||||
name String
|
||||
@@ -82,5 +145,5 @@ model LauncherCommonSetting {
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([type, isActive])
|
||||
@@map("launcher_common_settings")
|
||||
@@map("launcher_settings")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE IF EXISTS "launcher_common_settings" RENAME TO "launcher_settings";
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.launcher_settings') IS NOT NULL THEN
|
||||
COMMENT ON TABLE "launcher_settings" IS '런처 소스, 빌드 환경 등 내부 설정';
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,36 @@
|
||||
CREATE TABLE IF NOT EXISTS "admin_app_permissions" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"admin_id" UUID NOT NULL,
|
||||
"app_id" UUID NOT NULL,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "admin_app_permissions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "admin_app_permissions_admin_id_app_id_key"
|
||||
ON "admin_app_permissions"("admin_id", "app_id");
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "admin_app_permissions_app_id_idx"
|
||||
ON "admin_app_permissions"("app_id");
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'admin_app_permissions_admin_id_fkey'
|
||||
) THEN
|
||||
ALTER TABLE "admin_app_permissions"
|
||||
ADD CONSTRAINT "admin_app_permissions_admin_id_fkey"
|
||||
FOREIGN KEY ("admin_id") REFERENCES "admins"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'admin_app_permissions_app_id_fkey'
|
||||
) THEN
|
||||
ALTER TABLE "admin_app_permissions"
|
||||
ADD CONSTRAINT "admin_app_permissions_app_id_fkey"
|
||||
FOREIGN KEY ("app_id") REFERENCES "apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,32 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE TYPE "AdminRole" AS ENUM ('OWNER', 'ADMIN', 'OPERATOR', 'VIEWER');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "admin_roles" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"code" "AdminRole" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "admin_roles_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "admin_roles_code_key"
|
||||
ON "admin_roles"("code");
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "admin_roles_is_active_sort_order_idx"
|
||||
ON "admin_roles"("is_active", "sort_order");
|
||||
|
||||
INSERT INTO "admin_roles" ("id", "code", "name", "description", "sort_order", "is_active", "updated_at")
|
||||
VALUES
|
||||
(gen_random_uuid(), 'OWNER', 'OWNER', '전체 관리자 권한', 1, true, CURRENT_TIMESTAMP),
|
||||
(gen_random_uuid(), 'ADMIN', 'ADMIN', '관리자 권한', 2, true, CURRENT_TIMESTAMP),
|
||||
(gen_random_uuid(), 'VIEWER', 'VIEWER', '조회 권한', 3, true, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT ("code") DO NOTHING;
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE "build_jobs"
|
||||
ADD COLUMN IF NOT EXISTS "release_candidate_hidden_at" TIMESTAMPTZ(3);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "build_jobs_release_candidate_hidden_at_idx"
|
||||
ON "build_jobs"("release_candidate_hidden_at");
|
||||
@@ -0,0 +1,11 @@
|
||||
ALTER TABLE "launcher_build_jobs"
|
||||
ADD COLUMN "requested_by_id" UUID,
|
||||
ADD COLUMN "requested_by_name" TEXT,
|
||||
ADD COLUMN "requested_by_email" TEXT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "launcher_build_jobs_requested_by_id_idx"
|
||||
ON "launcher_build_jobs"("requested_by_id");
|
||||
|
||||
COMMENT ON COLUMN "launcher_build_jobs"."requested_by_id" IS '빌드 작업을 요청한 관리자 ID';
|
||||
COMMENT ON COLUMN "launcher_build_jobs"."requested_by_name" IS '빌드 작업을 요청한 관리자 이름';
|
||||
COMMENT ON COLUMN "launcher_build_jobs"."requested_by_email" IS '빌드 작업을 요청한 관리자 이메일';
|
||||
@@ -0,0 +1,41 @@
|
||||
ALTER TABLE "build_jobs"
|
||||
ADD COLUMN "requested_by_id" UUID,
|
||||
ADD COLUMN "requested_by_name" TEXT,
|
||||
ADD COLUMN "requested_by_email" TEXT;
|
||||
|
||||
ALTER TABLE "app_releases"
|
||||
ADD COLUMN "requested_by_id" UUID,
|
||||
ADD COLUMN "requested_by_name" TEXT,
|
||||
ADD COLUMN "requested_by_email" TEXT;
|
||||
|
||||
ALTER TABLE "deployment_jobs"
|
||||
ADD COLUMN "requested_by_id" UUID,
|
||||
ADD COLUMN "requested_by_name" TEXT,
|
||||
ADD COLUMN "requested_by_email" TEXT;
|
||||
|
||||
ALTER TABLE "runtime_control_jobs"
|
||||
ADD COLUMN "requested_by_id" UUID,
|
||||
ADD COLUMN "requested_by_name" TEXT,
|
||||
ADD COLUMN "requested_by_email" TEXT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "build_jobs_requested_by_id_idx"
|
||||
ON "build_jobs"("requested_by_id");
|
||||
CREATE INDEX IF NOT EXISTS "app_releases_requested_by_id_idx"
|
||||
ON "app_releases"("requested_by_id");
|
||||
CREATE INDEX IF NOT EXISTS "deployment_jobs_requested_by_id_idx"
|
||||
ON "deployment_jobs"("requested_by_id");
|
||||
CREATE INDEX IF NOT EXISTS "runtime_control_jobs_requested_by_id_idx"
|
||||
ON "runtime_control_jobs"("requested_by_id");
|
||||
|
||||
COMMENT ON COLUMN "build_jobs"."requested_by_id" IS '작업을 요청한 관리자 ID';
|
||||
COMMENT ON COLUMN "build_jobs"."requested_by_name" IS '작업을 요청한 관리자 이름';
|
||||
COMMENT ON COLUMN "build_jobs"."requested_by_email" IS '작업을 요청한 관리자 이메일';
|
||||
COMMENT ON COLUMN "app_releases"."requested_by_id" IS '작업을 요청한 관리자 ID';
|
||||
COMMENT ON COLUMN "app_releases"."requested_by_name" IS '작업을 요청한 관리자 이름';
|
||||
COMMENT ON COLUMN "app_releases"."requested_by_email" IS '작업을 요청한 관리자 이메일';
|
||||
COMMENT ON COLUMN "deployment_jobs"."requested_by_id" IS '작업을 요청한 관리자 ID';
|
||||
COMMENT ON COLUMN "deployment_jobs"."requested_by_name" IS '작업을 요청한 관리자 이름';
|
||||
COMMENT ON COLUMN "deployment_jobs"."requested_by_email" IS '작업을 요청한 관리자 이메일';
|
||||
COMMENT ON COLUMN "runtime_control_jobs"."requested_by_id" IS '작업을 요청한 관리자 ID';
|
||||
COMMENT ON COLUMN "runtime_control_jobs"."requested_by_name" IS '작업을 요청한 관리자 이름';
|
||||
COMMENT ON COLUMN "runtime_control_jobs"."requested_by_email" IS '작업을 요청한 관리자 이메일';
|
||||
@@ -0,0 +1,16 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.launcher_notices') IS NOT NULL THEN
|
||||
ALTER TABLE "launcher_notices"
|
||||
ADD COLUMN IF NOT EXISTS "created_by_id" UUID,
|
||||
ADD COLUMN IF NOT EXISTS "created_by_name" TEXT,
|
||||
ADD COLUMN IF NOT EXISTS "created_by_email" TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.launcher_notices') IS NOT NULL THEN
|
||||
CREATE INDEX IF NOT EXISTS "launcher_notices_created_by_email_idx" ON "launcher_notices"("created_by_email");
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,7 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.organizations') IS NOT NULL THEN
|
||||
ALTER TABLE "organizations"
|
||||
ADD COLUMN IF NOT EXISTS "default_license_code" TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,19 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.licenses') IS NOT NULL THEN
|
||||
CREATE TABLE IF NOT EXISTS "license_app_permissions" (
|
||||
"id" UUID NOT NULL,
|
||||
"license_id" UUID NOT NULL,
|
||||
"app_key" TEXT NOT NULL,
|
||||
"can_launch" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "license_app_permissions_pkey" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "license_app_permissions_license_id_fkey" FOREIGN KEY ("license_id") REFERENCES "licenses"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "license_app_permissions_license_id_app_key_key" ON "license_app_permissions"("license_id", "app_key");
|
||||
CREATE INDEX IF NOT EXISTS "license_app_permissions_app_key_idx" ON "license_app_permissions"("app_key");
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,28 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.launcher_notices') IS NOT NULL THEN
|
||||
CREATE TABLE IF NOT EXISTS "launcher_terms" (
|
||||
"id" UUID NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"is_required" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_by_id" UUID,
|
||||
"created_by_name" TEXT,
|
||||
"created_by_email" TEXT,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "launcher_terms_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "launcher_terms_key_key" ON "launcher_terms"("key");
|
||||
CREATE INDEX IF NOT EXISTS "launcher_terms_created_by_email_idx" ON "launcher_terms"("created_by_email");
|
||||
|
||||
ALTER TABLE "launcher_terms" DROP COLUMN IF EXISTS "title";
|
||||
ALTER TABLE "launcher_terms" DROP COLUMN IF EXISTS "priority";
|
||||
ALTER TABLE "launcher_terms" DROP COLUMN IF EXISTS "version";
|
||||
ALTER TABLE "launcher_terms" DROP COLUMN IF EXISTS "is_active";
|
||||
ALTER TABLE "launcher_terms" DROP COLUMN IF EXISTS "starts_at";
|
||||
ALTER TABLE "launcher_terms" DROP COLUMN IF EXISTS "ends_at";
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE "launcher_manual_documents" (
|
||||
"id" UUID NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"is_published" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "launcher_manual_documents_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "launcher_manual_documents_key_key" ON "launcher_manual_documents"("key");
|
||||
CREATE INDEX "launcher_manual_documents_is_published_sort_order_idx" ON "launcher_manual_documents"("is_published", "sort_order");
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE "launcher_manual_documents"
|
||||
ADD COLUMN "app_key" TEXT NOT NULL DEFAULT 'Launcher';
|
||||
|
||||
DROP INDEX IF EXISTS "launcher_manual_documents_is_published_sort_order_idx";
|
||||
CREATE INDEX "launcher_manual_documents_app_key_is_published_sort_order_idx"
|
||||
ON "launcher_manual_documents"("app_key", "is_published", "sort_order");
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP INDEX IF EXISTS "launcher_manual_documents_key_key";
|
||||
ALTER TABLE "launcher_manual_documents" DROP COLUMN IF EXISTS "key";
|
||||
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE "launcher_manual_documents"
|
||||
ADD COLUMN IF NOT EXISTS "created_by_id" UUID,
|
||||
ADD COLUMN IF NOT EXISTS "created_by_name" TEXT,
|
||||
ADD COLUMN IF NOT EXISTS "created_by_email" TEXT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "launcher_manual_documents_created_by_email_idx"
|
||||
ON "launcher_manual_documents"("created_by_email");
|
||||
@@ -0,0 +1,9 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.organizations') IS NOT NULL THEN
|
||||
ALTER TABLE "organizations"
|
||||
ADD COLUMN IF NOT EXISTS "created_by_id" UUID,
|
||||
ADD COLUMN IF NOT EXISTS "created_by_name" TEXT,
|
||||
ADD COLUMN IF NOT EXISTS "created_by_email" TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "build_jobs"
|
||||
ADD COLUMN "artifact_sha256" TEXT;
|
||||
@@ -74,11 +74,25 @@ model Admin {
|
||||
sessions AdminSession[]
|
||||
auditLogs AdminAuditLog[]
|
||||
sshKeys AdminSshKey[]
|
||||
appPermissions AdminAppPermission[]
|
||||
|
||||
@@index([isActive, role])
|
||||
@@map("admins")
|
||||
}
|
||||
|
||||
model AdminAppPermission {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
adminId String @map("admin_id") @db.Uuid
|
||||
appId String @map("app_id") @db.Uuid
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade)
|
||||
app App @relation(fields: [appId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([adminId, appId])
|
||||
@@index([appId])
|
||||
@@map("admin_app_permissions")
|
||||
}
|
||||
|
||||
model AdminSshKey {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
adminId String @map("admin_id") @db.Uuid
|
||||
@@ -142,6 +156,7 @@ model App {
|
||||
releases AppRelease[]
|
||||
deployments DeploymentJob[]
|
||||
runtimeProfiles RuntimeEnvProfile[]
|
||||
adminPermissions AdminAppPermission[]
|
||||
|
||||
@@map("apps")
|
||||
}
|
||||
@@ -200,15 +215,21 @@ model BuildJob {
|
||||
branch String
|
||||
commitSha String? @map("commit_sha")
|
||||
artifact String?
|
||||
artifactSha256 String? @map("artifact_sha256")
|
||||
image String?
|
||||
status JobStatus @default(PENDING)
|
||||
requestedById String? @map("requested_by_id") @db.Uuid
|
||||
requestedByName String? @map("requested_by_name")
|
||||
requestedByEmail String? @map("requested_by_email")
|
||||
logs String?
|
||||
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
|
||||
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
|
||||
releaseCandidateHiddenAt DateTime? @map("release_candidate_hidden_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])
|
||||
@@index([releaseCandidateHiddenAt])
|
||||
@@map("build_jobs")
|
||||
}
|
||||
|
||||
@@ -221,6 +242,9 @@ model AppRelease {
|
||||
artifact String?
|
||||
imageTags Json? @map("image_tags")
|
||||
status JobStatus @default(PENDING)
|
||||
requestedById String? @map("requested_by_id") @db.Uuid
|
||||
requestedByName String? @map("requested_by_name")
|
||||
requestedByEmail String? @map("requested_by_email")
|
||||
logs String?
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
@@ -239,6 +263,9 @@ model DeploymentJob {
|
||||
namespace String
|
||||
status JobStatus @default(PENDING)
|
||||
requestedBy String? @map("requested_by")
|
||||
requestedById String? @map("requested_by_id") @db.Uuid
|
||||
requestedByName String? @map("requested_by_name")
|
||||
requestedByEmail String? @map("requested_by_email")
|
||||
logs String?
|
||||
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
|
||||
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
|
||||
@@ -261,6 +288,9 @@ model LauncherBuildJob {
|
||||
uploadPath String @map("upload_path")
|
||||
downloadUrl String @map("download_url")
|
||||
status JobStatus @default(PENDING)
|
||||
requestedById String? @map("requested_by_id") @db.Uuid
|
||||
requestedByName String? @map("requested_by_name")
|
||||
requestedByEmail String? @map("requested_by_email")
|
||||
logs String?
|
||||
startedAt DateTime? @map("started_at") @db.Timestamptz(3)
|
||||
finishedAt DateTime? @map("finished_at") @db.Timestamptz(3)
|
||||
@@ -282,6 +312,9 @@ model RuntimeControlJob {
|
||||
action RuntimeAction
|
||||
status JobStatus @default(PENDING)
|
||||
requestedBy String? @map("requested_by")
|
||||
requestedById String? @map("requested_by_id") @db.Uuid
|
||||
requestedByName String? @map("requested_by_name")
|
||||
requestedByEmail String? @map("requested_by_email")
|
||||
request Json?
|
||||
result Json?
|
||||
logs String?
|
||||
|
||||
@@ -1,13 +1,39 @@
|
||||
import { Body, Controller, Post, Req } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Patch, Post, Put, Req } from '@nestjs/common';
|
||||
import type { Request } from 'express';
|
||||
|
||||
import { AdminAuthService } from './admin-auth.service';
|
||||
import { Public } from './decorators/admin-auth.decorators';
|
||||
import { AdminLoginDto } from './dto';
|
||||
import { AdminLoginDto, ChangeAdminPasswordDto } from './dto';
|
||||
|
||||
@Controller('auth/admin')
|
||||
export class AdminAuthController {
|
||||
constructor(private readonly adminAuthService: AdminAuthService) {}
|
||||
|
||||
@Get('me')
|
||||
me(@Req() request: { admin?: { id: string } }) {
|
||||
return this.adminAuthService.me(request.admin!.id);
|
||||
}
|
||||
|
||||
@Put('avatar')
|
||||
async updateAvatar(@Req() request: Request & { admin?: { id: string } }) {
|
||||
const mimeType = String(request.headers['content-type'] ?? '').split(';')[0].trim().toLowerCase();
|
||||
const imageData = await readRequestBody(request);
|
||||
return this.adminAuthService.updateAvatar(request.admin!.id, imageData, mimeType);
|
||||
}
|
||||
|
||||
@Delete('avatar')
|
||||
deleteAvatar(@Req() request: { admin?: { id: string } }) {
|
||||
return this.adminAuthService.deleteAvatar(request.admin!.id);
|
||||
}
|
||||
|
||||
@Patch('password')
|
||||
changePassword(
|
||||
@Req() request: { admin?: { id: string } },
|
||||
@Body() body: ChangeAdminPasswordDto,
|
||||
) {
|
||||
return this.adminAuthService.changePassword(request.admin!.id, body);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Post('login')
|
||||
login(
|
||||
@@ -22,3 +48,15 @@ export class AdminAuthController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function readRequestBody(request: Request) {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
const chunks: Buffer[] = [];
|
||||
|
||||
request.on('data', (chunk: Buffer | string) => {
|
||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
});
|
||||
request.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
request.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { GiteaModule } from '../gitea/gitea.module';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { AdminAuthController } from './admin-auth.controller';
|
||||
import { AdminAuthService } from './admin-auth.service';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [GiteaModule, PrismaModule],
|
||||
controllers: [AdminAuthController],
|
||||
providers: [AdminAuthService],
|
||||
})
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import { randomBytes, scryptSync, timingSafeEqual, createHash } from 'node:crypto';
|
||||
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { DeleteObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||
import { BadRequestException, Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { GiteaService } from '../gitea/gitea.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AdminLoginDto } from './dto';
|
||||
import { AdminLoginDto, ChangeAdminPasswordDto } from './dto';
|
||||
|
||||
@Injectable()
|
||||
export class AdminAuthService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
private r2Client?: S3Client;
|
||||
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly giteaService: GiteaService,
|
||||
private readonly prisma: PrismaService,
|
||||
) {}
|
||||
|
||||
async login(
|
||||
body: AdminLoginDto,
|
||||
@@ -22,6 +31,19 @@ export class AdminAuthService {
|
||||
role: true,
|
||||
isActive: true,
|
||||
passwordHash: true,
|
||||
appPermissions: {
|
||||
select: { appId: true },
|
||||
},
|
||||
sshKeys: {
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: {
|
||||
id: true,
|
||||
label: true,
|
||||
fingerprint: true,
|
||||
giteaUsername: true,
|
||||
createdAt: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -49,6 +71,8 @@ export class AdminAuthService {
|
||||
}),
|
||||
]);
|
||||
|
||||
const avatarUrl = this.getAdminAvatarUrl(admin.id);
|
||||
|
||||
return {
|
||||
token,
|
||||
expiresAt,
|
||||
@@ -57,9 +81,235 @@ export class AdminAuthService {
|
||||
email: admin.email,
|
||||
name: admin.name,
|
||||
role: admin.role,
|
||||
avatarUrl,
|
||||
appPermissions: admin.appPermissions.map((permission) => permission.appId),
|
||||
sshKeys: admin.sshKeys,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async me(adminId: string) {
|
||||
const admin = await this.prisma.admin.findUnique({
|
||||
where: { id: adminId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
appPermissions: {
|
||||
select: { appId: true },
|
||||
},
|
||||
sshKeys: {
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: {
|
||||
id: true,
|
||||
label: true,
|
||||
fingerprint: true,
|
||||
giteaUsername: true,
|
||||
createdAt: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
throw new UnauthorizedException('관리자 세션이 유효하지 않습니다.');
|
||||
}
|
||||
|
||||
return {
|
||||
admin: {
|
||||
id: admin.id,
|
||||
email: admin.email,
|
||||
name: admin.name,
|
||||
role: admin.role,
|
||||
avatarUrl: this.getAdminAvatarUrl(admin.id),
|
||||
appPermissions: admin.appPermissions.map((permission) => permission.appId),
|
||||
sshKeys: admin.sshKeys,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async updateAvatar(adminId: string, imageData: Buffer, mimeType: string) {
|
||||
if (!['image/jpeg', 'image/png', 'image/webp'].includes(mimeType)) {
|
||||
throw new BadRequestException('지원하지 않는 이미지 형식입니다.');
|
||||
}
|
||||
|
||||
if (!imageData.length) {
|
||||
throw new BadRequestException('이미지 파일이 비어 있습니다.');
|
||||
}
|
||||
|
||||
if (imageData.length > 80 * 1024) {
|
||||
throw new BadRequestException('프로필 이미지는 80KB 이하로 업로드해 주세요.');
|
||||
}
|
||||
|
||||
const admin = await this.getAdminForAvatar(adminId);
|
||||
const giteaUsername = await this.ensureGiteaUser(admin);
|
||||
|
||||
await this.uploadAvatarToR2(admin.id, imageData, mimeType);
|
||||
await this.giteaService.updateUserAvatar(giteaUsername, imageData, mimeType);
|
||||
|
||||
return {
|
||||
avatarUrl: this.getAdminAvatarUrl(admin.id),
|
||||
};
|
||||
}
|
||||
|
||||
async deleteAvatar(adminId: string) {
|
||||
const admin = await this.getAdminForAvatar(adminId);
|
||||
const giteaUsername = this.resolveGiteaUsername(admin);
|
||||
|
||||
if (this.isR2Enabled()) {
|
||||
await this.deleteAvatarFromR2(admin.id);
|
||||
}
|
||||
await this.giteaService.deleteUserAvatar(giteaUsername);
|
||||
|
||||
return {
|
||||
avatarUrl: this.getAdminAvatarUrl(admin.id),
|
||||
};
|
||||
}
|
||||
|
||||
async changePassword(adminId: string, body: ChangeAdminPasswordDto) {
|
||||
if (body.currentPassword === body.newPassword) {
|
||||
throw new BadRequestException('새 비밀번호는 현재 비밀번호와 달라야 합니다.');
|
||||
}
|
||||
|
||||
const admin = await this.prisma.admin.findUnique({
|
||||
where: { id: adminId },
|
||||
select: {
|
||||
id: true,
|
||||
passwordHash: true,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!admin || !admin.isActive) {
|
||||
throw new UnauthorizedException('관리자 세션이 유효하지 않습니다.');
|
||||
}
|
||||
|
||||
if (!verifyPassword(body.currentPassword, admin.passwordHash)) {
|
||||
throw new BadRequestException('현재 비밀번호가 올바르지 않습니다.');
|
||||
}
|
||||
|
||||
await this.prisma.admin.update({
|
||||
where: { id: admin.id },
|
||||
data: { passwordHash: hashPassword(body.newPassword) },
|
||||
});
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
private getAdminAvatarUrl(adminId: string) {
|
||||
const publicBaseUrl = this.configService.get<string>('R2_PUBLIC_BASE_URL')?.replace(/\/+$/, '');
|
||||
if (!publicBaseUrl) return null;
|
||||
return `${publicBaseUrl}/${this.createAvatarObjectKey(adminId)}`;
|
||||
}
|
||||
|
||||
private async getAdminForAvatar(adminId: string) {
|
||||
const admin = await this.prisma.admin.findUnique({
|
||||
where: { id: adminId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
sshKeys: {
|
||||
where: { giteaUsername: { not: null } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 1,
|
||||
select: { giteaUsername: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
throw new UnauthorizedException('관리자 세션이 유효하지 않습니다.');
|
||||
}
|
||||
|
||||
return admin;
|
||||
}
|
||||
|
||||
private async ensureGiteaUser(admin: { email: string; name: string; sshKeys?: Array<{ giteaUsername: string | null }> }) {
|
||||
const storedUsername = this.resolveStoredGiteaUsername(admin);
|
||||
if (storedUsername) return storedUsername;
|
||||
|
||||
const username = createGiteaUsername(admin.email);
|
||||
await this.giteaService.createUser({
|
||||
username,
|
||||
email: admin.email,
|
||||
password: randomBytes(24).toString('hex'),
|
||||
fullName: admin.name,
|
||||
});
|
||||
return username;
|
||||
}
|
||||
|
||||
private resolveGiteaUsername(admin: { email: string; sshKeys?: Array<{ giteaUsername: string | null }> }) {
|
||||
return this.resolveStoredGiteaUsername(admin) ?? createGiteaUsername(admin.email);
|
||||
}
|
||||
|
||||
private resolveStoredGiteaUsername(admin: { sshKeys?: Array<{ giteaUsername: string | null }> }) {
|
||||
return admin.sshKeys?.find((key) => key.giteaUsername?.trim())?.giteaUsername?.trim() || null;
|
||||
}
|
||||
|
||||
private async uploadAvatarToR2(adminId: string, imageData: Buffer, mimeType: string) {
|
||||
const bucket = this.configService.get<string>('R2_BUCKET');
|
||||
const accessKeyId = this.configService.get<string>('R2_ACCESS_KEY_ID');
|
||||
const secretAccessKey = this.configService.get<string>('R2_SECRET_ACCESS_KEY');
|
||||
|
||||
if (!bucket || !accessKeyId || !secretAccessKey) {
|
||||
throw new BadRequestException('R2 설정이 필요합니다.');
|
||||
}
|
||||
|
||||
await this.getR2Client(accessKeyId, secretAccessKey).send(new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: this.createAvatarObjectKey(adminId),
|
||||
Body: imageData,
|
||||
ContentType: mimeType,
|
||||
CacheControl: 'public, max-age=60',
|
||||
}));
|
||||
}
|
||||
|
||||
private async deleteAvatarFromR2(adminId: string) {
|
||||
const bucket = this.configService.get<string>('R2_BUCKET');
|
||||
const accessKeyId = this.configService.get<string>('R2_ACCESS_KEY_ID');
|
||||
const secretAccessKey = this.configService.get<string>('R2_SECRET_ACCESS_KEY');
|
||||
|
||||
if (!bucket || !accessKeyId || !secretAccessKey) return;
|
||||
|
||||
await this.getR2Client(accessKeyId, secretAccessKey).send(new DeleteObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: this.createAvatarObjectKey(adminId),
|
||||
}));
|
||||
}
|
||||
|
||||
private getR2Client(accessKeyId: string, secretAccessKey: string) {
|
||||
if (this.r2Client) return this.r2Client;
|
||||
|
||||
const endpoint = this.configService.get<string>('R2_ENDPOINT')
|
||||
|| `https://${this.configService.get<string>('R2_ACCOUNT_ID')}.r2.cloudflarestorage.com`;
|
||||
|
||||
this.r2Client = new S3Client({
|
||||
region: this.configService.get<string>('R2_REGION', 'auto'),
|
||||
endpoint,
|
||||
credentials: {
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
},
|
||||
forcePathStyle: this.configService.get<string>('R2_FORCE_PATH_STYLE', 'true') !== 'false',
|
||||
});
|
||||
|
||||
return this.r2Client;
|
||||
}
|
||||
|
||||
private isR2Enabled() {
|
||||
return Boolean(
|
||||
this.configService.get<string>('R2_BUCKET')
|
||||
&& this.configService.get<string>('R2_ACCESS_KEY_ID')
|
||||
&& this.configService.get<string>('R2_SECRET_ACCESS_KEY'),
|
||||
);
|
||||
}
|
||||
|
||||
private createAvatarObjectKey(adminId: string) {
|
||||
const prefix = this.configService.get<string>('R2_PUBLIC_FILE_PREFIX', 'studio-files/public');
|
||||
return `${prefix.replace(/^\/+|\/+$/g, '')}/admins/${adminId}/avatar`;
|
||||
}
|
||||
}
|
||||
|
||||
function verifyPassword(password: string, passwordHash: string) {
|
||||
@@ -81,3 +331,21 @@ function verifyPassword(password: string, passwordHash: string) {
|
||||
function hashToken(token: string) {
|
||||
return createHash('sha256').update(token).digest('hex');
|
||||
}
|
||||
|
||||
function hashPassword(password: string) {
|
||||
const salt = randomBytes(16).toString('hex');
|
||||
const hash = scryptSync(password, salt, 64).toString('hex');
|
||||
return `scrypt:${salt}:${hash}`;
|
||||
}
|
||||
|
||||
function createGiteaUsername(email: string) {
|
||||
const account = email.split('@')[0] || email;
|
||||
const username = account
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9._-]+/g, '-')
|
||||
.replace(/^[._-]+|[._-]+$/g, '')
|
||||
.slice(0, 40);
|
||||
|
||||
return username || `admin-${createHash('sha1').update(email).digest('hex').slice(0, 8)}`;
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,4 +1,4 @@
|
||||
import { IsEmail, IsString } from 'class-validator';
|
||||
import { IsEmail, IsString, MinLength } from 'class-validator';
|
||||
|
||||
export class AdminLoginDto {
|
||||
@IsEmail()
|
||||
@@ -7,3 +7,12 @@ export class AdminLoginDto {
|
||||
@IsString()
|
||||
password!: string;
|
||||
}
|
||||
|
||||
export class ChangeAdminPasswordDto {
|
||||
@IsString()
|
||||
currentPassword!: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
newPassword!: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
export function getAdminAvatarUrl(adminId: string | null | undefined, configService: ConfigService) {
|
||||
const publicBaseUrl = configService.get<string>('R2_PUBLIC_BASE_URL')?.replace(/\/+$/, '');
|
||||
if (!adminId || !publicBaseUrl) return null;
|
||||
|
||||
const prefix = configService.get<string>('R2_PUBLIC_FILE_PREFIX', 'studio-files/public');
|
||||
return `${publicBaseUrl}/${prefix.replace(/^\/+|\/+$/g, '')}/admins/${adminId}/avatar`;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
CreateAdminSshKeyDto,
|
||||
CreateAdminDto,
|
||||
ListAdminsQuery,
|
||||
ResetAdminPasswordDto,
|
||||
UpdateAdminAppPermissionsDto,
|
||||
UpdateAdminRoleDto,
|
||||
UpdateAdminStatusDto,
|
||||
} from './dto';
|
||||
@@ -22,8 +24,8 @@ export class AdminsController {
|
||||
}
|
||||
|
||||
@Get()
|
||||
list() {
|
||||
return this.adminsService.list();
|
||||
list(@Query() query: ListAdminsQuery) {
|
||||
return this.adminsService.list(query);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -46,6 +48,16 @@ export class AdminsController {
|
||||
return this.adminsService.resetPassword(id, body);
|
||||
}
|
||||
|
||||
@Get(':id/app-permissions')
|
||||
listAppPermissions(@Param('id') id: string) {
|
||||
return this.adminsService.listAppPermissions(id);
|
||||
}
|
||||
|
||||
@Put(':id/app-permissions')
|
||||
updateAppPermissions(@Param('id') id: string, @Body() body: UpdateAdminAppPermissionsDto) {
|
||||
return this.adminsService.updateAppPermissions(id, body);
|
||||
}
|
||||
|
||||
@Post(':id/ssh-keys')
|
||||
createSshKey(@Param('id') id: string, @Body() body: CreateAdminSshKeyDto) {
|
||||
return this.adminsService.createSshKey(id, body);
|
||||
|
||||
+140
-16
@@ -1,14 +1,18 @@
|
||||
import { createHash, randomBytes, scryptSync } from 'node:crypto';
|
||||
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { GiteaService } from '../gitea/gitea.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { getAdminAvatarUrl } from './admin-avatar';
|
||||
import { AdminRole } from './admin-role';
|
||||
import {
|
||||
CreateAdminSshKeyDto,
|
||||
CreateAdminDto,
|
||||
ListAdminsQuery,
|
||||
ResetAdminPasswordDto,
|
||||
UpdateAdminAppPermissionsDto,
|
||||
UpdateAdminRoleDto,
|
||||
UpdateAdminStatusDto,
|
||||
} from './dto';
|
||||
@@ -16,6 +20,7 @@ import {
|
||||
@Injectable()
|
||||
export class AdminsService {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly giteaService: GiteaService,
|
||||
private readonly prisma: PrismaService,
|
||||
) {}
|
||||
@@ -37,9 +42,29 @@ export class AdminsService {
|
||||
});
|
||||
}
|
||||
|
||||
list() {
|
||||
return this.prisma.admin.findMany({
|
||||
async list(query: ListAdminsQuery = {}) {
|
||||
const page = toPositiveInt(query.page, 1);
|
||||
const limit = Math.min(toPositiveInt(query.limit, 20), 100);
|
||||
const keyword = query.keyword?.trim();
|
||||
const where = {
|
||||
...(query.role ? { role: query.role as AdminRole } : {}),
|
||||
...(query.state === 'active' ? { isActive: true } : query.state === 'inactive' ? { isActive: false } : {}),
|
||||
...(keyword
|
||||
? {
|
||||
OR: [
|
||||
{ email: { contains: keyword, mode: 'insensitive' as const } },
|
||||
{ name: { contains: keyword, mode: 'insensitive' as const } },
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
const [admins, total] = await this.prisma.$transaction([
|
||||
this.prisma.admin.findMany({
|
||||
where,
|
||||
orderBy: [{ role: 'asc' }, { createdAt: 'desc' }],
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
@@ -63,17 +88,44 @@ export class AdminsService {
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: adminSshKeySelect,
|
||||
},
|
||||
appPermissions: {
|
||||
select: { appId: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
this.prisma.admin.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
admins: admins.map((admin, index) => {
|
||||
return {
|
||||
...admin,
|
||||
no: total - ((page - 1) * limit + index),
|
||||
avatarUrl: getAdminAvatarUrl(admin.id, this.configService),
|
||||
};
|
||||
}),
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
};
|
||||
}
|
||||
|
||||
async create(body: CreateAdminDto) {
|
||||
await this.ensureRoleEnabled(body.role);
|
||||
const email = body.email.trim().toLowerCase();
|
||||
const name = body.name.trim();
|
||||
|
||||
await this.giteaService.createUser({
|
||||
username: createGiteaUsername(email),
|
||||
email,
|
||||
password: body.temporaryPassword,
|
||||
fullName: name,
|
||||
});
|
||||
|
||||
const admin = await this.prisma.admin.create({
|
||||
data: {
|
||||
email: body.email.trim().toLowerCase(),
|
||||
name: body.name.trim(),
|
||||
email,
|
||||
name,
|
||||
role: body.role,
|
||||
passwordHash: hashPassword(body.temporaryPassword),
|
||||
},
|
||||
@@ -84,7 +136,12 @@ export class AdminsService {
|
||||
}
|
||||
|
||||
async updateStatus(id: string, body: UpdateAdminStatusDto) {
|
||||
await this.ensureExists(id);
|
||||
const existing = await this.ensureExists(id);
|
||||
|
||||
await this.giteaService.updateUserLoginDisabled(
|
||||
createGiteaUsername(existing.email),
|
||||
!body.isActive,
|
||||
);
|
||||
|
||||
const admin = await this.prisma.admin.update({
|
||||
where: { id },
|
||||
@@ -132,21 +189,68 @@ export class AdminsService {
|
||||
return { admin };
|
||||
}
|
||||
|
||||
async listAppPermissions(id: string) {
|
||||
await this.ensureExists(id);
|
||||
|
||||
const permissions = await this.prisma.adminAppPermission.findMany({
|
||||
where: { adminId: id },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
select: { appId: true },
|
||||
});
|
||||
|
||||
return { appIds: permissions.map((permission) => permission.appId) };
|
||||
}
|
||||
|
||||
async updateAppPermissions(id: string, body: UpdateAdminAppPermissionsDto) {
|
||||
const admin = await this.prisma.admin.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, role: true },
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
throw new NotFoundException('관리자를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
if (admin.role === AdminRole.OWNER) {
|
||||
throw new BadRequestException('오너 권한은 앱 권한을 개별 설정하지 않습니다.');
|
||||
}
|
||||
|
||||
const appIds = [...new Set(body.appIds.filter(Boolean))];
|
||||
const existingApps = await this.prisma.app.findMany({
|
||||
where: { id: { in: appIds } },
|
||||
select: { id: true },
|
||||
});
|
||||
const existingAppIds = new Set(existingApps.map((app) => app.id));
|
||||
const validAppIds = appIds.filter((appId) => existingAppIds.has(appId));
|
||||
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.adminAppPermission.deleteMany({ where: { adminId: id } }),
|
||||
...(validAppIds.length
|
||||
? [
|
||||
this.prisma.adminAppPermission.createMany({
|
||||
data: validAppIds.map((appId) => ({ adminId: id, appId })),
|
||||
skipDuplicates: true,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]);
|
||||
|
||||
return { appIds: validAppIds };
|
||||
}
|
||||
|
||||
async createSshKey(adminId: string, body: CreateAdminSshKeyDto) {
|
||||
await this.ensureExists(adminId);
|
||||
const admin = await this.ensureExists(adminId);
|
||||
|
||||
const parsedKey = parseSshPublicKey(body.publicKey);
|
||||
const label = body.label?.trim() || `${parsedKey.keyType} ${parsedKey.fingerprint}`;
|
||||
const giteaUsername = body.giteaUsername?.trim() || null;
|
||||
const giteaUsername = createGiteaUsername(admin.email);
|
||||
let giteaKeyId: number | null = null;
|
||||
|
||||
if (giteaUsername) {
|
||||
const giteaKey = await this.giteaService.createUserSshKey(giteaUsername, {
|
||||
title: label,
|
||||
key: parsedKey.normalizedKey,
|
||||
});
|
||||
giteaKeyId = giteaKey.id;
|
||||
}
|
||||
const giteaKey = await this.giteaService.createUserSshKey(giteaUsername, {
|
||||
title: label,
|
||||
key: parsedKey.normalizedKey,
|
||||
});
|
||||
giteaKeyId = giteaKey.id;
|
||||
|
||||
const sshKey = await this.prisma.adminSshKey.create({
|
||||
data: {
|
||||
@@ -220,12 +324,14 @@ export class AdminsService {
|
||||
private async ensureExists(id: string) {
|
||||
const admin = await this.prisma.admin.findUnique({
|
||||
where: { id },
|
||||
select: { id: true },
|
||||
select: { id: true, email: true },
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
throw new NotFoundException('관리자를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
return admin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,6 +362,24 @@ function hashPassword(password: string) {
|
||||
return `scrypt:${salt}:${hash}`;
|
||||
}
|
||||
|
||||
function toPositiveInt(value: string | number | undefined, fallback: number) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed) || parsed < 1) return fallback;
|
||||
return Math.floor(parsed);
|
||||
}
|
||||
|
||||
function createGiteaUsername(email: string) {
|
||||
const account = email.split('@')[0] || email;
|
||||
const username = account
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9._-]+/g, '-')
|
||||
.replace(/^[._-]+|[._-]+$/g, '')
|
||||
.slice(0, 40);
|
||||
|
||||
return username || `admin-${createHash('sha1').update(email).digest('hex').slice(0, 8)}`;
|
||||
}
|
||||
|
||||
function parseSshPublicKey(publicKey: string) {
|
||||
const normalizedKey = publicKey.trim().replace(/\s+/g, ' ');
|
||||
const [keyType, keyBody] = normalizedKey.split(' ');
|
||||
|
||||
+31
-1
@@ -1,7 +1,31 @@
|
||||
import { IsBoolean, IsEmail, IsEnum, IsOptional, IsString, MinLength } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsArray, IsBoolean, IsEmail, IsEnum, IsOptional, IsString, IsUUID, MinLength } from 'class-validator';
|
||||
import { AdminRole } from './admin-role';
|
||||
|
||||
export class ListAdminsQuery {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
page?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
limit?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
keyword?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
role?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
state?: string;
|
||||
}
|
||||
|
||||
export class CreateAdminDto {
|
||||
@Transform(({ value }) => typeof value === 'string' ? value.trim().toLowerCase() : value)
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
|
||||
@@ -44,3 +68,9 @@ export class CreateAdminSshKeyDto {
|
||||
@IsString()
|
||||
giteaUsername?: string;
|
||||
}
|
||||
|
||||
export class UpdateAdminAppPermissionsDto {
|
||||
@IsArray()
|
||||
@IsUUID('4', { each: true })
|
||||
appIds!: string[];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Controller, Delete, Get, Param, Patch, Query, Body } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
ListLauncherSshKeysQuery,
|
||||
CreateDeviceAuthorizationDto,
|
||||
ListDeviceAuthorizationUsersQuery,
|
||||
ListProvisionedDevicesQuery,
|
||||
UpdateProvisionedDeviceDto,
|
||||
} from './dto';
|
||||
@@ -38,4 +40,24 @@ export class AgentsController {
|
||||
removeSshKey(@Param('id') id: string) {
|
||||
return this.agentsService.removeSshKey(id);
|
||||
}
|
||||
|
||||
@Get('registration-users')
|
||||
listRegistrationUsers(@Query() query: ListDeviceAuthorizationUsersQuery) {
|
||||
return this.agentsService.listRegistrationUsers(query);
|
||||
}
|
||||
|
||||
@Get('device-authorizations')
|
||||
listDeviceAuthorizations() {
|
||||
return this.agentsService.listDeviceAuthorizations();
|
||||
}
|
||||
|
||||
@Post('device-authorizations')
|
||||
createDeviceAuthorization(@Body() body: CreateDeviceAuthorizationDto) {
|
||||
return this.agentsService.createDeviceAuthorization(body.userId);
|
||||
}
|
||||
|
||||
@Post('device-authorizations/:id/cancel')
|
||||
cancelDeviceAuthorization(@Param('id') id: string) {
|
||||
return this.agentsService.cancelDeviceAuthorization(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AuthPrismaModule } from '../auth-prisma/auth-prisma.module';
|
||||
import { LauncherPrismaModule } from '../launcher-prisma/launcher-prisma.module';
|
||||
import { AgentsController } from './agents.controller';
|
||||
import { AgentsService } from './agents.service';
|
||||
|
||||
@Module({
|
||||
imports: [LauncherPrismaModule],
|
||||
imports: [AuthPrismaModule, LauncherPrismaModule],
|
||||
controllers: [AgentsController],
|
||||
providers: [AgentsService],
|
||||
})
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
|
||||
import { AuthPrismaService } from '../auth-prisma/auth-prisma.service';
|
||||
import { LauncherPrismaService } from '../launcher-prisma/launcher-prisma.service';
|
||||
import {
|
||||
ListDeviceAuthorizationUsersQuery,
|
||||
ListLauncherSshKeysQuery,
|
||||
ListProvisionedDevicesQuery,
|
||||
UpdateProvisionedDeviceDto,
|
||||
@@ -9,7 +12,10 @@ import {
|
||||
|
||||
@Injectable()
|
||||
export class AgentsService {
|
||||
constructor(private readonly launcherPrisma: LauncherPrismaService) {}
|
||||
constructor(
|
||||
private readonly authPrisma: AuthPrismaService,
|
||||
private readonly launcherPrisma: LauncherPrismaService,
|
||||
) {}
|
||||
|
||||
async listDevices(query: ListProvisionedDevicesQuery) {
|
||||
const keyword = query.keyword?.trim();
|
||||
@@ -109,6 +115,114 @@ export class AgentsService {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async listRegistrationUsers(query: ListDeviceAuthorizationUsersQuery) {
|
||||
const keyword = query.keyword?.trim();
|
||||
const users = await this.authPrisma.user.findMany({
|
||||
where: {
|
||||
role: 'USER',
|
||||
isActive: true,
|
||||
...(keyword
|
||||
? {
|
||||
OR: [
|
||||
{ email: { contains: keyword, mode: 'insensitive' } },
|
||||
{ name: { contains: keyword, mode: 'insensitive' } },
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
orderBy: [{ name: 'asc' }, { email: 'asc' }],
|
||||
take: 20,
|
||||
select: { id: true, name: true, email: true },
|
||||
});
|
||||
|
||||
return { users };
|
||||
}
|
||||
|
||||
async listDeviceAuthorizations() {
|
||||
const now = new Date();
|
||||
const authorizations = await this.launcherPrisma.deviceAuthorization.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ status: { in: ['APPROVED', 'CONSUMING'] } },
|
||||
{ createdAt: { gte: new Date(now.getTime() - 24 * 60 * 60 * 1000) } },
|
||||
],
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 50,
|
||||
select: {
|
||||
id: true,
|
||||
userCode: true,
|
||||
status: true,
|
||||
authUserId: true,
|
||||
accountEmail: true,
|
||||
authUserName: true,
|
||||
expiresAt: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
authorizations: authorizations.map((authorization) => ({
|
||||
...authorization,
|
||||
status: authorization.status === 'APPROVED' && authorization.expiresAt <= now ? 'EXPIRED' : authorization.status,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
async createDeviceAuthorization(userId: string) {
|
||||
const user = await this.authPrisma.user.findFirst({
|
||||
where: { id: userId, role: 'USER', isActive: true },
|
||||
select: { id: true, email: true, name: true, role: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('플랫폼 등록 대상 사용자를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const expiresAt = new Date(now.getTime() + 5 * 60 * 1000);
|
||||
await this.launcherPrisma.deviceAuthorization.updateMany({
|
||||
where: {
|
||||
authUserId: user.id,
|
||||
accountEmail: user.email,
|
||||
status: 'APPROVED',
|
||||
expiresAt: { gt: now },
|
||||
},
|
||||
data: { status: 'CANCELLED' },
|
||||
});
|
||||
|
||||
const authorization = await this.launcherPrisma.deviceAuthorization.create({
|
||||
data: {
|
||||
deviceCodeHash: createHash('sha256').update(randomBytes(32).toString('base64url')).digest('hex'),
|
||||
userCode: String(randomBytes(4).readUInt32BE() % 1_000_000).padStart(6, '0'),
|
||||
status: 'APPROVED',
|
||||
authUserId: user.id,
|
||||
accountEmail: user.email,
|
||||
authUserName: user.name,
|
||||
authUserRole: user.role,
|
||||
authUserRoles: [user.role],
|
||||
expiresAt,
|
||||
approvedAt: now,
|
||||
},
|
||||
select: { id: true, userCode: true, expiresAt: true, accountEmail: true, authUserName: true, status: true },
|
||||
});
|
||||
|
||||
return { authorization };
|
||||
}
|
||||
|
||||
async cancelDeviceAuthorization(id: string) {
|
||||
const result = await this.launcherPrisma.deviceAuthorization.updateMany({
|
||||
where: { id, status: { in: ['APPROVED', 'CONSUMING'] } },
|
||||
data: { status: 'CANCELLED' },
|
||||
});
|
||||
|
||||
if (!result.count) {
|
||||
throw new NotFoundException('취소할 플랫폼 등록 키를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
private async ensureDeviceExists(id: string) {
|
||||
const device = await this.launcherPrisma.provisionedDevice.findUnique({
|
||||
where: { id },
|
||||
|
||||
+12
-1
@@ -1,4 +1,4 @@
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
import { IsOptional, IsString, IsUUID } from 'class-validator';
|
||||
|
||||
export class ListProvisionedDevicesQuery {
|
||||
@IsOptional()
|
||||
@@ -37,3 +37,14 @@ export class ListLauncherSshKeysQuery {
|
||||
@IsString()
|
||||
keyword?: string;
|
||||
}
|
||||
|
||||
export class ListDeviceAuthorizationUsersQuery {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
keyword?: string;
|
||||
}
|
||||
|
||||
export class CreateDeviceAuthorizationDto {
|
||||
@IsUUID()
|
||||
userId!: string;
|
||||
}
|
||||
|
||||
@@ -11,11 +11,14 @@ import { GiteaModule } from './gitea/gitea.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { LauncherModule } from './launcher/launcher.module';
|
||||
import { LicensesModule } from './licenses/licenses.module';
|
||||
import { ManualsModule } from './manuals/manuals.module';
|
||||
import { NoticesModule } from './notices/notices.module';
|
||||
import { OrganizationsModule } from './organizations/organizations.module';
|
||||
import { PageAgentModule } from './page-agent/page-agent.module';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { RuntimeModule } from './runtime/runtime.module';
|
||||
import { TailscaleModule } from './tailscale/tailscale.module';
|
||||
import { TermsModule } from './terms/terms.module';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { WoodpeckerModule } from './woodpecker/woodpecker.module';
|
||||
|
||||
@@ -38,11 +41,14 @@ import { WoodpeckerModule } from './woodpecker/woodpecker.module';
|
||||
HealthModule,
|
||||
LauncherModule,
|
||||
LicensesModule,
|
||||
ManualsModule,
|
||||
NoticesModule,
|
||||
OrganizationsModule,
|
||||
PageAgentModule,
|
||||
PrismaModule,
|
||||
RuntimeModule,
|
||||
TailscaleModule,
|
||||
TermsModule,
|
||||
UsersModule,
|
||||
WoodpeckerModule,
|
||||
],
|
||||
|
||||
+47
-10
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req } from '@nestjs/common';
|
||||
|
||||
import { LinkComponentSourceDto } from '../gitea/dto';
|
||||
import { AppsService } from './apps.service';
|
||||
@@ -77,13 +77,17 @@ export class AppsController {
|
||||
@Query('limit') limit?: string,
|
||||
@Query('since') since?: string,
|
||||
@Query('status') status?: string,
|
||||
@Query('hideReleaseCandidates') hideReleaseCandidates?: string,
|
||||
) {
|
||||
return this.appsService.listBuildJobs(appKey, page, limit, since, status);
|
||||
return this.appsService.listBuildJobs(appKey, page, limit, since, status, hideReleaseCandidates);
|
||||
}
|
||||
|
||||
@Post('builds')
|
||||
createBuildJob(@Body() body: CreateBuildJobDto) {
|
||||
return this.appsService.createBuildJob(body);
|
||||
createBuildJob(
|
||||
@Body() body: CreateBuildJobDto,
|
||||
@Req() request: { admin?: { id: string; name: string; email: string } },
|
||||
) {
|
||||
return this.appsService.createBuildJob(body, request.admin);
|
||||
}
|
||||
|
||||
@Patch('builds/:buildJobId/cancel')
|
||||
@@ -91,6 +95,11 @@ export class AppsController {
|
||||
return this.appsService.cancelBuildJob(buildJobId);
|
||||
}
|
||||
|
||||
@Patch('builds/:buildJobId/hide-release-candidate')
|
||||
hideReleaseCandidate(@Param('buildJobId') buildJobId: string) {
|
||||
return this.appsService.hideReleaseCandidate(buildJobId);
|
||||
}
|
||||
|
||||
@Delete('builds/:buildJobId')
|
||||
deleteBuildJob(@Param('buildJobId') buildJobId: string) {
|
||||
return this.appsService.deleteBuildJob(buildJobId);
|
||||
@@ -128,8 +137,16 @@ export class AppsController {
|
||||
}
|
||||
|
||||
@Post('releases')
|
||||
createRelease(@Body() body: CreateAppReleaseDto) {
|
||||
return this.appsService.createRelease(body);
|
||||
createRelease(
|
||||
@Body() body: CreateAppReleaseDto,
|
||||
@Req() request: { admin?: { id: string; name: string; email: string } },
|
||||
) {
|
||||
return this.appsService.createRelease(body, request.admin);
|
||||
}
|
||||
|
||||
@Delete('releases/:releaseId')
|
||||
deleteRelease(@Param('releaseId') releaseId: string) {
|
||||
return this.appsService.deleteRelease(releaseId);
|
||||
}
|
||||
|
||||
@Get('deployments')
|
||||
@@ -143,8 +160,11 @@ export class AppsController {
|
||||
}
|
||||
|
||||
@Post('deployments')
|
||||
createDeploymentJob(@Body() body: CreateDeploymentJobDto) {
|
||||
return this.appsService.createDeploymentJob(body);
|
||||
createDeploymentJob(
|
||||
@Body() body: CreateDeploymentJobDto,
|
||||
@Req() request: { admin?: { id: string; name: string; email: string } },
|
||||
) {
|
||||
return this.appsService.createDeploymentJob(body, request.admin);
|
||||
}
|
||||
|
||||
@Get('migrations')
|
||||
@@ -158,8 +178,11 @@ export class AppsController {
|
||||
}
|
||||
|
||||
@Post('migrations')
|
||||
createMigrationJob(@Body() body: CreateMigrationJobDto) {
|
||||
return this.appsService.createMigrationJob(body);
|
||||
createMigrationJob(
|
||||
@Body() body: CreateMigrationJobDto,
|
||||
@Req() request: { admin?: { id: string; name: string; email: string } },
|
||||
) {
|
||||
return this.appsService.createMigrationJob(body, request.admin);
|
||||
}
|
||||
|
||||
@Get('runtime-namespaces/:namespace')
|
||||
@@ -172,6 +195,15 @@ export class AppsController {
|
||||
return this.appsService.listRuntimeResources(namespace);
|
||||
}
|
||||
|
||||
@Get('runtime-namespaces/:namespace/resources/:kind/:name/yaml')
|
||||
getRuntimeResourceYaml(
|
||||
@Param('namespace') namespace: string,
|
||||
@Param('kind') kind: string,
|
||||
@Param('name') name: string,
|
||||
) {
|
||||
return this.appsService.getRuntimeResourceYaml(namespace, kind, name);
|
||||
}
|
||||
|
||||
@Post('runtime-namespaces')
|
||||
ensureRuntimeNamespace(@Body() body: EnsureRuntimeNamespaceDto) {
|
||||
return this.appsService.ensureRuntimeNamespace(body);
|
||||
@@ -195,6 +227,11 @@ export class AppsController {
|
||||
return this.appsService.createRuntimeProfile(body);
|
||||
}
|
||||
|
||||
@Delete('runtime-profiles/:profileId')
|
||||
deleteRuntimeProfile(@Param('profileId') profileId: string) {
|
||||
return this.appsService.deleteRuntimeProfile(profileId);
|
||||
}
|
||||
|
||||
@Post('runtime-profiles/:profileId/variables')
|
||||
createRuntimeVariable(
|
||||
@Param('profileId') profileId: string,
|
||||
|
||||
+503
-166
File diff suppressed because it is too large
Load Diff
@@ -300,6 +300,25 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
const contextPath = this.configService.get<string>('BUILD_CONTEXT_PATH', '.');
|
||||
const contextDir = path.resolve(workDir, contextPath);
|
||||
const pushEnabled = this.configService.get<string>('BUILD_PUSH_ENABLED', 'true') !== 'false';
|
||||
const viteBuildVariables = job.component.buildVariables
|
||||
.filter((variable) => !variable.isSecret && variable.key.trim().startsWith('VITE_'));
|
||||
const buildArgs = job.component.buildVariables
|
||||
.filter((variable) => !variable.isSecret && variable.key.trim())
|
||||
.filter((variable) => !variable.key.trim().startsWith('VITE_'))
|
||||
.flatMap((variable) => ['--build-arg', `${variable.key}=${variable.value}`]);
|
||||
|
||||
if (viteBuildVariables.length > 0) {
|
||||
await fs.writeFile(
|
||||
path.join(contextDir, '.env.production.local'),
|
||||
`${viteBuildVariables.map((variable) => `${variable.key}=${JSON.stringify(variable.value)}`).join('\n')}\n`,
|
||||
'utf8',
|
||||
);
|
||||
await append(`[image] Vite build env ${viteBuildVariables.map((variable) => variable.key).join(', ')}`);
|
||||
}
|
||||
|
||||
if (buildArgs.length > 0) {
|
||||
await append(`[image] build args ${job.component.buildVariables.filter((variable) => !variable.isSecret && variable.key.trim() && !variable.key.trim().startsWith('VITE_')).map((variable) => variable.key).join(', ')}`);
|
||||
}
|
||||
|
||||
if (image !== job.image) {
|
||||
await this.prisma.buildJob.update({
|
||||
@@ -316,7 +335,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
await runCommand({
|
||||
jobId: job.id,
|
||||
command: this.configService.get<string>('NERDCTL_BIN', 'nerdctl'),
|
||||
args: [...prefix, 'build', '-t', image, '-f', dockerfile, '.'],
|
||||
args: [...prefix, 'build', ...buildArgs, '-t', image, '-f', dockerfile, '.'],
|
||||
cwd: contextDir,
|
||||
append,
|
||||
onProcess: (child) => this.activeProcesses.set(job.id, child),
|
||||
@@ -340,7 +359,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
await runCommand({
|
||||
jobId: job.id,
|
||||
command: this.configService.get<string>('DOCKER_BIN', 'docker'),
|
||||
args: ['build', '-t', image, '-f', dockerfile, '.'],
|
||||
args: ['build', ...buildArgs, '-t', image, '-f', dockerfile, '.'],
|
||||
cwd: contextDir,
|
||||
append,
|
||||
onProcess: (child) => this.activeProcesses.set(job.id, child),
|
||||
@@ -428,11 +447,13 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
const destination = path.join(artifactDir, destinationName);
|
||||
await fs.copyFile(artifact, destination);
|
||||
|
||||
const artifactSha256 = await sha256File(artifact);
|
||||
const uploadedArtifact = await this.uploadArtifactIfConfigured(
|
||||
job,
|
||||
destination,
|
||||
destinationName,
|
||||
artifactVersion,
|
||||
artifactSha256,
|
||||
append,
|
||||
);
|
||||
const publicBaseUrl = this.configService.get<string>('BUILD_ARTIFACT_BASE_URL');
|
||||
@@ -443,9 +464,10 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
|
||||
await this.prisma.buildJob.update({
|
||||
where: { id: job.id },
|
||||
data: { artifact: artifactValue },
|
||||
data: { artifact: artifactValue, artifactSha256 },
|
||||
});
|
||||
job.artifact = artifactValue;
|
||||
job.artifactSha256 = artifactSha256;
|
||||
|
||||
await append(`[artifact] found ${path.relative(workDir, artifact)}`);
|
||||
await append(`[artifact] copied to ${artifactValue}`);
|
||||
@@ -456,6 +478,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
artifactPath: string,
|
||||
artifactName: string,
|
||||
artifactVersion: string | null,
|
||||
sha256: string,
|
||||
append: (line: string) => Promise<void>,
|
||||
) {
|
||||
const bucket = this.configService.get<string>('R2_BUCKET');
|
||||
@@ -469,7 +492,6 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy {
|
||||
|
||||
const key = this.createArtifactObjectKey(job, artifactName, artifactVersion);
|
||||
const stat = await fs.stat(artifactPath);
|
||||
const sha256 = await sha256File(artifactPath);
|
||||
const client = this.getR2Client(accessKeyId, secretAccessKey);
|
||||
|
||||
await append(`[r2] upload ${key}`);
|
||||
|
||||
+37
-2
@@ -1,4 +1,5 @@
|
||||
import { ArrayNotEmpty, IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, IsUUID, Min } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ArrayNotEmpty, IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, IsUUID, Min, ValidateNested } from 'class-validator';
|
||||
import { AppStatus, ComponentType } from './app-enums';
|
||||
|
||||
export class CreateAppDto {
|
||||
@@ -115,6 +116,7 @@ export class CreateAppComponentDto {
|
||||
name?: string;
|
||||
port?: number;
|
||||
targetPort?: number;
|
||||
nodePort?: number;
|
||||
protocol?: string;
|
||||
}>;
|
||||
}
|
||||
@@ -194,9 +196,37 @@ export class CreateAppReleaseDto {
|
||||
buildJobIds!: string[];
|
||||
}
|
||||
|
||||
export class DeploymentResourceLimitsDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
replicas?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
cpuRequest?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
cpuLimit?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
memoryRequest?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
memoryLimit?: string;
|
||||
}
|
||||
|
||||
export class CreateDeploymentJobDto {
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
releaseId!: string;
|
||||
releaseId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
buildJobId?: string;
|
||||
|
||||
@IsString()
|
||||
environment!: string;
|
||||
@@ -207,6 +237,11 @@ export class CreateDeploymentJobDto {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
updateLauncherAppVersion?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => DeploymentResourceLimitsDto)
|
||||
resources?: DeploymentResourceLimitsDto;
|
||||
}
|
||||
|
||||
export class CreateRuntimeEnvProfileDto {
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./index"
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('.') }
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./index"
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('#main-entry-point') }
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
export * from "./default"
|
||||
File diff suppressed because one or more lines are too long
@@ -1,217 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
Decimal,
|
||||
objectEnumValues,
|
||||
makeStrictEnum,
|
||||
Public,
|
||||
getRuntime,
|
||||
skip
|
||||
} = require('./runtime/index-browser.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 6.19.3
|
||||
* Query Engine version: c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "6.19.3",
|
||||
engine: "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)};
|
||||
Prisma.PrismaClientUnknownRequestError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientRustPanicError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientInitializationError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientValidationError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.empty = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.join = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.raw = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.defineExtension = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = objectEnumValues.instances.DbNull
|
||||
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
||||
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
||||
|
||||
Prisma.NullTypes = {
|
||||
DbNull: objectEnumValues.classes.DbNull,
|
||||
JsonNull: objectEnumValues.classes.JsonNull,
|
||||
AnyNull: objectEnumValues.classes.AnyNull
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
ReadUncommitted: 'ReadUncommitted',
|
||||
ReadCommitted: 'ReadCommitted',
|
||||
RepeatableRead: 'RepeatableRead',
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
email: 'email',
|
||||
passwordHash: 'passwordHash',
|
||||
name: 'name',
|
||||
role: 'role',
|
||||
isActive: 'isActive',
|
||||
lastLoginAt: 'lastLoginAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.LicenseScalarFieldEnum = {
|
||||
id: 'id',
|
||||
code: 'code',
|
||||
name: 'name',
|
||||
description: 'description',
|
||||
defaultDurationDays: 'defaultDurationDays',
|
||||
isActive: 'isActive',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserLicenseScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
licenseId: 'licenseId',
|
||||
startsAt: 'startsAt',
|
||||
expiresAt: 'expiresAt',
|
||||
assignedById: 'assignedById',
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.QueryMode = {
|
||||
default: 'default',
|
||||
insensitive: 'insensitive'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
exports.UserRole = exports.$Enums.UserRole = {
|
||||
ADMIN: 'ADMIN',
|
||||
USER: 'USER'
|
||||
};
|
||||
|
||||
exports.LicenseCode = exports.$Enums.LicenseCode = {
|
||||
FREE: 'FREE',
|
||||
EDUCATION: 'EDUCATION',
|
||||
PROFESSIONAL: 'PROFESSIONAL'
|
||||
};
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
User: 'User',
|
||||
License: 'License',
|
||||
UserLicense: 'UserLicense'
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a stub Prisma Client that will error at runtime if called.
|
||||
*/
|
||||
class PrismaClient {
|
||||
constructor() {
|
||||
return new Proxy(this, {
|
||||
get(target, prop) {
|
||||
let message
|
||||
const runtime = getRuntime()
|
||||
if (runtime.isEdge) {
|
||||
message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
|
||||
- Use Prisma Accelerate: https://pris.ly/d/accelerate
|
||||
- Use Driver Adapters: https://pris.ly/d/driver-adapters
|
||||
`;
|
||||
} else {
|
||||
message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).'
|
||||
}
|
||||
|
||||
message += `
|
||||
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
|
||||
|
||||
throw new Error(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.PrismaClient = PrismaClient
|
||||
|
||||
Object.assign(exports, Prisma)
|
||||
Vendored
-6218
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,183 +0,0 @@
|
||||
{
|
||||
"name": "prisma-client-378612c311cf1c6fac598c21e6ea51068a0548bfbc7fdc08b65098bb7a2e5523",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"browser": "default.js",
|
||||
"exports": {
|
||||
"./client": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./edge": {
|
||||
"types": "./edge.d.ts",
|
||||
"require": "./edge.js",
|
||||
"import": "./edge.js",
|
||||
"default": "./edge.js"
|
||||
},
|
||||
"./react-native": {
|
||||
"types": "./react-native.d.ts",
|
||||
"require": "./react-native.js",
|
||||
"import": "./react-native.js",
|
||||
"default": "./react-native.js"
|
||||
},
|
||||
"./extension": {
|
||||
"types": "./extension.d.ts",
|
||||
"require": "./extension.js",
|
||||
"import": "./extension.js",
|
||||
"default": "./extension.js"
|
||||
},
|
||||
"./index-browser": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index-browser.js",
|
||||
"import": "./index-browser.js",
|
||||
"default": "./index-browser.js"
|
||||
},
|
||||
"./index": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index.js",
|
||||
"import": "./index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./wasm": {
|
||||
"types": "./wasm.d.ts",
|
||||
"require": "./wasm.js",
|
||||
"import": "./wasm.mjs",
|
||||
"default": "./wasm.mjs"
|
||||
},
|
||||
"./runtime/client": {
|
||||
"types": "./runtime/client.d.ts",
|
||||
"node": {
|
||||
"require": "./runtime/client.js",
|
||||
"default": "./runtime/client.js"
|
||||
},
|
||||
"require": "./runtime/client.js",
|
||||
"import": "./runtime/client.mjs",
|
||||
"default": "./runtime/client.mjs"
|
||||
},
|
||||
"./runtime/library": {
|
||||
"types": "./runtime/library.d.ts",
|
||||
"require": "./runtime/library.js",
|
||||
"import": "./runtime/library.mjs",
|
||||
"default": "./runtime/library.mjs"
|
||||
},
|
||||
"./runtime/binary": {
|
||||
"types": "./runtime/binary.d.ts",
|
||||
"require": "./runtime/binary.js",
|
||||
"import": "./runtime/binary.mjs",
|
||||
"default": "./runtime/binary.mjs"
|
||||
},
|
||||
"./runtime/wasm-engine-edge": {
|
||||
"types": "./runtime/wasm-engine-edge.d.ts",
|
||||
"require": "./runtime/wasm-engine-edge.js",
|
||||
"import": "./runtime/wasm-engine-edge.mjs",
|
||||
"default": "./runtime/wasm-engine-edge.mjs"
|
||||
},
|
||||
"./runtime/wasm-compiler-edge": {
|
||||
"types": "./runtime/wasm-compiler-edge.d.ts",
|
||||
"require": "./runtime/wasm-compiler-edge.js",
|
||||
"import": "./runtime/wasm-compiler-edge.mjs",
|
||||
"default": "./runtime/wasm-compiler-edge.mjs"
|
||||
},
|
||||
"./runtime/edge": {
|
||||
"types": "./runtime/edge.d.ts",
|
||||
"require": "./runtime/edge.js",
|
||||
"import": "./runtime/edge-esm.js",
|
||||
"default": "./runtime/edge-esm.js"
|
||||
},
|
||||
"./runtime/react-native": {
|
||||
"types": "./runtime/react-native.d.ts",
|
||||
"require": "./runtime/react-native.js",
|
||||
"import": "./runtime/react-native.js",
|
||||
"default": "./runtime/react-native.js"
|
||||
},
|
||||
"./runtime/index-browser": {
|
||||
"types": "./runtime/index-browser.d.ts",
|
||||
"require": "./runtime/index-browser.js",
|
||||
"import": "./runtime/index-browser.mjs",
|
||||
"default": "./runtime/index-browser.mjs"
|
||||
},
|
||||
"./generator-build": {
|
||||
"require": "./generator-build/index.js",
|
||||
"import": "./generator-build/index.js",
|
||||
"default": "./generator-build/index.js"
|
||||
},
|
||||
"./sql": {
|
||||
"require": {
|
||||
"types": "./sql.d.ts",
|
||||
"node": "./sql.js",
|
||||
"default": "./sql.js"
|
||||
},
|
||||
"import": {
|
||||
"types": "./sql.d.ts",
|
||||
"node": "./sql.mjs",
|
||||
"default": "./sql.mjs"
|
||||
},
|
||||
"default": "./sql.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"version": "6.19.3",
|
||||
"sideEffects": false,
|
||||
"imports": {
|
||||
"#wasm-engine-loader": {
|
||||
"edge-light": "./wasm-edge-light-loader.mjs",
|
||||
"workerd": "./wasm-worker-loader.mjs",
|
||||
"worker": "./wasm-worker-loader.mjs",
|
||||
"default": "./wasm-worker-loader.mjs"
|
||||
},
|
||||
"#main-entry-point": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,370 +0,0 @@
|
||||
declare class AnyNull extends NullTypesEnumValue {
|
||||
#private;
|
||||
}
|
||||
|
||||
declare type Args<T, F extends Operation> = T extends {
|
||||
[K: symbol]: {
|
||||
types: {
|
||||
operations: {
|
||||
[K in F]: {
|
||||
args: any;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
} ? T[symbol]['types']['operations'][F]['args'] : any;
|
||||
|
||||
declare class DbNull extends NullTypesEnumValue {
|
||||
#private;
|
||||
}
|
||||
|
||||
export declare function Decimal(n: Decimal.Value): Decimal;
|
||||
|
||||
export declare namespace Decimal {
|
||||
export type Constructor = typeof Decimal;
|
||||
export type Instance = Decimal;
|
||||
export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
||||
export type Modulo = Rounding | 9;
|
||||
export type Value = string | number | Decimal;
|
||||
|
||||
// http://mikemcl.github.io/decimal.js/#constructor-properties
|
||||
export interface Config {
|
||||
precision?: number;
|
||||
rounding?: Rounding;
|
||||
toExpNeg?: number;
|
||||
toExpPos?: number;
|
||||
minE?: number;
|
||||
maxE?: number;
|
||||
crypto?: boolean;
|
||||
modulo?: Modulo;
|
||||
defaults?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export declare class Decimal {
|
||||
readonly d: number[];
|
||||
readonly e: number;
|
||||
readonly s: number;
|
||||
|
||||
constructor(n: Decimal.Value);
|
||||
|
||||
absoluteValue(): Decimal;
|
||||
abs(): Decimal;
|
||||
|
||||
ceil(): Decimal;
|
||||
|
||||
clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||
clamp(min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||
|
||||
comparedTo(n: Decimal.Value): number;
|
||||
cmp(n: Decimal.Value): number;
|
||||
|
||||
cosine(): Decimal;
|
||||
cos(): Decimal;
|
||||
|
||||
cubeRoot(): Decimal;
|
||||
cbrt(): Decimal;
|
||||
|
||||
decimalPlaces(): number;
|
||||
dp(): number;
|
||||
|
||||
dividedBy(n: Decimal.Value): Decimal;
|
||||
div(n: Decimal.Value): Decimal;
|
||||
|
||||
dividedToIntegerBy(n: Decimal.Value): Decimal;
|
||||
divToInt(n: Decimal.Value): Decimal;
|
||||
|
||||
equals(n: Decimal.Value): boolean;
|
||||
eq(n: Decimal.Value): boolean;
|
||||
|
||||
floor(): Decimal;
|
||||
|
||||
greaterThan(n: Decimal.Value): boolean;
|
||||
gt(n: Decimal.Value): boolean;
|
||||
|
||||
greaterThanOrEqualTo(n: Decimal.Value): boolean;
|
||||
gte(n: Decimal.Value): boolean;
|
||||
|
||||
hyperbolicCosine(): Decimal;
|
||||
cosh(): Decimal;
|
||||
|
||||
hyperbolicSine(): Decimal;
|
||||
sinh(): Decimal;
|
||||
|
||||
hyperbolicTangent(): Decimal;
|
||||
tanh(): Decimal;
|
||||
|
||||
inverseCosine(): Decimal;
|
||||
acos(): Decimal;
|
||||
|
||||
inverseHyperbolicCosine(): Decimal;
|
||||
acosh(): Decimal;
|
||||
|
||||
inverseHyperbolicSine(): Decimal;
|
||||
asinh(): Decimal;
|
||||
|
||||
inverseHyperbolicTangent(): Decimal;
|
||||
atanh(): Decimal;
|
||||
|
||||
inverseSine(): Decimal;
|
||||
asin(): Decimal;
|
||||
|
||||
inverseTangent(): Decimal;
|
||||
atan(): Decimal;
|
||||
|
||||
isFinite(): boolean;
|
||||
|
||||
isInteger(): boolean;
|
||||
isInt(): boolean;
|
||||
|
||||
isNaN(): boolean;
|
||||
|
||||
isNegative(): boolean;
|
||||
isNeg(): boolean;
|
||||
|
||||
isPositive(): boolean;
|
||||
isPos(): boolean;
|
||||
|
||||
isZero(): boolean;
|
||||
|
||||
lessThan(n: Decimal.Value): boolean;
|
||||
lt(n: Decimal.Value): boolean;
|
||||
|
||||
lessThanOrEqualTo(n: Decimal.Value): boolean;
|
||||
lte(n: Decimal.Value): boolean;
|
||||
|
||||
logarithm(n?: Decimal.Value): Decimal;
|
||||
log(n?: Decimal.Value): Decimal;
|
||||
|
||||
minus(n: Decimal.Value): Decimal;
|
||||
sub(n: Decimal.Value): Decimal;
|
||||
|
||||
modulo(n: Decimal.Value): Decimal;
|
||||
mod(n: Decimal.Value): Decimal;
|
||||
|
||||
naturalExponential(): Decimal;
|
||||
exp(): Decimal;
|
||||
|
||||
naturalLogarithm(): Decimal;
|
||||
ln(): Decimal;
|
||||
|
||||
negated(): Decimal;
|
||||
neg(): Decimal;
|
||||
|
||||
plus(n: Decimal.Value): Decimal;
|
||||
add(n: Decimal.Value): Decimal;
|
||||
|
||||
precision(includeZeros?: boolean): number;
|
||||
sd(includeZeros?: boolean): number;
|
||||
|
||||
round(): Decimal;
|
||||
|
||||
sine() : Decimal;
|
||||
sin() : Decimal;
|
||||
|
||||
squareRoot(): Decimal;
|
||||
sqrt(): Decimal;
|
||||
|
||||
tangent() : Decimal;
|
||||
tan() : Decimal;
|
||||
|
||||
times(n: Decimal.Value): Decimal;
|
||||
mul(n: Decimal.Value) : Decimal;
|
||||
|
||||
toBinary(significantDigits?: number): string;
|
||||
toBinary(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toDecimalPlaces(decimalPlaces?: number): Decimal;
|
||||
toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
|
||||
toDP(decimalPlaces?: number): Decimal;
|
||||
toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
|
||||
|
||||
toExponential(decimalPlaces?: number): string;
|
||||
toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toFixed(decimalPlaces?: number): string;
|
||||
toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toFraction(max_denominator?: Decimal.Value): Decimal[];
|
||||
|
||||
toHexadecimal(significantDigits?: number): string;
|
||||
toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
toHex(significantDigits?: number): string;
|
||||
toHex(significantDigits: number, rounding?: Decimal.Rounding): string;
|
||||
|
||||
toJSON(): string;
|
||||
|
||||
toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal;
|
||||
|
||||
toNumber(): number;
|
||||
|
||||
toOctal(significantDigits?: number): string;
|
||||
toOctal(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toPower(n: Decimal.Value): Decimal;
|
||||
pow(n: Decimal.Value): Decimal;
|
||||
|
||||
toPrecision(significantDigits?: number): string;
|
||||
toPrecision(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toSignificantDigits(significantDigits?: number): Decimal;
|
||||
toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal;
|
||||
toSD(significantDigits?: number): Decimal;
|
||||
toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal;
|
||||
|
||||
toString(): string;
|
||||
|
||||
truncated(): Decimal;
|
||||
trunc(): Decimal;
|
||||
|
||||
valueOf(): string;
|
||||
|
||||
static abs(n: Decimal.Value): Decimal;
|
||||
static acos(n: Decimal.Value): Decimal;
|
||||
static acosh(n: Decimal.Value): Decimal;
|
||||
static add(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static asin(n: Decimal.Value): Decimal;
|
||||
static asinh(n: Decimal.Value): Decimal;
|
||||
static atan(n: Decimal.Value): Decimal;
|
||||
static atanh(n: Decimal.Value): Decimal;
|
||||
static atan2(y: Decimal.Value, x: Decimal.Value): Decimal;
|
||||
static cbrt(n: Decimal.Value): Decimal;
|
||||
static ceil(n: Decimal.Value): Decimal;
|
||||
static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||
static clone(object?: Decimal.Config): Decimal.Constructor;
|
||||
static config(object: Decimal.Config): Decimal.Constructor;
|
||||
static cos(n: Decimal.Value): Decimal;
|
||||
static cosh(n: Decimal.Value): Decimal;
|
||||
static div(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static exp(n: Decimal.Value): Decimal;
|
||||
static floor(n: Decimal.Value): Decimal;
|
||||
static hypot(...n: Decimal.Value[]): Decimal;
|
||||
static isDecimal(object: any): object is Decimal;
|
||||
static ln(n: Decimal.Value): Decimal;
|
||||
static log(n: Decimal.Value, base?: Decimal.Value): Decimal;
|
||||
static log2(n: Decimal.Value): Decimal;
|
||||
static log10(n: Decimal.Value): Decimal;
|
||||
static max(...n: Decimal.Value[]): Decimal;
|
||||
static min(...n: Decimal.Value[]): Decimal;
|
||||
static mod(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static mul(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static noConflict(): Decimal.Constructor; // Browser only
|
||||
static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal;
|
||||
static random(significantDigits?: number): Decimal;
|
||||
static round(n: Decimal.Value): Decimal;
|
||||
static set(object: Decimal.Config): Decimal.Constructor;
|
||||
static sign(n: Decimal.Value): number;
|
||||
static sin(n: Decimal.Value): Decimal;
|
||||
static sinh(n: Decimal.Value): Decimal;
|
||||
static sqrt(n: Decimal.Value): Decimal;
|
||||
static sub(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static sum(...n: Decimal.Value[]): Decimal;
|
||||
static tan(n: Decimal.Value): Decimal;
|
||||
static tanh(n: Decimal.Value): Decimal;
|
||||
static trunc(n: Decimal.Value): Decimal;
|
||||
|
||||
static readonly default?: Decimal.Constructor;
|
||||
static readonly Decimal?: Decimal.Constructor;
|
||||
|
||||
static readonly precision: number;
|
||||
static readonly rounding: Decimal.Rounding;
|
||||
static readonly toExpNeg: number;
|
||||
static readonly toExpPos: number;
|
||||
static readonly minE: number;
|
||||
static readonly maxE: number;
|
||||
static readonly crypto: boolean;
|
||||
static readonly modulo: Decimal.Modulo;
|
||||
|
||||
static readonly ROUND_UP: 0;
|
||||
static readonly ROUND_DOWN: 1;
|
||||
static readonly ROUND_CEIL: 2;
|
||||
static readonly ROUND_FLOOR: 3;
|
||||
static readonly ROUND_HALF_UP: 4;
|
||||
static readonly ROUND_HALF_DOWN: 5;
|
||||
static readonly ROUND_HALF_EVEN: 6;
|
||||
static readonly ROUND_HALF_CEIL: 7;
|
||||
static readonly ROUND_HALF_FLOOR: 8;
|
||||
static readonly EUCLID: 9;
|
||||
}
|
||||
|
||||
declare type Exact<A, W> = (A extends unknown ? (W extends A ? {
|
||||
[K in keyof A]: Exact<A[K], W[K]>;
|
||||
} : W) : never) | (A extends Narrowable ? A : never);
|
||||
|
||||
export declare function getRuntime(): GetRuntimeOutput;
|
||||
|
||||
declare type GetRuntimeOutput = {
|
||||
id: RuntimeName;
|
||||
prettyName: string;
|
||||
isEdge: boolean;
|
||||
};
|
||||
|
||||
declare class JsonNull extends NullTypesEnumValue {
|
||||
#private;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates more strict variant of an enum which, unlike regular enum,
|
||||
* throws on non-existing property access. This can be useful in following situations:
|
||||
* - we have an API, that accepts both `undefined` and `SomeEnumType` as an input
|
||||
* - enum values are generated dynamically from DMMF.
|
||||
*
|
||||
* In that case, if using normal enums and no compile-time typechecking, using non-existing property
|
||||
* will result in `undefined` value being used, which will be accepted. Using strict enum
|
||||
* in this case will help to have a runtime exception, telling you that you are probably doing something wrong.
|
||||
*
|
||||
* Note: if you need to check for existence of a value in the enum you can still use either
|
||||
* `in` operator or `hasOwnProperty` function.
|
||||
*
|
||||
* @param definition
|
||||
* @returns
|
||||
*/
|
||||
export declare function makeStrictEnum<T extends Record<PropertyKey, string | number>>(definition: T): T;
|
||||
|
||||
declare type Narrowable = string | number | bigint | boolean | [];
|
||||
|
||||
declare class NullTypesEnumValue extends ObjectEnumValue {
|
||||
_getNamespace(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for unique values of object-valued enums.
|
||||
*/
|
||||
declare abstract class ObjectEnumValue {
|
||||
constructor(arg?: symbol);
|
||||
abstract _getNamespace(): string;
|
||||
_getName(): string;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export declare const objectEnumValues: {
|
||||
classes: {
|
||||
DbNull: typeof DbNull;
|
||||
JsonNull: typeof JsonNull;
|
||||
AnyNull: typeof AnyNull;
|
||||
};
|
||||
instances: {
|
||||
DbNull: DbNull;
|
||||
JsonNull: JsonNull;
|
||||
AnyNull: AnyNull;
|
||||
};
|
||||
};
|
||||
|
||||
declare type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy' | '$queryRaw' | '$executeRaw' | '$queryRawUnsafe' | '$executeRawUnsafe' | 'findRaw' | 'aggregateRaw' | '$runCommandRaw';
|
||||
|
||||
declare namespace Public {
|
||||
export {
|
||||
validator
|
||||
}
|
||||
}
|
||||
export { Public }
|
||||
|
||||
declare type RuntimeName = 'workerd' | 'deno' | 'netlify' | 'node' | 'bun' | 'edge-light' | '';
|
||||
|
||||
declare function validator<V>(): <S>(select: Exact<S, V>) => S;
|
||||
|
||||
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation>(client: C, model: M, operation: O): <S>(select: Exact<S, Args<C[M], O>>) => S;
|
||||
|
||||
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation, P extends keyof Args<C[M], O>>(client: C, model: M, operation: O, prop: P): <S>(select: Exact<S, Args<C[M], O>[P]>) => S;
|
||||
|
||||
export { }
|
||||
File diff suppressed because one or more lines are too long
-3982
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,66 +0,0 @@
|
||||
generator authClient {
|
||||
provider = "prisma-client-js"
|
||||
output = "../src/generated/auth-client"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("AUTH_DATABASE_URL")
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
ADMIN
|
||||
USER
|
||||
}
|
||||
|
||||
enum LicenseCode {
|
||||
FREE
|
||||
EDUCATION
|
||||
PROFESSIONAL
|
||||
}
|
||||
|
||||
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[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
export default import('./query_engine_bg.wasm?module')
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
export default import('./query_engine_bg.wasm')
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
export * from "./default"
|
||||
@@ -1,238 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
PrismaClientKnownRequestError,
|
||||
PrismaClientUnknownRequestError,
|
||||
PrismaClientRustPanicError,
|
||||
PrismaClientInitializationError,
|
||||
PrismaClientValidationError,
|
||||
getPrismaClient,
|
||||
sqltag,
|
||||
empty,
|
||||
join,
|
||||
raw,
|
||||
skip,
|
||||
Decimal,
|
||||
Debug,
|
||||
objectEnumValues,
|
||||
makeStrictEnum,
|
||||
Extensions,
|
||||
warnOnce,
|
||||
defineDmmfProperty,
|
||||
Public,
|
||||
getRuntime,
|
||||
createParam,
|
||||
} = require('./runtime/wasm-engine-edge.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 6.19.3
|
||||
* Query Engine version: c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "6.19.3",
|
||||
engine: "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
|
||||
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
|
||||
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
|
||||
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
|
||||
Prisma.PrismaClientValidationError = PrismaClientValidationError
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = sqltag
|
||||
Prisma.empty = empty
|
||||
Prisma.join = join
|
||||
Prisma.raw = raw
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = Extensions.getExtensionContext
|
||||
Prisma.defineExtension = Extensions.defineExtension
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = objectEnumValues.instances.DbNull
|
||||
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
||||
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
||||
|
||||
Prisma.NullTypes = {
|
||||
DbNull: objectEnumValues.classes.DbNull,
|
||||
JsonNull: objectEnumValues.classes.JsonNull,
|
||||
AnyNull: objectEnumValues.classes.AnyNull
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
ReadUncommitted: 'ReadUncommitted',
|
||||
ReadCommitted: 'ReadCommitted',
|
||||
RepeatableRead: 'RepeatableRead',
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
email: 'email',
|
||||
passwordHash: 'passwordHash',
|
||||
name: 'name',
|
||||
role: 'role',
|
||||
isActive: 'isActive',
|
||||
lastLoginAt: 'lastLoginAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.LicenseScalarFieldEnum = {
|
||||
id: 'id',
|
||||
code: 'code',
|
||||
name: 'name',
|
||||
description: 'description',
|
||||
defaultDurationDays: 'defaultDurationDays',
|
||||
isActive: 'isActive',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserLicenseScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
licenseId: 'licenseId',
|
||||
startsAt: 'startsAt',
|
||||
expiresAt: 'expiresAt',
|
||||
assignedById: 'assignedById',
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.QueryMode = {
|
||||
default: 'default',
|
||||
insensitive: 'insensitive'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
exports.UserRole = exports.$Enums.UserRole = {
|
||||
ADMIN: 'ADMIN',
|
||||
USER: 'USER'
|
||||
};
|
||||
|
||||
exports.LicenseCode = exports.$Enums.LicenseCode = {
|
||||
FREE: 'FREE',
|
||||
EDUCATION: 'EDUCATION',
|
||||
PROFESSIONAL: 'PROFESSIONAL'
|
||||
};
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
User: 'User',
|
||||
License: 'License',
|
||||
UserLicense: 'UserLicense'
|
||||
};
|
||||
/**
|
||||
* Create the Client
|
||||
*/
|
||||
const config = {
|
||||
"generator": {
|
||||
"name": "authClient",
|
||||
"provider": {
|
||||
"fromEnvVar": null,
|
||||
"value": "prisma-client-js"
|
||||
},
|
||||
"output": {
|
||||
"value": "/home/chyoo/tmp/atnmy-stdo-v2/atnmy-stdo-adm-v2-be/src/generated/auth-client",
|
||||
"fromEnvVar": null
|
||||
},
|
||||
"config": {
|
||||
"engineType": "library"
|
||||
},
|
||||
"binaryTargets": [
|
||||
{
|
||||
"fromEnvVar": null,
|
||||
"value": "debian-openssl-3.0.x",
|
||||
"native": true
|
||||
}
|
||||
],
|
||||
"previewFeatures": [],
|
||||
"sourceFilePath": "/home/chyoo/tmp/atnmy-stdo-v2/atnmy-stdo-adm-v2-be/prisma/auth.schema.prisma",
|
||||
"isCustomOutput": true
|
||||
},
|
||||
"relativeEnvPaths": {
|
||||
"rootEnvPath": null,
|
||||
"schemaEnvPath": "../../../.env"
|
||||
},
|
||||
"relativePath": "../../../prisma",
|
||||
"clientVersion": "6.19.3",
|
||||
"engineVersion": "c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
|
||||
"datasourceNames": [
|
||||
"db"
|
||||
],
|
||||
"activeProvider": "postgresql",
|
||||
"postinstall": false,
|
||||
"inlineDatasources": {
|
||||
"db": {
|
||||
"url": {
|
||||
"fromEnvVar": "AUTH_DATABASE_URL",
|
||||
"value": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"inlineSchema": "generator authClient {\n provider = \"prisma-client-js\"\n output = \"../src/generated/auth-client\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"AUTH_DATABASE_URL\")\n}\n\nenum UserRole {\n ADMIN\n USER\n}\n\nenum LicenseCode {\n FREE\n EDUCATION\n PROFESSIONAL\n}\n\nmodel User {\n id String @id @default(uuid()) @db.Uuid\n email String @unique\n passwordHash String @map(\"password_hash\")\n name String?\n role UserRole @default(USER)\n isActive Boolean @default(true) @map(\"is_active\")\n lastLoginAt DateTime? @map(\"last_login_at\") @db.Timestamptz(3)\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamptz(3)\n updatedAt DateTime @updatedAt @map(\"updated_at\") @db.Timestamptz(3)\n userLicenses UserLicense[]\n\n @@map(\"users\")\n}\n\nmodel License {\n id String @id @default(uuid()) @db.Uuid\n code LicenseCode @unique\n name String\n description String?\n defaultDurationDays Int? @default(365) @map(\"default_duration_days\")\n isActive Boolean @default(true) @map(\"is_active\")\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamptz(3)\n updatedAt DateTime @updatedAt @map(\"updated_at\") @db.Timestamptz(3)\n userLicenses UserLicense[]\n\n @@index([isActive])\n @@map(\"licenses\")\n}\n\nmodel UserLicense {\n id String @id @default(uuid()) @db.Uuid\n userId String @map(\"user_id\") @db.Uuid\n licenseId String @map(\"license_id\") @db.Uuid\n startsAt DateTime @default(now()) @map(\"starts_at\") @db.Timestamptz(3)\n expiresAt DateTime? @map(\"expires_at\") @db.Timestamptz(3)\n assignedById String? @map(\"assigned_by\") @db.Uuid\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamptz(3)\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n license License @relation(fields: [licenseId], references: [id], onDelete: Restrict)\n\n @@index([userId, startsAt])\n @@index([licenseId])\n @@map(\"user_licenses\")\n}\n",
|
||||
"inlineSchemaHash": "b41ead22c04f404adbe239bd44c2751b9ed9adf3c5b7b7013be05252086f79d7",
|
||||
"copyEngine": true
|
||||
}
|
||||
config.dirname = '/'
|
||||
|
||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"password_hash\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"UserRole\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"type\":\"Boolean\",\"dbName\":\"is_active\"},{\"name\":\"lastLoginAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"last_login_at\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"updated_at\"},{\"name\":\"userLicenses\",\"kind\":\"object\",\"type\":\"UserLicense\",\"relationName\":\"UserToUserLicense\"}],\"dbName\":\"users\"},\"License\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"code\",\"kind\":\"enum\",\"type\":\"LicenseCode\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"defaultDurationDays\",\"kind\":\"scalar\",\"type\":\"Int\",\"dbName\":\"default_duration_days\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"type\":\"Boolean\",\"dbName\":\"is_active\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"updated_at\"},{\"name\":\"userLicenses\",\"kind\":\"object\",\"type\":\"UserLicense\",\"relationName\":\"LicenseToUserLicense\"}],\"dbName\":\"licenses\"},\"UserLicense\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"user_id\"},{\"name\":\"licenseId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"license_id\"},{\"name\":\"startsAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"starts_at\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"expires_at\"},{\"name\":\"assignedById\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"assigned_by\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"UserToUserLicense\"},{\"name\":\"license\",\"kind\":\"object\",\"type\":\"License\",\"relationName\":\"LicenseToUserLicense\"}],\"dbName\":\"user_licenses\"}},\"enums\":{},\"types\":{}}")
|
||||
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
||||
config.engineWasm = {
|
||||
getRuntime: async () => require('./query_engine_bg.js'),
|
||||
getQueryEngineWasmModule: async () => {
|
||||
const loader = (await import('#wasm-engine-loader')).default
|
||||
const engine = (await loader).default
|
||||
return engine
|
||||
}
|
||||
}
|
||||
config.compilerWasm = undefined
|
||||
|
||||
config.injectableEdgeEnv = () => ({
|
||||
parsed: {
|
||||
AUTH_DATABASE_URL: typeof globalThis !== 'undefined' && globalThis['AUTH_DATABASE_URL'] || typeof process !== 'undefined' && process.env && process.env.AUTH_DATABASE_URL || undefined
|
||||
}
|
||||
})
|
||||
|
||||
if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined) {
|
||||
Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined)
|
||||
}
|
||||
|
||||
const PrismaClient = getPrismaClient(config)
|
||||
exports.PrismaClient = PrismaClient
|
||||
Object.assign(exports, Prisma)
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./index"
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('.') }
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./index"
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('#main-entry-point') }
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./default"
|
||||
File diff suppressed because one or more lines are too long
@@ -1,233 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
Decimal,
|
||||
objectEnumValues,
|
||||
makeStrictEnum,
|
||||
Public,
|
||||
getRuntime,
|
||||
skip
|
||||
} = require('./runtime/index-browser.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 6.19.3
|
||||
* Query Engine version: c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "6.19.3",
|
||||
engine: "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)};
|
||||
Prisma.PrismaClientUnknownRequestError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientRustPanicError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientInitializationError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientValidationError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.empty = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.join = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.raw = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.defineExtension = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = objectEnumValues.instances.DbNull
|
||||
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
||||
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
||||
|
||||
Prisma.NullTypes = {
|
||||
DbNull: objectEnumValues.classes.DbNull,
|
||||
JsonNull: objectEnumValues.classes.JsonNull,
|
||||
AnyNull: objectEnumValues.classes.AnyNull
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
ReadUncommitted: 'ReadUncommitted',
|
||||
ReadCommitted: 'ReadCommitted',
|
||||
RepeatableRead: 'RepeatableRead',
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.LauncherNoticeScalarFieldEnum = {
|
||||
id: 'id',
|
||||
appKey: 'appKey',
|
||||
title: 'title',
|
||||
body: 'body',
|
||||
linkUrl: 'linkUrl',
|
||||
isActive: 'isActive',
|
||||
startsAt: 'startsAt',
|
||||
endsAt: 'endsAt',
|
||||
priority: 'priority',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.ProvisionedDeviceScalarFieldEnum = {
|
||||
id: 'id',
|
||||
accountEmail: 'accountEmail',
|
||||
authUserId: 'authUserId',
|
||||
hostname: 'hostname',
|
||||
tailscaleHostname: 'tailscaleHostname',
|
||||
sshUser: 'sshUser',
|
||||
workspaceRoot: 'workspaceRoot',
|
||||
usageType: 'usageType',
|
||||
os: 'os',
|
||||
arch: 'arch',
|
||||
kernel: 'kernel',
|
||||
distro: 'distro',
|
||||
device: 'device',
|
||||
installedAt: 'installedAt',
|
||||
lastProvisionedAt: 'lastProvisionedAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.LauncherSshKeyScalarFieldEnum = {
|
||||
id: 'id',
|
||||
accountEmail: 'accountEmail',
|
||||
authUserId: 'authUserId',
|
||||
publicKey: 'publicKey',
|
||||
publicKeyHash: 'publicKeyHash',
|
||||
keyType: 'keyType',
|
||||
label: 'label',
|
||||
lastProvisionedAt: 'lastProvisionedAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.NullableJsonNullValueInput = {
|
||||
DbNull: Prisma.DbNull,
|
||||
JsonNull: Prisma.JsonNull
|
||||
};
|
||||
|
||||
exports.Prisma.QueryMode = {
|
||||
default: 'default',
|
||||
insensitive: 'insensitive'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
|
||||
exports.Prisma.JsonNullValueFilter = {
|
||||
DbNull: Prisma.DbNull,
|
||||
JsonNull: Prisma.JsonNull,
|
||||
AnyNull: Prisma.AnyNull
|
||||
};
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
LauncherNotice: 'LauncherNotice',
|
||||
ProvisionedDevice: 'ProvisionedDevice',
|
||||
LauncherSshKey: 'LauncherSshKey'
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a stub Prisma Client that will error at runtime if called.
|
||||
*/
|
||||
class PrismaClient {
|
||||
constructor() {
|
||||
return new Proxy(this, {
|
||||
get(target, prop) {
|
||||
let message
|
||||
const runtime = getRuntime()
|
||||
if (runtime.isEdge) {
|
||||
message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
|
||||
- Use Prisma Accelerate: https://pris.ly/d/accelerate
|
||||
- Use Driver Adapters: https://pris.ly/d/driver-adapters
|
||||
`;
|
||||
} else {
|
||||
message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).'
|
||||
}
|
||||
|
||||
message += `
|
||||
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
|
||||
|
||||
throw new Error(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.PrismaClient = PrismaClient
|
||||
|
||||
Object.assign(exports, Prisma)
|
||||
-5870
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,183 +0,0 @@
|
||||
{
|
||||
"name": "prisma-client-2cc4fd701019d0254ce12699ae25121c6ed85eba27006e31abbdac7779ae92f0",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"browser": "default.js",
|
||||
"exports": {
|
||||
"./client": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./edge": {
|
||||
"types": "./edge.d.ts",
|
||||
"require": "./edge.js",
|
||||
"import": "./edge.js",
|
||||
"default": "./edge.js"
|
||||
},
|
||||
"./react-native": {
|
||||
"types": "./react-native.d.ts",
|
||||
"require": "./react-native.js",
|
||||
"import": "./react-native.js",
|
||||
"default": "./react-native.js"
|
||||
},
|
||||
"./extension": {
|
||||
"types": "./extension.d.ts",
|
||||
"require": "./extension.js",
|
||||
"import": "./extension.js",
|
||||
"default": "./extension.js"
|
||||
},
|
||||
"./index-browser": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index-browser.js",
|
||||
"import": "./index-browser.js",
|
||||
"default": "./index-browser.js"
|
||||
},
|
||||
"./index": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index.js",
|
||||
"import": "./index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./wasm": {
|
||||
"types": "./wasm.d.ts",
|
||||
"require": "./wasm.js",
|
||||
"import": "./wasm.mjs",
|
||||
"default": "./wasm.mjs"
|
||||
},
|
||||
"./runtime/client": {
|
||||
"types": "./runtime/client.d.ts",
|
||||
"node": {
|
||||
"require": "./runtime/client.js",
|
||||
"default": "./runtime/client.js"
|
||||
},
|
||||
"require": "./runtime/client.js",
|
||||
"import": "./runtime/client.mjs",
|
||||
"default": "./runtime/client.mjs"
|
||||
},
|
||||
"./runtime/library": {
|
||||
"types": "./runtime/library.d.ts",
|
||||
"require": "./runtime/library.js",
|
||||
"import": "./runtime/library.mjs",
|
||||
"default": "./runtime/library.mjs"
|
||||
},
|
||||
"./runtime/binary": {
|
||||
"types": "./runtime/binary.d.ts",
|
||||
"require": "./runtime/binary.js",
|
||||
"import": "./runtime/binary.mjs",
|
||||
"default": "./runtime/binary.mjs"
|
||||
},
|
||||
"./runtime/wasm-engine-edge": {
|
||||
"types": "./runtime/wasm-engine-edge.d.ts",
|
||||
"require": "./runtime/wasm-engine-edge.js",
|
||||
"import": "./runtime/wasm-engine-edge.mjs",
|
||||
"default": "./runtime/wasm-engine-edge.mjs"
|
||||
},
|
||||
"./runtime/wasm-compiler-edge": {
|
||||
"types": "./runtime/wasm-compiler-edge.d.ts",
|
||||
"require": "./runtime/wasm-compiler-edge.js",
|
||||
"import": "./runtime/wasm-compiler-edge.mjs",
|
||||
"default": "./runtime/wasm-compiler-edge.mjs"
|
||||
},
|
||||
"./runtime/edge": {
|
||||
"types": "./runtime/edge.d.ts",
|
||||
"require": "./runtime/edge.js",
|
||||
"import": "./runtime/edge-esm.js",
|
||||
"default": "./runtime/edge-esm.js"
|
||||
},
|
||||
"./runtime/react-native": {
|
||||
"types": "./runtime/react-native.d.ts",
|
||||
"require": "./runtime/react-native.js",
|
||||
"import": "./runtime/react-native.js",
|
||||
"default": "./runtime/react-native.js"
|
||||
},
|
||||
"./runtime/index-browser": {
|
||||
"types": "./runtime/index-browser.d.ts",
|
||||
"require": "./runtime/index-browser.js",
|
||||
"import": "./runtime/index-browser.mjs",
|
||||
"default": "./runtime/index-browser.mjs"
|
||||
},
|
||||
"./generator-build": {
|
||||
"require": "./generator-build/index.js",
|
||||
"import": "./generator-build/index.js",
|
||||
"default": "./generator-build/index.js"
|
||||
},
|
||||
"./sql": {
|
||||
"require": {
|
||||
"types": "./sql.d.ts",
|
||||
"node": "./sql.js",
|
||||
"default": "./sql.js"
|
||||
},
|
||||
"import": {
|
||||
"types": "./sql.d.ts",
|
||||
"node": "./sql.mjs",
|
||||
"default": "./sql.mjs"
|
||||
},
|
||||
"default": "./sql.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"version": "6.19.3",
|
||||
"sideEffects": false,
|
||||
"imports": {
|
||||
"#wasm-engine-loader": {
|
||||
"edge-light": "./wasm-edge-light-loader.mjs",
|
||||
"workerd": "./wasm-worker-loader.mjs",
|
||||
"worker": "./wasm-worker-loader.mjs",
|
||||
"default": "./wasm-worker-loader.mjs"
|
||||
},
|
||||
"#main-entry-point": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./wasm.js",
|
||||
"workerd": "./wasm.js",
|
||||
"worker": "./wasm.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,370 +0,0 @@
|
||||
declare class AnyNull extends NullTypesEnumValue {
|
||||
#private;
|
||||
}
|
||||
|
||||
declare type Args<T, F extends Operation> = T extends {
|
||||
[K: symbol]: {
|
||||
types: {
|
||||
operations: {
|
||||
[K in F]: {
|
||||
args: any;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
} ? T[symbol]['types']['operations'][F]['args'] : any;
|
||||
|
||||
declare class DbNull extends NullTypesEnumValue {
|
||||
#private;
|
||||
}
|
||||
|
||||
export declare function Decimal(n: Decimal.Value): Decimal;
|
||||
|
||||
export declare namespace Decimal {
|
||||
export type Constructor = typeof Decimal;
|
||||
export type Instance = Decimal;
|
||||
export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
||||
export type Modulo = Rounding | 9;
|
||||
export type Value = string | number | Decimal;
|
||||
|
||||
// http://mikemcl.github.io/decimal.js/#constructor-properties
|
||||
export interface Config {
|
||||
precision?: number;
|
||||
rounding?: Rounding;
|
||||
toExpNeg?: number;
|
||||
toExpPos?: number;
|
||||
minE?: number;
|
||||
maxE?: number;
|
||||
crypto?: boolean;
|
||||
modulo?: Modulo;
|
||||
defaults?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export declare class Decimal {
|
||||
readonly d: number[];
|
||||
readonly e: number;
|
||||
readonly s: number;
|
||||
|
||||
constructor(n: Decimal.Value);
|
||||
|
||||
absoluteValue(): Decimal;
|
||||
abs(): Decimal;
|
||||
|
||||
ceil(): Decimal;
|
||||
|
||||
clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||
clamp(min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||
|
||||
comparedTo(n: Decimal.Value): number;
|
||||
cmp(n: Decimal.Value): number;
|
||||
|
||||
cosine(): Decimal;
|
||||
cos(): Decimal;
|
||||
|
||||
cubeRoot(): Decimal;
|
||||
cbrt(): Decimal;
|
||||
|
||||
decimalPlaces(): number;
|
||||
dp(): number;
|
||||
|
||||
dividedBy(n: Decimal.Value): Decimal;
|
||||
div(n: Decimal.Value): Decimal;
|
||||
|
||||
dividedToIntegerBy(n: Decimal.Value): Decimal;
|
||||
divToInt(n: Decimal.Value): Decimal;
|
||||
|
||||
equals(n: Decimal.Value): boolean;
|
||||
eq(n: Decimal.Value): boolean;
|
||||
|
||||
floor(): Decimal;
|
||||
|
||||
greaterThan(n: Decimal.Value): boolean;
|
||||
gt(n: Decimal.Value): boolean;
|
||||
|
||||
greaterThanOrEqualTo(n: Decimal.Value): boolean;
|
||||
gte(n: Decimal.Value): boolean;
|
||||
|
||||
hyperbolicCosine(): Decimal;
|
||||
cosh(): Decimal;
|
||||
|
||||
hyperbolicSine(): Decimal;
|
||||
sinh(): Decimal;
|
||||
|
||||
hyperbolicTangent(): Decimal;
|
||||
tanh(): Decimal;
|
||||
|
||||
inverseCosine(): Decimal;
|
||||
acos(): Decimal;
|
||||
|
||||
inverseHyperbolicCosine(): Decimal;
|
||||
acosh(): Decimal;
|
||||
|
||||
inverseHyperbolicSine(): Decimal;
|
||||
asinh(): Decimal;
|
||||
|
||||
inverseHyperbolicTangent(): Decimal;
|
||||
atanh(): Decimal;
|
||||
|
||||
inverseSine(): Decimal;
|
||||
asin(): Decimal;
|
||||
|
||||
inverseTangent(): Decimal;
|
||||
atan(): Decimal;
|
||||
|
||||
isFinite(): boolean;
|
||||
|
||||
isInteger(): boolean;
|
||||
isInt(): boolean;
|
||||
|
||||
isNaN(): boolean;
|
||||
|
||||
isNegative(): boolean;
|
||||
isNeg(): boolean;
|
||||
|
||||
isPositive(): boolean;
|
||||
isPos(): boolean;
|
||||
|
||||
isZero(): boolean;
|
||||
|
||||
lessThan(n: Decimal.Value): boolean;
|
||||
lt(n: Decimal.Value): boolean;
|
||||
|
||||
lessThanOrEqualTo(n: Decimal.Value): boolean;
|
||||
lte(n: Decimal.Value): boolean;
|
||||
|
||||
logarithm(n?: Decimal.Value): Decimal;
|
||||
log(n?: Decimal.Value): Decimal;
|
||||
|
||||
minus(n: Decimal.Value): Decimal;
|
||||
sub(n: Decimal.Value): Decimal;
|
||||
|
||||
modulo(n: Decimal.Value): Decimal;
|
||||
mod(n: Decimal.Value): Decimal;
|
||||
|
||||
naturalExponential(): Decimal;
|
||||
exp(): Decimal;
|
||||
|
||||
naturalLogarithm(): Decimal;
|
||||
ln(): Decimal;
|
||||
|
||||
negated(): Decimal;
|
||||
neg(): Decimal;
|
||||
|
||||
plus(n: Decimal.Value): Decimal;
|
||||
add(n: Decimal.Value): Decimal;
|
||||
|
||||
precision(includeZeros?: boolean): number;
|
||||
sd(includeZeros?: boolean): number;
|
||||
|
||||
round(): Decimal;
|
||||
|
||||
sine() : Decimal;
|
||||
sin() : Decimal;
|
||||
|
||||
squareRoot(): Decimal;
|
||||
sqrt(): Decimal;
|
||||
|
||||
tangent() : Decimal;
|
||||
tan() : Decimal;
|
||||
|
||||
times(n: Decimal.Value): Decimal;
|
||||
mul(n: Decimal.Value) : Decimal;
|
||||
|
||||
toBinary(significantDigits?: number): string;
|
||||
toBinary(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toDecimalPlaces(decimalPlaces?: number): Decimal;
|
||||
toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
|
||||
toDP(decimalPlaces?: number): Decimal;
|
||||
toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
|
||||
|
||||
toExponential(decimalPlaces?: number): string;
|
||||
toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toFixed(decimalPlaces?: number): string;
|
||||
toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toFraction(max_denominator?: Decimal.Value): Decimal[];
|
||||
|
||||
toHexadecimal(significantDigits?: number): string;
|
||||
toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
toHex(significantDigits?: number): string;
|
||||
toHex(significantDigits: number, rounding?: Decimal.Rounding): string;
|
||||
|
||||
toJSON(): string;
|
||||
|
||||
toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal;
|
||||
|
||||
toNumber(): number;
|
||||
|
||||
toOctal(significantDigits?: number): string;
|
||||
toOctal(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toPower(n: Decimal.Value): Decimal;
|
||||
pow(n: Decimal.Value): Decimal;
|
||||
|
||||
toPrecision(significantDigits?: number): string;
|
||||
toPrecision(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||
|
||||
toSignificantDigits(significantDigits?: number): Decimal;
|
||||
toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal;
|
||||
toSD(significantDigits?: number): Decimal;
|
||||
toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal;
|
||||
|
||||
toString(): string;
|
||||
|
||||
truncated(): Decimal;
|
||||
trunc(): Decimal;
|
||||
|
||||
valueOf(): string;
|
||||
|
||||
static abs(n: Decimal.Value): Decimal;
|
||||
static acos(n: Decimal.Value): Decimal;
|
||||
static acosh(n: Decimal.Value): Decimal;
|
||||
static add(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static asin(n: Decimal.Value): Decimal;
|
||||
static asinh(n: Decimal.Value): Decimal;
|
||||
static atan(n: Decimal.Value): Decimal;
|
||||
static atanh(n: Decimal.Value): Decimal;
|
||||
static atan2(y: Decimal.Value, x: Decimal.Value): Decimal;
|
||||
static cbrt(n: Decimal.Value): Decimal;
|
||||
static ceil(n: Decimal.Value): Decimal;
|
||||
static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||
static clone(object?: Decimal.Config): Decimal.Constructor;
|
||||
static config(object: Decimal.Config): Decimal.Constructor;
|
||||
static cos(n: Decimal.Value): Decimal;
|
||||
static cosh(n: Decimal.Value): Decimal;
|
||||
static div(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static exp(n: Decimal.Value): Decimal;
|
||||
static floor(n: Decimal.Value): Decimal;
|
||||
static hypot(...n: Decimal.Value[]): Decimal;
|
||||
static isDecimal(object: any): object is Decimal;
|
||||
static ln(n: Decimal.Value): Decimal;
|
||||
static log(n: Decimal.Value, base?: Decimal.Value): Decimal;
|
||||
static log2(n: Decimal.Value): Decimal;
|
||||
static log10(n: Decimal.Value): Decimal;
|
||||
static max(...n: Decimal.Value[]): Decimal;
|
||||
static min(...n: Decimal.Value[]): Decimal;
|
||||
static mod(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static mul(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static noConflict(): Decimal.Constructor; // Browser only
|
||||
static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal;
|
||||
static random(significantDigits?: number): Decimal;
|
||||
static round(n: Decimal.Value): Decimal;
|
||||
static set(object: Decimal.Config): Decimal.Constructor;
|
||||
static sign(n: Decimal.Value): number;
|
||||
static sin(n: Decimal.Value): Decimal;
|
||||
static sinh(n: Decimal.Value): Decimal;
|
||||
static sqrt(n: Decimal.Value): Decimal;
|
||||
static sub(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||
static sum(...n: Decimal.Value[]): Decimal;
|
||||
static tan(n: Decimal.Value): Decimal;
|
||||
static tanh(n: Decimal.Value): Decimal;
|
||||
static trunc(n: Decimal.Value): Decimal;
|
||||
|
||||
static readonly default?: Decimal.Constructor;
|
||||
static readonly Decimal?: Decimal.Constructor;
|
||||
|
||||
static readonly precision: number;
|
||||
static readonly rounding: Decimal.Rounding;
|
||||
static readonly toExpNeg: number;
|
||||
static readonly toExpPos: number;
|
||||
static readonly minE: number;
|
||||
static readonly maxE: number;
|
||||
static readonly crypto: boolean;
|
||||
static readonly modulo: Decimal.Modulo;
|
||||
|
||||
static readonly ROUND_UP: 0;
|
||||
static readonly ROUND_DOWN: 1;
|
||||
static readonly ROUND_CEIL: 2;
|
||||
static readonly ROUND_FLOOR: 3;
|
||||
static readonly ROUND_HALF_UP: 4;
|
||||
static readonly ROUND_HALF_DOWN: 5;
|
||||
static readonly ROUND_HALF_EVEN: 6;
|
||||
static readonly ROUND_HALF_CEIL: 7;
|
||||
static readonly ROUND_HALF_FLOOR: 8;
|
||||
static readonly EUCLID: 9;
|
||||
}
|
||||
|
||||
declare type Exact<A, W> = (A extends unknown ? (W extends A ? {
|
||||
[K in keyof A]: Exact<A[K], W[K]>;
|
||||
} : W) : never) | (A extends Narrowable ? A : never);
|
||||
|
||||
export declare function getRuntime(): GetRuntimeOutput;
|
||||
|
||||
declare type GetRuntimeOutput = {
|
||||
id: RuntimeName;
|
||||
prettyName: string;
|
||||
isEdge: boolean;
|
||||
};
|
||||
|
||||
declare class JsonNull extends NullTypesEnumValue {
|
||||
#private;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates more strict variant of an enum which, unlike regular enum,
|
||||
* throws on non-existing property access. This can be useful in following situations:
|
||||
* - we have an API, that accepts both `undefined` and `SomeEnumType` as an input
|
||||
* - enum values are generated dynamically from DMMF.
|
||||
*
|
||||
* In that case, if using normal enums and no compile-time typechecking, using non-existing property
|
||||
* will result in `undefined` value being used, which will be accepted. Using strict enum
|
||||
* in this case will help to have a runtime exception, telling you that you are probably doing something wrong.
|
||||
*
|
||||
* Note: if you need to check for existence of a value in the enum you can still use either
|
||||
* `in` operator or `hasOwnProperty` function.
|
||||
*
|
||||
* @param definition
|
||||
* @returns
|
||||
*/
|
||||
export declare function makeStrictEnum<T extends Record<PropertyKey, string | number>>(definition: T): T;
|
||||
|
||||
declare type Narrowable = string | number | bigint | boolean | [];
|
||||
|
||||
declare class NullTypesEnumValue extends ObjectEnumValue {
|
||||
_getNamespace(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for unique values of object-valued enums.
|
||||
*/
|
||||
declare abstract class ObjectEnumValue {
|
||||
constructor(arg?: symbol);
|
||||
abstract _getNamespace(): string;
|
||||
_getName(): string;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export declare const objectEnumValues: {
|
||||
classes: {
|
||||
DbNull: typeof DbNull;
|
||||
JsonNull: typeof JsonNull;
|
||||
AnyNull: typeof AnyNull;
|
||||
};
|
||||
instances: {
|
||||
DbNull: DbNull;
|
||||
JsonNull: JsonNull;
|
||||
AnyNull: AnyNull;
|
||||
};
|
||||
};
|
||||
|
||||
declare type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy' | '$queryRaw' | '$executeRaw' | '$queryRawUnsafe' | '$executeRawUnsafe' | 'findRaw' | 'aggregateRaw' | '$runCommandRaw';
|
||||
|
||||
declare namespace Public {
|
||||
export {
|
||||
validator
|
||||
}
|
||||
}
|
||||
export { Public }
|
||||
|
||||
declare type RuntimeName = 'workerd' | 'deno' | 'netlify' | 'node' | 'bun' | 'edge-light' | '';
|
||||
|
||||
declare function validator<V>(): <S>(select: Exact<S, V>) => S;
|
||||
|
||||
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation>(client: C, model: M, operation: O): <S>(select: Exact<S, Args<C[M], O>>) => S;
|
||||
|
||||
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation, P extends keyof Args<C[M], O>>(client: C, model: M, operation: O, prop: P): <S>(select: Exact<S, Args<C[M], O>[P]>) => S;
|
||||
|
||||
export { }
|
||||
File diff suppressed because one or more lines are too long
-3982
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,68 +0,0 @@
|
||||
generator launcherClient {
|
||||
provider = "prisma-client-js"
|
||||
output = "../src/generated/launcher-client"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("LAUNCHER_DATABASE_URL")
|
||||
}
|
||||
|
||||
model LauncherNotice {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @default("login") @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")
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
export default import('./query_engine_bg.wasm?module')
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
export default import('./query_engine_bg.wasm')
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./default"
|
||||
@@ -1,254 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
PrismaClientKnownRequestError,
|
||||
PrismaClientUnknownRequestError,
|
||||
PrismaClientRustPanicError,
|
||||
PrismaClientInitializationError,
|
||||
PrismaClientValidationError,
|
||||
getPrismaClient,
|
||||
sqltag,
|
||||
empty,
|
||||
join,
|
||||
raw,
|
||||
skip,
|
||||
Decimal,
|
||||
Debug,
|
||||
objectEnumValues,
|
||||
makeStrictEnum,
|
||||
Extensions,
|
||||
warnOnce,
|
||||
defineDmmfProperty,
|
||||
Public,
|
||||
getRuntime,
|
||||
createParam,
|
||||
} = require('./runtime/wasm-engine-edge.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 6.19.3
|
||||
* Query Engine version: c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "6.19.3",
|
||||
engine: "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
|
||||
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
|
||||
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
|
||||
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
|
||||
Prisma.PrismaClientValidationError = PrismaClientValidationError
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = sqltag
|
||||
Prisma.empty = empty
|
||||
Prisma.join = join
|
||||
Prisma.raw = raw
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = Extensions.getExtensionContext
|
||||
Prisma.defineExtension = Extensions.defineExtension
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = objectEnumValues.instances.DbNull
|
||||
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
||||
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
||||
|
||||
Prisma.NullTypes = {
|
||||
DbNull: objectEnumValues.classes.DbNull,
|
||||
JsonNull: objectEnumValues.classes.JsonNull,
|
||||
AnyNull: objectEnumValues.classes.AnyNull
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
ReadUncommitted: 'ReadUncommitted',
|
||||
ReadCommitted: 'ReadCommitted',
|
||||
RepeatableRead: 'RepeatableRead',
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.LauncherNoticeScalarFieldEnum = {
|
||||
id: 'id',
|
||||
appKey: 'appKey',
|
||||
title: 'title',
|
||||
body: 'body',
|
||||
linkUrl: 'linkUrl',
|
||||
isActive: 'isActive',
|
||||
startsAt: 'startsAt',
|
||||
endsAt: 'endsAt',
|
||||
priority: 'priority',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.ProvisionedDeviceScalarFieldEnum = {
|
||||
id: 'id',
|
||||
accountEmail: 'accountEmail',
|
||||
authUserId: 'authUserId',
|
||||
hostname: 'hostname',
|
||||
tailscaleHostname: 'tailscaleHostname',
|
||||
sshUser: 'sshUser',
|
||||
workspaceRoot: 'workspaceRoot',
|
||||
usageType: 'usageType',
|
||||
os: 'os',
|
||||
arch: 'arch',
|
||||
kernel: 'kernel',
|
||||
distro: 'distro',
|
||||
device: 'device',
|
||||
installedAt: 'installedAt',
|
||||
lastProvisionedAt: 'lastProvisionedAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.LauncherSshKeyScalarFieldEnum = {
|
||||
id: 'id',
|
||||
accountEmail: 'accountEmail',
|
||||
authUserId: 'authUserId',
|
||||
publicKey: 'publicKey',
|
||||
publicKeyHash: 'publicKeyHash',
|
||||
keyType: 'keyType',
|
||||
label: 'label',
|
||||
lastProvisionedAt: 'lastProvisionedAt',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.NullableJsonNullValueInput = {
|
||||
DbNull: Prisma.DbNull,
|
||||
JsonNull: Prisma.JsonNull
|
||||
};
|
||||
|
||||
exports.Prisma.QueryMode = {
|
||||
default: 'default',
|
||||
insensitive: 'insensitive'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
|
||||
exports.Prisma.JsonNullValueFilter = {
|
||||
DbNull: Prisma.DbNull,
|
||||
JsonNull: Prisma.JsonNull,
|
||||
AnyNull: Prisma.AnyNull
|
||||
};
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
LauncherNotice: 'LauncherNotice',
|
||||
ProvisionedDevice: 'ProvisionedDevice',
|
||||
LauncherSshKey: 'LauncherSshKey'
|
||||
};
|
||||
/**
|
||||
* Create the Client
|
||||
*/
|
||||
const config = {
|
||||
"generator": {
|
||||
"name": "launcherClient",
|
||||
"provider": {
|
||||
"fromEnvVar": null,
|
||||
"value": "prisma-client-js"
|
||||
},
|
||||
"output": {
|
||||
"value": "/home/chyoo/tmp/atnmy-stdo-v2/atnmy-stdo-adm-v2-be/src/generated/launcher-client",
|
||||
"fromEnvVar": null
|
||||
},
|
||||
"config": {
|
||||
"engineType": "library"
|
||||
},
|
||||
"binaryTargets": [
|
||||
{
|
||||
"fromEnvVar": null,
|
||||
"value": "debian-openssl-3.0.x",
|
||||
"native": true
|
||||
}
|
||||
],
|
||||
"previewFeatures": [],
|
||||
"sourceFilePath": "/home/chyoo/tmp/atnmy-stdo-v2/atnmy-stdo-adm-v2-be/prisma/launcher.schema.prisma",
|
||||
"isCustomOutput": true
|
||||
},
|
||||
"relativeEnvPaths": {
|
||||
"rootEnvPath": null,
|
||||
"schemaEnvPath": "../../../.env"
|
||||
},
|
||||
"relativePath": "../../../prisma",
|
||||
"clientVersion": "6.19.3",
|
||||
"engineVersion": "c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
|
||||
"datasourceNames": [
|
||||
"db"
|
||||
],
|
||||
"activeProvider": "postgresql",
|
||||
"postinstall": false,
|
||||
"inlineDatasources": {
|
||||
"db": {
|
||||
"url": {
|
||||
"fromEnvVar": "LAUNCHER_DATABASE_URL",
|
||||
"value": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"inlineSchema": "generator launcherClient {\n provider = \"prisma-client-js\"\n output = \"../src/generated/launcher-client\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"LAUNCHER_DATABASE_URL\")\n}\n\nmodel LauncherNotice {\n id String @id @default(uuid()) @db.Uuid\n appKey String @default(\"login\") @map(\"app_key\")\n title String\n body String\n linkUrl String? @map(\"link_url\")\n isActive Boolean @default(true) @map(\"is_active\")\n startsAt DateTime? @map(\"starts_at\") @db.Timestamptz(3)\n endsAt DateTime? @map(\"ends_at\") @db.Timestamptz(3)\n priority Int @default(0)\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamptz(3)\n updatedAt DateTime @updatedAt @map(\"updated_at\") @db.Timestamptz(3)\n\n @@index([appKey, isActive, priority])\n @@map(\"launcher_notices\")\n}\n\nmodel ProvisionedDevice {\n id String @id @default(uuid()) @db.Uuid\n accountEmail String @map(\"account_email\")\n authUserId String? @map(\"auth_user_id\") @db.Uuid\n hostname String\n tailscaleHostname String @map(\"tailscale_hostname\")\n sshUser String? @map(\"ssh_user\")\n workspaceRoot String? @map(\"workspace_root\")\n usageType String @default(\"personal\") @map(\"usage_type\")\n os String?\n arch String?\n kernel String?\n distro String?\n device Json?\n installedAt DateTime @default(now()) @map(\"installed_at\") @db.Timestamptz(3)\n lastProvisionedAt DateTime @default(now()) @map(\"last_provisioned_at\") @db.Timestamptz(3)\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamptz(3)\n updatedAt DateTime @updatedAt @map(\"updated_at\") @db.Timestamptz(3)\n\n @@unique([accountEmail, tailscaleHostname])\n @@index([accountEmail])\n @@index([tailscaleHostname])\n @@map(\"provisioned_devices\")\n}\n\nmodel LauncherSshKey {\n id String @id @default(uuid()) @db.Uuid\n accountEmail String @map(\"account_email\")\n authUserId String? @map(\"auth_user_id\") @db.Uuid\n publicKey String @map(\"public_key\")\n publicKeyHash String @map(\"public_key_hash\")\n keyType String? @map(\"key_type\")\n label String?\n lastProvisionedAt DateTime @default(now()) @map(\"last_provisioned_at\") @db.Timestamptz(3)\n createdAt DateTime @default(now()) @map(\"created_at\") @db.Timestamptz(3)\n updatedAt DateTime @updatedAt @map(\"updated_at\") @db.Timestamptz(3)\n\n @@unique([accountEmail, publicKeyHash])\n @@index([accountEmail])\n @@map(\"launcher_ssh_keys\")\n}\n",
|
||||
"inlineSchemaHash": "ad20160732d203c5e1ea82cbcac0488ce94e47432c3136a3833df4661c0a049b",
|
||||
"copyEngine": true
|
||||
}
|
||||
config.dirname = '/'
|
||||
|
||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"LauncherNotice\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appKey\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"app_key\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"body\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"linkUrl\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"link_url\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"type\":\"Boolean\",\"dbName\":\"is_active\"},{\"name\":\"startsAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"starts_at\"},{\"name\":\"endsAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"ends_at\"},{\"name\":\"priority\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"updated_at\"}],\"dbName\":\"launcher_notices\"},\"ProvisionedDevice\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountEmail\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"account_email\"},{\"name\":\"authUserId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"auth_user_id\"},{\"name\":\"hostname\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tailscaleHostname\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"tailscale_hostname\"},{\"name\":\"sshUser\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"ssh_user\"},{\"name\":\"workspaceRoot\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"workspace_root\"},{\"name\":\"usageType\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"usage_type\"},{\"name\":\"os\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"arch\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"kernel\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"distro\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"device\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"installedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"installed_at\"},{\"name\":\"lastProvisionedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"last_provisioned_at\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"updated_at\"}],\"dbName\":\"provisioned_devices\"},\"LauncherSshKey\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountEmail\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"account_email\"},{\"name\":\"authUserId\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"auth_user_id\"},{\"name\":\"publicKey\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"public_key\"},{\"name\":\"publicKeyHash\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"public_key_hash\"},{\"name\":\"keyType\",\"kind\":\"scalar\",\"type\":\"String\",\"dbName\":\"key_type\"},{\"name\":\"label\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastProvisionedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"last_provisioned_at\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"created_at\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\",\"dbName\":\"updated_at\"}],\"dbName\":\"launcher_ssh_keys\"}},\"enums\":{},\"types\":{}}")
|
||||
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
||||
config.engineWasm = {
|
||||
getRuntime: async () => require('./query_engine_bg.js'),
|
||||
getQueryEngineWasmModule: async () => {
|
||||
const loader = (await import('#wasm-engine-loader')).default
|
||||
const engine = (await loader).default
|
||||
return engine
|
||||
}
|
||||
}
|
||||
config.compilerWasm = undefined
|
||||
|
||||
config.injectableEdgeEnv = () => ({
|
||||
parsed: {
|
||||
LAUNCHER_DATABASE_URL: typeof globalThis !== 'undefined' && globalThis['LAUNCHER_DATABASE_URL'] || typeof process !== 'undefined' && process.env && process.env.LAUNCHER_DATABASE_URL || undefined
|
||||
}
|
||||
})
|
||||
|
||||
if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined) {
|
||||
Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined)
|
||||
}
|
||||
|
||||
const PrismaClient = getPrismaClient(config)
|
||||
exports.PrismaClient = PrismaClient
|
||||
Object.assign(exports, Prisma)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
BadGatewayException,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
@@ -118,6 +119,10 @@ export class GiteaService {
|
||||
params.set('page', String(page));
|
||||
if (query.sha) {
|
||||
params.set('sha', query.sha);
|
||||
const { branches } = await this.listBranches(owner, repo) as { branches: Array<{ name?: string }> };
|
||||
if (!branches.some((branch) => branch.name === query.sha)) {
|
||||
throw new BadRequestException('해당 브랜치가 없습니다.');
|
||||
}
|
||||
}
|
||||
if (query.since) {
|
||||
params.set('since', query.since);
|
||||
@@ -237,6 +242,68 @@ export class GiteaService {
|
||||
);
|
||||
}
|
||||
|
||||
async createUser(body: { username: string; email: string; password: string; fullName?: string }) {
|
||||
this.assertToken();
|
||||
|
||||
await this.request(
|
||||
'/api/v1/admin/users',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
username: body.username,
|
||||
email: body.email,
|
||||
password: body.password,
|
||||
full_name: body.fullName,
|
||||
must_change_password: false,
|
||||
send_notify: false,
|
||||
}),
|
||||
allowedStatuses: [409, 422],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async updateUserLoginDisabled(username: string, disabled: boolean) {
|
||||
this.assertToken();
|
||||
|
||||
await this.request(
|
||||
`/api/v1/admin/users/${encodeURIComponent(username)}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
login_name: username,
|
||||
prohibit_login: disabled,
|
||||
}),
|
||||
allowedStatuses: [404],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async updateUserAvatar(username: string, imageData: Buffer, mimeType: string) {
|
||||
this.assertToken();
|
||||
|
||||
await this.request(
|
||||
`/api/v1/user/avatar?sudo=${encodeURIComponent(username)}`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
image: imageData.toString('base64'),
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async deleteUserAvatar(username: string) {
|
||||
this.assertToken();
|
||||
|
||||
await this.request(
|
||||
`/api/v1/user/avatar?sudo=${encodeURIComponent(username)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
allowedStatuses: [404],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async deleteUserSshKey(username: string, keyId: number) {
|
||||
this.assertToken();
|
||||
|
||||
|
||||
+28
-56
@@ -71,9 +71,37 @@ export class LinkLauncherVersionSourceDto {
|
||||
}
|
||||
|
||||
|
||||
export class DeploymentResourceLimitsDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
replicas?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
cpuRequest?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
cpuLimit?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
memoryRequest?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
memoryLimit?: string;
|
||||
}
|
||||
|
||||
export class DeployLauncherBackendDto {
|
||||
@IsString()
|
||||
image!: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => DeploymentResourceLimitsDto)
|
||||
resources?: DeploymentResourceLimitsDto;
|
||||
}
|
||||
|
||||
export class CreateLauncherBuildJobDto {
|
||||
@@ -279,59 +307,3 @@ export class UpdateLauncherBuildEnvDto {
|
||||
@Type(() => LauncherBuildEnvVariableDto)
|
||||
variables!: LauncherBuildEnvVariableDto[];
|
||||
}
|
||||
|
||||
export class CreateLauncherCommonSettingDto {
|
||||
@IsString()
|
||||
key!: string;
|
||||
|
||||
@IsString()
|
||||
name!: string;
|
||||
|
||||
@IsString()
|
||||
type!: string;
|
||||
|
||||
@IsString()
|
||||
value!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isSecret?: boolean;
|
||||
}
|
||||
|
||||
export class UpdateLauncherCommonSettingDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
key?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
type?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
value?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isSecret?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req } from '@nestjs/common';
|
||||
|
||||
import { CreateLauncherAppLinkDto, CreateLauncherBuildJobDto, CreateLauncherCommonSettingDto, CreateLauncherVersionDto, UpdateLauncherSourceDto, LinkLauncherVersionSourceDto, MoveLauncherAppLinkDto, UpdateLauncherAppLinkDto, UpdateLauncherBuildEnvDto, UpdateLauncherCommonSettingDto, UpdateLauncherUserAppPermissionsDto, DeployLauncherBackendDto, ListLauncherBuildJobsQuery } from './dto';
|
||||
import { CreateLauncherAppLinkDto, CreateLauncherBuildJobDto, CreateLauncherVersionDto, UpdateLauncherSourceDto, LinkLauncherVersionSourceDto, MoveLauncherAppLinkDto, UpdateLauncherAppLinkDto, UpdateLauncherBuildEnvDto, UpdateLauncherUserAppPermissionsDto, DeployLauncherBackendDto, ListLauncherBuildJobsQuery } from './dto';
|
||||
import { LauncherService } from './launcher.service';
|
||||
|
||||
@Controller('launcher')
|
||||
@@ -54,33 +54,11 @@ export class LauncherController {
|
||||
}
|
||||
|
||||
@Post('builds')
|
||||
createBuildJob(@Body() body: CreateLauncherBuildJobDto) {
|
||||
return this.launcherService.createBuildJob(body);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Get('common-settings')
|
||||
listCommonSettings() {
|
||||
return this.launcherService.listCommonSettings();
|
||||
}
|
||||
|
||||
@Post('common-settings')
|
||||
createCommonSetting(@Body() body: CreateLauncherCommonSettingDto) {
|
||||
return this.launcherService.createCommonSetting(body);
|
||||
}
|
||||
|
||||
@Patch('common-settings/:id')
|
||||
updateCommonSetting(
|
||||
@Param('id') id: string,
|
||||
@Body() body: UpdateLauncherCommonSettingDto,
|
||||
createBuildJob(
|
||||
@Body() body: CreateLauncherBuildJobDto,
|
||||
@Req() request: { admin?: { id: string; name: string; email: string } },
|
||||
) {
|
||||
return this.launcherService.updateCommonSetting(id, body);
|
||||
}
|
||||
|
||||
@Delete('common-settings/:id')
|
||||
deleteCommonSetting(@Param('id') id: string) {
|
||||
return this.launcherService.deleteCommonSetting(id);
|
||||
return this.launcherService.createBuildJob(body, request.admin);
|
||||
}
|
||||
|
||||
@Get('app-links')
|
||||
|
||||
@@ -10,10 +10,10 @@ import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||
|
||||
import { LauncherPrismaService } from '../launcher-prisma/launcher-prisma.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { getAdminAvatarUrl } from '../admins/admin-avatar';
|
||||
import {
|
||||
CreateLauncherAppLinkDto,
|
||||
CreateLauncherBuildJobDto,
|
||||
CreateLauncherCommonSettingDto,
|
||||
CreateLauncherVersionDto,
|
||||
DeployLauncherBackendDto,
|
||||
ListLauncherBuildJobsQuery,
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
LinkLauncherVersionSourceDto,
|
||||
UpdateLauncherAppLinkDto,
|
||||
UpdateLauncherBuildEnvDto,
|
||||
UpdateLauncherCommonSettingDto,
|
||||
UpdateLauncherUserAppPermissionsDto,
|
||||
} from './dto';
|
||||
|
||||
@@ -76,51 +75,6 @@ export class LauncherService {
|
||||
return rows.map(mapLauncherVersionRow);
|
||||
}
|
||||
|
||||
|
||||
listCommonSettings() {
|
||||
return this.launcherPrisma.launcherCommonSetting.findMany({
|
||||
orderBy: [{ isActive: 'desc' }, { type: 'asc' }, { name: 'asc' }],
|
||||
});
|
||||
}
|
||||
|
||||
async createCommonSetting(body: CreateLauncherCommonSettingDto) {
|
||||
const setting = await this.launcherPrisma.launcherCommonSetting.create({
|
||||
data: {
|
||||
key: body.key.trim(),
|
||||
name: body.name.trim(),
|
||||
type: body.type.trim(),
|
||||
value: body.value.trim(),
|
||||
description: body.description?.trim() || null,
|
||||
isActive: body.isActive ?? true,
|
||||
isSecret: body.isSecret ?? false,
|
||||
},
|
||||
});
|
||||
|
||||
return { setting };
|
||||
}
|
||||
|
||||
async updateCommonSetting(id: string, body: UpdateLauncherCommonSettingDto) {
|
||||
const setting = await this.launcherPrisma.launcherCommonSetting.update({
|
||||
where: { id },
|
||||
data: {
|
||||
key: body.key === undefined ? undefined : body.key.trim(),
|
||||
name: body.name === undefined ? undefined : body.name.trim(),
|
||||
type: body.type === undefined ? undefined : body.type.trim(),
|
||||
value: body.value === undefined ? undefined : body.value.trim(),
|
||||
description: body.description === undefined ? undefined : body.description.trim() || null,
|
||||
isActive: body.isActive,
|
||||
isSecret: body.isSecret,
|
||||
},
|
||||
});
|
||||
|
||||
return { setting };
|
||||
}
|
||||
|
||||
async deleteCommonSetting(id: string) {
|
||||
await this.launcherPrisma.launcherCommonSetting.delete({ where: { id } });
|
||||
return { deleted: true };
|
||||
}
|
||||
|
||||
async getSource() {
|
||||
const source = await this.getSourceSettings();
|
||||
return { source };
|
||||
@@ -136,35 +90,35 @@ export class LauncherService {
|
||||
const dockerfile = body.dockerfile?.trim() || 'Dockerfile';
|
||||
|
||||
const writes = isBackend ? [
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.backendRepository,
|
||||
'런처 백엔드 소스 저장소',
|
||||
'source',
|
||||
repository,
|
||||
'런처 백엔드 컨테이너 빌드에 사용할 저장소',
|
||||
),
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.backendBranch,
|
||||
'런처 백엔드 소스 브랜치',
|
||||
'source',
|
||||
branch,
|
||||
'런처 백엔드 컨테이너 빌드 기준 브랜치',
|
||||
),
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.backendCommitSha,
|
||||
'런처 백엔드 소스 커밋',
|
||||
'source',
|
||||
commitSha,
|
||||
'비워두면 브랜치 최신 커밋 기준',
|
||||
),
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.backendImage,
|
||||
'런처 백엔드 이미지',
|
||||
'source',
|
||||
image,
|
||||
'런처 백엔드 컨테이너 이미지 이름',
|
||||
),
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.backendDockerfile,
|
||||
'런처 백엔드 Dockerfile',
|
||||
'source',
|
||||
@@ -172,21 +126,21 @@ export class LauncherService {
|
||||
'런처 백엔드 빌드에 사용할 Dockerfile 경로',
|
||||
),
|
||||
] : [
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.repository,
|
||||
'런처 소스 저장소',
|
||||
'source',
|
||||
repository,
|
||||
'런처 클라이언트 빌드에 사용할 저장소',
|
||||
),
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.branch,
|
||||
'런처 소스 브랜치',
|
||||
'source',
|
||||
branch,
|
||||
'런처 클라이언트 빌드 기준 브랜치',
|
||||
),
|
||||
this.upsertCommonSettingByKey(
|
||||
this.upsertLauncherSettingByKey(
|
||||
LAUNCHER_SOURCE_SETTING_KEYS.commitSha,
|
||||
'런처 소스 커밋',
|
||||
'source',
|
||||
@@ -209,7 +163,7 @@ export class LauncherService {
|
||||
}
|
||||
|
||||
private async getSourceSettings() {
|
||||
const rows = await this.launcherPrisma.launcherCommonSetting.findMany({
|
||||
const rows = await this.launcherPrisma.launcherSetting.findMany({
|
||||
where: {
|
||||
key: {
|
||||
in: [
|
||||
@@ -256,7 +210,7 @@ export class LauncherService {
|
||||
|
||||
|
||||
private async getLauncherClientBuildEnv() {
|
||||
const rows = await this.launcherPrisma.launcherCommonSetting.findMany({
|
||||
const rows = await this.launcherPrisma.launcherSetting.findMany({
|
||||
where: {
|
||||
key: {
|
||||
startsWith: LAUNCHER_BUILD_ENV_PREFIX,
|
||||
@@ -283,7 +237,7 @@ export class LauncherService {
|
||||
.filter((row) => row.key);
|
||||
}
|
||||
|
||||
private upsertCommonSettingByKey(
|
||||
private upsertLauncherSettingByKey(
|
||||
key: string,
|
||||
name: string,
|
||||
type: string,
|
||||
@@ -291,7 +245,7 @@ export class LauncherService {
|
||||
description: string,
|
||||
isSecret = false,
|
||||
) {
|
||||
return this.launcherPrisma.launcherCommonSetting.upsert({
|
||||
return this.launcherPrisma.launcherSetting.upsert({
|
||||
where: { key },
|
||||
create: {
|
||||
key,
|
||||
@@ -322,7 +276,7 @@ export class LauncherService {
|
||||
async updateBuildEnv(body: UpdateLauncherBuildEnvDto) {
|
||||
const variables = normalizeBuildEnvVariables(body.variables ?? []);
|
||||
const nextKeys = new Set(variables.map((variable) => variable.key));
|
||||
const existing = await this.launcherPrisma.launcherCommonSetting.findMany({
|
||||
const existing = await this.launcherPrisma.launcherSetting.findMany({
|
||||
where: {
|
||||
key: {
|
||||
startsWith: LAUNCHER_BUILD_ENV_PREFIX,
|
||||
@@ -333,8 +287,8 @@ export class LauncherService {
|
||||
await this.launcherPrisma.$transaction([
|
||||
...existing
|
||||
.filter((setting) => !nextKeys.has(setting.key.slice(LAUNCHER_BUILD_ENV_PREFIX.length)))
|
||||
.map((setting) => this.launcherPrisma.launcherCommonSetting.delete({ where: { id: setting.id } })),
|
||||
...variables.map((variable) => this.upsertCommonSettingByKey(
|
||||
.map((setting) => this.launcherPrisma.launcherSetting.delete({ where: { id: setting.id } })),
|
||||
...variables.map((variable) => this.upsertLauncherSettingByKey(
|
||||
`${LAUNCHER_BUILD_ENV_PREFIX}${variable.key}`,
|
||||
variable.key,
|
||||
'launcher-build-env',
|
||||
@@ -406,6 +360,43 @@ export class LauncherService {
|
||||
'-n',
|
||||
namespace,
|
||||
]));
|
||||
|
||||
const resources = body.resources;
|
||||
if (resources?.replicas && resources.replicas > 0) {
|
||||
logs.push(`[scale] deployment/${deployment} replicas=${resources.replicas}`);
|
||||
appendOutput(logs, await this.kubectl([
|
||||
'scale',
|
||||
`deployment/${deployment}`,
|
||||
`--replicas=${resources.replicas}`,
|
||||
'-n',
|
||||
namespace,
|
||||
]));
|
||||
}
|
||||
|
||||
const requests = [
|
||||
resources?.cpuRequest ? `cpu=${resources.cpuRequest}` : null,
|
||||
resources?.memoryRequest ? `memory=${resources.memoryRequest}` : null,
|
||||
].filter(Boolean);
|
||||
const limits = [
|
||||
resources?.cpuLimit ? `cpu=${resources.cpuLimit}` : null,
|
||||
resources?.memoryLimit ? `memory=${resources.memoryLimit}` : null,
|
||||
].filter(Boolean);
|
||||
|
||||
if (requests.length || limits.length) {
|
||||
const args = [
|
||||
'set',
|
||||
'resources',
|
||||
`deployment/${deployment}`,
|
||||
`--containers=${container}`,
|
||||
...(requests.length ? [`--requests=${requests.join(',')}`] : []),
|
||||
...(limits.length ? [`--limits=${limits.join(',')}`] : []),
|
||||
'-n',
|
||||
namespace,
|
||||
];
|
||||
logs.push(`[resources] ${args.slice(3, -2).join(' ')}`);
|
||||
appendOutput(logs, await this.kubectl(args));
|
||||
}
|
||||
|
||||
logs.push(`[rollout] deployment/${deployment}`);
|
||||
appendOutput(logs, await this.kubectl([
|
||||
'rollout',
|
||||
@@ -427,7 +418,7 @@ export class LauncherService {
|
||||
};
|
||||
}
|
||||
|
||||
listBuildJobs(query: ListLauncherBuildJobsQuery = {}) {
|
||||
async listBuildJobs(query: ListLauncherBuildJobsQuery = {}) {
|
||||
const paged = Boolean(query.page || query.limit || query.target || query.since);
|
||||
const page = toPositiveInt(query.page, 1);
|
||||
const limit = Math.min(toPositiveInt(query.limit, 30), 100);
|
||||
@@ -438,10 +429,11 @@ export class LauncherService {
|
||||
};
|
||||
|
||||
if (!paged) {
|
||||
return this.prisma.launcherBuildJob.findMany({
|
||||
const jobs = await this.prisma.launcherBuildJob.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 30,
|
||||
});
|
||||
return this.withLauncherBuildRequesters(jobs);
|
||||
}
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
@@ -455,10 +447,17 @@ export class LauncherService {
|
||||
tx.launcherBuildJob.count({ where }),
|
||||
]);
|
||||
|
||||
return { jobs, total, page, limit };
|
||||
return { jobs: await this.withLauncherBuildRequesters(jobs), total, page, limit };
|
||||
});
|
||||
}
|
||||
|
||||
private withLauncherBuildRequesters<T extends { requestedById?: string | null }>(jobs: T[]) {
|
||||
return jobs.map((job) => ({
|
||||
...job,
|
||||
requestedByAvatarUrl: getAdminAvatarUrl(job.requestedById, this.configService),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
async listAppLinks() {
|
||||
const [launcherRows, adminApps] = await Promise.all([
|
||||
@@ -501,6 +500,13 @@ export class LauncherService {
|
||||
};
|
||||
}
|
||||
|
||||
async listEnabledAppKeys() {
|
||||
const rows = await this.launcherPrisma.$queryRawUnsafe<Array<{ app_key: string }>>(
|
||||
`SELECT app_key FROM launcher_apps WHERE is_enabled = true`,
|
||||
);
|
||||
return rows.map((row) => row.app_key);
|
||||
}
|
||||
|
||||
async listUserAppPermissions(userId: string) {
|
||||
const rows = await this.launcherPrisma.$queryRawUnsafe<LauncherUserAppPermissionRow[]>(
|
||||
`SELECT app.id AS app_id, app.app_key, app.display_name, app.description, app.sort_order, app.is_enabled,
|
||||
@@ -953,7 +959,10 @@ export class LauncherService {
|
||||
return { version: mapLauncherVersionRow(rows[0]) };
|
||||
}
|
||||
|
||||
async createBuildJob(body: CreateLauncherBuildJobDto) {
|
||||
async createBuildJob(
|
||||
body: CreateLauncherBuildJobDto,
|
||||
requester?: { id: string; name: string; email: string },
|
||||
) {
|
||||
const source = await this.getSourceSettings();
|
||||
const target = body.target?.trim() || 'launcher-client';
|
||||
const isBackend = target === 'launcher-backend';
|
||||
@@ -997,12 +1006,15 @@ export class LauncherService {
|
||||
artifact: requestedArtifact,
|
||||
uploadPath: plan.uploadPath,
|
||||
downloadUrl: plan.downloadUrl,
|
||||
requestedById: requester?.id,
|
||||
requestedByName: requester?.name,
|
||||
requestedByEmail: requester?.email,
|
||||
logs,
|
||||
},
|
||||
});
|
||||
|
||||
void this.runBuildJob(job.id);
|
||||
return { job };
|
||||
return { job: (await this.withLauncherBuildRequesters([job]))[0] };
|
||||
}
|
||||
|
||||
async linkVersionSource(id: string, body: LinkLauncherVersionSourceDto) {
|
||||
|
||||
+55
-1
@@ -1,8 +1,17 @@
|
||||
import { IsEnum, IsISO8601, IsOptional, IsString } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsBoolean, IsEnum, IsInt, IsISO8601, IsOptional, IsString, Min, ValidateNested } from 'class-validator';
|
||||
|
||||
import { LicenseCode } from './license-enums';
|
||||
|
||||
export class ListUserLicensesQuery {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
page?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
limit?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
keyword?: string;
|
||||
@@ -10,6 +19,10 @@ export class ListUserLicensesQuery {
|
||||
@IsOptional()
|
||||
@IsEnum(LicenseCode)
|
||||
licenseCode?: LicenseCode;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export class AssignLicenseDto {
|
||||
@@ -28,3 +41,44 @@ export class AssignLicenseDto {
|
||||
@IsString()
|
||||
assignedById?: string;
|
||||
}
|
||||
|
||||
export class UpdateLicenseAppPermissionDto {
|
||||
@IsString()
|
||||
appKey!: string;
|
||||
|
||||
@IsBoolean()
|
||||
canLaunch!: boolean;
|
||||
}
|
||||
|
||||
export class UpdateLicenseAppPermissionsDto {
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => UpdateLicenseAppPermissionDto)
|
||||
permissions!: UpdateLicenseAppPermissionDto[];
|
||||
}
|
||||
|
||||
export class UpdateLicensePlanDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
defaultDurationDays?: number | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => UpdateLicenseAppPermissionDto)
|
||||
permissions?: UpdateLicenseAppPermissionDto[];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req } from '@nestjs/common';
|
||||
|
||||
import { AssignLicenseDto, ListUserLicensesQuery } from './dto';
|
||||
import { AssignLicenseDto, ListUserLicensesQuery, UpdateLicenseAppPermissionsDto, UpdateLicensePlanDto } from './dto';
|
||||
import { LicensesService } from './licenses.service';
|
||||
|
||||
@Controller('licenses')
|
||||
@@ -12,19 +12,47 @@ export class LicensesController {
|
||||
return this.licensesService.listPlans();
|
||||
}
|
||||
|
||||
@Get('plans/:code/app-permissions')
|
||||
listPlanAppPermissions(@Param('code') code: string) {
|
||||
return this.licensesService.listPlanAppPermissions(code);
|
||||
}
|
||||
|
||||
@Put('plans/:code/app-permissions')
|
||||
updatePlanAppPermissions(@Param('code') code: string, @Body() body: UpdateLicenseAppPermissionsDto) {
|
||||
return this.licensesService.updatePlanAppPermissions(code, body);
|
||||
}
|
||||
|
||||
@Patch('plans/:code')
|
||||
updatePlan(@Param('code') code: string, @Body() body: UpdateLicensePlanDto) {
|
||||
return this.licensesService.updatePlan(code, body);
|
||||
}
|
||||
|
||||
@Get('users')
|
||||
listUserLicenses(@Query() query: ListUserLicensesQuery) {
|
||||
return this.licensesService.listUserLicenses(query);
|
||||
}
|
||||
|
||||
@Get('assignments')
|
||||
listAssignments(@Query() query: ListUserLicensesQuery) {
|
||||
return this.licensesService.listAssignments(query);
|
||||
}
|
||||
|
||||
@Post('users/:userId')
|
||||
assign(@Param('userId') userId: string, @Body() body: AssignLicenseDto) {
|
||||
return this.licensesService.assign(userId, body);
|
||||
assign(
|
||||
@Param('userId') userId: string,
|
||||
@Body() body: AssignLicenseDto,
|
||||
@Req() request: { admin?: { id: string } },
|
||||
) {
|
||||
return this.licensesService.assign(userId, body, request.admin?.id);
|
||||
}
|
||||
|
||||
@Patch('user-licenses/:id')
|
||||
update(@Param('id') id: string, @Body() body: AssignLicenseDto) {
|
||||
return this.licensesService.update(id, body);
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() body: AssignLicenseDto,
|
||||
@Req() request: { admin?: { id: string } },
|
||||
) {
|
||||
return this.licensesService.update(id, body, request.admin?.id);
|
||||
}
|
||||
|
||||
@Delete('user-licenses/:id')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user