9db922703b
migrations.py - schema_migrations table tracks applied versions (version, description, applied_at) - MIGRATIONS list is append-only; each entry is (version, description, sql) - backup() dumps all user-data tables to a timestamped JSON file in backups/ before any schema changes so data can be recovered if something goes wrong - run_migrations() is idempotent: already-applied versions are skipped Integration - app.py calls _run_startup_migrations() at module load so every restart applies any pending migrations (no-op if schema is current) - setup_routes.py calls run_migrations() after the initial setup form is submitted so all tables exist before the user hits the main page for the first time - notes.py and admin.py: removed all per-request CREATE TABLE DDL; schema is now owned entirely by the migration system Docker - docker-compose.dev.yml: add backups-data volume so JSON backups survive container restarts and rebuilds - Dockerfile: pre-create /app/backend/logs and /app/backend/backups so the directories exist even before volumes are mounted Adding future schema changes - Append a new (version, description, sql) tuple to MIGRATIONS in migrations.py - The next restart will detect it as pending, back up first, then apply it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
62 lines
2.3 KiB
Python
62 lines
2.3 KiB
Python
import time
|
||
import os
|
||
from flask import Blueprint, request, render_template, redirect, jsonify, send_from_directory
|
||
from util.db_config import load_config, save_config, test_connection, is_configured
|
||
from util.db_pool import reset_pool
|
||
from auth.setup_admin import initialize_admin_user
|
||
from util.logger import logger
|
||
|
||
setup_blueprint = Blueprint("setup", __name__, template_folder="../backend/templates", static_folder="../backend/static")
|
||
|
||
MAX_WAIT = 30
|
||
WAIT_INTERVAL = 10
|
||
|
||
def wait_for_db():
|
||
elapsed = 0
|
||
config = load_config()
|
||
while not test_connection(config) and elapsed < MAX_WAIT:
|
||
logger.info(f"[SETUP] DB nicht erreichbar – warte {WAIT_INTERVAL}s...")
|
||
time.sleep(WAIT_INTERVAL)
|
||
elapsed += WAIT_INTERVAL
|
||
return elapsed < MAX_WAIT
|
||
|
||
@setup_blueprint.route('/api/status')
|
||
def status():
|
||
if not is_configured():
|
||
return jsonify({"status": "init", "db_connected": False})
|
||
elif test_connection(load_config()):
|
||
return jsonify({"status": "ready", "db_connected": True})
|
||
else:
|
||
return jsonify({"status": "error", "db_connected": False})
|
||
|
||
@setup_blueprint.route('/setup', methods=['GET', 'POST'])
|
||
def setup():
|
||
if request.method == 'POST':
|
||
db_config = {
|
||
"host": request.form['host'],
|
||
"port": int(request.form['port']),
|
||
"user": request.form['user'],
|
||
"password": request.form['password'],
|
||
"database": request.form['database']
|
||
}
|
||
save_config(db_config)
|
||
reset_pool()
|
||
if test_connection(db_config):
|
||
initialize_admin_user(db_config)
|
||
# Apply schema migrations immediately so all tables exist before
|
||
# the user lands on the main page.
|
||
try:
|
||
from util.db_pool import get_connection
|
||
from util.migrations import run_migrations
|
||
conn = get_connection()
|
||
try:
|
||
run_migrations(conn)
|
||
finally:
|
||
conn.close()
|
||
except Exception as e:
|
||
logger.warning(f"[setup] Post-setup migration error: {e}")
|
||
return redirect('/')
|
||
else:
|
||
return "Verbindung fehlgeschlagen. Bitte zurück und prüfen.", 500
|
||
return render_template('setup.html')
|