# Backup-Konfiguration

Backups werden in `config.php` konfiguriert. Ohne Remote-Konfiguration funktionieren sie sofort: Ein Admin kann unter **Einstellungen** lokale ZIP-Backups erstellen.

## Inhalt des Backups

Das Backup-ZIP enthält nur Betriebsdaten:

- `data/*.json`
- `data/uploads/**`

Nicht enthalten sind App-Dateien, `config.php`, bestehende Backups, Updater-Arbeitsdateien, Logs, Rate-Limit-Daten und temporäre Dateien.

Lokale Backup-ZIPs liegen in `data/backups/`. Mit der Standard-`.htaccess` ist dieses Verzeichnis nicht öffentlich lesbar.

## Lokale Grundkonfiguration

Diese Defaults sind bereits in `includes/backup.php` hinterlegt. Wenn eine Installation explizite Werte benötigt, werden sie in `config.php` gesetzt:

```php
define('BACKUP_DIR', DATA_DIR . 'backups/');
define('BACKUP_LOCAL_RETENTION', 4);
define('BACKUP_AUTO_INTERVAL_SECONDS', 604800);
define('BACKUP_REMOTE_TARGETS', []);
```

`BACKUP_LOCAL_RETENTION` legt fest, wie viele lokale ZIP-Dateien behalten werden. Ältere Backups werden nach einem erfolgreichen neuen Backup gelöscht.

`BACKUP_AUTO_INTERVAL_SECONDS` steuert automatische Backups durch Admin-Aktivität. Der Standard ist wöchentlich (`604800`). Mit `0` werden automatische Backups deaktiviert.

Es gibt keinen Cron-Endpunkt. Automatische Backups laufen nur, wenn ein Admin die Einstellungsseite öffnet und das Intervall abgelaufen ist.

## Bedienung im Admin

Pfad: **Admin > Einstellungen > Backups**.

- **Backup erstellen** erstellt sofort ein manuelles Backup.
- **Letzte Backups** zeigt lokale Backups mit Erstellzeit, Auslöser, Größe, Dateianzahl, Remote-Status und Download-Aktion.
- Fehlgeschlagene Remote-Uploads machen das lokale Backup nicht ungültig. Der Fehler wird in den Backup-Metadaten gespeichert.

## S3-Ziel

S3-Uploads nutzen eingebaute PHP-HTTPS-Streams und AWS Signature V4. Es ist keine Bibliothek erforderlich.

```php
define('BACKUP_REMOTE_TARGETS', [
    [
        'name' => 'S3 Backup',
        'type' => 's3',
        'bucket' => 'example-bucket',
        'region' => 'eu-central-1',
        'prefix' => 'psa-orderform',
        'access_key' => 'AKIA...',
        'secret_key' => '...',
    ],
]);
```

Für S3-kompatiblen Speicher kann `endpoint` ergänzt werden:

```php
'endpoint' => 'https://s3.example.org',
```

Der Objektpfad besteht aus `prefix` plus generiertem ZIP-Dateinamen.

## SFTP-Ziel

SFTP benötigt die optionale PHP-SSH2-Erweiterung. Wenn die Erweiterung fehlt, funktionieren lokale Backups weiterhin; der SFTP-Upload wird als nicht verfügbar beziehungsweise fehlgeschlagen gemeldet.

Passwort-Anmeldung:

```php
define('BACKUP_REMOTE_TARGETS', [
    [
        'name' => 'SFTP Backup',
        'type' => 'sftp',
        'host' => 'backup.example.org',
        'port' => 22,
        'username' => 'backup-user',
        'password' => '...',
        'path' => '/backups/psa-orderform',
    ],
]);
```

Key-Anmeldung:

```php
define('BACKUP_REMOTE_TARGETS', [
    [
        'name' => 'SFTP Backup',
        'type' => 'sftp',
        'host' => 'backup.example.org',
        'port' => 22,
        'username' => 'backup-user',
        'public_key' => __DIR__ . '/keys/backup.pub',
        'private_key' => __DIR__ . '/keys/backup',
        'password' => '',
        'path' => '/backups/psa-orderform',
    ],
]);
```

Das Remote-Verzeichnis muss bereits existieren und für den konfigurierten Benutzer beschreibbar sein.

## Custom-Uploader

Ein Custom-Uploader ist ein PHP-Callback, der in `config.php` konfiguriert wird.

```php
define('BACKUP_REMOTE_TARGETS', [
    [
        'name' => 'Custom Backup',
        'type' => 'custom',
        'file' => __DIR__ . '/custom-backup-uploader.php',
        'callback' => 'uploadPsaOrderformBackup',
    ],
]);
```

Beispiel-Callback:

```php
<?php

function uploadPsaOrderformBackup(string $archivePath, array $metadata, array $target)
{
    $targetDir = __DIR__ . '/external-backups';
    if (!is_dir($targetDir) && !mkdir($targetDir, 02775, true) && !is_dir($targetDir)) {
        return [
            'success' => false,
            'error' => 'Target directory cannot be created.',
        ];
    }

    $remotePath = $targetDir . '/' . basename($archivePath);
    if (!copy($archivePath, $remotePath)) {
        return [
            'success' => false,
            'error' => 'Copy failed.',
        ];
    }

    return [
        'remote_path' => $remotePath,
    ];
}
```

Der Callback erhält:

- `$archivePath`: absoluter Pfad zur lokalen ZIP-Datei.
- `$metadata`: Dateiname, Erstellzeitpunkt, Auslöser und Prüfsumme.
- `$target`: das konfigurierte Ziel-Array.

Für Erfolg kann `true` oder ein Array zurückgegeben werden. Für Fehler kann eine Exception geworfen werden. Ein Array mit `'success' => false` wird als fehlgeschlagener Remote-Upload protokolliert.

## Mehrere Remote-Ziele

Remote-Ziele können kombiniert werden:

```php
define('BACKUP_REMOTE_TARGETS', [
    [
        'name' => 'Primary S3',
        'type' => 's3',
        'bucket' => 'example-bucket',
        'region' => 'eu-central-1',
        'prefix' => 'psa-orderform',
        'access_key' => 'AKIA...',
        'secret_key' => '...',
    ],
    [
        'name' => 'Secondary SFTP',
        'type' => 'sftp',
        'host' => 'backup.example.org',
        'port' => 22,
        'username' => 'backup-user',
        'password' => '...',
        'path' => '/backups/psa-orderform',
    ],
]);
```

Jedes Ziel wird unabhängig versucht.

## Managed Backup Server

Der Managed Backup Server ist ein separates Verzeichnis `backup-server/`, das auf einem zentralen Webspace bereitgestellt werden kann.

Server-Setup:

1. `backup-server/config.sample.php` nach `backup-server/config.php` kopieren.
2. `BACKUP_SERVER_PASSWORD` ändern.
3. Schreibrechte für `backup-server/backups/` sicherstellen.
4. `backup-server/manage.php` öffnen und einloggen.
5. Jede erlaubte verteilte Instanz in der Management-Oberfläche anlegen.

Der Upload-Endpunkt `backup-server/upload.php` benötigt keine Anmeldung. Jede verteilte Instanz muss aber einen eindeutigen `instance`-Wert senden, der serverseitig in der Management-Oberfläche erlaubt wurde.

Client-Konfiguration:

```php
define('BACKUP_REMOTE_TARGETS', [
    [
        'name' => 'Managed Backup Server',
        'type' => 'managed',
        'url' => 'https://backup.example.org/upload.php',
        'instance' => 'stadt-freising-prod',
    ],
]);
```

`url` ist die vollständige URL zu `upload.php`. `instance` darf Buchstaben, Zahlen, Punkte, Unterstriche und Bindestriche enthalten.

Die Server-Retention gilt pro Instanz. Standard ist `30`; der Wert kann in `backup-server/config.php` und in der Management-Oberfläche geändert werden.

Uploads von unbekannten Instanzen werden abgelehnt. Entfernte Instanzen können keine neuen Backups mehr senden; bereits gespeicherte Backups bleiben in der Management-Oberfläche sichtbar.

## Betriebshinweise

- Remote-Zugangsdaten gehören in `config.php`, nicht in `data/settings.json`.
- `data/` muss für PHP beschreibbar sein.
- Auf Apache-Hosting muss `.htaccess` aktiv bleiben, damit `data/backups/` nicht direkt erreichbar ist.
- Remote-Ziele sollten nach Konfigurationsänderungen mit einem manuellen Backup getestet werden.
- Remote-Backup-Erfolge werden knapp in `data/logs/access.log` protokolliert; Fehler mit Debug-Kontext in `data/logs/error.log`. Die App-Logs rotieren nach `LOG_MAX_BYTES` und entfernen alte rotierte Logs nach `LOG_MAX_AGE_SECONDS`.
- Lokale Backups sollten nur über den authentifizierten Adminbereich heruntergeladen werden.
