Files
Grinch-AP/WebHost/__init__.py

158 lines
4.9 KiB
Python
Raw Normal View History

2020-06-21 15:37:36 +02:00
"""Friendly reminder that if you want to host this somewhere on the internet, that it's licensed under MIT Berserker66
So unless you're Berserker you need to include license information."""
import json
import os
import logging
import typing
import multiprocessing
2020-06-20 20:03:06 +02:00
import threading
import zlib
2020-06-20 20:03:06 +02:00
from pony.flask import Pony
from flask import Flask, request, redirect, url_for, render_template, Response, session, abort, flash
from flask_caching import Cache
2020-06-20 20:03:06 +02:00
from .models import *
UPLOAD_FOLDER = os.path.relpath('uploads')
LOGS_FOLDER = os.path.relpath('logs')
os.makedirs(LOGS_FOLDER, exist_ok=True)
def allowed_file(filename):
return filename.endswith('multidata')
app = Flask(__name__)
2020-06-20 20:03:06 +02:00
Pony(app)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 # 1 megabyte limit
2020-06-20 20:03:06 +02:00
# if you want persistent sessions on your server, make sure you make this a constant in your config.yaml
app.config["SECRET_KEY"] = os.urandom(32)
2020-06-20 20:03:06 +02:00
app.config['SESSION_PERMANENT'] = True
2020-06-14 07:44:59 +02:00
app.config["PONY"] = {
'provider': 'sqlite',
2020-06-20 20:03:06 +02:00
'filename': os.path.abspath('db.db3'),
2020-06-14 07:44:59 +02:00
'create_db': True
}
app.config["CACHE_TYPE"] = "simple"
cache = Cache(app)
2020-06-20 20:03:06 +02:00
multiworlds = {}
2020-06-20 22:48:38 +02:00
@app.before_request
2020-06-20 20:03:06 +02:00
def register_session():
session.permanent = True # technically 31 days after the last visit
if not session.get("_id", None):
session["_id"] = uuid4() # uniquely identify each session without needing a login
2020-06-20 20:03:06 +02:00
class MultiworldInstance():
def __init__(self, room: Room):
self.room_id = room.id
self.process: typing.Optional[multiprocessing.Process] = None
2020-06-20 20:03:06 +02:00
multiworlds[self.room_id] = self
def start(self):
if self.process and self.process.is_alive():
2020-06-13 22:49:57 +02:00
return False
2020-06-14 07:44:59 +02:00
2020-06-20 20:03:06 +02:00
logging.info(f"Spinning up {self.room_id}")
with db_session:
self.process = multiprocessing.Process(group=None, target=run_server_process,
args=(self.room_id, app.config["PONY"]),
name="MultiHost")
self.process.start()
def stop(self):
if self.process:
self.process.terminate()
self.process = None
2020-06-20 20:03:06 +02:00
@app.route('/seed/<int:seed>')
def view_seed(seed: int):
seed = Seed.get(id=seed)
if seed:
return render_template("view_seed.html", seed=seed)
else:
abort(404)
2020-06-20 20:03:06 +02:00
@app.route('/new_room/<int:seed>')
def new_room(seed: int):
seed = Seed.get(id=seed)
room = Room(seed=seed, owner=session["_id"])
commit()
return redirect(url_for("host_room", room=room.id))
def _read_log(path: str):
2020-06-14 08:11:56 +02:00
if os.path.exists(path):
2020-06-22 03:53:00 +02:00
with open(path, encoding="utf-8-sig") as log:
2020-06-14 08:11:56 +02:00
yield from log
else:
yield f"Logfile {path} does not exist. " \
f"Likely a crash during spinup of multiworld instance or it is still spinning up."
2020-06-20 20:03:06 +02:00
@app.route('/log/<int:room>')
def display_log(room: int):
# noinspection PyTypeChecker
2020-06-20 20:03:06 +02:00
return Response(_read_log(os.path.join("logs", str(room) + ".txt")), mimetype="text/plain;charset=UTF-8")
processstartlock = threading.Lock()
2020-06-20 20:03:06 +02:00
@app.route('/hosted/<int:room>', methods=['GET', 'POST'])
def host_room(room: int):
room = Room.get(id=room)
if request.method == "POST":
if room.owner == session["_id"]:
cmd = request.form["cmd"]
Command(room=room, commandtext=cmd)
commit()
2020-06-14 07:44:59 +02:00
with db_session:
2020-06-20 20:03:06 +02:00
multiworld = multiworlds.get(room.id, None)
2020-06-14 07:44:59 +02:00
if not multiworld:
2020-06-20 20:03:06 +02:00
multiworld = MultiworldInstance(room)
2020-06-14 07:44:59 +02:00
2020-06-20 20:03:06 +02:00
with processstartlock:
multiworld.start()
2020-06-20 20:03:06 +02:00
return render_template("host_room.html", room=room)
from WebHost.customserver import run_server_process
2020-06-22 20:25:24 +02:00
from . import tracker # to trigger app routing picking up on it
@app.route('/', methods=['GET', 'POST'])
def upload_multidata():
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
else:
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
if file.filename == '':
flash('No selected file')
elif file and allowed_file(file.filename):
try:
multidata = json.loads(zlib.decompress(file.read()).decode("utf-8-sig"))
except:
flash("Could not load multidata. File may be corrupted or incompatible.")
else:
seed = Seed(multidata=multidata)
commit() # place into DB and generate ids
return redirect(url_for("view_seed", seed=seed.id))
else:
flash("Not recognized file format. Awaiting a .multidata file.")
2020-06-22 20:25:24 +02:00
rooms = select(room for room in Room if room.owner == session["_id"])
return render_template("upload_multidata.html", rooms=rooms)