Overwrought/overwrought_server.py

110 lines
2.6 KiB
Python
Raw Normal View History

2019-06-02 18:20:55 -04:00
#!/usr/bin/env python3
"""
The overwrought server, for exchanging mods from master to slave recipients.
"""
import os
2019-06-02 18:20:55 -04:00
import json
2019-06-03 07:04:50 -04:00
import getpass
2019-06-02 18:20:55 -04:00
import sqlite3
import threading
from passlib.hash import argon2
from flask import Flask, send_file, request, abort
import config_server
app = Flask(__name__)
app.config['db_lock'] = threading.Lock()
os.makedirs('mods', exist_ok=True)
def init_db():
"""
If no database is found, a dummy database is created to allow the
initial modpack upload to go smoothly.
"""
con = sqlite3.connect('modpack.db')
cur = con.cursor()
try:
cur.execute("SELECT filename FROM mod").fetchone()
except sqlite3.OperationalError:
cur.execute("CREATE TABLE mod(filename TEXT)")
con.commit()
con.close()
init_db()
2019-06-02 18:20:55 -04:00
@app.route('/')
def index():
"""
Main index page. Displays the mod summary page.
"""
return send_file(config_server.modpack_name + '.html')
@app.route('/get')
def get():
"""
Returns all mod file entries contained in the database as JSON.
Determing which files to update is left as an exercise up to the
client.
"""
app.config.get('db_lock').acquire()
con = sqlite3.connect('modpack.db')
2019-06-02 20:28:30 -04:00
cur = con.cursor()
2019-06-02 18:20:55 -04:00
mods = cur.execute('SELECT filename FROM mod').fetchall()
app.config.get('db_lock').release()
mods = [mod[0] for mod in mods]
return json.dumps(mods)
@app.route('/post', methods=['POST'])
def post():
"""
Allows the modpack owner to upload new mods and a new database to the
server. Owner must be validated.
"""
password = request.form.get('password')
if not argon2.verify(password, config_server.pw_hash):
abort(401)
2019-06-02 20:10:00 -04:00
files = request.files.to_dict()
2019-06-02 20:28:30 -04:00
if 'modpack.db' in files.keys():
db = request.files.get('modpack.db')
2019-06-02 20:10:00 -04:00
app.config.get('db_lock').acquire()
db.save('modpack.db')
app.config.get('db_lock').release()
files.pop('modpack.db')
2019-06-02 18:20:55 -04:00
2019-06-02 20:28:30 -04:00
if config_server.modpack_name + '.html' in files.keys():
2019-06-02 20:10:00 -04:00
summary = request.files.get(config_server.modpack_name + '.html')
summary.save(config_server.modpack_name + '.html')
files.pop(config_server.modpack_name + '.html')
2019-06-02 18:26:07 -04:00
2019-06-02 20:10:00 -04:00
for fname, file in files.items():
2019-06-02 18:20:55 -04:00
file.save(os.path.join(config_server.mods_dir, fname))
2019-06-02 20:28:30 -04:00
return "Success!"
2019-06-02 18:20:55 -04:00
2019-06-03 07:04:50 -04:00
def generate_hash(password=None):
2019-06-02 18:20:55 -04:00
"""
A utility for generating an argon2 password hash.
"""
2019-06-03 07:04:50 -04:00
if not password:
password = getpass.getpass()
2019-06-02 18:20:55 -04:00
pw_hash = argon2.hash(password)
return pw_hash
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="The overwrought server. Use gunicorn to start."
)
2019-06-02 19:11:16 -04:00
parser.add_argument(
2019-06-02 18:20:55 -04:00
'action',
choices=['hash'],
help="What action to perform.",
)
2019-06-02 19:11:16 -04:00
args = parser.parse_args()
2019-06-02 18:20:55 -04:00
if args.action == "hash":
2019-06-03 07:04:50 -04:00
pw_hash = generate_hash()
2019-06-02 18:20:55 -04:00
print(pw_hash)