fix: restrict NAV2 prebuilt artifacts to amd64

This commit is contained in:
2026-08-11 14:21:21 +09:00
parent 196ba51ec6
commit 324e644ef6
9 changed files with 109 additions and 22 deletions
@@ -0,0 +1,13 @@
UPDATE "course_week_modules"
SET "prebuilt_artifacts" = jsonb_build_array(
jsonb_build_object(
'type', 'build-install',
'fileName', 'lms-nav2-mkmini-build-install-amd64.tar.zst',
'url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-mkmini/1.0.0/prebuilt/amd64/lms-nav2-mkmini-build-install-amd64.tar.zst',
'checksumSha256', 'a11ac9e920fcbe5c5a54e086b72d453932ece32e0c7976858435c0a0ba73a8f9',
'architecture', 'amd64',
'required', false,
'extractPath', '.'
)
)
WHERE "folder_name" = '[lms-nav2-mkmini]nav2-mkmini_v1.0.0_ws';
@@ -0,0 +1,18 @@
UPDATE course_week_modules AS module
SET prebuilt_artifacts = (
SELECT jsonb_agg(
CASE
WHEN artifact->>'url' LIKE '%/studio-artifacts/modules/lms-nav2-%/1.0.0/prebuilt/%'
AND COALESCE(NULLIF(artifact->>'architecture', ''), '') = ''
THEN jsonb_set(artifact, '{architecture}', '"amd64"'::jsonb, true)
ELSE artifact
END
)
FROM jsonb_array_elements(module.prebuilt_artifacts) AS artifact
)
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(module.prebuilt_artifacts) AS artifact
WHERE artifact->>'url' LIKE '%/studio-artifacts/modules/lms-nav2-%/1.0.0/prebuilt/%'
AND COALESCE(NULLIF(artifact->>'architecture', ''), '') = ''
);
+9 -9
View File
@@ -153,23 +153,23 @@ WHERE w.course_id IN (SELECT id FROM target_course) AND w.week = 2;
WITH module_definitions(category, name, folder_name, template_repository_url, prebuilt_artifacts, default_parameters, docker_image) AS (
VALUES
('Simulator', 'NAV2-Gazebo', '[lms-nav2-gazebo-simulator]nav2-simulator_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-simulator.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-simulator/1.0.0/prebuilt/latest/lms-nav2-gazebo-simulator-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-simulator-build-install.tar.zst', 'required', false, 'extractPath', '.')), jsonb_build_array('gui:=false'), 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-simulator/1.0.0/prebuilt/latest/lms-nav2-gazebo-simulator-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-simulator-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('gui:=false'), 'future-nav2-gazebo:humble'),
('Vehicle', 'NAV2-TurtleBot3Burger', '[lms-nav2-gazebo-vehicle]nav2-vehicle_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-vehicle.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-vehicle/1.0.0/prebuilt/latest/lms-nav2-gazebo-vehicle-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-vehicle-build-install.tar.zst', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=true', 'platform:=turtlebot3', 'level:=2d'), 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-vehicle/1.0.0/prebuilt/latest/lms-nav2-gazebo-vehicle-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-vehicle-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=true', 'platform:=turtlebot3', 'level:=2d'), 'future-nav2-gazebo:humble'),
('Map', 'NAV2-Map', '[lms-nav2-gazebo-map]nav2-map_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-map.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-map/1.0.0/prebuilt/latest/lms-nav2-gazebo-map-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-map-build-install.tar.zst', 'required', false, 'extractPath', '.')), '[]'::jsonb, 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-map/1.0.0/prebuilt/latest/lms-nav2-gazebo-map-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-map-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), '[]'::jsonb, 'future-nav2-gazebo:humble'),
('Localization', 'NAV2-SLAM', '[lms-nav2-gazebo-slam]nav2-slam_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-slam.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-slam/1.0.0/prebuilt/latest/lms-nav2-gazebo-slam-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-slam-build-install.tar.zst', 'required', false, 'extractPath', '.')), '[]'::jsonb, 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-slam/1.0.0/prebuilt/latest/lms-nav2-gazebo-slam-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-slam-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), '[]'::jsonb, 'future-nav2-gazebo:humble'),
('Localization', 'NAV2-Localization', '[lms-nav2-gazebo-localization]nav2-localization_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-localization.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-localization/1.0.0/prebuilt/latest/lms-nav2-gazebo-localization-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-localization-build-install.tar.zst', 'required', false, 'extractPath', '.')), jsonb_build_array('localization_mode:=slam'), 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-localization/1.0.0/prebuilt/latest/lms-nav2-gazebo-localization-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-localization-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('localization_mode:=slam'), 'future-nav2-gazebo:humble'),
('Perception', 'NAV2-Perception', '[lms-nav2-gazebo-perception]nav2-perception_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-perception.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-perception/1.0.0/prebuilt/latest/lms-nav2-gazebo-perception-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-perception-build-install.tar.zst', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=true'), 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-perception/1.0.0/prebuilt/latest/lms-nav2-gazebo-perception-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-perception-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=true'), 'future-nav2-gazebo:humble'),
('Planning', 'NAV2-Planning', '[lms-nav2-gazebo-planning]nav2-planning_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-planning.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-planning/1.0.0/prebuilt/latest/lms-nav2-gazebo-planning-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-planning-build-install.tar.zst', 'required', false, 'extractPath', '.')), '[]'::jsonb, 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-planning/1.0.0/prebuilt/latest/lms-nav2-gazebo-planning-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-planning-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), '[]'::jsonb, 'future-nav2-gazebo:humble'),
('Control', 'NAV2-Control', '[lms-nav2-gazebo-control]nav2-control_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-gazebo-control.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-control/1.0.0/prebuilt/latest/lms-nav2-gazebo-control-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-control-build-install.tar.zst', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=true', 'robot:=turtlebot3', 'level:=2d'), 'future-nav2-gazebo:humble'),
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-gazebo-control/1.0.0/prebuilt/latest/lms-nav2-gazebo-control-build-install.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-gazebo-control-build-install.tar.zst', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=true', 'robot:=turtlebot3', 'level:=2d'), 'future-nav2-gazebo:humble'),
('Vehicle', 'NAV2-MKMini', '[lms-nav2-mkmini]nav2-mkmini_v1.0.0_ws', 'http://localhost:3000/templates/lms-nav2-mkmini.git',
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-mkmini/1.0.0/prebuilt/amd64/lms-nav2-mkmini-build-install-amd64.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-mkmini-build-install-amd64.tar.zst', 'checksumSha256', '8b36d3fda6a81a49da140529d9874e99b734d05e01e12a49e719e2b26f8803d9', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=false', 'platform:=mkmini', 'robot:=mkmini', 'level:=2d'), 'future-nav2:humble-jetson_orin')
jsonb_build_array(jsonb_build_object('url', 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/modules/lms-nav2-mkmini/1.0.0/prebuilt/amd64/lms-nav2-mkmini-build-install-amd64.tar.zst', 'type', 'build-install', 'fileName', 'lms-nav2-mkmini-build-install-amd64.tar.zst', 'checksumSha256', 'a11ac9e920fcbe5c5a54e086b72d453932ece32e0c7976858435c0a0ba73a8f9', 'architecture', 'amd64', 'required', false, 'extractPath', '.')), jsonb_build_array('sim_mode:=false', 'platform:=mkmini', 'robot:=mkmini', 'level:=2d'), 'future-nav2:humble-jetson_orin')
)
INSERT INTO course_week_modules (week_id, sort_order, category, name, folder_name, template_repository_url, source_type, prebuilt_artifacts, default_parameters, docker_image)
SELECT w.id, row_number() OVER (PARTITION BY w.id ORDER BY module_definitions.folder_name), category, name, folder_name, template_repository_url, 'GIT', prebuilt_artifacts, default_parameters, docker_image
+3 -1
View File
@@ -2,6 +2,7 @@ import { Body, Controller, Delete, Get, Param, Post, Query, Req } from '@nestjs/
import { Request } from 'express';
import { ChatService } from './chat.service';
import { CancelActiveAiResponseDto } from './dto/cancel-active-ai-response.dto';
import { CreateAiResponseDto } from './dto/create-ai-response.dto';
import { CreateChatMessageDto } from './dto/create-chat-message.dto';
import { CreateInstructorChatMessageDto } from './dto/create-instructor-chat-message.dto';
@@ -64,10 +65,11 @@ export class ChatController {
@Post('ai/cancel-active')
cancelActiveAiResponse(
@Req() request: Request,
@Body() body: { week?: number },
@Body() body: CancelActiveAiResponseDto,
) {
return this.chatService.cancelActiveAiResponse(
this.getBearerToken(request),
body.courseId,
body.week,
);
}
+41 -9
View File
@@ -57,6 +57,7 @@ type FutureDriveLlmChatResponse = {
const STUDENT_CHAT_VISIBLE_MESSAGE_LIMIT = 20;
const AI_CHAT_CONTEXT_MESSAGE_LIMIT = 6;
const PUBLIC_GUIDE_COURSE_SLUG = 'autonomy-studio-lms-guide';
@Injectable()
export class ChatService {
@@ -79,7 +80,7 @@ export class ChatService {
async listThreads(accessToken: string | undefined, query: ListChatThreadsDto) {
const student = await this.getLmsUser(accessToken);
const course = await this.getActiveCourse();
const course = await this.getStudentCourse(student.id, query.courseId);
const week = query.week
? await this.getCourseWeek(course.id, query.week)
: null;
@@ -122,7 +123,7 @@ export class ChatService {
async clearStudentThreads(accessToken: string | undefined, query: ListChatThreadsDto) {
const student = await this.getLmsUser(accessToken);
const course = await this.getActiveCourse();
const course = await this.getStudentCourse(student.id, query.courseId);
const week = query.week
? await this.getCourseWeek(course.id, query.week)
: null;
@@ -167,7 +168,7 @@ export class ChatService {
dto: CreateChatMessageDto,
) {
const student = await this.getLmsUser(accessToken);
const course = await this.getActiveCourse();
const course = await this.getStudentCourse(student.id, dto.courseId);
const week = dto.week ? await this.getCourseWeek(course.id, dto.week) : null;
const thread = await this.getOrCreateThread({
courseId: course.id,
@@ -220,7 +221,7 @@ export class ChatService {
dto: CreateAiResponseDto,
) {
const student = await this.getLmsUser(accessToken);
const course = await this.getActiveCourse();
const course = await this.getStudentCourse(student.id, dto.courseId);
const week = dto.week ? await this.getCourseWeek(course.id, dto.week) : null;
const thread = await this.getOrCreateThread({
courseId: course.id,
@@ -425,10 +426,11 @@ export class ChatService {
async cancelActiveAiResponse(
accessToken: string | undefined,
courseId: string,
weekNumber?: number,
) {
const student = await this.getLmsUser(accessToken);
const course = await this.getActiveCourse();
const course = await this.getStudentCourse(student.id, courseId);
const week = weekNumber ? await this.getCourseWeek(course.id, weekNumber) : null;
const message = await this.prisma.chatMessage.findFirst({
where: {
@@ -487,7 +489,7 @@ export class ChatService {
query: ListChatThreadsDto,
) {
const instructor = await this.getInstructorLmsUser(accessToken);
const course = await this.getActiveCourse();
const course = await this.getInstructorCourse(instructor.id, query.courseId);
const week = query.week
? await this.getCourseWeek(course.id, query.week)
: null;
@@ -680,11 +682,41 @@ export class ChatService {
};
}
private async getActiveCourse() {
private async getStudentCourse(studentId: string, courseId?: string) {
if (!courseId) throw new NotFoundException('Course not found');
const course = await this.prisma.course.findFirst({
where: { slug: 'futuredrive-autonomy-basic', isActive: true },
where: {
id: courseId,
isActive: true,
OR: [
{ slug: PUBLIC_GUIDE_COURSE_SLUG },
{
enrollments: {
some: {
userId: studentId,
status: 'ACTIVE',
},
},
},
],
},
});
if (!course) throw new NotFoundException('Curriculum course not found');
if (!course) throw new NotFoundException('Course not found');
return course;
}
private async getInstructorCourse(instructorId: string, courseId?: string) {
if (!courseId) throw new NotFoundException('Course not found');
const course = await this.prisma.course.findFirst({
where: {
id: courseId,
isActive: true,
teacherId: instructorId,
},
});
if (!course) throw new NotFoundException('Course not found');
return course;
}
@@ -0,0 +1,12 @@
import { IsInt, IsOptional, IsUUID, Max, Min } from 'class-validator';
export class CancelActiveAiResponseDto {
@IsUUID()
courseId: string;
@IsOptional()
@IsInt()
@Min(1)
@Max(100)
week?: number;
}
+4 -1
View File
@@ -1,6 +1,9 @@
import { IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
import { IsInt, IsOptional, IsString, IsUUID, Max, Min } from 'class-validator';
export class CreateAiResponseDto {
@IsUUID()
courseId: string;
@IsString()
content: string;
+4 -1
View File
@@ -1,6 +1,9 @@
import { IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
import { IsIn, IsInt, IsOptional, IsString, IsUUID, Max, Min } from 'class-validator';
export class CreateChatMessageDto {
@IsUUID()
courseId: string;
@IsIn(['ai', 'teacher'])
mode: 'ai' | 'teacher';
+5 -1
View File
@@ -1,7 +1,11 @@
import { Transform } from 'class-transformer';
import { IsBoolean, IsIn, IsInt, IsOptional, Max, Min } from 'class-validator';
import { IsBoolean, IsIn, IsInt, IsOptional, IsUUID, Max, Min } from 'class-validator';
export class ListChatThreadsDto {
@IsOptional()
@IsUUID()
courseId?: string;
@IsOptional()
@IsIn(['ai', 'teacher'])
mode?: 'ai' | 'teacher';