Source code for server
import server.serverlogging as serverlogging
import logging
from flask import Flask
from flask_socketio import SocketIO
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager
from server.resources import TempdirManager, SocketioConnections
from server.proxy_helpers import behind_proxy_options
import time
slacklog = logging.getLogger("slack")
# Info: slacklog sends messages to a specified Slack channel. Usage:
#
# slacklog.info("Hello Slack")
# slacklog.info("Sending log file", extra=dict(file=serverlogging.LOG_DIR / "server.log"))
#
socketio = SocketIO(async_mode="eventlet")
db = SQLAlchemy()
jwt = JWTManager()
tempdirs_devices = TempdirManager()
connections_users = SocketioConnections(multiple_connections=True)
connections_devices = SocketioConnections(multiple_connections=False)
start_time = time.time()
[docs]
def create_app(config_override={}):
warnings = serverlogging.configure()
app = Flask(
"server",
static_folder="../plasmafront/build/static",
template_folder="../plasmafront/build",
)
# Configuration: Load from different sources in a specific order
app.config.from_pyfile("config.py")
app.config.from_prefixed_env(prefix="FLASK")
app.config.update(config_override)
# Configuration: Override with contents of a file specified by a *_FILE var
for var_from_file in ["SECRET_KEY_FILE"]:
if var_from_file in app.config:
with open(app.config[var_from_file]) as file:
app.config[var_from_file[:-len("_FILE")]] = file.read()
# Start message and verify that the configuration is ok
if app.env == "production":
app.logger.info(16 * "=" + " New instance started " + 16 * "=")
for w in warnings:
app.logger.warning(w)
slacklog.info("*Server has started* %s", time.strftime("%d.%m.%Y %H:%M:%S"))
check_secrets(app)
if app.config.get("BEHIND_PROXY") not in behind_proxy_options:
raise KeyError("Unknown proxy service set in the BEHIND_PROXY config")
# Register all blueprints
from .views_main import blueprints as main_blueprints
for bp in main_blueprints:
app.register_blueprint(bp)
from .views_api import blueprints as api_blueprints
for bp in api_blueprints:
app.register_blueprint(bp)
from .events import blueprints as events_blueprints
for bp in events_blueprints:
app.register_blueprint(bp)
# Init everything (app, database, ...)
app.logger.info("Database: %s", app.config["SQLALCHEMY_DATABASE_URI"])
socketio.init_app(app)
db.init_app(app)
jwt.init_app(app)
with app.app_context():
from .models import User
from .blacklist_helpers import prune_database
db.create_all()
# If database is empty, create user "admin" and more
if not User.query.all():
populate_database()
prune_database()
return app
[docs]
def check_secrets(app):
if app.config["SECRET_KEY"] is None:
app.logger.fatal("Missing SECRET_KEY env variable.")
raise RuntimeError("Missing SECRET_KEY env variable.")
for key in ["SECRET_KEY", "JWT_SECRET_KEY"]:
if app.config.get(key) == "secret!":
app.logger.fatal("Cannot use default value of %s in production."
" Please set a %s env variable.", key, key)
raise RuntimeError(f"Using default {key} when in production mode!")
[docs]
def populate_database():
"""Initial commits to a new empty database."""
import time
from .models import Device
from .functions import submit_user, submit_device, submit_experiment, submit_session
admin = submit_user("admin", "admin", "admin", "")
submit_user("student", "student", "user", "")
submit_device("test", "test", "127.0.0.1")
devices = Device.query.all()
exper = submit_experiment("All", devices)
now_ms = int(time.time()) * 1e3
submit_session("All", now_ms, now_ms + 3600*24*30*1e3, exper, [admin]) # 1 month
[docs]
def run_server(config={}, server_kwargs={}):
app = create_app(config)
host = {"production": "0.0.0.0", "development": "127.0.0.1"}[app.env]
socketio.run(
app,
host=app.config["LISTEN_HOST"] or host,
port=app.config["LISTEN_PORT"] or 5000,
**server_kwargs
)