@@ -94,12 +94,16 @@ router.get('/libs/*', (req, res) => {
9494
9595 // Security: prevent directory traversal
9696 if ( requestedPath . includes ( '..' ) ) {
97+ console . log ( '[H5P-LIBS] BLOCKED traversal attempt:' , requestedPath ) ;
9798 return res . status ( 400 ) . send ( 'Invalid path' ) ;
9899 }
99100
100101 const filePath = path . join ( H5P_LIBS_DIR , requestedPath ) ;
102+ console . log ( '[H5P-LIBS] Serving:' , requestedPath , '->' , filePath ) ;
103+
101104 res . sendFile ( filePath , ( err ) => {
102105 if ( err ) {
106+ console . error ( '[H5P-LIBS] NOT FOUND:' , filePath , err . message ) ;
103107 res . status ( 404 ) . send ( 'Library file not found' ) ;
104108 }
105109 } ) ;
@@ -189,6 +193,17 @@ body { margin:0; padding:40px; font-family:-apple-system,BlinkMacSystemFont,"Seg
189193 // Read directly from h5p-libs (no temp dir or symlinks needed).
190194 const { cssFiles, jsFiles } = await resolveLibraryDependencies ( syntheticH5pJson ) ;
191195
196+ // Build server-side diagnostic info to embed in HTML (user has no backend access)
197+ const serverDiag = {
198+ quizId,
199+ questionTypes : [ ...questionTypes ] ,
200+ dependencies : preloadedDependencies . map ( d => `${ d . machineName } -${ d . majorVersion } .${ d . minorVersion } ` ) ,
201+ resolvedJsFiles : jsFiles ,
202+ resolvedCssFiles : cssFiles ,
203+ h5pLibsDir : H5P_LIBS_DIR ,
204+ timestamp : new Date ( ) . toISOString ( )
205+ } ;
206+
192207 // Serve library files through the existing /api route — works on all environments
193208 // without needing extra nginx/proxy configuration.
194209 const libBasePath = '/api/create/h5p-preview/libs' ;
@@ -280,12 +295,12 @@ ${questionBlocks.join('\n')}
280295 </div>
281296
282297 <script>
283- // Track JS errors during library loading (must be before library scripts)
298+ // ===== DIAGNOSTICS: Track all errors during library loading =====
284299 window.__h5pErrors = [];
285300 window.__h5pLoadErrors = [];
286301 window.onerror = function(msg, src, line, col, err) {
287- window.__h5pErrors.push({msg: msg, src: src, line: line});
288- console.error('H5P Script Error:', msg, 'in', src, 'line', line);
302+ window.__h5pErrors.push({msg: String( msg) , src: String( src) , line: line});
303+ console.error('[ H5P-DIAG] Script Error:', msg, 'in', src, 'line', line);
289304 };
290305 </script>
291306 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
@@ -297,16 +312,122 @@ ${jsTags}
297312 H5P.$body = jQuery('body');
298313 H5P.$window = jQuery(window);
299314
300- jQuery(document).ready(function() {
301- // Log load failures and available constructors for debugging
302- if (window.__h5pLoadErrors.length > 0) {
303- console.error('H5P: Failed to load scripts:', window.__h5pLoadErrors);
315+ // ===== SERVER-SIDE INFO (embedded at render time) =====
316+ var __serverDiag = ${ JSON . stringify ( serverDiag ) } ;
317+ var __libScripts = __serverDiag.resolvedJsFiles;
318+ var __libBasePath = '${ libBasePath } ';
319+
320+ async function runDiagnostics() {
321+ console.log('%c===== H5P LOADING DIAGNOSTICS =====', 'color: yellow; font-size: 16px; font-weight: bold;');
322+ console.log('Time:', new Date().toISOString());
323+ console.log('Page URL:', window.location.href);
324+
325+ // 1. Server-side info (dependency resolution results from backend)
326+ console.log('%c--- SERVER-SIDE INFO (from backend) ---', 'color: cyan; font-weight: bold;');
327+ console.log('Quiz ID:', __serverDiag.quizId);
328+ console.log('Question types:', __serverDiag.questionTypes);
329+ console.log('h5p-libs dir:', __serverDiag.h5pLibsDir);
330+ console.log('Dependencies resolved:', __serverDiag.dependencies);
331+ console.log('JS files resolved (' + __serverDiag.resolvedJsFiles.length + '):', __serverDiag.resolvedJsFiles);
332+ console.log('CSS files resolved (' + __serverDiag.resolvedCssFiles.length + '):', __serverDiag.resolvedCssFiles);
333+
334+ // 2. Check which H5P constructors are registered after script loading
335+ console.log('%c--- H5P CONSTRUCTOR CHECK ---', 'color: cyan; font-weight: bold;');
336+ var h5pKeys = Object.keys(window.H5P || {}).filter(function(k) {
337+ return typeof H5P[k] === 'function';
338+ });
339+ console.log('Registered constructors (' + h5pKeys.length + '):', h5pKeys);
340+ var criticalLibs = ['DragText', 'Dialogcards', 'MultiChoice', 'TrueFalse', 'Question', 'Column', 'JoubelUI', 'TextUtilities'];
341+ criticalLibs.forEach(function(name) {
342+ var val = H5P[name];
343+ var status = val ? '✅ ' + typeof val : '❌ MISSING';
344+ console.log(' H5P.' + name + ':', status);
345+ });
346+
347+ // 3. Fetch each script URL to check HTTP status and content-type
348+ console.log('%c--- SCRIPT URL FETCH CHECKS ---', 'color: cyan; font-weight: bold;');
349+
350+ // Check h5p-core.js first
351+ try {
352+ var coreResp = await fetch('/api/create/h5p-preview/core/h5p-core.js', { method: 'HEAD' });
353+ console.log('h5p-core.js → Status:', coreResp.status, 'Content-Type:', coreResp.headers.get('content-type'));
354+ } catch(e) {
355+ console.error('h5p-core.js → FETCH ERROR:', e.message);
356+ }
357+
358+ // Check each library script
359+ var problems = [];
360+ for (var i = 0; i < __libScripts.length; i++) {
361+ var scriptUrl = __libBasePath + '/' + __libScripts[i];
362+ try {
363+ var resp = await fetch(scriptUrl);
364+ var ct = resp.headers.get('content-type') || 'unknown';
365+ var status = resp.status;
366+ var bodyText = await resp.text();
367+ var isJS = ct.includes('javascript') || ct.includes('ecmascript');
368+ var isHTML = ct.includes('html') || bodyText.trimStart().startsWith('<!') || bodyText.trimStart().startsWith('<html');
369+ var bodyPreview = bodyText.substring(0, 150).replace(/\\n/g, ' ');
370+
371+ if (status === 200 && isJS && !isHTML) {
372+ console.log('✅', __libScripts[i], '→ Status:', status, 'Type:', ct, 'Size:', bodyText.length, 'bytes');
373+ } else {
374+ console.error('❌', __libScripts[i], '→ Status:', status, 'Type:', ct, 'Size:', bodyText.length, 'IsHTML:', isHTML);
375+ console.error(' Body preview:', bodyPreview);
376+ problems.push({ file: __libScripts[i], status: status, contentType: ct, isHTML: isHTML, preview: bodyPreview });
377+ }
378+ } catch(fetchErr) {
379+ console.error('❌', __libScripts[i], '→ FETCH ERROR:', fetchErr.message);
380+ problems.push({ file: __libScripts[i], error: fetchErr.message });
381+ }
304382 }
305- if (window.__h5pErrors.length > 0) {
306- console.error('H5P: JS errors during loading:', window.__h5pErrors);
383+
384+ // 4. Errors collected during loading
385+ console.log('%c--- ERRORS DURING SCRIPT LOADING ---', 'color: cyan; font-weight: bold;');
386+ console.log('window.onerror events:', window.__h5pErrors.length);
387+ window.__h5pErrors.forEach(function(e, i) {
388+ console.error(' Error ' + i + ':', e.msg, '| src:', e.src, '| line:', e.line);
389+ });
390+ console.log('Script onerror events:', window.__h5pLoadErrors.length);
391+ window.__h5pLoadErrors.forEach(function(f, i) {
392+ console.error(' Failed to load:', f);
393+ });
394+
395+ // 5. List all script tags actually in the DOM
396+ console.log('%c--- ALL SCRIPT TAGS IN DOM ---', 'color: cyan; font-weight: bold;');
397+ var allScripts = document.querySelectorAll('script[src]');
398+ console.log('Total script tags:', allScripts.length);
399+ allScripts.forEach(function(s, i) {
400+ console.log(' [' + i + ']', s.src);
401+ });
402+
403+ // 6. Summary
404+ console.log('%c--- SUMMARY ---', 'color: yellow; font-weight: bold; font-size: 14px;');
405+ if (problems.length > 0) {
406+ console.error('🔴 ' + problems.length + ' scripts have problems:');
407+ problems.forEach(function(p) {
408+ if (p.isHTML) {
409+ console.error(' ' + p.file + ' → Server returned HTML instead of JS! This means nginx is NOT routing to Express.');
410+ } else if (p.error) {
411+ console.error(' ' + p.file + ' → Network error: ' + p.error);
412+ } else {
413+ console.error(' ' + p.file + ' → Status: ' + p.status + ', Type: ' + p.contentType);
414+ }
415+ });
416+ } else if (h5pKeys.length === 0) {
417+ console.error('🔴 No H5P constructors registered even though all scripts loaded OK. Possible execution error.');
418+ } else {
419+ console.log('🟢 All ' + __libScripts.length + ' scripts loaded, ' + h5pKeys.length + ' constructors registered.');
307420 }
421+ console.log('%c===== END DIAGNOSTICS =====', 'color: yellow; font-size: 16px; font-weight: bold;');
422+
423+ return problems;
424+ }
308425
426+ jQuery(document).ready(function() {
427+ // Run diagnostics first, then render
428+ runDiagnostics().then(function(problems) {
309429${ runnableCalls . join ( '\n' ) }
430+ });
310431 });
311432 </script>
312433</body>
0 commit comments