diff --git a/public/index.php b/public/index.php index 13220e8..549b156 100644 --- a/public/index.php +++ b/public/index.php @@ -45,7 +45,7 @@ $action = $_GET['action'] ?? 'list'; $uuid = $_GET['uuid'] ?? ''; $slug = $_GET['slug'] ?? ''; -$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found', 'add_feed', 'delete_feed', 'add_link', 'delete_link', 'reorder_links', 'react', 'comment', 'verify_comment', 'comment_moderate', 'comment_delete', 'comment_resend', 'create_tag_type', 'delete_tag_type', 'edit_tags', 'book_save', 'book_delete', 'admin_save_as_groups', 'admin_save_folio_config', 'run_engine_update']; +$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found', 'add_feed', 'delete_feed', 'add_link', 'delete_link', 'reorder_links', 'react', 'comment', 'verify_comment', 'comment_moderate', 'comment_delete', 'comment_resend', 'create_tag_type', 'delete_tag_type', 'edit_tags', 'book_save', 'book_delete', 'admin_save_as_groups', 'admin_save_folio_config', 'run_engine_update', 'run_content_migrations']; $metaRobots = in_array($action, $_noindexActions, true) ? 'noindex, nofollow' : null; unset($_noindexActions); @@ -2823,74 +2823,20 @@ switch ($action) { exit; } - // 1. git pull — vérifier que origin pointe vers le dépôt folio configuré - $_folioRepo = rtrim(folioRepoUrl(), '/'); - exec('git -C ' . escapeshellarg(BASE_PATH) . ' remote get-url origin 2>&1', $_originOut, $_originCode); - $_originUrl = rtrim(trim(implode('', $_originOut)), '/'); - // Normaliser : supprimer les credentials éventuels de l'URL (token@host → host) - $_originNorm = preg_replace('#https?://[^@]+@#', 'https://', $_originUrl); - $_repoNorm = preg_replace('#https?://[^@]+@#', 'https://', $_folioRepo); - if ($_originCode !== 0 || $_originNorm !== $_repoNorm) { - $_SESSION['_update_log'] = "Le remote git 'origin' (" . $_originUrl . ") ne correspond pas à FOLIO_REPO_URL (" . $_folioRepo . "). git pull annulé."; - header('Location: /admin?tab=dashboard¬ice=update_git_error'); - exit; - } - exec('cd ' . escapeshellarg(BASE_PATH) . ' && git pull origin main 2>&1', $_gitOut, $_gitCode); - if ($_gitCode !== 0) { - $_SESSION['_update_log'] = implode("\n", $_gitOut); - header('Location: /admin?tab=dashboard¬ice=update_git_error'); - exit; - } + set_time_limit(0); + ignore_user_abort(true); - // 2. composer install (non-bloquant si absent) - exec('which composer 2>/dev/null', $_composerPath); - if (!empty($_composerPath)) { - exec('cd ' . escapeshellarg(BASE_PATH) . ' && composer install --no-dev --optimize-autoloader -q 2>&1'); - } - - // 3. Migrations SQL - $pdo->exec('CREATE TABLE IF NOT EXISTS schema_migrations (name TEXT NOT NULL PRIMARY KEY, applied_at TIMESTAMP NOT NULL DEFAULT NOW())'); - $_sqlApplied = array_flip($pdo->query('SELECT name FROM schema_migrations ORDER BY name')->fetchAll(PDO::FETCH_COLUMN)); - $_sqlFiles = glob(BASE_PATH . '/database/migration_*.sql') ?: []; - sort($_sqlFiles); - foreach ($_sqlFiles as $_sqlFile) { - $_sqlName = basename($_sqlFile); - if (isset($_sqlApplied[$_sqlName])) { - continue; - } - $pdo->exec((string) file_get_contents($_sqlFile)); - $pdo->prepare('INSERT INTO schema_migrations (name) VALUES (:n)')->execute([':n' => $_sqlName]); - } - - // 4. Migrations de contenu - $_cmDataDir = DATA_PATH; - $_cmTrack = $_cmDataDir . '/.content_migrations.json'; - $_cmFlag = $_cmDataDir . '/.maintenance'; - $_cmApplied = file_exists($_cmTrack) ? (json_decode((string) file_get_contents($_cmTrack), true) ?? []) : []; - $_cmFiles = glob(BASE_PATH . '/scripts/content/migration_*.php') ?: []; - sort($_cmFiles); - $_cmPending = array_values(array_filter($_cmFiles, fn ($f) => !isset($_cmApplied[basename($f)]))); - $_cmErrors = 0; - if (!empty($_cmPending)) { - file_put_contents($_cmFlag, date('Y-m-d H:i:s')); - $dataDir = $_cmDataDir; - foreach ($_cmPending as $_cmFile) { - try { - require $_cmFile; - $_cmApplied[basename($_cmFile)] = date('Y-m-d H:i:s'); - file_put_contents($_cmTrack, json_encode($_cmApplied, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n"); - } catch (Throwable $_cmEx) { - $_cmErrors++; - break; - } - } - if (file_exists($_cmFlag)) { - unlink($_cmFlag); - } - } + exec('sudo /usr/local/bin/folio-upgrade.sh ' . escapeshellarg(folioUpdateBranch()) . ' 2>&1', $_upgradeOut, $_upgradeCode); $_updateChecker->clearCache(); - header('Location: /admin?tab=dashboard¬ice=' . ($_cmErrors ? 'update_content_error' : 'engine_updated')); + + if ($_upgradeCode !== 0) { + $_SESSION['_upgrade_log'] = implode("\n", $_upgradeOut); + header('Location: /admin?tab=dashboard¬ice=upgrade_error'); + exit; + } + + header('Location: /admin?tab=dashboard¬ice=engine_updated'); exit; case 'force_update_check': diff --git a/scripts/server/folio-upgrade.sh b/scripts/server/folio-upgrade.sh new file mode 100644 index 0000000..d0a8d72 --- /dev/null +++ b/scripts/server/folio-upgrade.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# folio-upgrade.sh — déploie Folio à la demande (appelé par PHP via sudo). +# +# Usage : folio-upgrade.sh +# +# Installation sur chaque serveur : +# sudo install -o root -m 750 folio-upgrade.sh /usr/local/bin/folio-upgrade.sh +# +# Autorisation sudo (sans mot de passe) : +# echo "www-data ALL=(root) NOPASSWD: /usr/local/bin/folio-upgrade.sh" \ +# | sudo tee /etc/sudoers.d/folio-upgrade +# +# ── Configuration (à adapter par site) ─────────────────────────────────────── +APP_DIR=/var/www/lan.acegrp.abonnel-www +REPO_URL=https://git.abonnel.fr/cedricAbonnel/folio.git +# ────────────────────────────────────────────────────────────────────────────── + +BRANCH=${1:-main} + +ENV_FILE="$APP_DIR/.env" +[ -f "$ENV_FILE" ] || { echo "ERREUR : $ENV_FILE introuvable"; exit 1; } + +DATA_DIR=$(grep -m1 '^DATA_PATH=' "$ENV_FILE" | cut -d= -f2- | tr -d '"'"'" | xargs) +[ -n "$DATA_DIR" ] || { echo "ERREUR : DATA_PATH absent du .env"; exit 1; } + +LOG="$DATA_DIR/.upgrade-log" +WORK_DIR=$(mktemp -d) +trap 'rm -rf "$WORK_DIR"' EXIT + +{ +echo "=== $(date '+%Y-%m-%d %H:%M:%S') — démarrage ===" +echo "Branche : $BRANCH" +echo "" + +# 1. Sauvegarder .env avant de toucher APP_DIR +cp "$ENV_FILE" "$WORK_DIR/.env.bak" || { echo "ERREUR : sauvegarde .env impossible"; exit 1; } + +# 2. Cloner dans un répertoire de travail (APP_DIR reste intact en cas d'échec du clone) +git clone --depth=1 --branch "$BRANCH" "$REPO_URL" "$WORK_DIR/app" \ + || { echo "ERREUR : git clone"; exit 1; } + +# 3. Déployer +rm -rf "$APP_DIR" +mv "$WORK_DIR/app" "$APP_DIR" + +# 4. Permissions (PHP-FPM tourne en www-data) +chown -R www-data:www-data "$APP_DIR" +chmod -R g+rwX,o= "$APP_DIR" + +# 5. Restaurer .env +cp "$WORK_DIR/.env.bak" "$APP_DIR/.env" +chown www-data:www-data "$APP_DIR/.env" +chmod 640 "$APP_DIR/.env" + +cd "$APP_DIR" + +# 6. Dépendances Composer +if command -v composer > /dev/null 2>&1; then + sudo -u www-data composer install --no-dev --optimize-autoloader \ + || echo "AVERTISSEMENT : composer install a échoué" +else + echo "AVERTISSEMENT : composer introuvable — dépendances non installées" +fi + +# 7. Migrations SQL +sudo -u www-data php database/migrate.php \ + || echo "AVERTISSEMENT : migrations SQL — vérifier manuellement" + +# 8. Répertoire de sessions PHP +mkdir -p "$APP_DIR/.sessions" +chown www-data:www-data "$APP_DIR/.sessions" +chmod 700 "$APP_DIR/.sessions" + +# 9. Autoriser git pour ce répertoire (accès multi-utilisateurs) +git config --system --add safe.directory "$APP_DIR" 2>/dev/null || true + +echo "" +echo "=== $(date '+%Y-%m-%d %H:%M:%S') — succès ===" +} > "$LOG" 2>&1 diff --git a/src/UpdateChecker.php b/src/UpdateChecker.php index cad72e4..1b2d75c 100644 --- a/src/UpdateChecker.php +++ b/src/UpdateChecker.php @@ -112,6 +112,15 @@ class UpdateChecker } } + public function getLastUpgradeLog(): ?string + { + $logFile = $this->dataDir . '/.upgrade-log'; + if (!file_exists($logFile)) { + return null; + } + return (string) file_get_contents($logFile); + } + /** * Récupère `public/version.txt` depuis le dépôt Gitea. * Résultat mis en cache 1 h dans `data/.version_check_cache.json`. diff --git a/templates/admin.php b/templates/admin.php index 3d02e0e..5e531bf 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -103,9 +103,10 @@ function adminStatusBadge(array $a, int $now): string adminNotices() : []; - $_branch = isset($_updateChecker) ? $_updateChecker->getBranch() : 'main'; - $_lastChecked = isset($_updateChecker) ? $_updateChecker->getLastChecked() : null; + $_notices = isset($_updateChecker) ? $_updateChecker->adminNotices() : []; + $_branch = isset($_updateChecker) ? $_updateChecker->getBranch() : 'main'; + $_lastChecked = isset($_updateChecker) ? $_updateChecker->getLastChecked() : null; + $_upgradeLog = isset($_updateChecker) ? $_updateChecker->getLastUpgradeLog() : null; $_repoConfigured = folioRepoUrl() !== ''; $_remoteLabel = '—'; foreach ($_notices as $_n) { @@ -145,19 +146,28 @@ function adminStatusBadge(array $a, int $now): string · vérifié le ' . date('d/m/Y à H:i', $_lastChecked) . '' : '' ?> -
Moteur mis à jour avec succès (code, base de données, contenu).
- +
Moteur mis à jour avec succès.
+
- Erreur git pull — vérifiez les droits d'accès au dépôt. - -
- + Erreur lors de la mise à jour. + +
+
- -
Code et base de données mis à jour, mais une migration de contenu a échoué.
+ + + + Journal + +
+ Dernière mise à jour +
+
+ +