@@ -20,15 +20,16 @@ import {
2020 projectMember ,
2121 type ProjectCategory ,
2222} from "$lib/shared/models/schema" ;
23+ import {
24+ log ,
25+ startMigration ,
26+ completeMigration ,
27+ failMigration ,
28+ isRunning ,
29+ } from "./migration-state.server" ;
2330
2431const REPO_URL = "https://github.com/ut-code/utcode.net.git" ;
2532
26- export interface MigrationResult {
27- members : { created : number ; skipped : number ; errors : string [ ] } ;
28- articles : { created : number ; skipped : number ; errors : string [ ] } ;
29- projects : { created : number ; skipped : number ; errors : string [ ] } ;
30- }
31-
3233async function runCommand ( cmd : string , args : string [ ] , cwd ?: string ) : Promise < void > {
3334 return new Promise ( ( resolve , reject ) => {
3435 const child = spawn ( cmd , args , { cwd, stdio : "pipe" } ) ;
@@ -46,7 +47,9 @@ async function runCommand(cmd: string, args: string[], cwd?: string): Promise<vo
4647
4748async function cloneRepo ( ) : Promise < string > {
4849 const tempDir = join ( tmpdir ( ) , `utcode-migration-${ Date . now ( ) } ` ) ;
50+ log ( `Cloning ${ REPO_URL } ...` ) ;
4951 await runCommand ( "git" , [ "clone" , "--depth" , "1" , REPO_URL , tempDir ] ) ;
52+ log ( "Repository cloned successfully" ) ;
5053 return tempDir ;
5154}
5255
@@ -99,12 +102,15 @@ const MemberFrontmatterSchema = v.object({
99102
100103async function migrateMembers (
101104 repoPath : string ,
102- ) : Promise < { created : number ; skipped : number ; errors : string [ ] } > {
105+ ) : Promise < { created : number ; skipped : number ; errors : number } > {
106+ log ( "--- Migrating Members ---" ) ;
103107 const membersPath = join ( repoPath , "contents/members" ) ;
104108 const files = await findMarkdownFiles ( membersPath ) ;
109+ log ( `Found ${ files . length } member files` ) ;
110+
105111 let created = 0 ;
106112 let skipped = 0 ;
107- const errors : string [ ] = [ ] ;
113+ let errorCount = 0 ;
108114
109115 for ( const file of files ) {
110116 const relPath = file . replace ( membersPath + "/" , "" ) ;
@@ -124,6 +130,7 @@ async function migrateMembers(
124130 . limit ( 1 ) ;
125131
126132 if ( existing . length > 0 ) {
133+ log ( ` ⊘ Skipped: ${ slug } (already exists)` ) ;
127134 skipped ++ ;
128135 continue ;
129136 }
@@ -143,13 +150,17 @@ async function migrateMembers(
143150 pageContent : body || null ,
144151 } ) ;
145152
153+ log ( ` ✓ Created: ${ slug } (${ frontmatter . nameJa } )` ) ;
146154 created ++ ;
147155 } catch ( e ) {
148- errors . push ( `${ slug } : ${ e instanceof Error ? e . message : String ( e ) } ` ) ;
156+ const msg = e instanceof Error ? e . message : String ( e ) ;
157+ log ( ` ✗ Error: ${ slug } - ${ msg } ` ) ;
158+ errorCount ++ ;
149159 }
150160 }
151161
152- return { created, skipped, errors } ;
162+ log ( `Members: ${ created } created, ${ skipped } skipped, ${ errorCount } errors` ) ;
163+ return { created, skipped, errors : errorCount } ;
153164}
154165
155166// Article migration
@@ -185,12 +196,15 @@ function generateExcerpt(content: string, maxLength = 200): string {
185196
186197async function migrateArticles (
187198 repoPath : string ,
188- ) : Promise < { created : number ; skipped : number ; errors : string [ ] } > {
199+ ) : Promise < { created : number ; skipped : number ; errors : number } > {
200+ log ( "--- Migrating Articles ---" ) ;
189201 const articlesPath = join ( repoPath , "contents/articles" ) ;
190202 const files = await findMarkdownFiles ( articlesPath ) ;
203+ log ( `Found ${ files . length } article files` ) ;
204+
191205 let created = 0 ;
192206 let skipped = 0 ;
193- const errors : string [ ] = [ ] ;
207+ let errorCount = 0 ;
194208
195209 for ( const file of files ) {
196210 const relPath = file . replace ( articlesPath + "/" , "" ) ;
@@ -208,6 +222,7 @@ async function migrateArticles(
208222 . limit ( 1 ) ;
209223
210224 if ( existing . length > 0 ) {
225+ log ( ` ⊘ Skipped: ${ slug } (already exists)` ) ;
211226 skipped ++ ;
212227 continue ;
213228 }
@@ -238,13 +253,17 @@ async function migrateArticles(
238253 viewCount : 0 ,
239254 } ) ;
240255
256+ log ( ` ✓ Created: ${ slug } ` ) ;
241257 created ++ ;
242258 } catch ( e ) {
243- errors . push ( `${ slug } : ${ e instanceof Error ? e . message : String ( e ) } ` ) ;
259+ const msg = e instanceof Error ? e . message : String ( e ) ;
260+ log ( ` ✗ Error: ${ slug } - ${ msg } ` ) ;
261+ errorCount ++ ;
244262 }
245263 }
246264
247- return { created, skipped, errors } ;
265+ log ( `Articles: ${ created } created, ${ skipped } skipped, ${ errorCount } errors` ) ;
266+ return { created, skipped, errors : errorCount } ;
248267}
249268
250269// Project migration
@@ -279,12 +298,15 @@ function mapCategory(kind: string | undefined): ProjectCategory {
279298
280299async function migrateProjects (
281300 repoPath : string ,
282- ) : Promise < { created : number ; skipped : number ; errors : string [ ] } > {
301+ ) : Promise < { created : number ; skipped : number ; errors : number } > {
302+ log ( "--- Migrating Projects ---" ) ;
283303 const projectsPath = join ( repoPath , "contents/projects" ) ;
284304 const files = await findMarkdownFiles ( projectsPath ) ;
305+ log ( `Found ${ files . length } project files` ) ;
306+
285307 let created = 0 ;
286308 let skipped = 0 ;
287- const errors : string [ ] = [ ] ;
309+ let errorCount = 0 ;
288310
289311 for ( const file of files ) {
290312 const dirPath = dirname ( file ) ;
@@ -303,6 +325,7 @@ async function migrateProjects(
303325 . limit ( 1 ) ;
304326
305327 if ( existing . length > 0 ) {
328+ log ( ` ⊘ Skipped: ${ slug } (already exists)` ) ;
306329 skipped ++ ;
307330 continue ;
308331 }
@@ -344,16 +367,34 @@ async function migrateProjects(
344367 }
345368 }
346369
370+ log ( ` ✓ Created: ${ slug } (${ name } )` ) ;
347371 created ++ ;
348372 } catch ( e ) {
349- errors . push ( `${ slug } : ${ e instanceof Error ? e . message : String ( e ) } ` ) ;
373+ const msg = e instanceof Error ? e . message : String ( e ) ;
374+ log ( ` ✗ Error: ${ slug } - ${ msg } ` ) ;
375+ errorCount ++ ;
350376 }
351377 }
352378
353- return { created, skipped, errors } ;
379+ log ( `Projects: ${ created } created, ${ skipped } skipped, ${ errorCount } errors` ) ;
380+ return { created, skipped, errors : errorCount } ;
381+ }
382+
383+ export function startDataMigration ( ) : { started : boolean ; message : string } {
384+ if ( isRunning ( ) ) {
385+ return { started : false , message : "Migration already in progress" } ;
386+ }
387+
388+ startMigration ( ) ;
389+ log ( "=== Data Migration Started ===" ) ;
390+
391+ // Run migration in background (fire and forget with proper error handling)
392+ runMigrationAsync ( ) . catch ( console . error ) ;
393+
394+ return { started : true , message : "Migration started" } ;
354395}
355396
356- export async function runDataMigration ( ) : Promise < MigrationResult > {
397+ async function runMigrationAsync ( ) : Promise < void > {
357398 let repoPath : string | null = null ;
358399
359400 try {
@@ -365,10 +406,16 @@ export async function runDataMigration(): Promise<MigrationResult> {
365406 const articles = await migrateArticles ( repoPath ) ;
366407 const projects = await migrateProjects ( repoPath ) ;
367408
368- return { members, articles, projects } ;
409+ log ( "=== Migration Complete ===" ) ;
410+ completeMigration ( { members, articles, projects } ) ;
411+ } catch ( e ) {
412+ const msg = e instanceof Error ? e . message : String ( e ) ;
413+ log ( `=== Migration Failed: ${ msg } ===` ) ;
414+ failMigration ( msg ) ;
369415 } finally {
370416 // Cleanup
371417 if ( repoPath ) {
418+ log ( "Cleaning up temporary files..." ) ;
372419 await rm ( repoPath , { recursive : true , force : true } ) . catch ( ( ) => { } ) ;
373420 }
374421 }
0 commit comments