96 lines
3.0 KiB
Python
96 lines
3.0 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import secrets
|
|
from datetime import date, timedelta
|
|
from pathlib import Path
|
|
|
|
from flask import Flask, g, send_from_directory
|
|
|
|
from . import db
|
|
from .admin import admin_bp
|
|
from .auth import auth_bp
|
|
from .constants import (
|
|
CATEGORIES,
|
|
DAYPARTS,
|
|
ITEM_KIND_LABELS,
|
|
ITEM_KIND_SINGULAR_LABELS,
|
|
ROLE_LABELS,
|
|
VISIBILITY_DESCRIPTIONS,
|
|
VISIBILITY_LABELS,
|
|
)
|
|
from .main import main_bp
|
|
|
|
|
|
WEEKDAY_NAMES = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
|
|
WEEKDAY_SHORT_NAMES = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
|
|
|
|
|
|
def load_secret_key(data_dir: Path) -> str:
|
|
env_secret = os.environ.get("NOURI_SECRET_KEY")
|
|
if env_secret:
|
|
return env_secret
|
|
|
|
secret_path = data_dir / ".secret_key"
|
|
if secret_path.exists():
|
|
return secret_path.read_text(encoding="utf-8").strip()
|
|
|
|
secret_value = secrets.token_hex(24)
|
|
try:
|
|
with secret_path.open("x", encoding="utf-8") as handle:
|
|
handle.write(secret_value)
|
|
except FileExistsError:
|
|
return secret_path.read_text(encoding="utf-8").strip()
|
|
return secret_value
|
|
|
|
|
|
def create_app() -> Flask:
|
|
root_dir = Path(__file__).resolve().parent.parent
|
|
data_dir = Path(os.environ.get("NOURI_DATA_DIR", root_dir / "data")).resolve()
|
|
upload_dir = data_dir / "uploads"
|
|
db_path = data_dir / "nouri.sqlite3"
|
|
|
|
data_dir.mkdir(parents=True, exist_ok=True)
|
|
upload_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
app = Flask(__name__, instance_relative_config=False)
|
|
app.config.update(
|
|
SECRET_KEY=load_secret_key(data_dir),
|
|
DATABASE_PATH=str(db_path),
|
|
DATA_DIR=str(data_dir),
|
|
UPLOAD_FOLDER=str(upload_dir),
|
|
MAX_CONTENT_LENGTH=int(os.environ.get("NOURI_MAX_UPLOAD_MB", "5")) * 1024 * 1024,
|
|
PERMANENT_SESSION_LIFETIME=timedelta(days=30),
|
|
SESSION_COOKIE_HTTPONLY=True,
|
|
SESSION_COOKIE_SAMESITE="Lax",
|
|
)
|
|
|
|
db.init_app(app)
|
|
db.init_db_if_needed(app)
|
|
|
|
app.register_blueprint(auth_bp)
|
|
app.register_blueprint(admin_bp)
|
|
app.register_blueprint(main_bp)
|
|
|
|
@app.context_processor
|
|
def inject_globals() -> dict[str, object]:
|
|
return {
|
|
"item_kind_labels": ITEM_KIND_LABELS,
|
|
"item_kind_singular_labels": ITEM_KIND_SINGULAR_LABELS,
|
|
"category_suggestions": CATEGORIES,
|
|
"daypart_suggestions": DAYPARTS,
|
|
"visibility_labels": VISIBILITY_LABELS,
|
|
"visibility_descriptions": VISIBILITY_DESCRIPTIONS,
|
|
"role_labels": ROLE_LABELS,
|
|
"today": date.today(),
|
|
"weekday_name": lambda value: WEEKDAY_NAMES[value.weekday()],
|
|
"weekday_short_name": lambda value: WEEKDAY_SHORT_NAMES[value.weekday()],
|
|
"is_admin": lambda: bool(getattr(g, "user", None)) and g.user["role"] == "admin",
|
|
}
|
|
|
|
@app.get("/uploads/<path:filename>")
|
|
def uploaded_file(filename: str):
|
|
return send_from_directory(app.config["UPLOAD_FOLDER"], filename)
|
|
|
|
return app
|