@@ -10,6 +10,7 @@ import { setCorsHeaders } from './utils/cors';
1010import { sanitizeUrlPath , sanitizeErrorMessage } from './utils/pathSecurity' ;
1111import { validateGameId , validateHostname } from './validation/schemas' ;
1212import { gameDataDownloader } from './services' ;
13+ import { DownloadRegistry } from '../services/DownloadRegistry' ;
1314
1415/** Escape HTML special characters to prevent XSS */
1516function escapeHtml ( str : string ) : string {
@@ -104,18 +105,40 @@ export class GameZipServer {
104105 }
105106
106107 if ( ! zipExists && gameId && dateAdded ) {
108+ // Check shared registry first (prevents duplicate if DownloadManager already downloading)
109+ if ( DownloadRegistry . isActive ( gameId ) ) {
110+ logger . info (
111+ `[GameZipServer] Download already in progress for game ${ gameId } (via DownloadManager)`
112+ ) ;
113+ return { success : true , downloading : true , statusCode : 202 } ;
114+ }
115+
107116 if ( downloadsInProgress . has ( gameId ) ) {
108117 logger . info ( `[GameZipServer] Download already in progress for game ${ gameId } ` ) ;
109118 return { success : true , downloading : true , statusCode : 202 } ;
110119 }
111120
112- if ( downloadsInProgress . size >= MAX_CONCURRENT_DOWNLOADS ) {
121+ const totalActive = downloadsInProgress . size + DownloadRegistry . getActiveCount ( ) ;
122+ if ( totalActive >= MAX_CONCURRENT_DOWNLOADS ) {
113123 logger . warn (
114- `[GameZipServer] Too many concurrent downloads (${ downloadsInProgress . size } ), rejecting ${ gameId } `
124+ `[GameZipServer] Too many concurrent downloads (${ totalActive } ), rejecting ${ gameId } `
115125 ) ;
116126 return { success : false , statusCode : 503 } ;
117127 }
118128
129+ // Register with shared download registry
130+ const registered = DownloadRegistry . register ( gameId , {
131+ gameId,
132+ source : 'game-zip-server' ,
133+ } ) ;
134+
135+ if ( ! registered ) {
136+ logger . info (
137+ `[GameZipServer] Download already in progress for game ${ gameId } (detected by registry)`
138+ ) ;
139+ return { success : true , downloading : true , statusCode : 202 } ;
140+ }
141+
119142 logger . info (
120143 `[GameZipServer] ZIP not found locally, starting background download for game ${ gameId } `
121144 ) ;
@@ -280,15 +303,17 @@ export class GameZipServer {
280303 let fileData = result . data ;
281304 if ( ext === 'html' || ext === 'htm' ) {
282305 fileData = injectPolyfills ( result . data ) ;
283- logger . info ( `[GameZipServer] Injected polyfills into HTML file: ${ relPath } ` ) ;
306+ logger . debug ( `[GameZipServer] Injected polyfills into HTML file: ${ relPath } ` ) ;
284307 }
285308
286- logger . info (
309+ logger . debug (
287310 `[GameZipServer] ✓ Serving from ZIP ${ result . mountId } : ${ relPath } (${ fileData . length } bytes)`
288311 ) ;
289312
290313 const settings = ConfigManager . getSettings ( ) ;
291314 setCorsHeaders ( res , settings ) ;
315+ res . setHeader ( 'X-Content-Type-Options' , 'nosniff' ) ;
316+ res . setHeader ( 'X-Frame-Options' , 'SAMEORIGIN' ) ;
292317
293318 res . setHeader ( 'Content-Type' , contentType ) ;
294319 res . setHeader ( 'Content-Length' , fileData . length ) ;
@@ -351,22 +376,27 @@ export class GameZipServer {
351376 logger . error (
352377 `[GameZipServer] Downloaded file path outside allowed directory: ${ result . filePath } `
353378 ) ;
379+ DownloadRegistry . fail ( gameId ) ;
354380 return ;
355381 }
356382
357383 try {
358384 await zipManager . mount ( mountId , result . filePath ) ;
359385 logger . info ( `[GameZipServer] ✓ ZIP downloaded and mounted for game ${ gameId } ` ) ;
386+ DownloadRegistry . complete ( gameId ) ;
360387 } catch ( mountError ) {
361388 const errorMessage = mountError instanceof Error ? mountError . message : 'Mount failed' ;
362389 logger . error ( `[GameZipServer] Failed to mount downloaded ZIP: ${ errorMessage } ` ) ;
390+ DownloadRegistry . fail ( gameId ) ;
363391 }
364392 } else {
365393 logger . error ( `[GameZipServer] Download failed for ${ gameId } : ${ result . error } ` ) ;
394+ DownloadRegistry . fail ( gameId ) ;
366395 }
367396 } catch ( error ) {
368397 const errorMsg = error instanceof Error ? error . message : 'Unknown error' ;
369398 logger . error ( `[GameZipServer] Background download error for ${ gameId } : ${ errorMsg } ` ) ;
399+ DownloadRegistry . fail ( gameId ) ;
370400 } finally {
371401 downloadsInProgress . delete ( gameId ) ;
372402 }
@@ -377,6 +407,7 @@ export class GameZipServer {
377407 for ( const [ gameId , progress ] of downloadsInProgress . entries ( ) ) {
378408 if ( now - progress . startTime > DOWNLOAD_STALE_MS ) {
379409 logger . warn ( `[GameZipServer] Removing stale download for ${ gameId } ` ) ;
410+ DownloadRegistry . fail ( gameId ) ;
380411 downloadsInProgress . delete ( gameId ) ;
381412 }
382413 }
@@ -529,6 +560,7 @@ export class GameZipServer {
529560
530561 const settings = ConfigManager . getSettings ( ) ;
531562 setCorsHeaders ( res , settings ) ;
563+ res . setHeader ( 'X-Content-Type-Options' , 'nosniff' ) ;
532564
533565 res . setHeader ( 'Content-Type' , 'text/plain' ) ;
534566 res . writeHead ( statusCode ) ;
0 commit comments