import os import sys import time as _time if __name__ != '__main__': sys.path.append(os.path.dirname(__file__)) from flask import Flask, send_from_directory, redirect, abort from util.logger import logger from util.db_config import is_configured, load_config, test_connection from util.setup_routes import setup_blueprint from util.limiter import limiter from auth import auth_bp from tools import ( md5_blueprint, hasher_blueprint, base64_blueprint, jwt_decoder_blueprint, passwordgen_blueprint, timestamp_blueprint, textdiff_blueprint, qrcode_blueprint, markdown_blueprint, color_blueprint, json_formatter_blueprint, regex_blueprint, hashverifier_blueprint, url_blueprint, stringutils_blueprint, cron_blueprint, ipcalc_blueprint, lorem_blueprint, csv_blueprint, notes_blueprint, ) from admin import admin_bp app = Flask(__name__, template_folder="templates") limiter.init_app(app) app.register_blueprint(setup_blueprint) app.register_blueprint(auth_bp) app.register_blueprint(md5_blueprint) app.register_blueprint(hasher_blueprint) app.register_blueprint(base64_blueprint) app.register_blueprint(jwt_decoder_blueprint) app.register_blueprint(passwordgen_blueprint) app.register_blueprint(timestamp_blueprint) app.register_blueprint(textdiff_blueprint) app.register_blueprint(qrcode_blueprint) app.register_blueprint(markdown_blueprint) app.register_blueprint(color_blueprint) app.register_blueprint(json_formatter_blueprint) app.register_blueprint(regex_blueprint) app.register_blueprint(hashverifier_blueprint) app.register_blueprint(url_blueprint) app.register_blueprint(stringutils_blueprint) app.register_blueprint(cron_blueprint) app.register_blueprint(ipcalc_blueprint) app.register_blueprint(lorem_blueprint) app.register_blueprint(csv_blueprint) app.register_blueprint(notes_blueprint) app.register_blueprint(admin_bp) # Cache DB liveness check so we don't open a new TCP connection on every page load. _db_check = {"ok": False, "ts": 0.0} _DB_CHECK_TTL = 30.0 # seconds def _is_db_ready(): now = _time.monotonic() if now - _db_check["ts"] > _DB_CHECK_TTL: _db_check["ok"] = is_configured() and bool(test_connection(load_config())) _db_check["ts"] = now return _db_check["ok"] @app.route('/', defaults={'path': ''}) @app.route('/') def serve_frontend(path): # Unmatched API / setup paths get a clean 404 before any DB work. if path.startswith('api') or path.startswith('setup'): abort(404) if not _is_db_ready(): return redirect('/setup') dist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'frontend', 'dist')) file_path = os.path.join(dist_dir, path) if path and os.path.exists(file_path): return send_from_directory(dist_dir, path) return send_from_directory(dist_dir, 'index.html') def _run_startup_migrations(): """Run pending DB migrations if the database is already configured.""" if not is_configured(): logger.info("[startup] DB not yet configured — skipping migrations.") return 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.error(f"[startup] Migration error: {e}") _run_startup_migrations() if __name__ == '__main__': os.makedirs("config", exist_ok=True) app.run(host='0.0.0.0', port=5000, debug=True)