diff --git a/TODO.md b/TODO.md index 32ac85f..e720d79 100644 --- a/TODO.md +++ b/TODO.md @@ -11,6 +11,7 @@ - [x] **Missing `v_smtp_active` view** on server — made all `CREATE VIEW` statements idempotent with `IF NOT EXISTS` in schema.sql - [x] **`bars.svg` 404** — created `app/public/assets/img/bars.svg` (animated SVG spinner) - [x] **Nginx rate limiting too aggressive** — increased admin zone to 300r/m, burst=30 to handle ~11 concurrent HTMX fragment requests on contenus.php page load +- [x] **Migration idempotency** — `CREATE INDEX` / `CREATE TRIGGER` / `CREATE VIEW` now use `IF NOT EXISTS` in schema.sql and generate-schema.py; migrate.sh no longer fails on re-run - [ ] **Database readonly** — intermittent permission issue after deploy (added deploy-nginx recipe; permissions should be fixed by --chown + deploy-server.sh) ## SQLite Backup & Data Integrity (docs/backup-plan.md) diff --git a/app/storage/schema.sql b/app/storage/schema.sql index df04866..f75798c 100644 --- a/app/storage/schema.sql +++ b/app/storage/schema.sql @@ -317,71 +317,71 @@ CREATE TABLE IF NOT EXISTS audit_log ( -- INDEXES -- ============================================================================ -CREATE INDEX idx_admin_audit_log_action ON admin_audit_log(action); +CREATE INDEX IF NOT EXISTS idx_admin_audit_log_action ON admin_audit_log(action); -CREATE INDEX idx_admin_audit_log_created_at ON admin_audit_log(created_at); +CREATE INDEX IF NOT EXISTS idx_admin_audit_log_created_at ON admin_audit_log(created_at); -CREATE INDEX idx_admin_audit_log_resource ON admin_audit_log(resource); +CREATE INDEX IF NOT EXISTS idx_admin_audit_log_resource ON admin_audit_log(resource); -CREATE INDEX idx_audit_log_table_record ON audit_log(table_name, record_id); +CREATE INDEX IF NOT EXISTS idx_audit_log_table_record ON audit_log(table_name, record_id); -CREATE INDEX idx_audit_log_timestamp ON audit_log(timestamp); +CREATE INDEX IF NOT EXISTS idx_audit_log_timestamp ON audit_log(timestamp); -CREATE INDEX idx_authors_email ON authors(email); +CREATE INDEX IF NOT EXISTS idx_authors_email ON authors(email); -CREATE INDEX idx_file_access_audit_request +CREATE INDEX IF NOT EXISTS idx_file_access_audit_request ON file_access_audit(request_id); -CREATE INDEX idx_file_access_requests_email +CREATE INDEX IF NOT EXISTS idx_file_access_requests_email ON file_access_requests(email); -CREATE INDEX idx_file_access_requests_status +CREATE INDEX IF NOT EXISTS idx_file_access_requests_status ON file_access_requests(status); -CREATE INDEX idx_file_access_requests_thesis_id +CREATE INDEX IF NOT EXISTS idx_file_access_requests_thesis_id ON file_access_requests(thesis_id); -CREATE INDEX idx_file_access_sessions_expires +CREATE INDEX IF NOT EXISTS idx_file_access_sessions_expires ON file_access_sessions(expires_at); -CREATE INDEX idx_file_access_sessions_token +CREATE INDEX IF NOT EXISTS idx_file_access_sessions_token ON file_access_sessions(session_token); -CREATE INDEX idx_file_access_tokens_expires_at +CREATE INDEX IF NOT EXISTS idx_file_access_tokens_expires_at ON file_access_tokens(expires_at); -CREATE INDEX idx_file_access_tokens_token +CREATE INDEX IF NOT EXISTS idx_file_access_tokens_token ON file_access_tokens(token); -CREATE INDEX idx_share_links_active ON share_links(is_active); +CREATE INDEX IF NOT EXISTS idx_share_links_active ON share_links(is_active); -CREATE INDEX idx_share_links_archived ON share_links(is_archived); +CREATE INDEX IF NOT EXISTS idx_share_links_archived ON share_links(is_archived); -CREATE INDEX idx_share_links_slug ON share_links(slug); +CREATE INDEX IF NOT EXISTS idx_share_links_slug ON share_links(slug); -CREATE INDEX idx_tags_name ON tags(name); +CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name); -CREATE INDEX idx_theses_access_type ON theses(access_type_id); +CREATE INDEX IF NOT EXISTS idx_theses_access_type ON theses(access_type_id); -CREATE INDEX idx_theses_ap_program ON theses(ap_program_id); +CREATE INDEX IF NOT EXISTS idx_theses_ap_program ON theses(ap_program_id); -CREATE INDEX idx_theses_identifier ON theses(identifier); +CREATE INDEX IF NOT EXISTS idx_theses_identifier ON theses(identifier); -CREATE INDEX idx_theses_orientation ON theses(orientation_id); +CREATE INDEX IF NOT EXISTS idx_theses_orientation ON theses(orientation_id); -CREATE INDEX idx_theses_pub_year ON theses(is_published, year DESC); +CREATE INDEX IF NOT EXISTS idx_theses_pub_year ON theses(is_published, year DESC); -CREATE INDEX idx_theses_published ON theses(is_published); +CREATE INDEX IF NOT EXISTS idx_theses_published ON theses(is_published); -CREATE INDEX idx_theses_year ON theses(year); +CREATE INDEX IF NOT EXISTS idx_theses_year ON theses(year); -CREATE INDEX idx_thesis_authors_author ON thesis_authors(author_id); +CREATE INDEX IF NOT EXISTS idx_thesis_authors_author ON thesis_authors(author_id); -CREATE INDEX idx_thesis_authors_thesis ON thesis_authors(thesis_id); +CREATE INDEX IF NOT EXISTS idx_thesis_authors_thesis ON thesis_authors(thesis_id); -CREATE INDEX idx_thesis_tags_tag ON thesis_tags(tag_id); +CREATE INDEX IF NOT EXISTS idx_thesis_tags_tag ON thesis_tags(tag_id); -CREATE INDEX idx_thesis_tags_thesis ON thesis_tags(thesis_id); +CREATE INDEX IF NOT EXISTS idx_thesis_tags_thesis ON thesis_tags(thesis_id); -- ============================================================================ -- VIEWS @@ -458,37 +458,37 @@ WHERE is_published = 1; -- TRIGGERS -- ============================================================================ -CREATE TRIGGER update_apropos_contents_timestamp +CREATE TRIGGER IF NOT EXISTS update_apropos_contents_timestamp AFTER UPDATE ON apropos_contents BEGIN UPDATE apropos_contents SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; -CREATE TRIGGER update_authors_timestamp +CREATE TRIGGER IF NOT EXISTS update_authors_timestamp AFTER UPDATE ON authors BEGIN UPDATE authors SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; -CREATE TRIGGER update_form_help_blocks_timestamp +CREATE TRIGGER IF NOT EXISTS update_form_help_blocks_timestamp AFTER UPDATE ON form_help_blocks BEGIN UPDATE form_help_blocks SET updated_at = CURRENT_TIMESTAMP WHERE key = NEW.key; END; -CREATE TRIGGER update_pages_timestamp +CREATE TRIGGER IF NOT EXISTS update_pages_timestamp AFTER UPDATE ON pages BEGIN UPDATE pages SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; -CREATE TRIGGER update_supervisors_timestamp +CREATE TRIGGER IF NOT EXISTS update_supervisors_timestamp AFTER UPDATE ON supervisors BEGIN UPDATE supervisors SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; -CREATE TRIGGER update_theses_timestamp +CREATE TRIGGER IF NOT EXISTS update_theses_timestamp AFTER UPDATE ON theses BEGIN UPDATE theses SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; @@ -553,12 +553,12 @@ INSERT OR IGNORE INTO license_types (name) VALUES ('Domaine public'); INSERT OR IGNORE INTO license_types (name) VALUES ('Tous droits réservés'); INSERT OR IGNORE INTO site_settings (key, value) VALUES ('access_type_interdit_enabled', '1'); -INSERT OR IGNORE INTO site_settings (key, value) VALUES ('access_type_interne_enabled', '0'); +INSERT OR IGNORE INTO site_settings (key, value) VALUES ('access_type_interne_enabled', '1'); INSERT OR IGNORE INTO site_settings (key, value) VALUES ('access_type_libre_enabled', '0'); -INSERT OR IGNORE INTO site_settings (key, value) VALUES ('objet_frart_enabled', '1'); -INSERT OR IGNORE INTO site_settings (key, value) VALUES ('objet_these_enabled', '1'); +INSERT OR IGNORE INTO site_settings (key, value) VALUES ('objet_frart_enabled', '0'); +INSERT OR IGNORE INTO site_settings (key, value) VALUES ('objet_these_enabled', '0'); INSERT OR IGNORE INTO site_settings (key, value) VALUES ('peertube_upload_enabled', '0'); -INSERT OR IGNORE INTO site_settings (key, value) VALUES ('restricted_files_enabled', '0'); +INSERT OR IGNORE INTO site_settings (key, value) VALUES ('restricted_files_enabled', '1'); INSERT OR IGNORE INTO pages (slug, title, content, is_published) VALUES ('about', 'À propos', 'Contenu à venir', 1); INSERT OR IGNORE INTO pages (slug, title, content, is_published) VALUES ('charte', 'Charte', 'Contenu à venir', 1); @@ -570,7 +570,7 @@ INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_synopsis', 'Note Synopsis', '', 0, 2); INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_jury', 'Composition du jury', '', 0, 3); INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_academic', 'Cadre académique', '', 0, 4); -INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_files', 'Fichiers', '', 0, 5); +INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_files', 'Fichiers', '', 1, 5); INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_access', 'Visibilité / Accès', 'qsldkjlfkjdsqmflkjq', 1, 6); INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_email', 'E-mail de confirmation', '', 0, 7); INSERT OR IGNORE INTO form_help_blocks (key, name, content, enabled, sort_order) VALUES ('fieldset_languages', 'Langue(s)', 'Hahah', 0, 0); diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php index f7a7ead..21d6276 100644 --- a/app/templates/admin/acces.php +++ b/app/templates/admin/acces.php @@ -766,6 +766,19 @@ +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +\\\\\\\ to: pylyqurz b7080cbb "feat(backup): deploy cron-based SQLite backups to production" (rebased revision) ++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: pylyqurz b7080cbb "feat(backup): deploy cron-based SQLite backups to production" (rebased revision) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +- $linkName = $link['name'] ?? ''; +- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: qrtmmwro e81fdc2f "fix: make schema.sql fully idempotent — add IF NOT EXISTS to all CREATE INDEX, CREATE TRIGGER, and CREATE VIEW statements" (rebased revision) + $linkName = $link['name'] ?? ''; + $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; + $linkLockedYear = $link['locked_year'] ?? null; ++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) ++\\\\\\\ to: qrtmmwro c96116c4 "fix: make schema.sql fully idempotent — add IF NOT EXISTS to all CREATE INDEX, CREATE TRIGGER, and CREATE VIEW statements" (rebased revision) +++ $linkName = $link['name'] ?? ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ?>