prevent data in database and added migration from old databasename lexoffice
This commit is contained in:
parent
f74f936d72
commit
9fdada5dbe
100
app/database.py
100
app/database.py
|
|
@ -1,8 +1,12 @@
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
DB_PATH = os.environ.get("DB_PATH", "/data/belegimport.db")
|
DB_PATH = os.environ.get("DB_PATH", "/data/belegimport.db")
|
||||||
|
SCHEMA_VERSION = 2
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_fernet = None
|
_fernet = None
|
||||||
|
|
||||||
|
|
@ -78,9 +82,86 @@ def _decrypt(fernet: Fernet, value: str) -> str:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
async def _get_schema_version(db: aiosqlite.Connection) -> int:
|
||||||
|
"""Read current schema version from DB. Returns 0 if not set."""
|
||||||
|
try:
|
||||||
|
cursor = await db.execute(
|
||||||
|
"SELECT value FROM settings WHERE key = 'schema_version'"
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return int(row[0]) if row else 0
|
||||||
|
except Exception:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
async def _set_schema_version(db: aiosqlite.Connection, version: int):
|
||||||
|
await db.execute(
|
||||||
|
"INSERT OR REPLACE INTO settings (key, value) VALUES ('schema_version', ?)",
|
||||||
|
(str(version),),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _run_migrations(db: aiosqlite.Connection, current_version: int):
|
||||||
|
"""Run all pending migrations sequentially."""
|
||||||
|
|
||||||
|
if current_version < 1:
|
||||||
|
logger.info("Migration v1: Initiale Tabellenstruktur")
|
||||||
|
# v1: Base tables (idempotent via IF NOT EXISTS)
|
||||||
|
await db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL DEFAULT ''
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
await db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS processing_log (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
timestamp TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
|
||||||
|
email_subject TEXT,
|
||||||
|
email_from TEXT,
|
||||||
|
attachments_count INTEGER DEFAULT 0,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
error_message TEXT
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
await db.commit()
|
||||||
|
await _set_schema_version(db, 1)
|
||||||
|
|
||||||
|
if current_version < 2:
|
||||||
|
logger.info("Migration v2: lexoffice_email -> import_email")
|
||||||
|
# v2: Rename lexoffice_email -> import_email
|
||||||
|
cursor = await db.execute(
|
||||||
|
"SELECT value FROM settings WHERE key = 'lexoffice_email'"
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
if row and row[0]:
|
||||||
|
# Copy value to import_email if it's empty
|
||||||
|
cursor2 = await db.execute(
|
||||||
|
"SELECT value FROM settings WHERE key = 'import_email'"
|
||||||
|
)
|
||||||
|
row2 = await cursor2.fetchone()
|
||||||
|
if not row2 or not row2[0]:
|
||||||
|
await db.execute(
|
||||||
|
"INSERT OR REPLACE INTO settings (key, value) VALUES ('import_email', ?)",
|
||||||
|
(row[0],),
|
||||||
|
)
|
||||||
|
logger.info(" lexoffice_email Wert nach import_email übertragen")
|
||||||
|
await db.execute("DELETE FROM settings WHERE key = 'lexoffice_email'")
|
||||||
|
await db.commit()
|
||||||
|
await _set_schema_version(db, 2)
|
||||||
|
|
||||||
|
# --- Future migrations go here ---
|
||||||
|
# if current_version < 3:
|
||||||
|
# logger.info("Migration v3: ...")
|
||||||
|
# await _set_schema_version(db, 3)
|
||||||
|
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
|
||||||
async def init_db():
|
async def init_db():
|
||||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
os.makedirs(os.path.dirname(DB_PATH) or ".", exist_ok=True)
|
||||||
async with aiosqlite.connect(DB_PATH) as db:
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
|
# Ensure base tables exist (needed before we can read schema_version)
|
||||||
await db.execute("""
|
await db.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS settings (
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
key TEXT PRIMARY KEY,
|
key TEXT PRIMARY KEY,
|
||||||
|
|
@ -100,7 +181,18 @@ async def init_db():
|
||||||
""")
|
""")
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
# Insert default settings if not present
|
# Check version and run migrations
|
||||||
|
current_version = await _get_schema_version(db)
|
||||||
|
if current_version < SCHEMA_VERSION:
|
||||||
|
logger.info(
|
||||||
|
f"DB-Schema v{current_version} -> v{SCHEMA_VERSION}, starte Migrationen..."
|
||||||
|
)
|
||||||
|
await _run_migrations(db, current_version)
|
||||||
|
logger.info(f"DB-Schema auf v{SCHEMA_VERSION} aktualisiert")
|
||||||
|
else:
|
||||||
|
logger.info(f"DB-Schema v{current_version} ist aktuell")
|
||||||
|
|
||||||
|
# Insert default settings for any new keys (never overwrites existing values)
|
||||||
for key, value in DEFAULT_SETTINGS.items():
|
for key, value in DEFAULT_SETTINGS.items():
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
|
"INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
|
||||||
|
|
@ -116,7 +208,7 @@ async def get_settings() -> dict:
|
||||||
fernet = await _get_fernet()
|
fernet = await _get_fernet()
|
||||||
async with aiosqlite.connect(DB_PATH) as db:
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
"SELECT key, value FROM settings WHERE key != 'encryption_key'"
|
"SELECT key, value FROM settings WHERE key NOT IN ('encryption_key', 'schema_version')"
|
||||||
)
|
)
|
||||||
rows = await cursor.fetchall()
|
rows = await cursor.fetchall()
|
||||||
|
|
||||||
|
|
@ -133,7 +225,7 @@ async def save_settings(data: dict):
|
||||||
fernet = await _get_fernet()
|
fernet = await _get_fernet()
|
||||||
async with aiosqlite.connect(DB_PATH) as db:
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if key == "encryption_key":
|
if key in ("encryption_key", "schema_version"):
|
||||||
continue
|
continue
|
||||||
store_value = _encrypt(fernet, value) if key in ENCRYPTED_KEYS else value
|
store_value = _encrypt(fernet, value) if key in ENCRYPTED_KEYS else value
|
||||||
await db.execute(
|
await db.execute(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue