44 runMirrorSync ,
55 testUpstreamConnection ,
66 getMirrorStatus ,
7+ getSyncHistory ,
8+ syncEvents ,
9+ type SyncProgressEvent ,
710} from "../../lib/mirror-sync.js" ;
811
912export async function adminRoutes ( app : FastifyInstance ) {
@@ -28,9 +31,44 @@ export async function adminRoutes(app: FastifyInstance) {
2831 return result ;
2932 } ) ;
3033
31- // Trigger a sync manually
34+ // Trigger a sync manually (fire-and-forget, client uses SSE for progress)
3235 app . post ( "/admin/mirror/sync" , async ( ) => {
33- const result = await runMirrorSync ( ) ;
34- return result ;
36+ // Start sync in background — don't await
37+ runMirrorSync ( "manual" ) . catch ( ( err ) => {
38+ console . error ( "[mirror-sync] Unhandled sync error:" , err ) ;
39+ } ) ;
40+ return { started : true } ;
41+ } ) ;
42+
43+ // SSE endpoint for live sync progress
44+ app . get ( "/admin/mirror/sync/progress" , async ( request , reply ) => {
45+ reply . raw . writeHead ( 200 , {
46+ "Content-Type" : "text/event-stream" ,
47+ "Cache-Control" : "no-cache" ,
48+ Connection : "keep-alive" ,
49+ } ) ;
50+
51+ function onProgress ( event : SyncProgressEvent ) {
52+ reply . raw . write ( `data: ${ JSON . stringify ( event ) } \n\n` ) ;
53+ if ( event . type === "done" ) {
54+ // Give client a moment to process, then close
55+ setTimeout ( ( ) => reply . raw . end ( ) , 100 ) ;
56+ }
57+ }
58+
59+ syncEvents . on ( "progress" , onProgress ) ;
60+
61+ request . raw . on ( "close" , ( ) => {
62+ syncEvents . off ( "progress" , onProgress ) ;
63+ } ) ;
64+ } ) ;
65+
66+ // Sync history
67+ app . get ( "/admin/mirror/history" , async ( request ) => {
68+ const limit = Math . min (
69+ Number ( ( request . query as any ) ?. limit ) || 20 ,
70+ 100 ,
71+ ) ;
72+ return getSyncHistory ( limit ) ;
3573 } ) ;
3674}
0 commit comments