-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
101 lines (76 loc) · 3.52 KB
/
app.py
File metadata and controls
101 lines (76 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
Rectify — Application Factory.
Initialises the Flask application, registers blueprints, and configures
CSRF protection. Run directly for development:
python app.py
"""
from __future__ import annotations
from pathlib import Path
from flask import Flask
from flask_wtf.csrf import CSRFProtect
from werkzeug.middleware.proxy_fix import ProxyFix
from flask_talisman import Talisman
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import config
csrf = CSRFProtect()
talisman = Talisman()
limiter = Limiter(key_func=get_remote_address)
def create_app() -> Flask:
"""Build and configure the Flask application instance.
Returns:
A fully configured ``Flask`` application ready to serve.
"""
app = Flask(__name__)
# ── Load configuration ────────────────────────────────────────────────
app.config.from_object(config)
# Apply configuration specifically mapped
app.config["UPLOAD_FOLDER"] = str(config.UPLOAD_FOLDER)
# ── Ensure upload directory exists ────────────────────────────────────
Path(app.config["UPLOAD_FOLDER"]).mkdir(parents=True, exist_ok=True)
# ── Proxy support ────────────────────────────────────────────────────
# Trust reverse proxy headers when behind Nginx/Traefik
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)
# ── Security Extensions ──────────────────────────────────────────────
csrf.init_app(app)
limiter.init_app(app)
# Determine exact Content-Security-Policy
csp = {
'default-src': ["'self'"],
'style-src': [
"'self'",
"'unsafe-inline'", # Cropper.js / inline Vue/Alpine styles sometimes require this
],
'font-src': [
"'self'",
"data:" # allow localized fontawesome woff2 inline or static fonts
],
'script-src': [
"'self'",
"'unsafe-inline'", # Allowed for small inline initializations
],
'img-src': ["'self'", "data:", "blob:"],
}
# Disable HTTPS enforcement (HSTS/Secure cookies) in Local Dev
force_https = (config.APP_ENV == "production")
talisman.init_app(
app,
force_https=force_https,
content_security_policy=csp,
strict_transport_security=force_https,
session_cookie_secure=force_https,
)
# ── Register blueprints ──────────────────────────────────────────────
from routes.views import views_bp
from routes.api import api_bp
app.register_blueprint(views_bp)
app.register_blueprint(api_bp)
# ── Start cleanup daemon ─────────────────────────────────────────────
import threading
from cleanup_service import run_loop
cleanup_thread = threading.Thread(target=run_loop, name="cleanup-daemon", daemon=True)
cleanup_thread.start()
return app
if __name__ == "__main__":
application = create_app()
application.run(debug=True, host="127.0.0.1", port=5000)