mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
add structured logging for admin/partage form submissions + migration system
- AppLogger: JSON-line logger in storage/logs/form-submissions.log - Logs submissions (admin + partage) with IP, UA, thesis ID, author - Logs errors with context (post keys, share slug) - Migration runner (app/migrations/run.php) handles schema drift - 001_add_objet_column.sql fixes production DB missing 'objet' column - ThesisCreateController::getIdentifier() helper for logging
This commit is contained in:
7
app/migrations/applied/001_add_objet_column.sql
Normal file
7
app/migrations/applied/001_add_objet_column.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- Add 'objet' column to theses table if it doesn't already exist.
|
||||
-- Required: the admin form sends 'objet' in POST since commit ~Apr 2024
|
||||
-- but older production databases may lack the column.
|
||||
-- SQLite 3.35+ supports ALTER TABLE ADD COLUMN (bundled with PHP 8+).
|
||||
|
||||
ALTER TABLE theses ADD COLUMN objet TEXT NOT NULL DEFAULT 'tfe'
|
||||
CHECK (objet IN ('tfe', 'thèse', 'frart'));
|
||||
84
app/migrations/run.php
Normal file
84
app/migrations/run.php
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Run pending migrations on the production database.
|
||||
*
|
||||
* Usage: php app/migrations/run.php [DB_PATH]
|
||||
*
|
||||
* If no DB_PATH is given, defaults to storage/posterg.db.
|
||||
*
|
||||
* Each migration in migrations/pending/ is applied in alphabetical order.
|
||||
* After success, the file is moved to migrations/applied/.
|
||||
*/
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
$dbPath = $argv[1] ?? ($root . '/storage/posterg.db');
|
||||
|
||||
if (!file_exists($dbPath)) {
|
||||
die("Database not found: $dbPath\n");
|
||||
}
|
||||
|
||||
$pdo = new PDO('sqlite:' . $dbPath);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// Create migrations tracking table
|
||||
$pdo->exec("
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
name TEXT PRIMARY KEY,
|
||||
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
");
|
||||
|
||||
$applied = $pdo->query("SELECT name FROM _migrations")->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$pendingDir = $root . '/migrations/pending';
|
||||
$appliedDir = $root . '/migrations/applied';
|
||||
|
||||
if (!is_dir($pendingDir)) {
|
||||
echo "No pending migrations directory.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!is_dir($appliedDir)) {
|
||||
mkdir($appliedDir, 0755, true);
|
||||
}
|
||||
|
||||
$files = glob($pendingDir . '/*.sql');
|
||||
sort($files);
|
||||
|
||||
if (empty($files)) {
|
||||
echo "No pending migration files.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($files as $file) {
|
||||
$name = basename($file);
|
||||
|
||||
if (in_array($name, $applied, true)) {
|
||||
echo "Skip (already applied): $name\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "Applying: $name\n";
|
||||
$sql = file_get_contents($file);
|
||||
|
||||
try {
|
||||
$pdo->exec($sql);
|
||||
$pdo->prepare("INSERT INTO _migrations (name) VALUES (?)")->execute([$name]);
|
||||
rename($file, $appliedDir . '/' . $name);
|
||||
$count++;
|
||||
} catch (PDOException $e) {
|
||||
$msg = $e->getMessage();
|
||||
// SQLite: skip if column already exists
|
||||
if (stripos($msg, 'duplicate column name') !== false) {
|
||||
echo " Already exists (skipping): $name\n";
|
||||
$pdo->prepare("INSERT OR REPLACE INTO _migrations (name) VALUES (?)")->execute([$name]);
|
||||
rename($file, $appliedDir . '/' . $name);
|
||||
continue;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
echo "$count migration(s) applied.\n";
|
||||
Reference in New Issue
Block a user