chore : ajouter fichiers non versionnés (migrations SQL, 404, PROJET.md)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# FOLIO
|
||||
|
||||
Moteur de blog PHP — utilisé par plusieurs sites.
|
||||
|
||||
## Dépôt
|
||||
|
||||
`https://git.abonnel.fr/cedricAbonnel/folio` — branche `main`
|
||||
|
||||
## Sites utilisant Folio
|
||||
|
||||
| Site | Workspace local | Serveur |
|
||||
|---|---|---|
|
||||
| varlog.a5l.fr | `~/Projects/varlog/` | `ssh varlog` |
|
||||
| www.abonnel.fr | `~/Projects/fr.abonnel.www/` | `ssh abonnel-wiki` |
|
||||
|
||||
## Structure du moteur
|
||||
|
||||
```
|
||||
folio/
|
||||
├── src/ Classes PHP (ArticleManager, PostManager, auth…)
|
||||
├── public/ Point d'entrée web (index.php, route.php, assets/)
|
||||
├── templates/ Vues PHP (layout, header, footer, post_*)
|
||||
├── config/ Configuration (config.php)
|
||||
├── database/ Schéma SQL + migrate.php
|
||||
├── composer.json
|
||||
└── CHANGELOG.md
|
||||
```
|
||||
|
||||
## Workflow de modification du moteur
|
||||
|
||||
### 1. Développement et test sur varlog.a5l.fr
|
||||
|
||||
Modifier le code ici dans `~/Projects/folio/`, tester sur **varlog.a5l.fr** :
|
||||
|
||||
```bash
|
||||
# Déployer sur varlog pour test
|
||||
~/Projects/varlog/scripts/sync.sh
|
||||
|
||||
# Tester sur http://varlog.acegrp.lan (ou https://varlog.a5l.fr)
|
||||
```
|
||||
|
||||
### 2. Validation
|
||||
|
||||
Une fois validé sur varlog.a5l.fr :
|
||||
|
||||
```bash
|
||||
# Commiter sur le serveur varlog (git de déploiement)
|
||||
~/Projects/varlog/scripts/commit.sh "description du changement"
|
||||
```
|
||||
|
||||
### 3. Push vers le dépôt Folio
|
||||
|
||||
Pousser le code validé vers le dépôt canonique Folio :
|
||||
|
||||
```bash
|
||||
cd ~/Projects/folio
|
||||
./scripts/push.sh "description du changement"
|
||||
```
|
||||
|
||||
### 4. Déployer sur les autres sites si nécessaire
|
||||
|
||||
```bash
|
||||
~/Projects/fr.abonnel.www/scripts/sync.sh
|
||||
~/Projects/fr.abonnel.www/scripts/commit.sh "même message"
|
||||
```
|
||||
|
||||
## Credentials locaux
|
||||
|
||||
Aucun credential dans folio/ — les `.env` sont dans chaque workspace site.
|
||||
@@ -0,0 +1,49 @@
|
||||
-- Schéma initial : tables créées avant la mise en place du système de migrations.
|
||||
-- Remplace tables_create.sql et interactions_create.sql.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS posts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP,
|
||||
is_published BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS post_files (
|
||||
id SERIAL PRIMARY KEY,
|
||||
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
|
||||
file_type TEXT,
|
||||
file_path TEXT,
|
||||
original_name TEXT,
|
||||
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS article_reactions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
article_uuid TEXT NOT NULL,
|
||||
reaction_type TEXT NOT NULL CHECK (reaction_type IN ('useful', 'important', 'interesting')),
|
||||
visitor_hash TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (article_uuid, reaction_type, visitor_hash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS article_reactions_article_uuid_idx ON article_reactions (article_uuid);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS comments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
article_uuid TEXT NOT NULL,
|
||||
author_name TEXT NOT NULL,
|
||||
author_email TEXT NOT NULL,
|
||||
content TEXT NOT NULL CHECK (LENGTH(content) <= 2000),
|
||||
verify_token TEXT,
|
||||
verification_code TEXT,
|
||||
verify_attempts INTEGER NOT NULL DEFAULT 0,
|
||||
verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
published BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
ip_address TEXT,
|
||||
user_agent TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS comments_article_uuid_idx ON comments (article_uuid, verified, published);
|
||||
CREATE INDEX IF NOT EXISTS comments_verify_token_idx ON comments (verify_token)
|
||||
WHERE verified = FALSE AND verify_token IS NOT NULL;
|
||||
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS user_profiles (
|
||||
email TEXT NOT NULL PRIMARY KEY,
|
||||
display_name TEXT NOT NULL DEFAULT '',
|
||||
updated_at TIMESTAMP DEFAULT now(),
|
||||
profile_url TEXT NOT NULL DEFAULT '',
|
||||
profile_slug TEXT NOT NULL DEFAULT '',
|
||||
bio TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS user_profiles_profile_slug_idx
|
||||
ON user_profiles (profile_slug) WHERE profile_slug <> '';
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS journal_smtp (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
script_path VARCHAR(512),
|
||||
to_email VARCHAR(255) NOT NULL,
|
||||
subject VARCHAR(512),
|
||||
content_html TEXT,
|
||||
content_text TEXT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'queued',
|
||||
ip VARCHAR(128),
|
||||
user_agent VARCHAR(512),
|
||||
error_message VARCHAR(1000),
|
||||
sent_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_journal_smtp_created_at ON journal_smtp (created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_journal_smtp_to_email ON journal_smtp (to_email);
|
||||
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS role_capabilities (
|
||||
role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
||||
capability VARCHAR(50) NOT NULL,
|
||||
PRIMARY KEY (role_id, capability)
|
||||
);
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS user_capabilities (
|
||||
user_email TEXT NOT NULL,
|
||||
capability TEXT NOT NULL,
|
||||
granted_by TEXT,
|
||||
granted_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
PRIMARY KEY (user_email, capability)
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
updated_at TIMESTAMP,
|
||||
password_changed_at TIMESTAMP
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS profiles (
|
||||
id SERIAL PRIMARY KEY,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
label TEXT NOT NULL DEFAULT '',
|
||||
description TEXT,
|
||||
permissions JSONB NOT NULL DEFAULT '[]',
|
||||
is_system BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE IF NOT EXISTS app_config (
|
||||
id INTEGER PRIMARY KEY DEFAULT 1,
|
||||
allow_password BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
allow_oidc BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
registrations_open BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
oidc_issuer TEXT,
|
||||
oidc_name TEXT,
|
||||
oidc_client_id TEXT,
|
||||
oidc_client_secret TEXT,
|
||||
oidc_redirect_uri TEXT,
|
||||
updated_at TIMESTAMP,
|
||||
CONSTRAINT app_config_single_row CHECK (id = 1)
|
||||
);
|
||||
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS mail_queue (
|
||||
id SERIAL PRIMARY KEY,
|
||||
to_email TEXT NOT NULL,
|
||||
subject TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
available_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
locked_at TIMESTAMP WITH TIME ZONE,
|
||||
last_error TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_mail_queue_pending
|
||||
ON mail_queue (available_at ASC, id ASC)
|
||||
WHERE status = 'pending';
|
||||
@@ -0,0 +1,44 @@
|
||||
-- Tables du dictionnaire de données (formulaires dynamiques)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dd_entities (
|
||||
id SERIAL PRIMARY KEY,
|
||||
code TEXT NOT NULL UNIQUE,
|
||||
label TEXT NOT NULL DEFAULT '',
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dd_fields (
|
||||
id SERIAL PRIMARY KEY,
|
||||
entity_id INTEGER NOT NULL REFERENCES dd_entities(id) ON DELETE CASCADE,
|
||||
code TEXT NOT NULL,
|
||||
label TEXT NOT NULL DEFAULT '',
|
||||
field_type TEXT NOT NULL DEFAULT 'text',
|
||||
ui_order INTEGER,
|
||||
is_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
default_val TEXT,
|
||||
UNIQUE (entity_id, code)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dd_rules (
|
||||
id SERIAL PRIMARY KEY,
|
||||
entity_id INTEGER NOT NULL REFERENCES dd_entities(id) ON DELETE CASCADE,
|
||||
rule_type TEXT NOT NULL,
|
||||
expression TEXT,
|
||||
message TEXT,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dd_enums (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dd_enum_values (
|
||||
id SERIAL PRIMARY KEY,
|
||||
enum_id INTEGER NOT NULL REFERENCES dd_enums(id) ON DELETE CASCADE,
|
||||
code TEXT NOT NULL,
|
||||
label TEXT NOT NULL DEFAULT '',
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE (enum_id, code)
|
||||
);
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
// Page d'erreur 404 — à inclure après http_response_code(404).
|
||||
// Aucune variable externe requise.
|
||||
$title = '404 — ' . siteTitle();
|
||||
$metaRobots = 'noindex, nofollow';
|
||||
ob_start();
|
||||
?>
|
||||
<div class="container py-5 text-center">
|
||||
<p class="display-1 fw-bold text-muted mb-0">404</p>
|
||||
<h1 class="h3 mb-3">Page introuvable</h1>
|
||||
<p class="text-muted mb-4">
|
||||
Cette adresse ne correspond à aucun contenu.<br>
|
||||
Vous avez peut-être suivi un ancien lien.
|
||||
</p>
|
||||
<a href="/" class="btn btn-primary">← Retour à l'accueil</a>
|
||||
</div>
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
include __DIR__ . '/layout.php';
|
||||
Reference in New Issue
Block a user