mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
logs: standardise log filenames to xamxam-{service}-{date}.log
This commit is contained in:
@@ -53,17 +53,24 @@ if ($activeTab === 'status' || !array_key_exists($activeTab, SystemController::L
|
||||
$activeTab = 'app';
|
||||
}
|
||||
|
||||
// Optional date selection (YYYY-MM-DD) for daily app logs.
|
||||
$selectedDate = $_GET['date'] ?? null;
|
||||
if ($selectedDate !== null && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $selectedDate)) {
|
||||
$selectedDate = null;
|
||||
}
|
||||
|
||||
$selectedN = isset($_GET['n']) ? (int) $_GET['n'] : 100;
|
||||
if (!in_array($selectedN, SystemController::ALLOWED_LINES, true)) {
|
||||
$selectedN = 100;
|
||||
}
|
||||
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN);
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN, $selectedDate);
|
||||
$logLines = $logData['lines'];
|
||||
$logError = $logData['error'];
|
||||
$logFileMeta = $logData['meta'];
|
||||
$logIsJson = $logData['isJson'] ?? false;
|
||||
$notYet = $logData['notYet'] ?? false;
|
||||
$logDates = SystemController::listLogDates($activeTab);
|
||||
|
||||
$collapsed = $_COOKIE['sys_collapsed'] ?? null;
|
||||
$statusInitiallyCollapsed = $collapsed === '1';
|
||||
|
||||
@@ -33,6 +33,12 @@ if (!in_array($selectedN, SystemController::ALLOWED_LINES, true)) {
|
||||
$selectedN = 100;
|
||||
}
|
||||
|
||||
// Optional date selection (YYYY-MM-DD) for daily app logs.
|
||||
$selectedDate = $_GET['date'] ?? null;
|
||||
if ($selectedDate !== null && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $selectedDate)) {
|
||||
$selectedDate = null;
|
||||
}
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
header('X-Robots-Tag: noindex');
|
||||
|
||||
@@ -42,11 +48,12 @@ $_cache = new SystemCache($_db->getPDO());
|
||||
$_controller = new SystemController($_db, $_cache);
|
||||
|
||||
// ── Render ─────────────────────────────────────────────────────────────────
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN);
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN, $selectedDate);
|
||||
$logLines = $logData['lines'];
|
||||
$logError = $logData['error'];
|
||||
$logFileMeta = $logData['meta'];
|
||||
$logIsJson = $logData['isJson'] ?? false;
|
||||
$notYet = $logData['notYet'] ?? false;
|
||||
$logDates = SystemController::listLogDates($activeTab);
|
||||
|
||||
include APP_ROOT . '/templates/admin/partials/system-log-panel.php';
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
/**
|
||||
* Admin audit logger.
|
||||
*
|
||||
* Writes JSON-lines to /var/log/xamxam.log (production) or
|
||||
* storage/logs/admin.log (dev / cli-server).
|
||||
* Writes JSON-lines to /var/log/xamxam/xamxam-admin-YYYY-MM-DD.log (production)
|
||||
* or storage/logs/xamxam-admin.log (dev / cli-server).
|
||||
*
|
||||
* Each entry: timestamp, actor (admin IP/UA), action, resource, status, context.
|
||||
*
|
||||
|
||||
@@ -26,25 +26,39 @@ class SystemController
|
||||
'admin' => ['label' => 'Admin — actions', 'path' => null, 'json' => true],
|
||||
'error' => ['label' => 'Erreurs — application', 'path' => null, 'json' => true],
|
||||
'audit' => ['label' => 'Audit — données', 'path' => null, 'json' => true],
|
||||
'nginx_access' => ['label' => 'nginx — accès', 'path' => '/var/log/nginx/xamxam_access.log', 'json' => false],
|
||||
'nginx_error' => ['label' => 'nginx — erreurs','path' => '/var/log/nginx/xamxam_error.log', 'json' => false],
|
||||
'nginx_access' => ['label' => 'nginx — accès', 'path' => '/var/log/nginx/xamxam-nginx-access.log', 'json' => false],
|
||||
'nginx_error' => ['label' => 'nginx — erreurs','path' => '/var/log/nginx/xamxam-nginx-error.log', 'json' => false],
|
||||
];
|
||||
|
||||
/**
|
||||
* Resolve a log file path — app logs live under STORAGE_ROOT, system logs
|
||||
* have hard-coded paths (only valid in production).
|
||||
* Resolve a log file path — app logs live under /var/log/xamxam in
|
||||
* production (and storage/logs in dev/cli-server); nginx logs have
|
||||
* hard-coded paths (only valid in production).
|
||||
*
|
||||
* A $date (YYYY-MM-DD) selects a specific retained daily file for app
|
||||
* channels; when null the most recent file is returned.
|
||||
*/
|
||||
private static function resolveLogPath(string $tab): string
|
||||
private static function resolveLogPath(string $tab, ?string $date = null): string
|
||||
{
|
||||
$def = self::LOG_FILES[$tab];
|
||||
if ($def['path'] !== null) {
|
||||
return $def['path'];
|
||||
}
|
||||
// App logs: storage/logs/{channel}.log (Monolog RotatingFileHandler uses
|
||||
// this as base name; the current log is always at {channel}-YYYY-MM-DD.log)
|
||||
$dir = defined('STORAGE_ROOT') ? STORAGE_ROOT . '/logs' : APP_ROOT . '/storage/logs';
|
||||
// App logs: /var/log/xamxam/xamxam-{channel}.log (production) or
|
||||
// storage/logs/xamxam-{channel}.log (dev / cli-server). Monolog
|
||||
// RotatingFileHandler uses this as base name; the current log is always
|
||||
// at xamxam-{channel}-YYYY-MM-DD.log.
|
||||
$dir = php_sapi_name() === 'cli-server'
|
||||
? APP_ROOT . '/storage/logs'
|
||||
: '/var/log/xamxam';
|
||||
$base = $dir . '/xamxam-' . $tab;
|
||||
|
||||
// Explicit date selection (validated YYYY-MM-DD)
|
||||
if ($date !== null && preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
|
||||
return $base . '-' . $date . '.log';
|
||||
}
|
||||
|
||||
// Find the most recent dated log file for this channel
|
||||
$base = $dir . '/' . $tab;
|
||||
$dated = glob($base . '-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].log');
|
||||
if (!empty($dated)) {
|
||||
rsort($dated); // newest first
|
||||
@@ -54,6 +68,49 @@ class SystemController
|
||||
return $base . '.log';
|
||||
}
|
||||
|
||||
/**
|
||||
* List the retained daily log files (dates) for an app channel.
|
||||
*
|
||||
* Returns [['date' => 'YYYY-MM-DD', 'label' => '...'], …] newest-first.
|
||||
* Empty array for system logs (nginx) which have fixed names.
|
||||
*/
|
||||
public static function listLogDates(string $tab): array
|
||||
{
|
||||
$def = self::LOG_FILES[$tab];
|
||||
if (($def['json'] ?? false) !== true) {
|
||||
return []; // nginx logs are fixed-name, not daily
|
||||
}
|
||||
|
||||
$dir = php_sapi_name() === 'cli-server'
|
||||
? APP_ROOT . '/storage/logs'
|
||||
: '/var/log/xamxam';
|
||||
$base = $dir . '/xamxam-' . $tab;
|
||||
$dated = glob($base . '-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].log');
|
||||
if (empty($dated)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dates = [];
|
||||
foreach ($dated as $path) {
|
||||
if (preg_match('/(\d{4}-\d{2}-\d{2})\.log$/', $path, $m)) {
|
||||
$dates[] = $m[1];
|
||||
}
|
||||
}
|
||||
rsort($dates); // newest first
|
||||
|
||||
$out = [];
|
||||
foreach ($dates as $d) {
|
||||
$isToday = $d === date('Y-m-d');
|
||||
$out[] = [
|
||||
'date' => $d,
|
||||
'label' => $isToday
|
||||
? 'Aujourd\'hui (' . date('d/m/Y', strtotime($d)) . ')'
|
||||
: date('d/m/Y', strtotime($d)),
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public const ALLOWED_LINES = [50, 100, 200, 500];
|
||||
|
||||
// ── TTLs ──────────────────────────────────────────────────────────────────
|
||||
@@ -157,11 +214,13 @@ class SystemController
|
||||
/**
|
||||
* Read and return data for a log tab.
|
||||
*
|
||||
* @param string|null $date YYYY-MM-DD to select a specific daily file
|
||||
* (app channels); null = most recent.
|
||||
* @return array{lines: ?array, error: ?string, meta: ?array}
|
||||
*/
|
||||
public function getLogData(string $tab, int $n): array
|
||||
public function getLogData(string $tab, int $n, ?string $date = null): array
|
||||
{
|
||||
$logPath = self::resolveLogPath($tab);
|
||||
$logPath = self::resolveLogPath($tab, $date);
|
||||
$isJson = self::LOG_FILES[$tab]['json'] ?? false;
|
||||
$error = null;
|
||||
$rawLines = null;
|
||||
|
||||
+10
-4
@@ -55,7 +55,7 @@ class Logger
|
||||
|
||||
try {
|
||||
$handler = new RotatingFileHandler(
|
||||
$logDir . '/' . $channel . '.log',
|
||||
$logDir . '/xamxam-' . $channel . '.log',
|
||||
30, // keep 30 days of logs
|
||||
self::level()
|
||||
);
|
||||
@@ -99,12 +99,18 @@ class Logger
|
||||
|
||||
/**
|
||||
* Resolve the log directory.
|
||||
*
|
||||
* Production: /var/log/xamxam/ (standard system log root, provisioned by
|
||||
* setup-server.sh; owned by www-data:xamxam). Dev (cli-server): the
|
||||
* app's local storage/logs/ so no root provisioning is needed.
|
||||
*/
|
||||
private static function logDir(): string
|
||||
{
|
||||
if (defined('STORAGE_ROOT')) {
|
||||
return STORAGE_ROOT . '/logs';
|
||||
// cli-server (dev) → app-local storage/logs; everything else (php-fpm
|
||||
// production) → the standard system log directory.
|
||||
if (php_sapi_name() === 'cli-server') {
|
||||
return __DIR__ . '/../storage/logs';
|
||||
}
|
||||
return __DIR__ . '/../storage/logs';
|
||||
return '/var/log/xamxam';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ just migrate # run pending migrations
|
||||
| `schema.sql` | Full, fully-migrated schema + seed data (regenerated from the local DB) |
|
||||
| `backups/` | `just backup-snapshot` hot-backups (`*.db.gz`) |
|
||||
| `cache/` | Runtime cache (rate limits, etc.) |
|
||||
| `logs/` | Runtime logs (admin, audit) — **outside the webroot** |
|
||||
| `logs/` | Dev-only runtime logs (cli-server); production logs live in `/var/log/xamxam/` |
|
||||
| `covers/` | Cover images |
|
||||
| `theses/`, `tfe/`, `tmp/` | Uploaded files / staging |
|
||||
|
||||
|
||||
@@ -386,8 +386,8 @@
|
||||
|
||||
<p class="sys-refresh-note">
|
||||
Affiché le <?= date('d/m/Y à H:i:s') ?> —
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&n=<?= $selectedN ?>">Rafraîchir</a> —
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&n=<?= $selectedN ?>&refresh=1">Forcer actualisation</a>
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>">Rafraîchir</a> —
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>&refresh=1">Forcer actualisation</a>
|
||||
</p>
|
||||
|
||||
<div class="sys-status-header">
|
||||
@@ -460,11 +460,11 @@
|
||||
|
||||
<nav class="sys-tabs" aria-label="Journaux et configuration">
|
||||
<?php foreach (SystemController::LOG_FILES as $key => $def): ?>
|
||||
<a href="?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
<a href="?tab=<?= htmlspecialchars($key) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>"
|
||||
class="sys-tab <?= $activeTab === $key ? 'active' : '' ?>"
|
||||
hx-get="/admin/system-fragment.php?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
hx-get="/admin/system-fragment.php?tab=<?= htmlspecialchars($key) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>"
|
||||
hx-target="#sys-tab-panel"
|
||||
hx-push-url="?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
hx-push-url="?tab=<?= htmlspecialchars($key) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>"
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#sys-tab-panel"
|
||||
data-tab="<?= htmlspecialchars($key) ?>"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#sys-tab-panel"
|
||||
hx-trigger="change"
|
||||
hx-vals='{"tab":"<?= htmlspecialchars($activeTab) ?>"}'>
|
||||
hx-vals='{"tab":"<?= htmlspecialchars($activeTab) ?>","date":"<?= htmlspecialchars($selectedDate ?? '') ?>"}'>
|
||||
<label for="lines-select">Afficher</label>
|
||||
<select id="lines-select" name="n" aria-label="Nombre de lignes">
|
||||
<?php foreach (SystemController::ALLOWED_LINES as $opt): ?>
|
||||
@@ -12,6 +12,23 @@
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<?php if (!empty($logDates)): ?>
|
||||
<form id="date-form" hx-get="/admin/system-fragment.php"
|
||||
hx-target="#sys-tab-panel"
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#sys-tab-panel"
|
||||
hx-trigger="change"
|
||||
hx-vals='{"tab":"<?= htmlspecialchars($activeTab) ?>","n":<?= $selectedN ?>}'>
|
||||
<label for="date-select">Jour</label>
|
||||
<select id="date-select" name="date" aria-label="Jour du journal">
|
||||
<?php foreach ($logDates as $d): ?>
|
||||
<option value="<?= htmlspecialchars($d['date']) ?>" <?= ($selectedDate ?? null) === $d['date'] ? 'selected' : '' ?>><?= htmlspecialchars($d['label']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($logLines !== null && count($logLines) > 0): ?>
|
||||
<span class="log-count-badge"><?= count($logLines) ?> ligne(s)</span>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Installed to /etc/cron.d/xamxam-backup (system cron format: minute hour dom month dow user command)
|
||||
#
|
||||
# Hourly snapshot — kept 30 days
|
||||
0 * * * * www-data /usr/local/bin/backup-sqlite.sh >> /var/log/sqlite-backup.log 2>&1
|
||||
0 * * * * www-data /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +\%Y-\%m-\%d).log 2>&1
|
||||
|
||||
# Daily snapshot at 2am — kept 90 days
|
||||
0 2 * * * www-data RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/sqlite-backup.log 2>&1
|
||||
0 2 * * * www-data RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +\%Y-\%m-\%d).log 2>&1
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# XAMXAM — orphaned draft cleanup cron job
|
||||
# Installed to /etc/cron.d/xamxam-cleanup (system cron format: minute hour dom month dow user command)
|
||||
#
|
||||
# Deletes draft theses older than 24h that have no attached files.
|
||||
# Runs every 4 hours — drafts only become eligible after 24h, so this is ample.
|
||||
# Deletes draft theses older than 7 days (168h) that have no attached files.
|
||||
# Runs every 2 days — drafts only become eligible after 7 days, so this is ample.
|
||||
# The script is a dry-run unless --no-dry-run is passed.
|
||||
0 */4 * * * www-data php /var/www/xamxam/scripts/cleanup-drafts.php --no-dry-run >> /var/log/xamxam-cleanup.log 2>&1
|
||||
# Eligibility age is configurable via OLDER_THAN_HOURS (default 168).
|
||||
0 0 */2 * * www-data php /tmp/cleanup-drafts.php --no-dry-run >> /var/log/xamxam-cleanup-$(date +\%Y-\%m-\%d).log 2>&1
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# XAMXAM — log rotation for application, nginx, and cron logs.
|
||||
# Installed to /etc/logrotate.d/xamxam (run via `just deploy-logrotate`).
|
||||
#
|
||||
# Covers three roots:
|
||||
# /var/log/xamxam/ app channels (xamxam-app/admin/error/audit-*.log)
|
||||
# /var/log/nginx/xamxam-nginx-* nginx access + error logs
|
||||
# /var/log/xamxam-backup-*.log SQLite backup cron
|
||||
# /var/log/xamxam-cleanup-*.log draft cleanup cron
|
||||
#
|
||||
# The app channels additionally self-rotate via Monolog (RotatingFileHandler,
|
||||
# 30 days) — logrotate here mainly compresses/prunes the nginx and cron files
|
||||
# and any stragglers. Retention is aligned with the app's 30-day default.
|
||||
|
||||
/var/log/xamxam/*.log {
|
||||
daily
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 664 www-data xamxam
|
||||
su www-data xamxam
|
||||
}
|
||||
|
||||
/var/log/nginx/xamxam-nginx-access.log /var/log/nginx/xamxam-nginx-error.log {
|
||||
daily
|
||||
rotate 52
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 640 root adm
|
||||
su root adm
|
||||
sharedscripts
|
||||
postrotate
|
||||
if command -v systemctl >/dev/null 2>&1 && systemctl is-active nginx >/dev/null 2>&1; then
|
||||
systemctl reload nginx >/dev/null 2>&1 || true
|
||||
elif [ -x /usr/sbin/nginx ]; then
|
||||
/usr/sbin/nginx -s reopen 2>/dev/null || true
|
||||
fi
|
||||
endscript
|
||||
}
|
||||
|
||||
/var/log/xamxam-backup-*.log /var/log/xamxam-cleanup-*.log {
|
||||
daily
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 www-data www-data
|
||||
su www-data www-data
|
||||
}
|
||||
+3
-3
@@ -153,12 +153,12 @@
|
||||
- [ ] Add hourly and daily jobs:
|
||||
```cron
|
||||
# Hourly snapshot — kept 30 days
|
||||
0 * * * * /usr/local/bin/backup-sqlite.sh >> /var/log/sqlite-backup.log 2>&1
|
||||
0 * * * * /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +%Y-%m-%d).log 2>&1
|
||||
|
||||
# Daily snapshot at 2am — kept 90 days
|
||||
0 2 * * * RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/sqlite-backup.log 2>&1
|
||||
0 2 * * * RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +%Y-%m-%d).log 2>&1
|
||||
```
|
||||
- [ ] Verify the log after the next hour: `tail -f /var/log/sqlite-backup.log`
|
||||
- [ ] Verify the log after the next hour: `tail -f /var/log/xamxam-backup-$(date +%Y-%m-%d).log`
|
||||
|
||||
---
|
||||
|
||||
|
||||
+10
-4
@@ -55,8 +55,14 @@ just deploy-all-first # deploy + deploy-backup + deploy-cleanup-cron
|
||||
| `just deploy-db` | Push local `xamxam.db` → remote (**refuses** if a remote DB already exists) |
|
||||
| `just deploy-verify-permissions` | Check ownership / permissions on the server |
|
||||
|
||||
> ℹ️ **First deploy?** After `just deploy`, run `just deploy-backup` to install
|
||||
> the backup script + cron jobs.
|
||||
> ℹ️ **First deploy?** After `just deploy`, run `just deploy-backup` and
|
||||
> `just deploy-cleanup-cron` to install the backup/cleanup cron jobs, and
|
||||
> `just deploy-logrotate` to install log rotation. A single `just deploy-all-first`
|
||||
> chains all of these together.
|
||||
> The app log directory `/var/log/xamxam/` is provisioned automatically by
|
||||
> `deploy-code` on every run (via `deploy-server.sh`), so no separate step is
|
||||
> needed for it. To migrate logs written by an older build, run
|
||||
> `just migrate-log-names --apply` once.
|
||||
|
||||
### Environment file & re-encryption
|
||||
|
||||
@@ -81,7 +87,7 @@ gzipped to `/var/backups/xamxam/` on the server.
|
||||
| `just deploy-backup` | Install backup script + cron jobs (one-shot) |
|
||||
| `just deploy-backup-script` | Install `/usr/local/bin/backup-sqlite.sh` |
|
||||
| `just deploy-backup-cron` | Install `/etc/cron.d/xamxam-backup` (hourly 30d + daily 90d) + dirs/log |
|
||||
| `just deploy-check-backup-log` | Tail `/var/log/sqlite-backup.log` |
|
||||
| `just deploy-check-backup-log` | Tail `/var/log/xamxam-backup-YYYY-MM-DD.log` |
|
||||
| `just deploy-list-backups` | List backups on the server |
|
||||
| `just trigger-backup` | Run the backup script now |
|
||||
| `just test-restore <path.gz>` | Fetch + decompress + verify a remote snapshot |
|
||||
@@ -91,7 +97,7 @@ Backup files: `/var/backups/xamxam/db-<timestamp>.db.gz`.
|
||||
|
||||
Draft cleanup is handled by a separate cron (`/etc/cron.d/xamxam-cleanup`),
|
||||
installed via `just deploy-cleanup-cron`, logging to
|
||||
`/var/log/xamxam-cleanup.log`. Verify with `just deploy-check-cleanup-log`.
|
||||
`/var/log/xamxam-cleanup-YYYY-MM-DD.log`. Verify with `just deploy-check-cleanup-log`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ Do not replace any existing class yet. Just build the foundation.
|
||||
```
|
||||
|
||||
Each channel gets:
|
||||
- A `RotatingFileHandler` writing to `storage/logs/{channel}.log`, keeping 30 days
|
||||
- A `RotatingFileHandler` writing to `xamxam-{channel}.log` (production: `/var/log/xamxam/`; dev: `storage/logs/`), keeping 30 days
|
||||
- A `JsonFormatter` so log lines stay JSON (preserving the existing format contract)
|
||||
- Log level set from an environment variable (`LOG_LEVEL`, defaulting to `WARNING` in production, `DEBUG` in dev)
|
||||
|
||||
@@ -58,7 +58,7 @@ one file at a time without passing instances around.
|
||||
|
||||
- Rewrite `AppLogger` as a thin wrapper that delegates to `Logger::get('app')`
|
||||
- Keep the existing public method signatures identical — no call sites change in this step
|
||||
- Run the app, verify log output appears in `storage/logs/app.log`
|
||||
- Run the app, verify log output appears in `xamxam-app.log` (see the log dir for the active SAPI)
|
||||
- Delete the old file-writing implementation inside `AppLogger`, keep the class as a facade for now
|
||||
|
||||
---
|
||||
|
||||
+2
-1
@@ -59,7 +59,8 @@ spoofing.
|
||||
files. The DocumentRoot is `app/public/` only.
|
||||
- Restricted-file downloads are gated by a request/approval/token flow
|
||||
(`file_access_*` tables).
|
||||
- Logs write to `app/storage/logs/` — outside the webroot, not publicly served.
|
||||
- Logs write to `/var/log/xamxam/` in production (and `app/storage/logs/`
|
||||
only in dev/cli-server) — outside the webroot, not publicly served.
|
||||
|
||||
## Injection & output
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ deploy: build deploy-code deploy-deps deploy-migrate
|
||||
@just deploy-env
|
||||
@just deploy-verify-permissions
|
||||
@echo ""
|
||||
@echo "ℹ️ First deploy? Also run: just deploy-backup"
|
||||
@echo "ℹ️ First deploy? Also run: just deploy-all-first"
|
||||
@echo ""
|
||||
|
||||
[group('deploy')]
|
||||
@@ -294,6 +294,13 @@ deploy-verify-permissions:
|
||||
err "storage/cache/rate_limit → NOT WRITABLE"
|
||||
fi
|
||||
|
||||
# ── /var/log/xamxam writable (app log dir — NullHandler if missing) ─────────
|
||||
if ssh xamxam "[ -d /var/log/xamxam ] && [ -w /var/log/xamxam ]"; then
|
||||
ok "/var/log/xamxam → writable"
|
||||
else
|
||||
err "/var/log/xamxam → MISSING or NOT WRITABLE (run: sudo bash /tmp/deploy-server.sh)"
|
||||
fi
|
||||
|
||||
# ── .env must be 640 ──────────────────────────────────────────────────────────
|
||||
env_perm=$(ssh xamxam "stat -c '%a' /var/www/xamxam/.env 2>/dev/null" || echo "")
|
||||
if [ "$env_perm" = "640" ]; then
|
||||
@@ -335,6 +342,15 @@ deploy-script script_name:
|
||||
@echo " sudo DEPLOY_USER=\$USER bash /tmp/{{script_name}}.sh"
|
||||
@echo ""
|
||||
|
||||
[group('deploy')]
|
||||
migrate-log-names apply="":
|
||||
# Rename pre-existing logs to the xamxam-{service}-{date}.log convention.
|
||||
# Dry-run by default; pass apply="--apply" to actually rename on the server.
|
||||
# Requires sudo — renames files under /var/log and /var/www/xamxam/storage/logs.
|
||||
rsync -v scripts/migrate-log-names.sh xamxam:/tmp/migrate-log-names.sh
|
||||
ssh -t xamxam "sudo bash /tmp/migrate-log-names.sh {{apply}}"
|
||||
ssh xamxam "rm -f /tmp/migrate-log-names.sh"
|
||||
|
||||
[group('deploy')]
|
||||
deploy-backup-script:
|
||||
# Upload backup-sqlite.sh to /usr/local/bin on the server (requires sudo)
|
||||
@@ -353,11 +369,11 @@ deploy-backup-cron:
|
||||
rsync -v deploy/xamxam-backup.cron xamxam:/tmp/xamxam-backup.cron
|
||||
ssh -t xamxam "sudo install -o root -g root -m 644 /tmp/xamxam-backup.cron /etc/cron.d/xamxam-backup && rm -f /tmp/xamxam-backup.cron"
|
||||
ssh -t xamxam "sudo mkdir -p /var/backups/xamxam && sudo chown www-data:www-data /var/backups/xamxam && sudo chmod 755 /var/backups/xamxam"
|
||||
ssh -t xamxam "sudo touch /var/log/sqlite-backup.log && sudo chown www-data:www-data /var/log/sqlite-backup.log && sudo chmod 644 /var/log/sqlite-backup.log"
|
||||
ssh -t xamxam "sudo touch /var/log/xamxam-backup-\$(date +%Y-%m-%d).log && sudo chown www-data:www-data /var/log/xamxam-backup-\$(date +%Y-%m-%d).log && sudo chmod 644 /var/log/xamxam-backup-\$(date +%Y-%m-%d).log"
|
||||
@echo "✅ Cron jobs installed."
|
||||
@echo " Cron file: /etc/cron.d/xamxam-backup"
|
||||
@echo " Backup dir: /var/backups/xamxam"
|
||||
@echo " Log file: /var/log/sqlite-backup.log"
|
||||
@echo " Log file: /var/log/xamxam-backup-\$(date +%Y-%m-%d).log"
|
||||
@echo ""
|
||||
@echo "Verify with: just deploy-check-backup-log"
|
||||
|
||||
@@ -367,7 +383,7 @@ deploy-backup: deploy-backup-script deploy-backup-cron
|
||||
|
||||
[group('deploy')]
|
||||
deploy-check-backup-log:
|
||||
ssh xamxam "tail -20 /var/log/sqlite-backup.log 2>/dev/null || echo '(log file empty or missing — will be created on first cron run)'"
|
||||
ssh xamxam "tail -20 /var/log/xamxam-backup-\$(date +%Y-%m-%d).log 2>/dev/null || echo '(log file empty or missing — will be created on first cron run)'"
|
||||
|
||||
[group('deploy')]
|
||||
deploy-list-backups:
|
||||
@@ -376,24 +392,33 @@ deploy-list-backups:
|
||||
|
||||
[group('deploy')]
|
||||
deploy-cleanup-cron:
|
||||
# Install cron job for orphaned draft cleanup (every 4 hours, 24h threshold).
|
||||
# Install cron job for orphaned draft cleanup (every 2 days, 7-day threshold).
|
||||
# Creates /etc/cron.d/xamxam-cleanup and log file on the server.
|
||||
@echo "📋 Installing draft cleanup cron job…"
|
||||
rsync -v scripts/cleanup-drafts.php xamxam:/var/www/xamxam/scripts/cleanup-drafts.php
|
||||
ssh xamxam "chown www-data:xamxam /var/www/xamxam/scripts/cleanup-drafts.php && chmod 755 /var/www/xamxam/scripts/cleanup-drafts.php"
|
||||
rsync -v scripts/cleanup-drafts.php xamxam:/tmp/cleanup-drafts.php
|
||||
ssh xamxam "chmod 755 /tmp/cleanup-drafts.php"
|
||||
rsync -v deploy/xamxam-cleanup.cron xamxam:/tmp/xamxam-cleanup.cron
|
||||
ssh -t xamxam "sudo install -o root -g root -m 644 /tmp/xamxam-cleanup.cron /etc/cron.d/xamxam-cleanup && rm -f /tmp/xamxam-cleanup.cron"
|
||||
ssh -t xamxam "sudo touch /var/log/xamxam-cleanup.log && sudo chown www-data:www-data /var/log/xamxam-cleanup.log && sudo chmod 644 /var/log/xamxam-cleanup.log"
|
||||
ssh -t xamxam "sudo touch /var/log/xamxam-cleanup-\$(date +%Y-%m-%d).log && sudo chown www-data:www-data /var/log/xamxam-cleanup-\$(date +%Y-%m-%d).log && sudo chmod 644 /var/log/xamxam-cleanup-\$(date +%Y-%m-%d).log"
|
||||
@echo "✅ Cleanup cron installed."
|
||||
@echo " Cron file: /etc/cron.d/xamxam-cleanup"
|
||||
@echo " Script: /var/www/xamxam/scripts/cleanup-drafts.php"
|
||||
@echo " Log file: /var/log/xamxam-cleanup.log"
|
||||
@echo " Script: /tmp/cleanup-drafts.php"
|
||||
@echo " Log file: /var/log/xamxam-cleanup-\$(date +%Y-%m-%d).log"
|
||||
@echo ""
|
||||
@echo "Verify with: just deploy-check-cleanup-log"
|
||||
|
||||
[group('deploy')]
|
||||
deploy-check-cleanup-log:
|
||||
ssh xamxam "tail -20 /var/log/xamxam-cleanup.log 2>/dev/null || echo '(log file empty or missing — will be created on first cron run)'"
|
||||
ssh xamxam "tail -20 /var/log/xamxam-cleanup-\$(date +%Y-%m-%d).log 2>/dev/null || echo '(log file empty or missing — will be created on first cron run)'"
|
||||
|
||||
[group('deploy')]
|
||||
deploy-logrotate:
|
||||
# Install /etc/logrotate.d/xamxam for app + nginx + cron logs.
|
||||
@echo "📋 Installing logrotate config…"
|
||||
rsync -v deploy/xamxam-logrotate xamxam:/tmp/xamxam-logrotate
|
||||
ssh -t xamxam "sudo install -o root -g root -m 644 /tmp/xamxam-logrotate /etc/logrotate.d/xamxam && sudo logrotate -d /etc/logrotate.d/xamxam && rm -f /tmp/xamxam-logrotate"
|
||||
@echo "✅ logrotate config installed and validated (dry-run)."
|
||||
@echo " Config: /etc/logrotate.d/xamxam"
|
||||
|
||||
[group('deploy')]
|
||||
test-restore remote_gz_path:
|
||||
@@ -427,7 +452,7 @@ deploy-migrate-storage dry_run='' target_host='xamxam':
|
||||
ssh {{target_host}} 'rm -f /var/www/xamxam/migrate-storage-paths.php'
|
||||
|
||||
[group('deploy')]
|
||||
deploy-all-first: deploy deploy-backup deploy-cleanup-cron
|
||||
deploy-all-first: deploy deploy-backup deploy-cleanup-cron deploy-logrotate
|
||||
# One-shot: full initial deploy including backup and cleanup cron jobs.
|
||||
|
||||
# ============================================================================
|
||||
@@ -536,7 +561,8 @@ clean:
|
||||
|
||||
[group('utils')]
|
||||
cleanup-drafts dry_run='':
|
||||
# List (dry-run) or delete orphaned draft theses older than 24h.
|
||||
# Pass --no-dry-run to actually delete:
|
||||
# List (dry-run) or delete orphaned draft theses older than 7 days (168h).
|
||||
# Pass --no-dry-run to actually delete. Override the age with OLDER_THAN_HOURS:
|
||||
# just cleanup-drafts --no-dry-run
|
||||
@php -r 'define("APP_ROOT", getcwd()."/app");require APP_ROOT."/src/Database.php";$db=new Database();$dry="{{dry_run}}"!=="--no-dry-run";$res=$db->cleanupOrphanedDrafts(24,$dry);$c=count($res["candidates"]);if($c===0){echo"✅ No orphaned drafts found.\n";}elseif($dry){echo"🔍 Found {$c} orphaned draft(s):\n";foreach($res["candidates"]as$row){printf(" → #%d %s \"%s\" (submitted %s)\n",$row["id"],$row["identifier"],$row["title"],$row["submitted_at"]);}echo"\nRun \"just cleanup-drafts --no-dry-run\" to delete them.\n";}else{echo"🗑 Deleted {$res["deleted"]} orphaned draft(s).\n";}'
|
||||
# OLDER_THAN_HOURS=24 just cleanup-drafts
|
||||
@php scripts/cleanup-drafts.php {{dry_run}}
|
||||
|
||||
+2
-2
@@ -100,8 +100,8 @@ sudo nginx -t
|
||||
|
||||
```bash
|
||||
# Watch logs
|
||||
sudo tail -f /var/log/nginx/xamxam_access.log
|
||||
sudo tail -f /var/log/nginx/xamxam_error.log
|
||||
sudo tail -f /var/log/nginx/xamxam-nginx-access.log
|
||||
sudo tail -f /var/log/nginx/xamxam-nginx-error.log
|
||||
|
||||
# Check nginx status
|
||||
sudo systemctl status nginx
|
||||
|
||||
+2
-2
@@ -72,8 +72,8 @@ server {
|
||||
client_body_timeout 600s;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/xamxam_access.log;
|
||||
error_log /var/log/nginx/xamxam_error.log warn;
|
||||
access_log /var/log/nginx/xamxam-nginx-access.log;
|
||||
error_log /var/log/nginx/xamxam-nginx-error.log warn;
|
||||
|
||||
# Block access to hidden files (except .well-known for Let's Encrypt)
|
||||
location ~ /\.(?!well-known).* {
|
||||
|
||||
@@ -51,8 +51,8 @@ server {
|
||||
client_body_timeout 120s;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/xamxam_access.log;
|
||||
error_log /var/log/nginx/xamxam_error.log warn;
|
||||
access_log /var/log/nginx/xamxam-nginx-access.log;
|
||||
error_log /var/log/nginx/xamxam-nginx-error.log warn;
|
||||
|
||||
# Block access to hidden files (except .well-known for Let's Encrypt)
|
||||
location ~ /\.(?!well-known).* {
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
# RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh # 90 days
|
||||
#
|
||||
# Expected to be run from cron:
|
||||
# 0 * * * * /usr/local/bin/backup-sqlite.sh >> /var/log/sqlite-backup.log 2>&1
|
||||
# 0 2 * * * RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/sqlite-backup.log 2>&1
|
||||
# 0 * * * * /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +%Y-%m-%d).log 2>&1
|
||||
# 0 2 * * * RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +%Y-%m-%d).log 2>&1
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
@@ -1,31 +1,49 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* cleanup-drafts.php — Delete orphaned draft theses older than 24h.
|
||||
* cleanup-drafts.php — Delete orphaned draft theses older than a threshold.
|
||||
*
|
||||
* Draft theses are created with status='draft' during the two-phase commit
|
||||
* in ThesisCreateController. If the file phase throws after COMMIT, the
|
||||
* draft remains orphaned — no files attached, but blocks the identifier.
|
||||
*
|
||||
* The eligibility age defaults to 7 days (168 hours) and can be overridden
|
||||
* with the OLDER_THAN_HOURS environment variable:
|
||||
* OLDER_THAN_HOURS=24 php scripts/cleanup-drafts.php
|
||||
*
|
||||
* Usage:
|
||||
* php scripts/cleanup-drafts.php # dry-run (list candidates)
|
||||
* php scripts/cleanup-drafts.php --no-dry-run # actually delete
|
||||
* php /tmp/cleanup-drafts.php # dry-run (list candidates)
|
||||
* php /tmp/cleanup-drafts.php --no-dry-run # actually delete
|
||||
*
|
||||
* Exit codes: 0 on success, 1 on error.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
define('APP_ROOT', $root . '/app');
|
||||
// Resolve APP_ROOT robustly. In production the app code is deployed flat under
|
||||
// /var/www/xamxam/ (src/, storage/, templates/ at the root, no app/ subdir), and
|
||||
// this script may itself live in /tmp. Always point at /var/www/xamxam in
|
||||
// non-CLI-SAPI contexts; local dev (cli-server) keeps the app/ subdir layout.
|
||||
$prodRoot = '/var/www/xamxam';
|
||||
if (is_dir($prodRoot . '/src') && is_file($prodRoot . '/src/Database.php')) {
|
||||
define('APP_ROOT', $prodRoot);
|
||||
} else {
|
||||
define('APP_ROOT', dirname(__DIR__) . '/app');
|
||||
}
|
||||
|
||||
require_once APP_ROOT . '/src/Database.php';
|
||||
|
||||
$dryRun = !in_array('--no-dry-run', $argv, true);
|
||||
|
||||
// Eligibility threshold: default 7 days (168h); overridable via env.
|
||||
$olderThanHours = (int) (getenv('OLDER_THAN_HOURS') ?: 168);
|
||||
if ($olderThanHours < 1) {
|
||||
$olderThanHours = 168;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
$result = $db->cleanupOrphanedDrafts(24, $dryRun);
|
||||
$result = $db->cleanupOrphanedDrafts($olderThanHours, $dryRun);
|
||||
} catch (Exception $e) {
|
||||
error_log('[cleanup-drafts] Error: ' . $e->getMessage());
|
||||
exit(1);
|
||||
|
||||
@@ -8,12 +8,12 @@ echo "Creating investigation directory..."
|
||||
mkdir -p ~/crash_investigation
|
||||
|
||||
echo "Copying nginx logs..."
|
||||
sudo cp /var/log/nginx/xamxam_error.log ~/crash_investigation/
|
||||
sudo cp /var/log/nginx/xamxam_error.log.1 ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam_access.log ~/crash_investigation/
|
||||
sudo cp /var/log/nginx/xamxam_access.log.1 ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam_error.log.2.gz ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam_access.log.2.gz ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam-nginx-error.log ~/crash_investigation/
|
||||
sudo cp /var/log/nginx/xamxam-nginx-error.log.1 ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam-nginx-access.log ~/crash_investigation/
|
||||
sudo cp /var/log/nginx/xamxam-nginx-access.log.1 ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam-nginx-error.log.2.gz ~/crash_investigation/ 2>/dev/null || true
|
||||
sudo cp /var/log/nginx/xamxam-nginx-access.log.2.gz ~/crash_investigation/ 2>/dev/null || true
|
||||
|
||||
echo "Exporting journal from previous boot..."
|
||||
sudo journalctl -b -1 --no-pager > ~/crash_investigation/journal_previous_boot.log 2>&1
|
||||
|
||||
@@ -89,6 +89,14 @@ chown www-data:xamxam /var/www/xamxam/storage/tmp/php-uploads
|
||||
chmod 2775 /var/www/xamxam/storage/tmp/php-uploads
|
||||
ok "PHP upload temp dir: /var/www/xamxam/storage/tmp/php-uploads"
|
||||
|
||||
# Ensure the application log directory exists and is writable by php-fpm.
|
||||
# App logs (Monolog) go to /var/log/xamxam/xamxam-{channel}-YYYY-MM-DD.log in
|
||||
# production; without this the logger falls back to NullHandler (silent).
|
||||
mkdir -p /var/log/xamxam
|
||||
chown www-data:xamxam /var/log/xamxam
|
||||
chmod 2775 /var/log/xamxam
|
||||
ok "Log dir: /var/log/xamxam owned by www-data:xamxam (2775)"
|
||||
|
||||
# ── Step 2: Nginx config ──────────────────────────────────────────────────────
|
||||
printf "\n📋 Step 2: Deploying nginx configuration...\n"
|
||||
echo "--------------------------------------------"
|
||||
|
||||
Executable
+151
@@ -0,0 +1,151 @@
|
||||
#!/bin/bash
|
||||
# migrate-log-names.sh — rename (and relocate) pre-existing log files to the
|
||||
# xamxam-{service}-{date}.log convention under the standard /var/log root.
|
||||
#
|
||||
# Why: the app adopted a uniform log naming scheme where production app logs
|
||||
# live under /var/log/xamxam/ (system standard), nginx under /var/log/nginx/,
|
||||
# and cron jobs under /var/log/:
|
||||
# /var/log/xamxam/xamxam-{channel}-YYYY-MM-DD.log (app/admin/error/audit)
|
||||
# /var/log/nginx/xamxam-nginx-{access,error}.log (nginx, fixed live name)
|
||||
# /var/log/xamxam-{backup,cleanup}-YYYY-MM-DD.log (cron jobs)
|
||||
# but logs written before that change still carry the old names/locations
|
||||
# (storage/logs/admin-*.log, xamxam_error.log, sqlite-backup.log, …). This
|
||||
# script renames and moves those in place so they follow the same pattern and
|
||||
# stay visible to the admin log viewer (which globs for xamxam-{channel}-*.log).
|
||||
#
|
||||
# Behaviour:
|
||||
# - Idempotent: files already matching the target pattern are skipped.
|
||||
# - Non-destructive: never overwrites an existing destination. If the
|
||||
# destination already exists the source is left untouched and a warning
|
||||
# is printed so you can merge/decide manually.
|
||||
# - Dry-run by default; pass --apply to actually rename.
|
||||
# - Uses each file's own mtime as its date (app/cron logs embed the date in
|
||||
# their name already; nginx/cron flat files derive one from mtime).
|
||||
#
|
||||
# Usage (run on the server):
|
||||
# sudo bash /tmp/migrate-log-names.sh # preview only
|
||||
# sudo bash /tmp/migrate-log-names.sh --apply # perform the renames
|
||||
#
|
||||
# Install: just deploy-script migrate-log-names (uploads this file to /tmp)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
info() { printf "ℹ️ %s\n" "$*"; }
|
||||
ok() { printf "${GREEN}✓${NC} %s\n" "$*"; }
|
||||
warn() { printf "${YELLOW}!${NC} %s\n" "$*"; }
|
||||
err() { printf "${RED}✗${NC} %s\n" "$*" >&2; }
|
||||
|
||||
APPLY=0
|
||||
[ "${1:-}" = "--apply" ] && APPLY=1
|
||||
|
||||
APP_LOGS_OLD_DIR="/var/www/xamxam/storage/logs"
|
||||
APP_LOGS_DIR="/var/log/xamxam"
|
||||
NGINX_LOGS_DIR="/var/log/nginx"
|
||||
|
||||
# ── App channels (Monolog rotating files) ─────────────────────────────────────
|
||||
# App logs now live under /var/log/xamxam (production). Handle both the old
|
||||
# location (storage/logs, pre-move) and files already in the new location that
|
||||
# still carry the old naming (missing xamxam- prefix).
|
||||
# Old: {channel}-YYYY-MM-DD.log New: xamxam-{channel}-YYYY-MM-DD.log
|
||||
# Old bare base also possible: {channel}.log (no date)
|
||||
CHANNELS=(app admin error audit)
|
||||
changed=0
|
||||
|
||||
for ch in "${CHANNELS[@]}"; do
|
||||
# Source directory: prefer the old storage/logs location for files that
|
||||
# predate the move; also scan the new /var/log/xamxam in case a previous
|
||||
# partial migration already moved (but did not rename) some files.
|
||||
for srcdir in "$APP_LOGS_OLD_DIR" "$APP_LOGS_DIR"; do
|
||||
[ -d "$srcdir" ] || continue
|
||||
|
||||
# Dated files: {channel}-YYYY-MM-DD.log → xamxam-{channel}-YYYY-MM-DD.log
|
||||
for src in "$srcdir"/"$ch"-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].log; do
|
||||
[ -e "$src" ] || continue
|
||||
base=$(basename "$src")
|
||||
dst="$APP_LOGS_DIR/xamxam-$base"
|
||||
if [ -e "$dst" ]; then
|
||||
warn "skip (dest exists): $base"
|
||||
continue
|
||||
fi
|
||||
if [ "$APPLY" -eq 1 ]; then
|
||||
mv "$src" "$dst" && ok "moved $srcdir/$base → $dst"
|
||||
else
|
||||
info "[dry-run] $srcdir/$base → $dst"
|
||||
fi
|
||||
changed=$((changed + 1))
|
||||
done
|
||||
|
||||
# Bare base file (rare, pre-rotation): {channel}.log → xamxam-{channel}.log
|
||||
src="$srcdir/$ch.log"
|
||||
if [ -f "$src" ]; then
|
||||
dst="$APP_LOGS_DIR/xamxam-$ch.log"
|
||||
if [ -e "$dst" ]; then
|
||||
warn "skip (dest exists): $ch.log"
|
||||
elif [ "$APPLY" -eq 1 ]; then
|
||||
mv "$src" "$dst" && ok "moved $srcdir/$ch.log → $dst"
|
||||
else
|
||||
info "[dry-run] $srcdir/$ch.log → $dst"
|
||||
fi
|
||||
changed=$((changed + 1))
|
||||
fi
|
||||
done
|
||||
done
|
||||
[ "$changed" -eq 0 ] && info " none found"
|
||||
|
||||
# ── nginx logs (fixed live name + logrotate .N / .N.gz rotations) ─────────────
|
||||
# Old: xamxam_error.log[.N[.gz]] New: xamxam-nginx-error.log[.N[.gz]]
|
||||
# Old: xamxam_access.log[.N[.gz]] New: xamxam-nginx-access.log[.N[.gz]]
|
||||
info "nginx logs — $NGINX_LOGS_DIR"
|
||||
for kind in access error; do
|
||||
# Match the live file and any rotations (.1, .2.gz, …)
|
||||
for src in "$NGINX_LOGS_DIR"/xamxam_"$kind".log*; do
|
||||
[ -e "$src" ] || continue
|
||||
base=$(basename "$src")
|
||||
# strip the leading xamxam_{kind}.log, keep the rotation suffix
|
||||
suffix=${base#xamxam_"$kind".log}
|
||||
dst="$NGINX_LOGS_DIR/xamxam-nginx-$kind.log$suffix"
|
||||
if [ -e "$dst" ]; then
|
||||
warn "skip (dest exists): $base"
|
||||
continue
|
||||
fi
|
||||
if [ "$APPLY" -eq 1 ]; then
|
||||
mv "$src" "$dst" && ok "renamed $base → $(basename "$dst")"
|
||||
else
|
||||
info "[dry-run] $base → $(basename "$dst")"
|
||||
fi
|
||||
changed=$((changed + 1))
|
||||
done
|
||||
done
|
||||
|
||||
# ── Flat cron logs (derive a date from mtime) ────────────────────────────────
|
||||
# Old: /var/log/sqlite-backup.log → /var/log/xamxam-backup-YYYY-MM-DD.log
|
||||
# Old: /var/log/xamxam-cleanup.log → /var/log/xamxam-cleanup-YYYY-MM-DD.log
|
||||
info "cron logs — /var/log"
|
||||
rename_cron_log() {
|
||||
local src="$1" svc="$2"
|
||||
[ -f "$src" ] || return 0
|
||||
local d
|
||||
d=$(date -r "$src" +%Y-%m-%d 2>/dev/null || stat -c %y "$src" | cut -d' ' -f1)
|
||||
local dst="/var/log/xamxam-$svc-$d.log"
|
||||
if [ -e "$dst" ]; then
|
||||
warn "skip (dest exists): $(basename "$src")"
|
||||
return 0
|
||||
fi
|
||||
if [ "$APPLY" -eq 1 ]; then
|
||||
mv "$src" "$dst" && ok "renamed $(basename "$src") → $(basename "$dst")"
|
||||
else
|
||||
info "[dry-run] $(basename "$src") → $(basename "$dst")"
|
||||
fi
|
||||
changed=$((changed + 1))
|
||||
}
|
||||
rename_cron_log /var/log/sqlite-backup.log backup
|
||||
rename_cron_log /var/log/xamxam-cleanup.log cleanup
|
||||
|
||||
echo ""
|
||||
if [ "$APPLY" -eq 0 ]; then
|
||||
info "Dry-run complete. Re-run with --apply to perform the renames."
|
||||
else
|
||||
ok "Renames complete ($changed changed)."
|
||||
fi
|
||||
@@ -88,13 +88,12 @@ chown -R "$WEB_USER:$APP_GROUP" "$APP_DIR/storage/cache"
|
||||
chmod -R 2775 "$APP_DIR/storage/cache"
|
||||
ok "Cache dirs: created and owned by $WEB_USER:$APP_GROUP"
|
||||
|
||||
# ── 8. Provision /var/log/xamxam.log ─────────────────────────────────────────
|
||||
if [ ! -f /var/log/xamxam.log ]; then
|
||||
touch /var/log/xamxam.log
|
||||
fi
|
||||
chown "$WEB_USER:$APP_GROUP" /var/log/xamxam.log
|
||||
chmod 640 /var/log/xamxam.log
|
||||
ok "/var/log/xamxam.log: owned by $WEB_USER:$APP_GROUP (640)"
|
||||
# ── 8. Provision app log directory (/var/log/xamxam) ──────────────────────────────
|
||||
LOG_DIR="/var/log/xamxam"
|
||||
mkdir -p "$LOG_DIR"
|
||||
chown "$WEB_USER:$APP_GROUP" "$LOG_DIR"
|
||||
chmod 2775 "$LOG_DIR"
|
||||
ok "/var/log/xamxam: owned by $WEB_USER:$APP_GROUP (2775)"
|
||||
|
||||
printf "\n"
|
||||
ok "Setup complete."
|
||||
|
||||
Reference in New Issue
Block a user