mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-20 09:30:36 +00:00
feat(cli): automatic periodic SQLite export with retention (#8819)
Add an opt-in CLI that exports each user's database to `data/users/<user>/sqlite-backups/<YYYYMMDDTHHMMSSZ>.sqlite` (UTC) and prunes older files to a configured count. Gated by two new settings, `auto_sqlite_export.enabled` and `auto_sqlite_export.retention`. Kept separate from `cli/db-backup.php` / `cli/db-restore.php`, which stay the fixed-filename migration tool. First step of #8183. Co-authored-by: Bjørn A. Andersen <polybjorn@users.noreply.github.com>
This commit is contained in:
@@ -9,6 +9,7 @@ declare(strict_types=1);
|
||||
* @property bool $api_enabled
|
||||
* @property string $archiving
|
||||
* @property 'form'|'http_auth'|'none' $auth_type
|
||||
* @property array{enabled:bool,retention:int} $auto_sqlite_export
|
||||
* @property-read bool $reauth_required
|
||||
* @property-read int $reauth_time
|
||||
* @property-read string $auto_update_url
|
||||
|
||||
@@ -127,6 +127,11 @@ cd /usr/share/FreshRSS
|
||||
# Back-up all users respective database to `data/users/*/backup.sqlite`
|
||||
# -q, --quiet suppress non-error messages
|
||||
|
||||
./cli/export-sqlite-auto.php
|
||||
# Periodic SQLite export per user to `data/users/*/sqlite-backups/<YYYYMMDDTHHMMSSZ>.sqlite`, pruned to retention.
|
||||
# Gated by `auto_sqlite_export` in `data/config.php`.
|
||||
# -q, --quiet suppress non-error messages
|
||||
|
||||
./cli/db-restore.php --delete-backup --force-overwrite
|
||||
# Restore all users respective database from `data/users/*/backup.sqlite`
|
||||
# --delete-backup: delete `data/users/*/backup.sqlite` after successful import
|
||||
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/_cli.php';
|
||||
|
||||
performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? '');
|
||||
|
||||
$cliOptions = new class extends CliOptionsParser {
|
||||
public bool $quiet;
|
||||
|
||||
public function __construct() {
|
||||
$this->addOption('quiet', (new CliOption('quiet', 'q'))->withValueNone());
|
||||
parent::__construct();
|
||||
}
|
||||
};
|
||||
|
||||
if (!empty($cliOptions->errors)) {
|
||||
fail('FreshRSS error: ' . array_shift($cliOptions->errors) . "\n" . $cliOptions->usage);
|
||||
}
|
||||
|
||||
$config = FreshRSS_Context::systemConf()->auto_sqlite_export;
|
||||
$enabled = !empty($config['enabled']);
|
||||
$retention = max(1, (int)($config['retention'] ?? 7));
|
||||
$verbose = !$cliOptions->quiet;
|
||||
|
||||
if (!$enabled) {
|
||||
if ($verbose) {
|
||||
echo "FreshRSS automatic SQLite export is disabled (see `auto_sqlite_export.enabled` in `data/config.php`).\n";
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$ok = true;
|
||||
$timestamp = gmdate('Ymd\THis\Z');
|
||||
|
||||
foreach (FreshRSS_user_Controller::listUsers() as $username) {
|
||||
$username = cliInitUser($username);
|
||||
$exportDir = DATA_PATH . '/users/' . $username . '/sqlite-backups';
|
||||
if (!is_dir($exportDir) && !@mkdir($exportDir, 0755, true)) {
|
||||
fwrite(STDERR, "FreshRSS error: unable to create export directory: {$exportDir}\n");
|
||||
$ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
$filename = $exportDir . '/' . $timestamp . '.sqlite';
|
||||
|
||||
if ($verbose) {
|
||||
echo 'FreshRSS automatic SQLite export for user “', $username, '” -> ', $filename, "\n";
|
||||
}
|
||||
|
||||
$databaseDAO = FreshRSS_Factory::createDatabaseDAO($username);
|
||||
$exported = $databaseDAO->dbCopy($filename, FreshRSS_DatabaseDAO::SQLITE_EXPORT, false, $verbose);
|
||||
$ok = $ok && $exported;
|
||||
|
||||
if (!$exported) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = glob($exportDir . '/*.sqlite') ?: [];
|
||||
if (count($existing) > $retention) {
|
||||
sort($existing);
|
||||
$toDelete = array_slice($existing, 0, count($existing) - $retention);
|
||||
foreach ($toDelete as $old) {
|
||||
if (@unlink($old)) {
|
||||
if ($verbose) {
|
||||
echo "Pruned old export: {$old}\n";
|
||||
}
|
||||
} else {
|
||||
fwrite(STDERR, "FreshRSS warning: failed to prune old export: {$old}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done($ok);
|
||||
@@ -210,6 +210,16 @@ return [
|
||||
'from' => 'root@localhost',
|
||||
],
|
||||
|
||||
# Automatic SQLite export of each user’s database, triggered by `./cli/export-sqlite-auto.php`.
|
||||
# Intended to be scheduled by an admin (e.g. via cron) for periodic on-server backups
|
||||
# distinct from the manual `./cli/db-backup.php` / `./cli/db-restore.php` migration workflow.
|
||||
'auto_sqlite_export' => [
|
||||
# Enable the automatic export. When false, `./cli/export-sqlite-auto.php` exits without writing.
|
||||
'enabled' => false,
|
||||
# Number of past exports to retain per user. Older files are pruned after a successful export.
|
||||
'retention' => 7,
|
||||
],
|
||||
|
||||
# List of enabled FreshRSS extensions.
|
||||
'extensions_enabled' => [
|
||||
],
|
||||
|
||||
@@ -57,6 +57,25 @@ cd /usr/share/FreshRSS/
|
||||
./cli/db-restore.php --delete-backup --force-overwrite
|
||||
```
|
||||
|
||||
## Automatic periodic SQLite export
|
||||
|
||||
For ongoing on-server backups, separate from the one-shot `db-backup.php` / `db-restore.php` migration workflow, enable automatic SQLite export in `./data/config.php`:
|
||||
|
||||
```php
|
||||
'auto_sqlite_export' => [
|
||||
'enabled' => true,
|
||||
'retention' => 7,
|
||||
],
|
||||
```
|
||||
|
||||
Then schedule it (for example via cron):
|
||||
|
||||
```sh
|
||||
./cli/export-sqlite-auto.php
|
||||
```
|
||||
|
||||
Each run writes `./data/users/<username>/sqlite-backups/<YYYYMMDDTHHMMSSZ>.sqlite` (UTC) for every user and prunes older files to the configured `retention` count.
|
||||
|
||||
## Migrating the database
|
||||
|
||||
First, back up all user databases to SQLite files:
|
||||
|
||||
Reference in New Issue
Block a user