Files
MIND/MIND.py
2024-02-28 13:48:24 +01:00

168 lines
4.3 KiB
Python

#!/usr/bin/env python3
#-*- coding: utf-8 -*-
"""
The main file where MIND is started from
"""
import logging
from os import execv, makedirs, urandom
from os.path import dirname, isfile
from shutil import move
from sys import argv
from typing import Union
from flask import Flask, render_template, request
from waitress.server import create_server
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from backend.db import (DBConnection, ThreadedTaskDispatcher, close_db,
revert_db_import, setup_db)
from backend.helpers import RestartVars, check_python_version, folder_path
from backend.reminders import ReminderHandler
from frontend.api import (APIVariables, admin_api, admin_api_prefix, api,
api_prefix, revert_db_thread)
from frontend.ui import UIVariables, ui
HOST = '0.0.0.0'
PORT = '8080'
URL_PREFIX = '' # Must either be empty or start with '/' e.g. '/mind'
LOGGING_LEVEL = logging.INFO
THREADS = 10
DB_FILENAME = 'db', 'MIND.db'
UIVariables.url_prefix = URL_PREFIX
logging.basicConfig(
level=LOGGING_LEVEL,
format='[%(asctime)s][%(threadName)s][%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def _create_app() -> Flask:
"""Create a Flask app instance
Returns:
Flask: The created app instance
"""
app = Flask(
__name__,
template_folder=folder_path('frontend','templates'),
static_folder=folder_path('frontend','static'),
static_url_path='/static'
)
app.config['SECRET_KEY'] = urandom(32)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
app.config['JSON_SORT_KEYS'] = False
app.config['APPLICATION_ROOT'] = URL_PREFIX
app.wsgi_app = DispatcherMiddleware(
Flask(__name__),
{URL_PREFIX: app.wsgi_app}
)
# Add error handlers
@app.errorhandler(400)
def bad_request(e):
return {'error': 'Bad request', 'result': {}}, 400
@app.errorhandler(405)
def method_not_allowed(e):
return {'error': 'Method not allowed', 'result': {}}, 405
@app.errorhandler(500)
def internal_error(e):
return {'error': 'Internal error', 'result': {}}, 500
@app.errorhandler(404)
def not_found(e):
if request.path.startswith(api_prefix):
return {'error': 'Not Found', 'result': {}}, 404
return render_template('page_not_found.html', url_prefix=UIVariables.url_prefix)
app.register_blueprint(ui)
app.register_blueprint(api, url_prefix=api_prefix)
app.register_blueprint(admin_api, url_prefix=admin_api_prefix)
# Setup closing database
app.teardown_appcontext(close_db)
return app
def _handle_flags(flag: Union[None, str]) -> None:
"""Run flag specific actions on startup.
Args:
flag (Union[None, str]): The flag or `None` if there is no flag set.
"""
if flag == RestartVars.DB_IMPORT:
logging.info('Starting timer for database import')
revert_db_thread.start()
return
def _handle_flags_pre_restart(flag: Union[None, str]) -> None:
"""Run flag specific actions just before restarting.
Args:
flag (Union[None, str]): The flag or `None` if there is no flag set.
"""
if flag == RestartVars.DB_IMPORT:
revert_db_import(swap=True)
return
def MIND() -> None:
"""The main function of MIND
"""
logging.info('Starting up MIND')
if not check_python_version():
exit(1)
flag = argv[1] if len(argv) > 1 else None
_handle_flags(flag)
if isfile(folder_path('db', 'Noted.db')):
move(folder_path('db', 'Noted.db'), folder_path(*DB_FILENAME))
db_location = folder_path(*DB_FILENAME)
logging.debug(f'Database location: {db_location}')
makedirs(dirname(db_location), exist_ok=True)
DBConnection.file = db_location
app = _create_app()
reminder_handler = ReminderHandler(app.app_context)
with app.app_context():
setup_db()
reminder_handler.find_next_reminder()
# Create waitress server and run
dispatcher = ThreadedTaskDispatcher()
dispatcher.set_thread_count(THREADS)
server = create_server(
app,
_dispatcher=dispatcher,
host=HOST,
port=PORT,
threads=THREADS
)
APIVariables.server_instance = server
logging.info(f'MIND running on http://{HOST}:{PORT}{URL_PREFIX}')
# =================
server.run()
# =================
reminder_handler.stop_handling()
if APIVariables.restart:
if APIVariables.handle_flags:
_handle_flags_pre_restart(flag)
logging.info('Restarting MIND')
execv(__file__, [argv[0], *APIVariables.restart_args])
return
if __name__ == "__main__":
MIND()