Files
adlist_mikrotik/app.py
T
2026-10-09 10:53:10 +02:00

69 lines
2.5 KiB
Python

"""WSGI / ASGI application entrypoint. Gunicorn target stays app:asgi_app.
Routes, caching, validation, and Redis statistics live in the adlist package.
"""
import hashlib
from pathlib import Path
from flask import Flask
from flask_compress import Compress
from flask_limiter import Limiter
from asgiref.wsgi import WsgiToAsgi
import config
from adlist import services, pages, converter, metrics
app = Flask(__name__)
app.config.update(MAX_CONTENT_LENGTH=config.MAX_CONTENT_LENGTH, SECRET_KEY=config.SECRET_KEY)
app.debug = config.FLASK_DEBUG
limiter = Limiter(
key_func=services.get_client_ip,
app=app,
default_limits=[config.RATE_LIMIT_DEFAULT],
storage_uri=config.REDIS_URL,
)
Compress(app)
app.before_request(services.track_request_data)
app.after_request(services.finalize_response)
app.add_template_filter(services.datetimeformat_filter, "datetimeformat")
# Fingerprinting keeps long-lived cache safe for upgraded CSS / JS assets.
def _asset_revision():
root = Path(__file__).resolve().parent / "static"
digest = hashlib.sha256()
for asset in sorted(root.rglob("*.css")) + sorted(root.rglob("*.js")):
digest.update(asset.relative_to(root).as_posix().encode("utf-8"))
digest.update(asset.read_bytes())
return digest.hexdigest()[:12]
ASSET_REVISION = _asset_revision()
@app.context_processor
def inject_common():
return {"asset_revision": ASSET_REVISION}
app.add_url_rule("/", "index", pages.index, methods=["GET"])
app.add_url_rule("/favicon.ico", "favicon", pages.favicon, methods=["GET"])
app.add_url_rule("/convert", "convert", limiter.limit(config.RATE_LIMIT_CONVERT)(converter.convert), methods=["GET", "HEAD"])
for name, func in (("stats", metrics.stats), ("stats_json", metrics.stats_json)):
wrapped = services.basic_auth_required(
realm=config.STATS_BASIC_AUTH_REALM,
user=config.STATS_BASIC_AUTH_USER,
password=config.STATS_BASIC_AUTH_PASS,
)(func)
app.add_url_rule("/" + ("stats" if name == "stats" else "stats.json"), name, wrapped, methods=["GET"])
for code in (400, 403, 404, 413, 415, 422, 429, 500, 502):
app.register_error_handler(code, pages.handle_errors)
# Compatibility: integrations refer to app:app or app:asgi_app.
asgi_app = WsgiToAsgi(app)
convert = converter.convert
stats = metrics.stats
stats_json = metrics.stats_json
redis_client = services.redis_client
cache_headers = converter.cache_headers
if __name__ == "__main__":
app.run(host=config.BIND_HOST, port=config.BIND_PORT)