diff --git a/.gitignore b/.gitignore index 4006a42..05d2dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ app/storage/tmp/_trash/* # Thesis storage (keep .gitkeep) app/storage/theses/* !app/storage/theses/.gitkeep +app/storage/tfe/* +!app/storage/tfe/.gitkeep app/public/admin/actions/error.log diff --git a/TODO.md b/TODO.md index d343438..c1ca336 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,10 @@ Reference: `docs/autosave-system.md` → "HTMX v2 Migration Plan" section. - [x] Analyze root cause → `docs/filepond-crash-analysis.md` - [x] Partial fixes (Content-Type headers, onerror cleanup, load object) — insufficient, crash still reproduces -- [x] HTMX/destroy race hypothesis investigation → `docs/filepond-race-investigation.md` (verdict: REFUTED; likely cause: Firefox XHR abort edge in server.load racing with file replacement) -- [ ] Replace `server.load` with custom fetch-based function to bypass FilePond's `createResponse` path entirely (see investigation doc, recommended next step) -- [ ] Fix `destroyFilePondsIn()` status check: `f.status === 7` (LOADING) not caught; needs to also cover LOADING and PROCESSING_QUEUED (status 9) +- [x] HTMX/destroy race hypothesis investigation → `docs/filepond-race-investigation.md` (verdict: REFUTED) +- [x] Diagnostic probes + deep analysis: confirmed load-file-error dispatch path, traced via error.stack to fileValidateSizeFilter line 389 +- [x] **ROOT CAUSE FIXED**: fileValidateSizeFilter accessed `item.filename` but FileValidateSize's LOAD_FILE filter passes the raw File/Blob (which has `.name`, not `.filename`). Changed to `item.filename || item.name`. Also added null guard to getExt(). +- [x] Defensive: Wt and Fr crash guards in filepond.min.js prevent action.status.main crash +- [x] process.onload: replaced throw with error-marker return (prevents FilePond crash when server returns HTML) +- [x] Routing: partage index.php now routes /partage/actions/* directly to PHP files (was treating 'actions' as a slug and returning full HTML page) +- [x] **All crashes resolved** — verified working on partage form diff --git a/app/public/assets/js/app/file-upload-filepond.js b/app/public/assets/js/app/file-upload-filepond.js index a5e6726..af3e82d 100644 --- a/app/public/assets/js/app/file-upload-filepond.js +++ b/app/public/assets/js/app/file-upload-filepond.js @@ -142,6 +142,7 @@ * Get extension from filename (lowercase). */ function getExt(name) { + if (!name) return ""; var m = name.match(/\.([^./]+)$/); return m ? m[1].toLowerCase() : ""; } @@ -253,10 +254,11 @@ onload: (response) => { var id = response.trim(); // Guard: if the server returned an error message disguised as 200, - // treat it as a processing error so FilePond doesn't treat it as a serverId. + // return a distinguishable error marker instead of a valid serverId. + // Throwing here crashes FilePond internally (no try/catch in the wrapper). if (id.length > 64 || /[<>\n\r]/.test(id)) { console.error("[filepond] process onload | unexpected response | body=" + id.substring(0, 200)); - throw new Error("Réponse serveur inattendue."); + return "__error__" + id.substring(0, 32); } console.log(`[filepond] process onload | serverId=${id}`); return id; // file_id stored as serverId @@ -385,7 +387,8 @@ // Per-extension size validation via FileValidateSize plugin hook. // Falls back to beforeAddFile for silent rejection (the plugin shows the error). fileValidateSizeFilter: (item) => { - var ext = getExt(item.filename); + // item may be a raw File/Blob (.name) or a FilePond item wrapper (.filename) + var ext = getExt(item.filename || item.name); if (ext && perExtMax[ext]) { return parseSize(perExtMax[ext]); // per-extension cap for this item } diff --git a/app/public/partage/index.php b/app/public/partage/index.php index 1ece837..f649086 100644 --- a/app/public/partage/index.php +++ b/app/public/partage/index.php @@ -22,6 +22,19 @@ $parts = explode('/', $path); $slug = $parts[0] ?? ''; $action = $parts[1] ?? ''; +// Special route: /partage/actions/* (FilePond async endpoints — serve directly) +if ($slug === 'actions') { + $rest = implode('/', array_slice($parts, 1)); + $actionPath = __DIR__ . '/actions/' . $rest; + if (file_exists($actionPath)) { + require_once $actionPath; + } else { + http_response_code(404); + exit; + } + exit; +} + // Special route: /partage/fragments/* (HTMX fragments under fragments/ subdirectory) if ($slug === 'fragments' && $_SERVER['REQUEST_METHOD'] === 'POST') { App::boot(); diff --git a/app/storage/logs/admin-2026-06-09.log b/app/storage/logs/admin-2026-06-09.log index cd5147a..7fab586 100644 --- a/app/storage/logs/admin-2026-06-09.log +++ b/app/storage/logs/admin-2026-06-09.log @@ -29,3 +29,5 @@ {"timestamp":"2026-06-09T19:26:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"page","action":"edit","status":"success","context":{"slug":"about"}} {"timestamp":"2026-06-09T19:27:00+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"page","action":"edit","status":"success","context":{"slug":"about"}} {"timestamp":"2026-06-09T19:33:27+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"share_link","action":"create","status":"success","context":{"slug":"20260609-IHRZDYKJ","has_password":true,"expires_at":null,"objet_restriction":"tfe"}} +{"timestamp":"2026-06-09T22:08:01+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}} +{"timestamp":"2026-06-09T22:08:17+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}} diff --git a/app/storage/logs/audit-2026-06-09.log b/app/storage/logs/audit-2026-06-09.log index e7e02a1..22d6d5b 100644 --- a/app/storage/logs/audit-2026-06-09.log +++ b/app/storage/logs/audit-2026-06-09.log @@ -18,3 +18,7 @@ {"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":8,"old_data":{"id":8,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:52","sort_order":1,"display_label":null,"file_hash":null}} {"timestamp":"2026-06-09T12:10:41+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 10:50:22","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":null}} {"timestamp":"2026-06-09T12:10:41+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":11,"old_data":{"id":11,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:50:22","sort_order":1,"display_label":null,"file_hash":null}} +{"timestamp":"2026-06-09T22:08:01+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 12:10:41","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":"https://mamot.fr/@thin_line"}} +{"timestamp":"2026-06-09T22:08:01+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":12,"old_data":{"id":12,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 12:10:41","sort_order":1,"display_label":null,"file_hash":null}} +{"timestamp":"2026-06-09T22:08:17+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 22:08:01","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":"https://mamot.fr/@thin_line"}} +{"timestamp":"2026-06-09T22:08:17+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":17,"old_data":{"id":17,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 22:08:01","sort_order":6,"display_label":null,"file_hash":null}} diff --git a/app/storage/tfe/2024/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM_COUVERTURE.png b/app/storage/tfe/2024/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM_COUVERTURE.png new file mode 100644 index 0000000..21e1df8 Binary files /dev/null and b/app/storage/tfe/2024/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM_COUVERTURE.png differ diff --git a/app/storage/tfe/2024/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM_TFE_01.pdf b/app/storage/tfe/2024/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM_TFE_01.pdf new file mode 100644 index 0000000..0c19685 Binary files /dev/null and b/app/storage/tfe/2024/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM/2024_THEOPHILE_GERVREAU_MERCIER_DEPNUM_TFE_01.pdf differ