67 lines
2.7 KiB
Python
67 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
import shutil
|
|
|
|
from flask import current_app
|
|
|
|
|
|
class BackupService:
|
|
def create_backup(self):
|
|
target = Path(current_app.config['BACKUP_PATH']) / f'backup_{datetime.utcnow().strftime("%Y%m%d_%H%M%S")}'
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
base_dir = Path(current_app.root_path).parent
|
|
for part in ['instance', 'storage/archive', 'storage/pdf']:
|
|
src = base_dir / part
|
|
if src.exists():
|
|
shutil.copytree(src, target / Path(part).name, dirs_exist_ok=True)
|
|
archive = shutil.make_archive(str(target), 'zip', root_dir=target)
|
|
return archive
|
|
|
|
def get_database_backup_meta(self) -> dict:
|
|
uri = current_app.config.get('SQLALCHEMY_DATABASE_URI', '')
|
|
backup_dir = Path(current_app.config['BACKUP_PATH'])
|
|
engine = 'unknown'
|
|
if '://' in uri:
|
|
engine = uri.split('://', 1)[0]
|
|
sqlite_supported = uri.startswith('sqlite:///') and not uri.endswith(':memory:')
|
|
sqlite_path = None
|
|
sqlite_exists = False
|
|
if sqlite_supported:
|
|
sqlite_path = Path(uri.replace('sqlite:///', '', 1))
|
|
sqlite_exists = sqlite_path.exists()
|
|
return {
|
|
'engine': engine,
|
|
'backup_dir': str(backup_dir),
|
|
'sqlite_supported': sqlite_supported,
|
|
'sqlite_path': str(sqlite_path) if sqlite_path else None,
|
|
'sqlite_exists': sqlite_exists,
|
|
'notes': [
|
|
'Kopia z panelu działa plikowo dla SQLite.',
|
|
'Dla PostgreSQL, MySQL i innych silników wymagany jest natywny dump bazy poza aplikacją.',
|
|
],
|
|
}
|
|
|
|
def create_database_backup(self) -> str:
|
|
target_dir = Path(current_app.config['BACKUP_PATH'])
|
|
target_dir.mkdir(parents=True, exist_ok=True)
|
|
timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
|
uri = current_app.config.get('SQLALCHEMY_DATABASE_URI', '')
|
|
if uri.startswith('sqlite:///') and not uri.endswith(':memory:'):
|
|
source = Path(uri.replace('sqlite:///', '', 1))
|
|
if not source.exists():
|
|
raise FileNotFoundError(f'Plik bazy nie istnieje: {source}')
|
|
target = target_dir / f'db_backup_{timestamp}.sqlite3'
|
|
shutil.copy2(source, target)
|
|
return str(target)
|
|
target = target_dir / f'db_backup_{timestamp}.txt'
|
|
target.write_text(
|
|
"""Automatyczna kopia DB dla bieżącego silnika nie jest obsługiwana plikowo.
|
|
W panelu admina kopia działa bezpośrednio tylko dla SQLite.
|
|
Wykonaj backup natywnym narzędziem bazy danych.
|
|
""",
|
|
encoding='utf-8',
|
|
)
|
|
return str(target)
|