init studio launcher backend
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE "launcher_notices" (
|
||||
"id" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"link_url" TEXT,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"starts_at" TIMESTAMP(3),
|
||||
"ends_at" TIMESTAMP(3),
|
||||
"priority" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "launcher_notices_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX "launcher_notices_is_active_priority_idx" ON "launcher_notices"("is_active", "priority");
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE "launcher_notices" ADD COLUMN "app_key" TEXT NOT NULL DEFAULT 'login';
|
||||
|
||||
DROP INDEX IF EXISTS "launcher_notices_is_active_priority_idx";
|
||||
CREATE INDEX "launcher_notices_app_key_is_active_priority_idx" ON "launcher_notices"("app_key", "is_active", "priority");
|
||||
@@ -0,0 +1,17 @@
|
||||
CREATE TABLE "launcher_app_versions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"app_key" TEXT NOT NULL,
|
||||
"display_name" TEXT NOT NULL,
|
||||
"latest_version" TEXT NOT NULL,
|
||||
"minimum_supported_version" TEXT,
|
||||
"download_url" TEXT,
|
||||
"release_notes" TEXT,
|
||||
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "launcher_app_versions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "launcher_app_versions_app_key_key" ON "launcher_app_versions"("app_key");
|
||||
CREATE INDEX "launcher_app_versions_is_enabled_idx" ON "launcher_app_versions"("is_enabled");
|
||||
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE "launcher_apps" (
|
||||
"id" TEXT NOT NULL,
|
||||
"app_key" TEXT NOT NULL,
|
||||
"display_name" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"subtitle" TEXT,
|
||||
"detail_url" TEXT,
|
||||
"video_path" TEXT,
|
||||
"icon_key" TEXT NOT NULL,
|
||||
"launch_url" TEXT,
|
||||
"dev_app_path" TEXT,
|
||||
"exec_path" TEXT,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "launcher_apps_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "launcher_apps_app_key_key" ON "launcher_apps"("app_key");
|
||||
CREATE INDEX "launcher_apps_is_enabled_sort_order_idx" ON "launcher_apps"("is_enabled", "sort_order");
|
||||
@@ -0,0 +1,41 @@
|
||||
COMMENT ON TABLE "launcher_notices" IS '런처 로그인 화면 및 앱별 화면에 노출할 공지사항';
|
||||
COMMENT ON COLUMN "launcher_notices"."id" IS '런처 공지 고유 ID';
|
||||
COMMENT ON COLUMN "launcher_notices"."app_key" IS '공지 대상 앱 식별자. login은 로그인 화면 공지';
|
||||
COMMENT ON COLUMN "launcher_notices"."title" IS '공지 제목';
|
||||
COMMENT ON COLUMN "launcher_notices"."body" IS '공지 본문';
|
||||
COMMENT ON COLUMN "launcher_notices"."link_url" IS '자세히보기 링크 URL';
|
||||
COMMENT ON COLUMN "launcher_notices"."is_active" IS '공지 활성 여부';
|
||||
COMMENT ON COLUMN "launcher_notices"."starts_at" IS '공지 노출 시작 일시';
|
||||
COMMENT ON COLUMN "launcher_notices"."ends_at" IS '공지 노출 종료 일시';
|
||||
COMMENT ON COLUMN "launcher_notices"."priority" IS '공지 우선순위. 값이 높을수록 우선 노출';
|
||||
COMMENT ON COLUMN "launcher_notices"."created_at" IS '공지 생성 일시';
|
||||
COMMENT ON COLUMN "launcher_notices"."updated_at" IS '공지 수정 일시';
|
||||
|
||||
COMMENT ON TABLE "launcher_app_versions" IS '런처 및 앱별 최신 버전, 다운로드 URL, 릴리즈 노트 관리';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."id" IS '앱 버전 레코드 고유 ID';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."app_key" IS '버전 대상 앱 식별자';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."display_name" IS '버전 대상 앱 표시명';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."latest_version" IS '파일서버에 배포된 최신 버전';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."minimum_supported_version" IS '강제 업데이트 판단용 최소 지원 버전';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."download_url" IS '설치 또는 업데이트 파일 다운로드 URL';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."release_notes" IS '업데이트 안내 문구 또는 릴리즈 노트';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."is_enabled" IS '버전 정보 사용 여부';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."created_at" IS '버전 정보 생성 일시';
|
||||
COMMENT ON COLUMN "launcher_app_versions"."updated_at" IS '버전 정보 수정 일시';
|
||||
|
||||
COMMENT ON TABLE "launcher_apps" IS '런처에 표시할 앱 카드와 실행 정보를 관리하는 앱 마스터';
|
||||
COMMENT ON COLUMN "launcher_apps"."id" IS '런처 앱 고유 ID';
|
||||
COMMENT ON COLUMN "launcher_apps"."app_key" IS '런처 앱 식별자';
|
||||
COMMENT ON COLUMN "launcher_apps"."display_name" IS '런처에 표시할 앱 이름';
|
||||
COMMENT ON COLUMN "launcher_apps"."description" IS '앱 소개 본문';
|
||||
COMMENT ON COLUMN "launcher_apps"."subtitle" IS '앱 소개 보조 문구';
|
||||
COMMENT ON COLUMN "launcher_apps"."detail_url" IS '자세히보기 링크 URL';
|
||||
COMMENT ON COLUMN "launcher_apps"."video_path" IS '앱 소개 영상 경로 또는 URL';
|
||||
COMMENT ON COLUMN "launcher_apps"."icon_key" IS '프론트엔드 아이콘 매핑 키';
|
||||
COMMENT ON COLUMN "launcher_apps"."launch_url" IS '웹 앱 실행 URL';
|
||||
COMMENT ON COLUMN "launcher_apps"."dev_app_path" IS '개발 환경에서 실행할 로컬 앱 경로';
|
||||
COMMENT ON COLUMN "launcher_apps"."exec_path" IS '설치 후 실행할 파일 경로 또는 파일명';
|
||||
COMMENT ON COLUMN "launcher_apps"."sort_order" IS '런처 앱 표시 순서';
|
||||
COMMENT ON COLUMN "launcher_apps"."is_enabled" IS '런처 앱 노출 여부';
|
||||
COMMENT ON COLUMN "launcher_apps"."created_at" IS '런처 앱 생성 일시';
|
||||
COMMENT ON COLUMN "launcher_apps"."updated_at" IS '런처 앱 수정 일시';
|
||||
@@ -0,0 +1,22 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
UPDATE "launcher_notices"
|
||||
SET "id" = gen_random_uuid()::text
|
||||
WHERE "id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
|
||||
|
||||
UPDATE "launcher_app_versions"
|
||||
SET "id" = gen_random_uuid()::text
|
||||
WHERE "id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
|
||||
|
||||
UPDATE "launcher_apps"
|
||||
SET "id" = gen_random_uuid()::text
|
||||
WHERE "id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
|
||||
|
||||
ALTER TABLE "launcher_notices"
|
||||
ALTER COLUMN "id" TYPE uuid USING "id"::uuid;
|
||||
|
||||
ALTER TABLE "launcher_app_versions"
|
||||
ALTER COLUMN "id" TYPE uuid USING "id"::uuid;
|
||||
|
||||
ALTER TABLE "launcher_apps"
|
||||
ALTER COLUMN "id" TYPE uuid USING "id"::uuid;
|
||||
@@ -0,0 +1,65 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
CREATE TABLE "notifications" (
|
||||
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
"app_key" TEXT NOT NULL DEFAULT 'all',
|
||||
"audience" TEXT NOT NULL DEFAULT 'all',
|
||||
"category" TEXT NOT NULL DEFAULT 'notice',
|
||||
"title" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"link_url" TEXT,
|
||||
"priority" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"starts_at" TIMESTAMP(3),
|
||||
"ends_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "notifications_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "notification_reads" (
|
||||
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
"notification_id" uuid NOT NULL,
|
||||
"auth_user_id" uuid NOT NULL,
|
||||
"read_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "notification_reads_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX "notifications_app_key_audience_is_active_priority_idx" ON "notifications"("app_key", "audience", "is_active", "priority");
|
||||
CREATE INDEX "notifications_is_active_created_at_idx" ON "notifications"("is_active", "created_at");
|
||||
CREATE UNIQUE INDEX "notification_reads_notification_id_auth_user_id_key" ON "notification_reads"("notification_id", "auth_user_id");
|
||||
CREATE INDEX "notification_reads_auth_user_id_idx" ON "notification_reads"("auth_user_id");
|
||||
|
||||
ALTER TABLE "notification_reads" ADD CONSTRAINT "notification_reads_notification_id_fkey" FOREIGN KEY ("notification_id") REFERENCES "notifications"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
COMMENT ON TABLE "notifications" IS '런처와 모든 앱이 공통으로 사용하는 알림 메시지';
|
||||
COMMENT ON COLUMN "notifications"."id" IS '알림 고유 ID';
|
||||
COMMENT ON COLUMN "notifications"."app_key" IS '알림 대상 앱 식별자. all이면 전체 앱 대상';
|
||||
COMMENT ON COLUMN "notifications"."audience" IS '알림 대상 사용자 그룹. all, user, student, instructor, admin 등을 사용';
|
||||
COMMENT ON COLUMN "notifications"."category" IS '알림 분류';
|
||||
COMMENT ON COLUMN "notifications"."title" IS '알림 제목';
|
||||
COMMENT ON COLUMN "notifications"."body" IS '알림 본문';
|
||||
COMMENT ON COLUMN "notifications"."link_url" IS '알림 클릭 시 이동할 링크 URL';
|
||||
COMMENT ON COLUMN "notifications"."priority" IS '알림 우선순위. 값이 높을수록 우선 노출';
|
||||
COMMENT ON COLUMN "notifications"."is_active" IS '알림 활성 여부';
|
||||
COMMENT ON COLUMN "notifications"."starts_at" IS '알림 노출 시작 일시';
|
||||
COMMENT ON COLUMN "notifications"."ends_at" IS '알림 노출 종료 일시';
|
||||
COMMENT ON COLUMN "notifications"."created_at" IS '알림 생성 일시';
|
||||
COMMENT ON COLUMN "notifications"."updated_at" IS '알림 수정 일시';
|
||||
|
||||
COMMENT ON TABLE "notification_reads" IS '사용자별 공통 알림 읽음 처리';
|
||||
COMMENT ON COLUMN "notification_reads"."id" IS '알림 읽음 레코드 고유 ID';
|
||||
COMMENT ON COLUMN "notification_reads"."notification_id" IS '알림 ID';
|
||||
COMMENT ON COLUMN "notification_reads"."auth_user_id" IS '중앙 auth DB users.id';
|
||||
COMMENT ON COLUMN "notification_reads"."read_at" IS '알림 읽음 일시';
|
||||
|
||||
INSERT INTO "notifications" ("app_key", "audience", "category", "title", "body", "priority", "created_at", "updated_at")
|
||||
VALUES
|
||||
('LMS', 'instructor', 'assignment', '신규 과제 검토 요청', '자율주행 인지 실습 과정에 제출된 과제 3건의 검토가 필요합니다.', 100, '2026-06-02T00:00:00.000Z', CURRENT_TIMESTAMP),
|
||||
('LMS', 'instructor', 'schedule', '라이브 세션 일정 안내', '오늘 15:00에 SLAM 기초 라이브 세션이 예정되어 있습니다.', 90, '2026-06-01T00:00:00.000Z', CURRENT_TIMESTAMP),
|
||||
('LMS', 'instructor', 'system', '시스템 점검 안내', '6월 5일 02:00부터 03:00까지 LMS 점검이 진행됩니다.', 80, '2026-05-31T00:00:00.000Z', CURRENT_TIMESTAMP),
|
||||
('LMS', 'student', 'assignment', '과제 제출 마감 안내', '인지 실습 리포트 제출 마감이 오늘 18:00까지입니다.', 100, '2026-06-02T00:00:00.000Z', CURRENT_TIMESTAMP),
|
||||
('LMS', 'student', 'course', '신규 강의 공개', '자율주행 센서 융합 기초 강의가 새로 공개되었습니다.', 90, '2026-06-01T00:00:00.000Z', CURRENT_TIMESTAMP),
|
||||
('LMS', 'student', 'schedule', '실습 세션 안내', '내일 10:00에 ROS2 실습 세션이 예정되어 있습니다.', 80, '2026-05-31T00:00:00.000Z', CURRENT_TIMESTAMP);
|
||||
@@ -0,0 +1,9 @@
|
||||
UPDATE "launcher_apps"
|
||||
SET "dev_app_path" = './apps/ads-ide/ads-ide-fe',
|
||||
"updated_at" = CURRENT_TIMESTAMP
|
||||
WHERE "app_key" = 'ADS IDE';
|
||||
|
||||
UPDATE "launcher_apps"
|
||||
SET "dev_app_path" = './apps/lms/lms-fe',
|
||||
"updated_at" = CURRENT_TIMESTAMP
|
||||
WHERE "app_key" = 'LMS';
|
||||
@@ -0,0 +1,31 @@
|
||||
CREATE TABLE "launcher_user_apps" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"auth_user_id" UUID NOT NULL,
|
||||
"app_id" UUID NOT NULL,
|
||||
"can_view" BOOLEAN NOT NULL DEFAULT true,
|
||||
"can_launch" BOOLEAN NOT NULL DEFAULT true,
|
||||
"assigned_by" UUID,
|
||||
"assigned_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "launcher_user_apps_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "launcher_user_apps_auth_user_id_app_id_key" ON "launcher_user_apps"("auth_user_id", "app_id");
|
||||
CREATE INDEX "launcher_user_apps_app_id_idx" ON "launcher_user_apps"("app_id");
|
||||
CREATE INDEX "launcher_user_apps_auth_user_id_can_view_can_launch_idx" ON "launcher_user_apps"("auth_user_id", "can_view", "can_launch");
|
||||
|
||||
ALTER TABLE "launcher_user_apps"
|
||||
ADD CONSTRAINT "launcher_user_apps_app_id_fkey"
|
||||
FOREIGN KEY ("app_id") REFERENCES "launcher_apps"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
COMMENT ON TABLE "launcher_user_apps" IS '사용자별 런처 앱 접근 권한. auth 사용자 UUID 기준으로 앱 표시 및 실행 가능 여부를 관리한다.';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."id" IS '사용자 앱 권한 고유 UUID';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."auth_user_id" IS 'auth DB users.id UUID';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."app_id" IS '권한이 부여된 런처 앱 UUID';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."can_view" IS '런처 앱 목록 및 상세 화면 표시 가능 여부';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."can_launch" IS '앱 설치, 업데이트, 실행 가능 여부';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."assigned_by" IS '권한을 부여한 관리자 auth 사용자 UUID';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."assigned_at" IS '권한 부여 일시';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."created_at" IS '권한 레코드 생성 일시';
|
||||
COMMENT ON COLUMN "launcher_user_apps"."updated_at" IS '권한 레코드 수정 일시';
|
||||
@@ -0,0 +1,40 @@
|
||||
CREATE TABLE "common_codes" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"group_key" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "common_codes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "common_codes_group_key_code_key" ON "common_codes"("group_key", "code");
|
||||
CREATE INDEX "common_codes_group_key_is_active_sort_order_idx" ON "common_codes"("group_key", "is_active", "sort_order");
|
||||
|
||||
COMMENT ON TABLE "common_codes" IS '공통 코드 마스터. 알림 카테고리 등 화면 표시명과 코드값을 관리한다.';
|
||||
COMMENT ON COLUMN "common_codes"."id" IS '공통 코드 고유 UUID';
|
||||
COMMENT ON COLUMN "common_codes"."group_key" IS '공통 코드 그룹 키';
|
||||
COMMENT ON COLUMN "common_codes"."code" IS '업무에서 사용하는 코드값';
|
||||
COMMENT ON COLUMN "common_codes"."label" IS '화면에 표시할 코드명';
|
||||
COMMENT ON COLUMN "common_codes"."description" IS '공통 코드 설명';
|
||||
COMMENT ON COLUMN "common_codes"."sort_order" IS '같은 그룹 내 표시 순서';
|
||||
COMMENT ON COLUMN "common_codes"."is_active" IS '공통 코드 사용 여부';
|
||||
COMMENT ON COLUMN "common_codes"."created_at" IS '공통 코드 생성 일시';
|
||||
COMMENT ON COLUMN "common_codes"."updated_at" IS '공통 코드 수정 일시';
|
||||
|
||||
INSERT INTO "common_codes" ("group_key", "code", "label", "description", "sort_order")
|
||||
VALUES
|
||||
('NOTIFICATION_CATEGORY', 'assignment', '과제', '과제 및 제출 관련 알림', 10),
|
||||
('NOTIFICATION_CATEGORY', 'course', '강의', '강의 및 커리큘럼 관련 알림', 20),
|
||||
('NOTIFICATION_CATEGORY', 'notice', '공지', '일반 공지 알림', 30),
|
||||
('NOTIFICATION_CATEGORY', 'update', '업데이트', '업데이트 및 배포 알림', 40),
|
||||
('NOTIFICATION_CATEGORY', 'system', '시스템', '시스템 운영 알림', 50)
|
||||
ON CONFLICT ("group_key", "code") DO UPDATE
|
||||
SET "label" = EXCLUDED."label",
|
||||
"description" = EXCLUDED."description",
|
||||
"sort_order" = EXCLUDED."sort_order",
|
||||
"is_active" = true,
|
||||
"updated_at" = CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,61 @@
|
||||
CREATE TABLE "download_files" (
|
||||
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
"app_key" TEXT NOT NULL,
|
||||
"context_key" TEXT NOT NULL DEFAULT 'general',
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"file_name" TEXT NOT NULL,
|
||||
"source_url" TEXT NOT NULL,
|
||||
"content_type" TEXT,
|
||||
"file_size_bytes" BIGINT,
|
||||
"uploaded_by_app_key" TEXT,
|
||||
"uploaded_by_auth_id" uuid,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
CONSTRAINT "download_files_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "download_events" (
|
||||
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
"file_id" uuid NOT NULL,
|
||||
"auth_user_id" uuid,
|
||||
"app_key" TEXT NOT NULL,
|
||||
"user_agent" TEXT,
|
||||
"ip_address" TEXT,
|
||||
"downloaded_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "download_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX "download_files_app_key_context_key_is_active_sort_order_idx" ON "download_files"("app_key", "context_key", "is_active", "sort_order");
|
||||
CREATE INDEX "download_files_uploaded_by_app_key_idx" ON "download_files"("uploaded_by_app_key");
|
||||
CREATE INDEX "download_events_file_id_downloaded_at_idx" ON "download_events"("file_id", "downloaded_at");
|
||||
CREATE INDEX "download_events_auth_user_id_downloaded_at_idx" ON "download_events"("auth_user_id", "downloaded_at");
|
||||
CREATE INDEX "download_events_app_key_downloaded_at_idx" ON "download_events"("app_key", "downloaded_at");
|
||||
|
||||
ALTER TABLE "download_events"
|
||||
ADD CONSTRAINT "download_events_file_id_fkey"
|
||||
FOREIGN KEY ("file_id") REFERENCES "download_files"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
COMMENT ON TABLE "download_files" IS '스튜디오 앱들이 공통 다운로드 API로 제공하는 파일 메타데이터';
|
||||
COMMENT ON COLUMN "download_files"."app_key" IS '파일이 노출될 앱 구분 키';
|
||||
COMMENT ON COLUMN "download_files"."context_key" IS '앱 내부 화면 또는 영역 구분 키';
|
||||
COMMENT ON COLUMN "download_files"."title" IS '사용자에게 표시할 파일 제목';
|
||||
COMMENT ON COLUMN "download_files"."description" IS '파일 설명';
|
||||
COMMENT ON COLUMN "download_files"."file_name" IS '다운로드 시 사용할 파일명';
|
||||
COMMENT ON COLUMN "download_files"."source_url" IS '실제 원본 파일 URL';
|
||||
COMMENT ON COLUMN "download_files"."content_type" IS '다운로드 응답에 사용할 MIME 타입';
|
||||
COMMENT ON COLUMN "download_files"."file_size_bytes" IS '파일 크기 바이트';
|
||||
COMMENT ON COLUMN "download_files"."uploaded_by_app_key" IS '파일을 등록한 앱 구분 키';
|
||||
COMMENT ON COLUMN "download_files"."uploaded_by_auth_id" IS '파일을 등록한 중앙 auth 사용자 ID';
|
||||
COMMENT ON COLUMN "download_files"."sort_order" IS '목록 표시 순서';
|
||||
COMMENT ON COLUMN "download_files"."is_active" IS '다운로드 노출 여부';
|
||||
|
||||
COMMENT ON TABLE "download_events" IS '공통 다운로드 API를 통한 파일 다운로드 이력';
|
||||
COMMENT ON COLUMN "download_events"."file_id" IS '다운로드한 파일 ID';
|
||||
COMMENT ON COLUMN "download_events"."auth_user_id" IS '다운로드한 중앙 auth 사용자 ID';
|
||||
COMMENT ON COLUMN "download_events"."app_key" IS '다운로드가 발생한 앱 구분 키';
|
||||
COMMENT ON COLUMN "download_events"."user_agent" IS '요청 User-Agent';
|
||||
COMMENT ON COLUMN "download_events"."ip_address" IS '요청 IP 주소';
|
||||
COMMENT ON COLUMN "download_events"."downloaded_at" IS '다운로드 발생 시각';
|
||||
@@ -0,0 +1,8 @@
|
||||
INSERT INTO "common_codes" ("group_key", "code", "label", "description", "sort_order")
|
||||
VALUES ('NOTIFICATION_CATEGORY', 'download', '다운로드', '파일 다운로드 관련 알림', 60)
|
||||
ON CONFLICT ("group_key", "code") DO UPDATE SET
|
||||
"label" = EXCLUDED."label",
|
||||
"description" = EXCLUDED."description",
|
||||
"sort_order" = EXCLUDED."sort_order",
|
||||
"is_active" = true,
|
||||
"updated_at" = CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,19 @@
|
||||
DO $$
|
||||
DECLARE
|
||||
column_record record;
|
||||
BEGIN
|
||||
FOR column_record IN
|
||||
SELECT table_schema, table_name, column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND data_type = 'timestamp without time zone'
|
||||
LOOP
|
||||
EXECUTE format(
|
||||
'ALTER TABLE %I.%I ALTER COLUMN %I TYPE timestamptz USING %I AT TIME ZONE ''UTC''',
|
||||
column_record.table_schema,
|
||||
column_record.table_name,
|
||||
column_record.column_name,
|
||||
column_record.column_name
|
||||
);
|
||||
END LOOP;
|
||||
END $$;
|
||||
@@ -0,0 +1,29 @@
|
||||
CREATE TABLE "provisioned_devices" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"account_email" TEXT NOT NULL,
|
||||
"auth_user_id" UUID,
|
||||
"hostname" TEXT NOT NULL,
|
||||
"tailscale_hostname" TEXT NOT NULL,
|
||||
"ssh_user" TEXT,
|
||||
"usage_type" TEXT NOT NULL DEFAULT 'personal',
|
||||
"os" TEXT,
|
||||
"arch" TEXT,
|
||||
"kernel" TEXT,
|
||||
"distro" TEXT,
|
||||
"device" JSONB,
|
||||
"installed_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"last_provisioned_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "provisioned_devices_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "provisioned_devices_account_email_tailscale_hostname_key"
|
||||
ON "provisioned_devices"("account_email", "tailscale_hostname");
|
||||
|
||||
CREATE INDEX "provisioned_devices_account_email_idx"
|
||||
ON "provisioned_devices"("account_email");
|
||||
|
||||
CREATE INDEX "provisioned_devices_tailscale_hostname_idx"
|
||||
ON "provisioned_devices"("tailscale_hostname");
|
||||
@@ -0,0 +1,20 @@
|
||||
CREATE TABLE "launcher_ssh_keys" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"account_email" TEXT NOT NULL,
|
||||
"auth_user_id" UUID,
|
||||
"public_key" TEXT NOT NULL,
|
||||
"public_key_hash" TEXT NOT NULL,
|
||||
"key_type" TEXT,
|
||||
"label" TEXT,
|
||||
"last_provisioned_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "launcher_ssh_keys_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "launcher_ssh_keys_account_email_public_key_hash_key"
|
||||
ON "launcher_ssh_keys"("account_email", "public_key_hash");
|
||||
|
||||
CREATE INDEX "launcher_ssh_keys_account_email_idx"
|
||||
ON "launcher_ssh_keys"("account_email");
|
||||
@@ -0,0 +1,29 @@
|
||||
COMMENT ON TABLE "provisioned_devices" IS '에이전트/런처 설치 시 계정과 장비를 프로비저닝한 이력을 저장한다.';
|
||||
COMMENT ON COLUMN "provisioned_devices"."id" IS '프로비저닝 장비 이력 ID';
|
||||
COMMENT ON COLUMN "provisioned_devices"."account_email" IS '설치 또는 로그인 프로비저닝을 요청한 계정 이메일';
|
||||
COMMENT ON COLUMN "provisioned_devices"."auth_user_id" IS '인증 서버의 사용자 ID';
|
||||
COMMENT ON COLUMN "provisioned_devices"."hostname" IS '설치 장비에서 감지한 원본 호스트명';
|
||||
COMMENT ON COLUMN "provisioned_devices"."tailscale_hostname" IS 'Headscale/Tailscale에 등록하는 노드 호스트명';
|
||||
COMMENT ON COLUMN "provisioned_devices"."ssh_user" IS '원격 SSH 접속에 사용할 장비 OS 사용자명';
|
||||
COMMENT ON COLUMN "provisioned_devices"."usage_type" IS '프로비저닝 용도. personal, class, launcher-client 등';
|
||||
COMMENT ON COLUMN "provisioned_devices"."os" IS '장비 운영체제 계열';
|
||||
COMMENT ON COLUMN "provisioned_devices"."arch" IS '장비 CPU 아키텍처';
|
||||
COMMENT ON COLUMN "provisioned_devices"."kernel" IS '장비 커널 버전';
|
||||
COMMENT ON COLUMN "provisioned_devices"."distro" IS '장비 배포판/OS 상세 정보';
|
||||
COMMENT ON COLUMN "provisioned_devices"."device" IS '설치 요청에서 전달된 장비 정보 원본 JSON';
|
||||
COMMENT ON COLUMN "provisioned_devices"."installed_at" IS '최초 설치 이력 생성 시각';
|
||||
COMMENT ON COLUMN "provisioned_devices"."last_provisioned_at" IS '마지막 프로비저닝 요청 시각';
|
||||
COMMENT ON COLUMN "provisioned_devices"."created_at" IS '레코드 생성 시각';
|
||||
COMMENT ON COLUMN "provisioned_devices"."updated_at" IS '레코드 수정 시각';
|
||||
|
||||
COMMENT ON TABLE "launcher_ssh_keys" IS '런처가 생성한 사용자/PC별 SSH public key를 저장한다. private key는 로컬 PC에만 존재한다.';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."id" IS '런처 SSH public key ID';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."account_email" IS 'public key를 등록한 사용자 이메일';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."auth_user_id" IS '인증 서버의 사용자 ID';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."public_key" IS '장비 authorized_keys에 배포할 SSH public key';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."public_key_hash" IS '중복 등록 방지를 위한 public key SHA-256 해시';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."key_type" IS 'SSH key 타입. 예: ssh-ed25519';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."label" IS '키 출처 또는 용도 라벨';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."last_provisioned_at" IS '마지막 키 등록/갱신 시각';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."created_at" IS '레코드 생성 시각';
|
||||
COMMENT ON COLUMN "launcher_ssh_keys"."updated_at" IS '레코드 수정 시각';
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE "provisioned_devices"
|
||||
ADD COLUMN "workspace_root" TEXT;
|
||||
|
||||
COMMENT ON COLUMN "provisioned_devices"."workspace_root" IS '에이전트 설치 시 입력한 원격 워크스페이스 루트 경로';
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE "launcher_notices"
|
||||
ALTER COLUMN "app_key" SET DEFAULT 'Launcher';
|
||||
|
||||
UPDATE "launcher_notices"
|
||||
SET "app_key" = 'Launcher'
|
||||
WHERE "app_key" IN ('login', 'launcher');
|
||||
|
||||
COMMENT ON COLUMN "launcher_notices"."app_key" IS '공지 대상 앱 식별자. Launcher는 런처 로그인 화면 공지';
|
||||
@@ -0,0 +1,2 @@
|
||||
UPDATE "launcher_notices"
|
||||
SET "priority" = 0;
|
||||
@@ -0,0 +1,33 @@
|
||||
ALTER TABLE "launcher_app_versions"
|
||||
ADD COLUMN IF NOT EXISTS "repository" TEXT,
|
||||
ADD COLUMN IF NOT EXISTS "branch" TEXT,
|
||||
ADD COLUMN IF NOT EXISTS "commit_sha" TEXT;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "launcher_common_settings" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"key" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"is_secret" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "launcher_common_settings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "launcher_common_settings_key_key"
|
||||
ON "launcher_common_settings"("key");
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "launcher_common_settings_type_is_active_idx"
|
||||
ON "launcher_common_settings"("type", "is_active");
|
||||
|
||||
COMMENT ON TABLE "launcher_common_settings" IS '런처 공통 연결 설정과 플랫폼 연동 설정';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."key" IS '공통 설정 식별 키';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."name" IS '공통 설정 표시 이름';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."type" IS '공통 설정 유형';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."value" IS '공통 설정 값';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."description" IS '공통 설정 설명';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."is_active" IS '공통 설정 활성 여부';
|
||||
COMMENT ON COLUMN "launcher_common_settings"."is_secret" IS '민감 정보 여부';
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,230 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model LauncherNotice {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @default("Launcher") @map("app_key")
|
||||
title String
|
||||
body String
|
||||
linkUrl String? @map("link_url")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
startsAt DateTime? @map("starts_at") @db.Timestamptz(3)
|
||||
endsAt DateTime? @map("ends_at") @db.Timestamptz(3)
|
||||
priority Int @default(0)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([appKey, isActive, priority])
|
||||
@@map("launcher_notices")
|
||||
}
|
||||
|
||||
model LauncherAppVersion {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @unique @map("app_key")
|
||||
displayName String @map("display_name")
|
||||
latestVersion String @map("latest_version")
|
||||
minimumSupportedVersion String? @map("minimum_supported_version")
|
||||
downloadUrl String? @map("download_url")
|
||||
releaseNotes String? @map("release_notes")
|
||||
repository String?
|
||||
branch String?
|
||||
commitSha String? @map("commit_sha")
|
||||
isEnabled Boolean @default(true) @map("is_enabled")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([isEnabled])
|
||||
@@map("launcher_app_versions")
|
||||
}
|
||||
|
||||
model LauncherCommonSetting {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
key String @unique
|
||||
name String
|
||||
type String
|
||||
value String
|
||||
description String?
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
isSecret Boolean @default(false) @map("is_secret")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@index([type, isActive])
|
||||
@@map("launcher_common_settings")
|
||||
}
|
||||
|
||||
model LauncherApp {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @unique @map("app_key")
|
||||
displayName String @map("display_name")
|
||||
description String
|
||||
subtitle String?
|
||||
detailUrl String? @map("detail_url")
|
||||
videoPath String? @map("video_path")
|
||||
iconKey String @map("icon_key")
|
||||
launchUrl String? @map("launch_url")
|
||||
devAppPath String? @map("dev_app_path")
|
||||
execPath String? @map("exec_path")
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
isEnabled Boolean @default(true) @map("is_enabled")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
userApps LauncherUserApp[]
|
||||
|
||||
@@index([isEnabled, sortOrder])
|
||||
@@map("launcher_apps")
|
||||
}
|
||||
|
||||
model LauncherUserApp {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
authUserId String @map("auth_user_id") @db.Uuid
|
||||
appId String @map("app_id") @db.Uuid
|
||||
canView Boolean @default(true) @map("can_view")
|
||||
canLaunch Boolean @default(true) @map("can_launch")
|
||||
assignedById String? @map("assigned_by") @db.Uuid
|
||||
assignedAt DateTime @default(now()) @map("assigned_at") @db.Timestamptz(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
app LauncherApp @relation(fields: [appId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([authUserId, appId])
|
||||
@@index([appId])
|
||||
@@index([authUserId, canView, canLaunch])
|
||||
@@map("launcher_user_apps")
|
||||
}
|
||||
|
||||
model Notification {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @default("all") @map("app_key")
|
||||
audience String @default("all")
|
||||
category String @default("notice")
|
||||
title String
|
||||
body String
|
||||
linkUrl String? @map("link_url")
|
||||
priority Int @default(0)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
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)
|
||||
reads NotificationRead[]
|
||||
|
||||
@@index([appKey, audience, isActive, priority])
|
||||
@@index([isActive, createdAt])
|
||||
@@map("notifications")
|
||||
}
|
||||
|
||||
model NotificationRead {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
notificationId String @map("notification_id") @db.Uuid
|
||||
authUserId String @map("auth_user_id") @db.Uuid
|
||||
readAt DateTime @default(now()) @map("read_at") @db.Timestamptz(3)
|
||||
notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([notificationId, authUserId])
|
||||
@@index([authUserId])
|
||||
@@map("notification_reads")
|
||||
}
|
||||
|
||||
model CommonCode {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
groupKey String @map("group_key")
|
||||
code String
|
||||
label String
|
||||
description String?
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
@@unique([groupKey, code])
|
||||
@@index([groupKey, isActive, sortOrder])
|
||||
@@map("common_codes")
|
||||
}
|
||||
|
||||
model DownloadFile {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
appKey String @map("app_key")
|
||||
contextKey String @default("general") @map("context_key")
|
||||
title String
|
||||
description String?
|
||||
fileName String @map("file_name")
|
||||
sourceUrl String @map("source_url")
|
||||
contentType String? @map("content_type")
|
||||
fileSizeBytes BigInt? @map("file_size_bytes")
|
||||
uploadedByAppKey String? @map("uploaded_by_app_key")
|
||||
uploadedByAuthId String? @map("uploaded_by_auth_id") @db.Uuid
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
events DownloadEvent[]
|
||||
|
||||
@@index([appKey, contextKey, isActive, sortOrder])
|
||||
@@index([uploadedByAppKey])
|
||||
@@map("download_files")
|
||||
}
|
||||
|
||||
model DownloadEvent {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
fileId String @map("file_id") @db.Uuid
|
||||
authUserId String? @map("auth_user_id") @db.Uuid
|
||||
appKey String @map("app_key")
|
||||
userAgent String? @map("user_agent")
|
||||
ipAddress String? @map("ip_address")
|
||||
downloadedAt DateTime @default(now()) @map("downloaded_at") @db.Timestamptz(3)
|
||||
file DownloadFile @relation(fields: [fileId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([fileId, downloadedAt])
|
||||
@@index([authUserId, downloadedAt])
|
||||
@@index([appKey, downloadedAt])
|
||||
@@map("download_events")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const appNotices = [
|
||||
{
|
||||
id: 'app-notice-ads-ide-v1-1-0',
|
||||
appKey: 'ADS IDE',
|
||||
title: 'v1.0.1 업데이트 안내',
|
||||
body: 'ADS IDE 신규 기능 및 성능 개선 사항이 포함된 업데이트가 배포되었습니다.',
|
||||
linkUrl: 'https://www.futuredrive.net/solutions/astudio',
|
||||
createdAt: new Date('2025-04-23T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
{
|
||||
id: 'app-notice-ads-ide-maintenance-2025-04-10',
|
||||
appKey: 'ADS IDE',
|
||||
title: '정기 점검 안내',
|
||||
body: '4월 15일 02:00 ~ 04:00 서버 정기 점검이 진행됩니다.',
|
||||
linkUrl: 'https://www.futuredrive.net/solutions/astudio',
|
||||
createdAt: new Date('2025-04-10T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
{
|
||||
id: 'app-notice-data-management-retention-2025-04-18',
|
||||
appKey: 'Data Management',
|
||||
title: '데이터 보존 정책 변경 안내',
|
||||
body: '5월 1일부터 로그 데이터 보존 기간이 90일로 변경됩니다.',
|
||||
createdAt: new Date('2025-04-18T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
{
|
||||
id: 'app-notice-fms-v2-3-1',
|
||||
appKey: 'FMS',
|
||||
title: 'FMS v2.3.1 배포',
|
||||
body: '차량 관제 대시보드 UI 개선 및 실시간 알림 기능이 추가되었습니다.',
|
||||
createdAt: new Date('2025-04-20T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
{
|
||||
id: 'app-notice-ads-education-curriculum-2025-04-15',
|
||||
appKey: 'ADS Education',
|
||||
title: '신규 커리큘럼 추가',
|
||||
body: '자율주행 센서 융합 과정이 새롭게 추가되었습니다.',
|
||||
createdAt: new Date('2025-04-15T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
{
|
||||
id: 'app-notice-calibration-lidar-2025-04-21',
|
||||
appKey: 'Calibration',
|
||||
title: 'LiDAR 캘리브레이션 개선',
|
||||
body: '다중 LiDAR 동시 캘리브레이션 지원이 추가되었습니다.',
|
||||
createdAt: new Date('2025-04-21T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
{
|
||||
id: 'app-notice-hdmap-builder-format-2025-04-12',
|
||||
appKey: 'HDMAP Builder',
|
||||
title: '지도 포맷 업데이트',
|
||||
body: 'OpenDRIVE 1.7 포맷 지원이 추가되었습니다.',
|
||||
createdAt: new Date('2025-04-12T00:00:00.000Z'),
|
||||
priority: 0,
|
||||
},
|
||||
];
|
||||
|
||||
const appVersions = [
|
||||
{
|
||||
appKey: 'launcher',
|
||||
displayName: 'Autonomy Studio Launcher',
|
||||
latestVersion: '1.0.1',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://testfile.autonomystudio.net/media/platform/autonomy-studio-launcher-1.0.1.deb',
|
||||
releaseNotes: '런처 업데이트 확인 및 앱별 설치 상태 판단 기능이 추가되었습니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'ADS IDE',
|
||||
displayName: 'ADS IDE',
|
||||
latestVersion: '1.1.0',
|
||||
minimumSupportedVersion: '0.1.0',
|
||||
downloadUrl: 'https://testfile.autonomystudio.net/media/platform/ADS-IDE-1.1.0.AppImage',
|
||||
releaseNotes: 'ADS IDE 신규 기능 및 성능 개선 사항이 포함되어 있습니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'LMS',
|
||||
displayName: 'LMS',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/lms',
|
||||
releaseNotes: '학습 관리 시스템 최초 배포 버전입니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'Data Management',
|
||||
displayName: 'Data Management',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/data-management',
|
||||
releaseNotes: '데이터 관리 앱 최초 배포 버전입니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'HMI',
|
||||
displayName: 'HMI',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/hmi',
|
||||
releaseNotes: 'HMI 앱 최초 배포 버전입니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'FMS',
|
||||
displayName: 'FMS',
|
||||
latestVersion: '2.3.1',
|
||||
minimumSupportedVersion: '2.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/fms',
|
||||
releaseNotes: '차량 관제 대시보드 UI 개선 및 실시간 알림 기능이 추가되었습니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'ADS Education',
|
||||
displayName: 'ADS Education',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/ads-education',
|
||||
releaseNotes: '자율주행 교육 앱 최초 배포 버전입니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'AI Benchmark',
|
||||
displayName: 'AI Benchmark',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/ai-benchmark',
|
||||
releaseNotes: 'AI Benchmark 앱 최초 배포 버전입니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'Calibration',
|
||||
displayName: 'Calibration',
|
||||
latestVersion: '1.0.4',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/calibration',
|
||||
releaseNotes: '다중 LiDAR 동시 캘리브레이션 지원이 추가되었습니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'IP Repository',
|
||||
displayName: 'IP Repository',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/ip-repository',
|
||||
releaseNotes: 'IP Repository 앱 최초 배포 버전입니다.',
|
||||
},
|
||||
{
|
||||
appKey: 'HDMAP Builder',
|
||||
displayName: 'HDMAP Builder',
|
||||
latestVersion: '1.0.0',
|
||||
minimumSupportedVersion: '1.0.0',
|
||||
downloadUrl: 'https://autonomystudio.example.com/downloads/hdmap-builder',
|
||||
releaseNotes: 'OpenDRIVE 1.7 포맷 지원이 추가되었습니다.',
|
||||
},
|
||||
];
|
||||
|
||||
const launcherApps = [
|
||||
{
|
||||
appKey: 'ADS IDE',
|
||||
displayName: 'ADS IDE',
|
||||
description: '클라우드 기반에서 자율주행 소프트웨어를 설계·빌드·실행까지 빠르게 수행할 수 있는 통합 개발 플랫폼으로, 오픈소스 ADS 기반의 유연한 구조를 통해 기능 수정과 배포가 가능하며 다양한 실차 및 시뮬레이션 환경을 지원합니다.',
|
||||
subtitle: '자율주행 모빌리티 소프트웨어\n통합 개발 환경 (IDE)',
|
||||
detailUrl: 'https://www.futuredrive.net/solutions/astudio',
|
||||
videoPath: './video/ads_ide.mp4',
|
||||
iconKey: 'code',
|
||||
launchUrl: 'http://localhost:5175',
|
||||
devAppPath: './apps/ads-ide/ads-ide-fe',
|
||||
execPath: './ADS-IDE-1.1.0.AppImage',
|
||||
sortOrder: 10,
|
||||
},
|
||||
{
|
||||
appKey: 'LMS',
|
||||
displayName: 'LMS',
|
||||
description: '자율주행 교육 과정, 실습 콘텐츠, 사용자 학습 이력을 통합 관리하는 학습 관리 시스템입니다.',
|
||||
subtitle: '자율주행 교육 콘텐츠\n학습 관리 시스템',
|
||||
detailUrl: null,
|
||||
videoPath: './video/lms.mp4',
|
||||
iconKey: 'graduation',
|
||||
launchUrl: 'http://localhost:5176',
|
||||
devAppPath: './apps/lms/lms-fe',
|
||||
execPath: './LMS-1.0.0.AppImage',
|
||||
sortOrder: 20,
|
||||
},
|
||||
{
|
||||
appKey: 'Data Management',
|
||||
displayName: 'Data Management',
|
||||
description: '주행 데이터 수집 · 저장 · 분석 플랫폼',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/data_management.mp4',
|
||||
iconKey: 'bar-chart',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 30,
|
||||
},
|
||||
{
|
||||
appKey: 'HMI',
|
||||
displayName: 'HMI',
|
||||
description: '차량 인터페이스 모니터링 및 제어',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/hmi.mp4',
|
||||
iconKey: 'monitor',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 40,
|
||||
},
|
||||
{
|
||||
appKey: 'FMS',
|
||||
displayName: 'FMS',
|
||||
description: '플릿 운영 및 차량 관제 시스템',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/fms.mp4',
|
||||
iconKey: 'truck',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 50,
|
||||
},
|
||||
{
|
||||
appKey: 'ADS Education',
|
||||
displayName: 'ADS Education',
|
||||
description: '자율주행 기술 학습 및 교육 콘텐츠',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/ads_education.mp4',
|
||||
iconKey: 'graduation',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 60,
|
||||
},
|
||||
{
|
||||
appKey: 'AI Benchmark',
|
||||
displayName: 'AI Benchmark',
|
||||
description: 'AI 모델 성능 평가 및 비교 분석',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/ai_benchmark.mp4',
|
||||
iconKey: 'activity',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 70,
|
||||
},
|
||||
{
|
||||
appKey: 'Calibration',
|
||||
displayName: 'Calibration',
|
||||
description: '센서 캘리브레이션 및 설정 도구',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/calibration.mp4',
|
||||
iconKey: 'sliders',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 80,
|
||||
},
|
||||
{
|
||||
appKey: 'IP Repository',
|
||||
displayName: 'IP Repository',
|
||||
description: '지식재산 및 소프트웨어 자산 저장소',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/ip_repository.mp4',
|
||||
iconKey: 'shopping-cart',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 90,
|
||||
},
|
||||
{
|
||||
appKey: 'HDMAP Builder',
|
||||
displayName: 'HDMAP Builder',
|
||||
description: '고정밀 지도 생성 및 편집 도구',
|
||||
subtitle: null,
|
||||
detailUrl: null,
|
||||
videoPath: './video/hdmap_builder.mp4',
|
||||
iconKey: 'map',
|
||||
launchUrl: null,
|
||||
devAppPath: null,
|
||||
execPath: null,
|
||||
sortOrder: 100,
|
||||
},
|
||||
];
|
||||
|
||||
async function main(): Promise<void> {
|
||||
await prisma.launcherNotice.upsert({
|
||||
where: { id: 'launcher-notice-v1-1-0' },
|
||||
create: {
|
||||
id: 'launcher-notice-v1-1-0',
|
||||
appKey: 'Launcher',
|
||||
title: '공지',
|
||||
body: 'v1.0.1 업데이트가 배포되었습니다. ADS IDE 신규 기능 및 성능 개선 사항이 포함되어 있습니다.',
|
||||
isActive: true,
|
||||
priority: 0,
|
||||
},
|
||||
update: {
|
||||
title: '공지',
|
||||
body: 'v1.0.1 업데이트가 배포되었습니다. ADS IDE 신규 기능 및 성능 개선 사항이 포함되어 있습니다.',
|
||||
appKey: 'Launcher',
|
||||
isActive: true,
|
||||
priority: 0,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notice of appNotices) {
|
||||
await prisma.launcherNotice.upsert({
|
||||
where: { id: notice.id },
|
||||
create: {
|
||||
...notice,
|
||||
isActive: true,
|
||||
},
|
||||
update: {
|
||||
appKey: notice.appKey,
|
||||
title: notice.title,
|
||||
body: notice.body,
|
||||
linkUrl: notice.linkUrl ?? null,
|
||||
createdAt: notice.createdAt,
|
||||
isActive: true,
|
||||
priority: notice.priority,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const appVersion of appVersions) {
|
||||
await prisma.launcherAppVersion.upsert({
|
||||
where: { appKey: appVersion.appKey },
|
||||
create: {
|
||||
...appVersion,
|
||||
isEnabled: true,
|
||||
},
|
||||
update: {
|
||||
displayName: appVersion.displayName,
|
||||
latestVersion: appVersion.latestVersion,
|
||||
minimumSupportedVersion: appVersion.minimumSupportedVersion,
|
||||
downloadUrl: appVersion.downloadUrl,
|
||||
releaseNotes: appVersion.releaseNotes,
|
||||
isEnabled: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const launcherApp of launcherApps) {
|
||||
await prisma.launcherApp.upsert({
|
||||
where: { appKey: launcherApp.appKey },
|
||||
create: {
|
||||
...launcherApp,
|
||||
isEnabled: true,
|
||||
},
|
||||
update: {
|
||||
displayName: launcherApp.displayName,
|
||||
description: launcherApp.description,
|
||||
subtitle: launcherApp.subtitle,
|
||||
detailUrl: launcherApp.detailUrl,
|
||||
videoPath: launcherApp.videoPath,
|
||||
iconKey: launcherApp.iconKey,
|
||||
launchUrl: launcherApp.launchUrl,
|
||||
devAppPath: launcherApp.devAppPath,
|
||||
execPath: launcherApp.execPath,
|
||||
sortOrder: launcherApp.sortOrder,
|
||||
isEnabled: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Seeded launcher notice: launcher-notice-v1-1-0');
|
||||
console.log(`Seeded app notices: ${appNotices.length}`);
|
||||
console.log(`Seeded app versions: ${appVersions.length}`);
|
||||
console.log(`Seeded launcher apps: ${launcherApps.length}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user