feat: FilePond production hardening — extension-based validation, server-side size limits (2GB), annexe validation, drop accept attributes, FilePond file styling

This commit is contained in:
Pontoporeia
2026-05-10 20:41:37 +02:00
parent 7b5f3efe40
commit 8db7b6e9eb
23 changed files with 4770 additions and 216 deletions

View File

@@ -1,21 +0,0 @@
-- Migration 021: PeerTube integration
-- Creates the peertube_settings singleton table and the peertube_upload_enabled feature flag.
-- The upload flag defaults to 0 (disabled) so existing deployments are unaffected.
CREATE TABLE IF NOT EXISTS peertube_settings (
id INTEGER PRIMARY KEY CHECK (id = 1), -- singleton row
instance_url TEXT NOT NULL DEFAULT '',
username TEXT NOT NULL DEFAULT '',
password TEXT NOT NULL DEFAULT '', -- AES-256-GCM encrypted via Crypto.php
channel_id INTEGER NOT NULL DEFAULT 1,
privacy INTEGER NOT NULL DEFAULT 1, -- 1=Public 2=Unlisted 3=Private
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Insert the singleton placeholder row so UPDATE always finds it
INSERT OR IGNORE INTO peertube_settings (id) VALUES (1);
-- Feature flag: disabled by default (waiting for upload quota)
INSERT INTO site_settings (key, value, updated_at)
VALUES ('peertube_upload_enabled', '0', CURRENT_TIMESTAMP)
ON CONFLICT(key) DO NOTHING;

View File

@@ -1,10 +0,0 @@
-- Migration 027: drop banner_path column from theses table.
-- Banners were merged into covers in migration 016; the column has been
-- vestigial since. This is safe to run even if the column is already absent.
-- Safe to re-run: IF EXISTS makes it idempotent.
-- SQLite does not support DROP COLUMN directly in older versions;
-- we use the ALTER TABLE … DROP COLUMN syntax (supported since SQLite 3.35.0).
-- If this fails on an older SQLite, the column stays as-is (harmless).
ALTER TABLE theses DROP COLUMN banner_path;

View File

@@ -0,0 +1,81 @@
-- Migration 028: drop banner_path from theses and v_theses_full.
--
-- 027_drop_banner_path failed because v_theses_full references banner_path.
-- This migration:
-- 1. Drops dependent views
-- 2. Drops the column
-- 3. Recreates the view without banner_path
-- Safe to re-run (views are re-created fresh each time, column drop is idempotent via error skip).
-- Drop dependent views first (v_theses_public depends on v_theses_full)
DROP VIEW IF EXISTS v_theses_public;
DROP VIEW IF EXISTS v_theses_full;
-- Drop column (may fail if already absent → run.php skips that error)
ALTER TABLE theses DROP COLUMN banner_path;
-- Recreate v_theses_full without banner_path
CREATE VIEW v_theses_full AS
SELECT
t.id,
t.identifier,
t.title,
t.subtitle,
t.year,
t.is_doctoral,
t.objet,
o.name as orientation,
ap.name as ap_program,
ft.name as finality_type,
t.synopsis,
t.context_note,
at.name as access_type,
lt.name as license_type,
t.license_id,
t.license_custom,
t.access_type_id,
t.jury_points,
t.submitted_at,
t.defense_date,
t.published_at,
t.is_published,
t.baiu_link,
t.exemplaire_baiu,
t.exemplaire_erg,
t.cc2r,
t.remarks,
t.jury_note_added,
GROUP_CONCAT(DISTINCT a.name ORDER BY a.name ASC) as authors,
GROUP_CONCAT(DISTINCT s.name) as supervisors,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'president' THEN s.name END) as jury_president,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'promoteur' AND ts.is_ulb = 0 THEN s.name END) as jury_promoteurs,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'promoteur' AND ts.is_ulb = 1 THEN s.name END) as jury_promoteurs_ulb,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' AND ts.is_external = 0 THEN s.name END) as jury_lecteurs_internes,
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' AND ts.is_external = 1 THEN s.name END) as jury_lecteurs_externes,
GROUP_CONCAT(DISTINCT UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2)) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT tg.name) as keywords,
(SELECT a2.email FROM authors a2 JOIN thesis_authors ta2 ON a2.id = ta2.author_id WHERE ta2.thesis_id = t.id ORDER BY ta2.author_order LIMIT 1) as contact_interne,
(SELECT a2.show_contact FROM authors a2 JOIN thesis_authors ta2 ON a2.id = ta2.author_id WHERE ta2.thesis_id = t.id ORDER BY ta2.author_order LIMIT 1) as contact_public
FROM theses t
LEFT JOIN orientations o ON t.orientation_id = o.id
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
LEFT JOIN finality_types ft ON t.finality_id = ft.id
LEFT JOIN access_types at ON t.access_type_id = at.id
LEFT JOIN license_types lt ON t.license_id = lt.id
LEFT JOIN thesis_authors ta ON t.id = ta.thesis_id
LEFT JOIN authors a ON ta.author_id = a.id
LEFT JOIN thesis_supervisors ts ON t.id = ts.thesis_id
LEFT JOIN supervisors s ON ts.supervisor_id = s.id
LEFT JOIN thesis_languages tl ON t.id = tl.thesis_id
LEFT JOIN languages l ON tl.language_id = l.id
LEFT JOIN thesis_formats tf ON t.id = tf.thesis_id
LEFT JOIN format_types fmt ON tf.format_id = fmt.id
LEFT JOIN thesis_tags tt ON t.id = tt.thesis_id
LEFT JOIN tags tg ON tt.tag_id = tg.id
GROUP BY t.id;
-- Recreate v_theses_public (depends on v_theses_full)
CREATE VIEW v_theses_public AS
SELECT * FROM v_theses_full
WHERE is_published = 1;