diff --git a/prisma/migrations/20260629090000_add_module_artifacts/migration.sql b/prisma/migrations/20260629090000_add_module_artifacts/migration.sql new file mode 100644 index 0000000..167be7d --- /dev/null +++ b/prisma/migrations/20260629090000_add_module_artifacts/migration.sql @@ -0,0 +1,6 @@ +ALTER TABLE "course_week_modules" + ADD COLUMN IF NOT EXISTS "source_type" TEXT NOT NULL DEFAULT 'GIT', + ADD COLUMN IF NOT EXISTS "prebuilt_artifacts" JSONB NOT NULL DEFAULT '[]'; + +COMMENT ON COLUMN "course_week_modules"."source_type" IS '모듈 소스 제공 방식입니다. 기본값 GIT은 template_repository_url을 사용합니다.'; +COMMENT ON COLUMN "course_week_modules"."prebuilt_artifacts" IS 'R2 등 외부 저장소에 둔 모듈별 사전 빌드 산출물 목록입니다. build/install tar 아카이브를 기록합니다.'; diff --git a/prisma/migrations/20260629093000_use_slam_runtime_image_for_bridge/migration.sql b/prisma/migrations/20260629093000_use_slam_runtime_image_for_bridge/migration.sql new file mode 100644 index 0000000..442a54c --- /dev/null +++ b/prisma/migrations/20260629093000_use_slam_runtime_image_for_bridge/migration.sql @@ -0,0 +1,49 @@ +UPDATE "course_environment_profiles" +SET "config" = jsonb_set( + jsonb_set("config", '{bridge,image}', '"nav2-slam:humble"'::jsonb, true), + '{bridge,buildContext}', + '"resources/slam-ros-bridge"'::jsonb, + true + ), + "updated_at" = CURRENT_TIMESTAMP +WHERE upper("profile_type") = 'SLAM'; + +UPDATE "course_environment_profiles" +SET "config" = jsonb_set( + "config", + '{runtime,containers}', + COALESCE(( + SELECT jsonb_agg(container) + FROM jsonb_array_elements("config" #> '{runtime,containers}') AS container + WHERE container->>'name' <> 'slam-ros-bridge' + ), '[]'::jsonb), + true + ), + "updated_at" = CURRENT_TIMESTAMP +WHERE upper("profile_type") = 'SLAM' + AND jsonb_typeof("config" #> '{runtime,containers}') = 'array'; + +UPDATE "visualization_platforms" +SET "default_config" = jsonb_set( + jsonb_set("default_config", '{bridge,image}', '"nav2-slam:humble"'::jsonb, true), + '{bridge,buildContext}', + '"resources/slam-ros-bridge"'::jsonb, + true + ), + "updated_at" = CURRENT_TIMESTAMP +WHERE "platform_key" = 'SLAM'; + +UPDATE "visualization_platforms" +SET "default_config" = jsonb_set( + "default_config", + '{runtime,containers}', + COALESCE(( + SELECT jsonb_agg(container) + FROM jsonb_array_elements("default_config" #> '{runtime,containers}') AS container + WHERE container->>'name' <> 'slam-ros-bridge' + ), '[]'::jsonb), + true + ), + "updated_at" = CURRENT_TIMESTAMP +WHERE "platform_key" = 'SLAM' + AND jsonb_typeof("default_config" #> '{runtime,containers}') = 'array'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d7a2f0a..3cb180c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -445,6 +445,8 @@ model CourseWeekModule { name String folderName String @map("folder_name") templateRepositoryUrl String? @map("template_repository_url") + sourceType String @default("GIT") @map("source_type") + prebuiltArtifacts Json @default("[]") @map("prebuilt_artifacts") dockerImage String? @map("docker_image") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 6b8f9e2..b11eab2 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -35,7 +35,7 @@ export type CreateManagedAuthUserInput = { organizationId: string; department?: string | null; position?: string | null; - licenseCode?: 'FREE' | 'EDUCATION' | 'PROFESSIONAL'; + licenseCode?: 'FREE' | 'EDU' | 'PRO'; }; type AuthTokenResponse = { diff --git a/src/curriculum/curriculum.service.ts b/src/curriculum/curriculum.service.ts index bd4f778..275c52e 100644 --- a/src/curriculum/curriculum.service.ts +++ b/src/curriculum/curriculum.service.ts @@ -371,15 +371,23 @@ export class CurriculumService { }, modules: week.modules .filter((module) => !disabledModuleIdSet.has(module.id)) - .map((module) => ({ - id: module.id, - sortOrder: module.sortOrder, - category: module.category, - name: module.name, - folderName: module.folderName, - templateRepositoryUrl: module.templateRepositoryUrl, - dockerImage: module.dockerImage, - })), + .map((module) => { + const moduleWithArtifacts = module as typeof module & { + sourceType?: string | null; + prebuiltArtifacts?: unknown; + }; + return { + id: module.id, + sortOrder: module.sortOrder, + category: module.category, + name: module.name, + folderName: module.folderName, + templateRepositoryUrl: module.templateRepositoryUrl, + sourceType: moduleWithArtifacts.sourceType ?? 'GIT', + prebuiltArtifacts: moduleWithArtifacts.prebuiltArtifacts ?? [], + dockerImage: module.dockerImage, + }; + }), environmentProfiles: visualizationProfilesByWeek.get(week.week) ?? [], }; }); diff --git a/src/instructor/instructor.service.ts b/src/instructor/instructor.service.ts index bbc8135..99e90ef 100644 --- a/src/instructor/instructor.service.ts +++ b/src/instructor/instructor.service.ts @@ -687,7 +687,7 @@ export class InstructorService { organizationId, department: authUser.primaryAffiliation?.department ?? null, position: '학생', - licenseCode: 'EDUCATION', + licenseCode: 'EDU', }, ); diff --git a/src/studio/studio.service.ts b/src/studio/studio.service.ts index 7549e9e..419c2a7 100644 --- a/src/studio/studio.service.ts +++ b/src/studio/studio.service.ts @@ -131,6 +131,11 @@ export class StudioService { }, }); + const moduleWithArtifacts = module as typeof module & { + sourceType?: string | null; + prebuiltArtifacts?: unknown; + }; + return { id: repository.id, owner: this.studentOwner, @@ -139,6 +144,8 @@ export class StudioService { repositoryUrlWithToken: this.withToken(repositoryUrl), templateRepositoryUrl, templateRepositoryUrlWithToken: this.withToken(templateRepositoryUrl), + sourceType: moduleWithArtifacts.sourceType ?? 'GIT', + prebuiltArtifacts: moduleWithArtifacts.prebuiltArtifacts ?? [], }; }