2020-01-24 15:38:03 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2022-04-19 21:31:40 +02:00
|
|
|
"""Main Haldis script"""
|
|
|
|
|
2015-04-02 12:41:19 +02:00
|
|
|
import logging
|
2022-10-27 19:38:46 +02:00
|
|
|
import sentry_sdk
|
2019-09-08 01:58:21 +02:00
|
|
|
import typing
|
2019-08-28 03:46:04 +02:00
|
|
|
from datetime import datetime
|
2022-04-19 22:03:00 +02:00
|
|
|
from logging.handlers import TimedRotatingFileHandler
|
2019-08-28 03:46:04 +02:00
|
|
|
|
2022-04-19 22:03:00 +02:00
|
|
|
from admin import init_admin
|
2022-10-27 19:38:46 +02:00
|
|
|
from config import Configuration
|
2022-10-27 19:29:50 +02:00
|
|
|
from flask import Flask, render_template, Response
|
2017-01-06 12:05:31 +01:00
|
|
|
from flask_bootstrap import Bootstrap, StaticCDN
|
2015-06-09 22:24:48 +02:00
|
|
|
from flask_debugtoolbar import DebugToolbarExtension
|
2019-08-28 03:46:04 +02:00
|
|
|
from flask_login import LoginManager
|
|
|
|
from flask_migrate import Migrate, MigrateCommand
|
2019-09-03 13:56:13 +02:00
|
|
|
from flask_script import Manager, Server
|
2022-04-19 22:03:00 +02:00
|
|
|
from markupsafe import Markup
|
2022-04-20 01:34:19 +02:00
|
|
|
|
2023-04-19 22:03:40 +02:00
|
|
|
from admin import init_admin
|
|
|
|
from auth.login import init_login
|
|
|
|
from auth.zeus import init_oauth
|
2022-04-20 01:34:19 +02:00
|
|
|
from config import Configuration
|
2019-08-28 03:46:04 +02:00
|
|
|
from models import db
|
|
|
|
from models.anonymous_user import AnonymouseUser
|
2022-10-27 19:38:46 +02:00
|
|
|
from sentry_sdk.integrations.flask import FlaskIntegration
|
2022-05-11 02:43:08 +02:00
|
|
|
from utils import euro_string, price_range_string, ignore_none
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
def register_plugins(app: Flask) -> Manager:
|
|
|
|
"""Register the plugins to the app"""
|
2019-09-10 15:17:35 +02:00
|
|
|
# pylint: disable=W0612
|
2022-04-19 23:59:23 +02:00
|
|
|
if not app.debug:
|
2019-09-05 03:33:29 +02:00
|
|
|
timedFileHandler = TimedRotatingFileHandler(
|
2022-04-19 23:59:23 +02:00
|
|
|
app.config["LOGFILE"], when="midnight", backupCount=100
|
2019-09-05 03:33:29 +02:00
|
|
|
)
|
2019-08-28 03:46:04 +02:00
|
|
|
timedFileHandler.setLevel(logging.DEBUG)
|
|
|
|
|
2019-09-05 03:33:29 +02:00
|
|
|
loglogger = logging.getLogger("werkzeug")
|
2019-08-28 03:46:04 +02:00
|
|
|
loglogger.setLevel(logging.DEBUG)
|
|
|
|
loglogger.addHandler(timedFileHandler)
|
2022-04-19 23:59:23 +02:00
|
|
|
app.logger.addHandler(timedFileHandler)
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# Initialize SQLAlchemy
|
2022-04-19 23:59:23 +02:00
|
|
|
db.init_app(app)
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# Initialize Flask-Migrate
|
2022-04-19 23:59:23 +02:00
|
|
|
migrate = Migrate(app, db)
|
|
|
|
app_manager = Manager(app)
|
|
|
|
app_manager.add_command("db", MigrateCommand)
|
|
|
|
app_manager.add_command("runserver", Server(port=8000))
|
|
|
|
init_admin(app, db)
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# Init login manager
|
|
|
|
login_manager = LoginManager()
|
2022-04-19 23:59:23 +02:00
|
|
|
login_manager.init_app(app)
|
2019-08-28 03:46:04 +02:00
|
|
|
login_manager.anonymous_user = AnonymouseUser
|
2022-04-19 23:59:23 +02:00
|
|
|
init_login(app)
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# Add oauth
|
2022-04-19 23:59:23 +02:00
|
|
|
zeus = init_oauth(app)
|
|
|
|
app.zeus = zeus
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# Load the bootstrap local cdn
|
2022-04-19 23:59:23 +02:00
|
|
|
Bootstrap(app)
|
|
|
|
app.config["BOOTSTRAP_SERVE_LOCAL"] = True
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# use our own bootstrap theme
|
2022-04-19 23:59:23 +02:00
|
|
|
app.extensions["bootstrap"]["cdns"]["bootstrap"] = StaticCDN()
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
# Load the flask debug toolbar
|
2022-04-19 23:59:23 +02:00
|
|
|
toolbar = DebugToolbarExtension(app)
|
2019-08-28 03:46:04 +02:00
|
|
|
|
2019-09-11 16:43:42 +02:00
|
|
|
# Make cookies more secure
|
2022-04-19 23:59:23 +02:00
|
|
|
app.config.update(
|
2022-04-19 22:03:00 +02:00
|
|
|
SESSION_COOKIE_HTTPONLY=True,
|
|
|
|
SESSION_COOKIE_SAMESITE="Lax",
|
2019-09-11 16:43:42 +02:00
|
|
|
)
|
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
if not app.debug:
|
|
|
|
app.config.update(SESSION_COOKIE_SECURE=True)
|
2019-09-11 16:43:42 +02:00
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
return app_manager
|
2019-08-28 03:46:04 +02:00
|
|
|
|
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
def add_handlers(app: Flask) -> None:
|
|
|
|
"""Add handlers for 4xx error codes"""
|
|
|
|
|
2019-09-10 15:17:35 +02:00
|
|
|
# pylint: disable=W0612,W0613
|
2022-04-19 23:59:23 +02:00
|
|
|
@app.errorhandler(404)
|
2019-09-08 01:58:21 +02:00
|
|
|
def handle404(e) -> typing.Tuple[str, int]:
|
2019-09-05 03:33:29 +02:00
|
|
|
return render_template("errors/404.html"), 404
|
2015-06-09 16:47:11 +02:00
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
@app.errorhandler(401)
|
2019-09-08 01:58:21 +02:00
|
|
|
def handle401(e) -> typing.Tuple[str, int]:
|
2019-09-05 03:33:29 +02:00
|
|
|
return render_template("errors/401.html"), 401
|
2015-03-31 20:15:22 +02:00
|
|
|
|
|
|
|
|
2019-09-08 01:58:21 +02:00
|
|
|
def add_routes(application: Flask) -> None:
|
2022-04-19 23:59:23 +02:00
|
|
|
"""Add all routes to Haldis"""
|
2019-08-28 03:46:04 +02:00
|
|
|
# import views # TODO convert to blueprint
|
|
|
|
# import views.stats # TODO convert to blueprint
|
2015-03-31 20:15:22 +02:00
|
|
|
|
2022-04-20 01:27:52 +02:00
|
|
|
from auth.login import auth_bp
|
|
|
|
from auth.microsoft import auth_microsoft_bp
|
|
|
|
from auth.zeus import auth_zeus_bp
|
2022-04-19 22:03:00 +02:00
|
|
|
from views.debug import debug_bp
|
2019-08-28 03:46:04 +02:00
|
|
|
from views.general import general_bp
|
2022-04-19 22:03:00 +02:00
|
|
|
from views.order import order_bp
|
2019-08-28 03:46:04 +02:00
|
|
|
from views.stats import stats_blueprint
|
2015-03-31 20:15:22 +02:00
|
|
|
|
2019-09-05 03:33:29 +02:00
|
|
|
application.register_blueprint(general_bp, url_prefix="/")
|
|
|
|
application.register_blueprint(order_bp, url_prefix="/order")
|
|
|
|
application.register_blueprint(stats_blueprint, url_prefix="/stats")
|
|
|
|
application.register_blueprint(auth_bp, url_prefix="/")
|
2022-04-20 01:34:19 +02:00
|
|
|
if Configuration.ENABLE_MICROSOFT_AUTH:
|
|
|
|
application.register_blueprint(auth_microsoft_bp,
|
|
|
|
url_prefix="/users/auth/microsoft_graph_auth") # "/auth/microsoft")
|
2022-04-20 01:27:52 +02:00
|
|
|
application.register_blueprint(auth_zeus_bp, url_prefix="/auth/zeus")
|
2015-06-09 22:24:48 +02:00
|
|
|
|
2019-09-04 17:18:11 +02:00
|
|
|
if application.debug:
|
2019-09-05 03:33:29 +02:00
|
|
|
application.register_blueprint(debug_bp, url_prefix="/debug")
|
2019-09-04 17:18:11 +02:00
|
|
|
|
2015-03-31 16:29:28 +02:00
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
def add_template_filters(app: Flask) -> None:
|
|
|
|
"""Add functions which can be used in the templates"""
|
|
|
|
|
2019-09-10 15:17:35 +02:00
|
|
|
# pylint: disable=W0612
|
2019-09-05 03:33:29 +02:00
|
|
|
@app.template_filter("countdown")
|
2020-07-17 11:40:15 +02:00
|
|
|
def countdown(
|
|
|
|
value, only_positive: bool = True, show_text: bool = True, reload: bool = True
|
|
|
|
) -> str:
|
2020-03-05 00:37:16 +01:00
|
|
|
delta = int(value.timestamp() - datetime.now().timestamp())
|
|
|
|
if delta < 0 and only_positive:
|
|
|
|
text = "closed"
|
|
|
|
else:
|
|
|
|
carry, seconds = divmod(delta, 60)
|
|
|
|
carry, minutes = divmod(carry, 60)
|
2020-07-17 11:40:15 +02:00
|
|
|
days, hours = divmod(carry, 24)
|
2020-03-05 00:37:16 +01:00
|
|
|
|
|
|
|
days_text = f"{days} days, " if days else ""
|
|
|
|
|
|
|
|
appendix = " left" if show_text else ""
|
|
|
|
text = f"{days_text}{hours:02d}:{minutes:02d}:{seconds:02d}{appendix}"
|
|
|
|
|
|
|
|
reload_str = "yes" if reload else "no"
|
|
|
|
|
2020-07-17 11:40:15 +02:00
|
|
|
return Markup(
|
|
|
|
f"<span class='time' data-seconds='{delta}' data-reload='{reload_str}'>"
|
|
|
|
+ text
|
|
|
|
+ "</span>"
|
|
|
|
)
|
2015-06-09 16:47:11 +02:00
|
|
|
|
2019-09-05 03:33:29 +02:00
|
|
|
@app.template_filter("year")
|
2020-02-29 21:56:04 +01:00
|
|
|
def current_year(_value: typing.Any) -> str:
|
2019-08-28 03:46:04 +02:00
|
|
|
return str(datetime.now().year)
|
2015-06-09 16:47:11 +02:00
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
app.template_filter("euro")(euro_string)
|
|
|
|
app.template_filter("price_range")(price_range_string)
|
|
|
|
app.template_filter("any")(any)
|
|
|
|
app.template_filter("all")(all)
|
2022-05-11 02:43:08 +02:00
|
|
|
app.template_filter("ignore_none")(ignore_none)
|
2022-04-19 23:59:23 +02:00
|
|
|
|
2020-01-27 03:01:49 +01:00
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
def create_app():
|
|
|
|
"""Initializer for the Flask app object"""
|
|
|
|
app = Flask(__name__)
|
2015-06-09 16:47:11 +02:00
|
|
|
|
2022-10-27 19:29:50 +02:00
|
|
|
@app.route('/robots.txt')
|
|
|
|
def noindex():
|
|
|
|
r = Response(response="User-Agent: *\nDisallow: /\n", status=200, mimetype="text/plain")
|
|
|
|
r.headers["Content-Type"] = "text/plain; charset=utf-8"
|
|
|
|
return r
|
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
# Load the config file
|
|
|
|
app.config.from_object("config.Configuration")
|
2020-06-22 19:22:47 +02:00
|
|
|
|
2022-04-19 23:59:23 +02:00
|
|
|
app_manager = register_plugins(app)
|
|
|
|
add_handlers(app)
|
|
|
|
add_routes(app)
|
|
|
|
add_template_filters(app)
|
2020-06-22 19:22:47 +02:00
|
|
|
|
2022-04-20 01:34:19 +02:00
|
|
|
@app.context_processor
|
|
|
|
def inject_config():
|
|
|
|
return dict(configuration=Configuration)
|
|
|
|
|
2022-05-02 18:25:54 +02:00
|
|
|
return app, app_manager
|
2020-06-22 19:22:47 +02:00
|
|
|
|
|
|
|
|
2019-08-28 03:46:04 +02:00
|
|
|
# For usage when you directly call the script with python
|
2019-09-05 03:33:29 +02:00
|
|
|
if __name__ == "__main__":
|
2022-10-27 19:47:12 +02:00
|
|
|
if Configuration.SENTRY_DSN:
|
|
|
|
sentry_sdk.init(
|
|
|
|
dsn=Configuration.SENTRY_DSN,
|
|
|
|
integrations=[FlaskIntegration()]
|
|
|
|
)
|
2022-10-27 19:38:46 +02:00
|
|
|
|
2022-05-02 18:25:54 +02:00
|
|
|
app, app_mgr = create_app()
|
2022-04-19 23:59:23 +02:00
|
|
|
app_mgr.run()
|