import { useCallback, useEffect } from 'react' import { LMS_API_BASE_URL } from '../../config/appConfig' import type { CurriculumWeek } from '../curriculum/types' import type { ModulePrebuiltArtifact, ModuleRow, RuntimeImageArtifact } from './types' type UseRemoteWorkspaceRuntimeOptions = { availableModules: ModuleRow[] curriculumModules: CurriculumWeek['modules'] courseKey: string weekNumber: number clonePathPreview: string remoteWorkspace: boolean remoteWorkspaceTargetKey: string activeRuntimeImages: RuntimeImageArtifact[] getFreshAccessToken: () => Promise getRemoteWorkspaceTarget: () => RemoteWorkspaceTarget saveDirtyFilesForModule: (module: ModuleRow) => Promise onRegisterStopRunningModules?: (handler: (() => Promise) | null) => void } type UseRemoteWorkspaceRuntimeResult = { prepareRemoteRuntimeModule: (module: ModuleRow) => Promise } export function useRemoteWorkspaceRuntime({ availableModules, curriculumModules, courseKey, weekNumber, clonePathPreview, remoteWorkspace, remoteWorkspaceTargetKey, activeRuntimeImages, getFreshAccessToken, getRemoteWorkspaceTarget, saveDirtyFilesForModule, onRegisterStopRunningModules }: UseRemoteWorkspaceRuntimeOptions): UseRemoteWorkspaceRuntimeResult { const prepareRemoteRuntimeModule = useCallback(async (module: ModuleRow): Promise => { const requestAccessToken = await getFreshAccessToken() await saveDirtyFilesForModule(module) const response = await fetch(`${LMS_API_BASE_URL}/studio/repositories/prepare`, { method: 'POST', headers: { Authorization: `Bearer ${requestAccessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ courseId: courseKey, week: weekNumber, moduleName: module.name, folderName: module.folderName, templateRepositoryUrl: module.templateRepositoryUrl }) }) if (!response.ok) throw new Error(await response.text()) const prepared = await response.json() as { repositoryUrlWithToken?: string templateRepositoryUrlWithToken?: string templateRepositoryUrl?: string prebuiltArtifacts?: ModulePrebuiltArtifact[] | null } const templateRepositoryUrl = prepared.templateRepositoryUrlWithToken ?? prepared.templateRepositoryUrl ?? module.templateRepositoryUrl await window.remoteWorkspace.modules.install( getRemoteWorkspaceTarget(), module.name, module.folderName, templateRepositoryUrl, prepared.repositoryUrlWithToken, { prebuiltArtifacts: prepared.prebuiltArtifacts ?? module.prebuiltArtifacts ?? null, runtimeImages: activeRuntimeImages } ) const submitResult = remoteWorkspace ? await window.remoteWorkspace.git.submit( getRemoteWorkspaceTarget(), module.name, module.folderName, `Run: ${weekNumber}주차 ${module.name}`, ) : await window.localWorkspace.git.submit( module.name, clonePathPreview, module.folderName, `Run: ${weekNumber}주차 ${module.name}`, undefined, undefined, undefined, ) if (!submitResult.ok && !submitResult.message.includes('제출할 변경사항이 없습니다')) { throw new Error(`[${module.name}] ${submitResult.message}`) } }, [ activeRuntimeImages, clonePathPreview, courseKey, getFreshAccessToken, getRemoteWorkspaceTarget, remoteWorkspace, saveDirtyFilesForModule, weekNumber ]) const stopRunningWorkspaceModules = useCallback(async (): Promise => { if (remoteWorkspace) { const moduleMap = new Map() for (const module of availableModules) { moduleMap.set(module.name, { moduleName: module.name, folderName: module.folderName, templateRepositoryUrl: module.templateRepositoryUrl, sortOrder: module.sortOrder }) } for (const module of curriculumModules ?? []) { if (!moduleMap.has(module.name)) { moduleMap.set(module.name, { moduleName: module.name, folderName: module.folderName, templateRepositoryUrl: module.templateRepositoryUrl, sortOrder: module.sortOrder }) } } const modules = Array.from(moduleMap.values()) .sort((left, right) => (right.sortOrder ?? 0) - (left.sortOrder ?? 0)) .map(({ moduleName, folderName }) => ({ moduleName, folderName })) if (!modules.length) return await window.remoteWorkspace.modules.resetSession(getRemoteWorkspaceTarget(), modules).catch(() => ({ ok: false, message: '플랫폼 초기화 실패' })) return } await window.localWorkspace.modules.stopRunning().catch(() => ({ ok: false, message: '모듈 중지 실패' })) }, [availableModules, curriculumModules, getRemoteWorkspaceTarget, remoteWorkspace, remoteWorkspaceTargetKey]) useEffect(() => { onRegisterStopRunningModules?.(stopRunningWorkspaceModules) return () => onRegisterStopRunningModules?.(null) }, [onRegisterStopRunningModules, stopRunningWorkspaceModules]) return { prepareRemoteRuntimeModule } }