NexusDash-izebra/app/__init__.py
izebra a3c4b027af
All checks were successful
Docker Build / build (push) Successful in 42s
Forced MAIL_USE_SSL to be false every time
2024-01-03 14:19:25 -08:00

364 lines
12 KiB
Python

import os
from flask import Flask, url_for, g, redirect
from functools import wraps
from flask_assets import Environment
from webassets import Bundle
import time
from app.models import db, migrate, PlayKey
from app.forms import CustomUserManager
from flask_user import user_registered, current_user, user_logged_in
from flask_wtf.csrf import CSRFProtect
from flask_apscheduler import APScheduler
from app.luclient import register_luclient_jinja_helpers
import pathlib
from app.commands import (
init_db,
init_accounts,
load_property,
gen_image_cache,
gen_model_cache,
fix_clone_ids,
remove_buffs
)
from app.models import Account, AccountInvitation, AuditLog
import logging
from logging.handlers import RotatingFileHandler
from werkzeug.exceptions import HTTPException
# Instantiate Flask extensions
csrf_protect = CSRFProtect()
scheduler = APScheduler()
# db and migrate is instantiated in models.py
def create_app():
app = Flask(__name__, instance_relative_config=True)
# decrement uses on a play key after a successful registration
# and increment the times it has been used
@user_registered.connect_via(app)
def after_register_hook(sender, user, **extra):
if app.config["REQUIRE_PLAY_KEY"]:
play_key_used = PlayKey.query.filter(PlayKey.id == user.play_key_id).first()
play_key_used.key_uses = play_key_used.key_uses - 1
play_key_used.times_used = play_key_used.times_used + 1
app.logger.info(
f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered \
using Play Key ID {play_key_used.id} : {play_key_used.key_string}"
)
db.session.add(play_key_used)
db.session.commit()
else:
app.logger.info(f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered")
@user_logged_in.connect_via(app)
def _after_login_hook(sender, user, **extra):
app.logger.info(f"{user.username} Logged in")
# A bunch of jinja filters to make things easiers
@app.template_filter('ctime')
def timectime(s):
if s:
return time.ctime(s) # or datetime.datetime.fromtimestamp(s)
else:
return "Never"
@app.template_filter('check_perm_map')
def check_perm_map(perm_map, bit):
if perm_map:
return perm_map & (1 << bit)
else:
return 0 & (1 << bit)
@app.template_filter('debug')
def debug(text):
print(text)
@app.teardown_appcontext
def close_connection(exception):
cdclient = getattr(g, '_cdclient', None)
if cdclient is not None:
cdclient.close()
@app.template_filter()
def numberFormat(value):
return format(int(value), ',d')
# add the commands to flask cli
app.cli.add_command(init_db)
app.cli.add_command(init_accounts)
app.cli.add_command(load_property)
app.cli.add_command(gen_image_cache)
app.cli.add_command(gen_model_cache)
app.cli.add_command(fix_clone_ids)
app.cli.add_command(remove_buffs)
register_logging(app)
register_settings(app)
register_extensions(app)
register_blueprints(app)
register_luclient_jinja_helpers(app)
# Extract the brickdb if it's not already extracted
materials = pathlib.Path(f'{app.config["CACHE_LOCATION"]}Materials.xml')
if not materials.is_file():
# unzip the brickdb, and remove the import after
from zipfile import ZipFile
with ZipFile(f"{app.config['CLIENT_LOCATION']}res/brickdb.zip","r") as zip_ref:
zip_ref.extractall(app.config["CACHE_LOCATION"])
del ZipFile
# copy over the brick primitives, and remove the import after
from shutil import copytree
copytree(f"{app.config['CLIENT_LOCATION']}res/brickprimitives", f"{app.config['CACHE_LOCATION']}brickprimitives")
del copytree
return app
def register_extensions(app):
"""Register extensions for Flask app
Args:
app (Flask): Flask app to register for
"""
db.init_app(app)
migrate.init_app(app, db)
scheduler.init_app(app)
scheduler.start()
csrf_protect.init_app(app)
user_manager = CustomUserManager(
app, db, Account, UserInvitationClass=AccountInvitation
)
assets = Environment(app)
assets.url = app.static_url_path
scss = Bundle('scss/site.scss', filters='libsass', output='css/site.css')
assets.register('scss_all', scss)
def register_blueprints(app):
"""Register blueprints for Flask app
Args:
app (Flask): Flask app to register for
"""
from .main import main_blueprint
app.register_blueprint(main_blueprint)
from .play_keys import play_keys_blueprint
app.register_blueprint(play_keys_blueprint, url_prefix='/play_keys')
from .accounts import accounts_blueprint
app.register_blueprint(accounts_blueprint, url_prefix='/accounts')
from .characters import character_blueprint
app.register_blueprint(character_blueprint, url_prefix='/characters')
from .properties import property_blueprint
app.register_blueprint(property_blueprint, url_prefix='/properties')
from .moderation import moderation_blueprint
app.register_blueprint(moderation_blueprint, url_prefix='/moderation')
from .log import log_blueprint
app.register_blueprint(log_blueprint, url_prefix='/log')
from .bug_reports import bug_report_blueprint
app.register_blueprint(bug_report_blueprint, url_prefix='/bug_reports')
from .mail import mail_blueprint
app.register_blueprint(mail_blueprint, url_prefix='/mail')
from .luclient import luclient_blueprint
app.register_blueprint(luclient_blueprint, url_prefix='/luclient')
from .reports import reports_blueprint
app.register_blueprint(reports_blueprint, url_prefix='/reports')
from .api import api_blueprint
app.register_blueprint(api_blueprint, url_prefix='/api')
def register_logging(app):
# file logger
file_handler = RotatingFileHandler('logs/nexus_dashboard.log', maxBytes=1024 * 1024 * 100, backupCount=20)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
file_handler.setFormatter(formatter)
app.logger.addHandler(file_handler)
def register_settings(app):
"""Register setting from setting and env
Args:
app (Flask): Flask app to register for
"""
# Load common settings
try:
app.config.from_object('app.settings')
except Exception:
app.logger.info("No settings.py, loading from example")
app.config.from_object('app.settings_example')
# Load environment specific settings
app.config['TESTING'] = False
app.config['DEBUG'] = False
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
"pool_pre_ping": True,
"pool_size": 10,
"max_overflow": 2,
"pool_recycle": 300,
"pool_pre_ping": True,
"pool_use_lifo": True
}
app.config['SECRET_KEY'] = os.getenv(
'APP_SECRET_KEY',
app.config['APP_SECRET_KEY']
)
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv(
'APP_DATABASE_URI',
app.config['APP_DATABASE_URI']
)
app.config['USER_ENABLE_REGISTER'] = os.getenv(
'USER_ENABLE_REGISTER',
app.config['USER_ENABLE_REGISTER']
)
app.config['USER_ENABLE_EMAIL'] = os.getenv(
'USER_ENABLE_EMAIL',
app.config['USER_ENABLE_EMAIL']
)
app.config['USER_ENABLE_CONFIRM_EMAIL'] = os.getenv(
'USER_ENABLE_CONFIRM_EMAIL',
app.config['USER_ENABLE_CONFIRM_EMAIL']
)
app.config['REQUIRE_PLAY_KEY'] = os.getenv(
'REQUIRE_PLAY_KEY',
app.config['REQUIRE_PLAY_KEY']
)
app.config['USER_ENABLE_INVITE_USER'] = os.getenv(
'USER_ENABLE_INVITE_USER',
app.config['USER_ENABLE_INVITE_USER']
)
app.config['USER_REQUIRE_INVITATION'] = os.getenv(
'USER_REQUIRE_INVITATION',
app.config['USER_REQUIRE_INVITATION']
)
app.config['MAIL_SERVER'] = os.getenv(
'MAIL_SERVER',
app.config['MAIL_SERVER']
)
app.config['MAIL_PORT'] = int(
os.getenv(
'MAIL_PORT',
app.config['MAIL_PORT']
)
)
app.config['MAIL_USE_SSL'] = False
app.config['MAIL_USE_TLS'] = os.getenv(
'MAIL_USE_TLS',
app.config['MAIL_USE_TLS']
)
app.config['MAIL_USERNAME'] = os.getenv(
'MAIL_USERNAME',
app.config['MAIL_USERNAME']
)
app.config['MAIL_PASSWORD'] = os.getenv(
'MAIL_PASSWORD',
app.config['MAIL_PASSWORD']
)
app.config['USER_EMAIL_SENDER_NAME'] = os.getenv(
'USER_EMAIL_SENDER_NAME',
app.config['USER_EMAIL_SENDER_NAME']
)
app.config['USER_EMAIL_SENDER_EMAIL'] = os.getenv(
'USER_EMAIL_SENDER_EMAIL',
app.config['USER_EMAIL_SENDER_EMAIL']
)
if "ENABLE_CHAR_XML_UPLOAD" not in app.config:
app.config['ENABLE_CHAR_XML_UPLOAD'] = False
app.config['ENABLE_CHAR_XML_UPLOAD'] = os.getenv(
'ENABLE_CHAR_XML_UPLOAD',
app.config['ENABLE_CHAR_XML_UPLOAD']
)
if "CLIENT_LOCATION" not in app.config:
app.config['CLIENT_LOCATION'] = 'app/luclient/'
app.config['CLIENT_LOCATION'] = os.getenv(
'CLIENT_LOCATION',
app.config['CLIENT_LOCATION']
)
if "CD_SQLITE_LOCATION" not in app.config:
app.config['CD_SQLITE_LOCATION'] = 'app/luclient/res/'
app.config['CD_SQLITE_LOCATION'] = os.getenv(
'CD_SQLITE_LOCATION',
app.config['CD_SQLITE_LOCATION']
)
if "CACHE_LOCATION" not in app.config:
app.config['CACHE_LOCATION'] = 'app/cache/'
app.config['CACHE_LOCATION'] = os.getenv(
'CACHE_LOCATION',
app.config['CACHE_LOCATION']
)
# Recaptcha settings
if "RECAPTCHA_ENABLE" not in app.config:
app.config['RECAPTCHA_ENABLE'] = False
app.config['RECAPTCHA_ENABLE'] = os.getenv(
'RECAPTCHA_ENABLE',
app.config['RECAPTCHA_ENABLE']
)
if "RECAPTCHA_PUBLIC_KEY" not in app.config:
app.config['RECAPTCHA_PUBLIC_KEY'] = ''
app.config['RECAPTCHA_PUBLIC_KEY'] = os.getenv(
'RECAPTCHA_PUBLIC_KEY',
app.config['RECAPTCHA_PUBLIC_KEY']
)
if "RECAPTCHA_PRIVATE_KEY" not in app.config:
app.config['RECAPTCHA_PRIVATE_KEY'] = ''
app.config['RECAPTCHA_PRIVATE_KEY'] = os.getenv(
'RECAPTCHA_PRIVATE_KEY',
app.config['RECAPTCHA_PRIVATE_KEY']
)
# Optional
if "RECAPTCHA_API_SERVER" in app.config:
app.config['RECAPTCHA_API_SERVER'] = os.getenv(
'RECAPTCHA_API_SERVER',
app.config['RECAPTCHA_API_SERVER']
)
if "RECAPTCHA_PARAMETERS" in app.config:
app.config['RECAPTCHA_PARAMETERS'] = os.getenv(
'RECAPTCHA_PARAMETERS',
app.config['RECAPTCHA_PARAMETERS']
)
if "RECAPTCHA_DATA_ATTRS" in app.config:
app.config['RECAPTCHA_DATA_ATTRS'] = os.getenv(
'RECAPTCHA_DATA_ATTRS',
app.config['RECAPTCHA_DATA_ATTRS']
)
def gm_level(gm_level):
"""Decorator for handling permissions based on the user's GM Level
Args:
gm_level (int): 0-9
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if current_user.gm_level < gm_level:
return redirect(url_for('main.index'))
return func(*args, **kwargs)
return wrapper
return decorator
def log_audit(message):
AuditLog(
account_id=current_user.id,
action=message
).save()