import os import aiosqlite from cryptography.fernet import Fernet DB_PATH = os.environ.get("DB_PATH", "/data/belegimport.db") _fernet = None ENCRYPTED_KEYS = {"imap_password", "smtp_password", "smb_password"} DEFAULT_SETTINGS = { "imap_server": "", "imap_port": "993", "imap_ssl": "true", "imap_username": "", "imap_password": "", "smtp_server": "", "smtp_port": "587", "smtp_ssl": "starttls", "smtp_username": "", "smtp_password": "", "lexoffice_email": "", "source_folder": "Rechnungen", "processed_folder": "Rechnungen/Verarbeitet", "interval_minutes": "5", "scheduler_enabled": "false", "fetch_since_date": "", # SMB "smb_enabled": "false", "smb_server": "", "smb_port": "445", "smb_username": "", "smb_password": "", "smb_domain": "", "smb_share": "", "smb_source_path": "", "smb_processed_path": "Verarbeitet", "smb_mode": "forward", } async def _get_fernet() -> Fernet: global _fernet if _fernet is not None: return _fernet async with aiosqlite.connect(DB_PATH) as db: cursor = await db.execute( "SELECT value FROM settings WHERE key = 'encryption_key'" ) row = await cursor.fetchone() if row: key = row[0].encode() else: key = Fernet.generate_key() await db.execute( "INSERT INTO settings (key, value) VALUES ('encryption_key', ?)", (key.decode(),), ) await db.commit() _fernet = Fernet(key) return _fernet def _encrypt(fernet: Fernet, value: str) -> str: if not value: return "" return fernet.encrypt(value.encode()).decode() def _decrypt(fernet: Fernet, value: str) -> str: if not value: return "" try: return fernet.decrypt(value.encode()).decode() except Exception: return "" async def init_db(): os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) async with aiosqlite.connect(DB_PATH) as db: 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() # Insert default settings if not present for key, value in DEFAULT_SETTINGS.items(): await db.execute( "INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)", (key, value), ) await db.commit() # Ensure encryption key exists await _get_fernet() async def get_settings() -> dict: fernet = await _get_fernet() async with aiosqlite.connect(DB_PATH) as db: cursor = await db.execute( "SELECT key, value FROM settings WHERE key != 'encryption_key'" ) rows = await cursor.fetchall() settings = {} for key, value in rows: if key in ENCRYPTED_KEYS: settings[key] = _decrypt(fernet, value) else: settings[key] = value return settings async def save_settings(data: dict): fernet = await _get_fernet() async with aiosqlite.connect(DB_PATH) as db: for key, value in data.items(): if key == "encryption_key": continue store_value = _encrypt(fernet, value) if key in ENCRYPTED_KEYS else value await db.execute( "INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", (key, store_value), ) await db.commit() async def add_log_entry( email_subject: str, email_from: str, attachments_count: int, status: str, error_message: str = "", ): async with aiosqlite.connect(DB_PATH) as db: await db.execute( """INSERT INTO processing_log (email_subject, email_from, attachments_count, status, error_message) VALUES (?, ?, ?, ?, ?)""", (email_subject, email_from, attachments_count, status, error_message), ) await db.commit() async def get_log_entries(limit: int = 100) -> list[dict]: async with aiosqlite.connect(DB_PATH) as db: db.row_factory = aiosqlite.Row cursor = await db.execute( "SELECT * FROM processing_log ORDER BY id DESC LIMIT ?", (limit,) ) rows = await cursor.fetchall() return [dict(row) for row in rows]