commit ba71ca4a35d4d74e35ec70c1c9f49ca69ae3bbb6 Author: Johannes Date: Thu Mar 12 23:58:37 2026 +0100 Initial save server diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..26ff118 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.13-slim + +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app.py . + +VOLUME ["/saves"] +EXPOSE 5000 + +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"] diff --git a/app.py b/app.py new file mode 100644 index 0000000..b16021e --- /dev/null +++ b/app.py @@ -0,0 +1,59 @@ +from flask import Flask, request, jsonify, send_from_directory, abort +from flask_cors import CORS +import os +import re + +app = Flask(__name__) +CORS(app) + +SAVES_DIR = os.environ.get("SAVES_DIR", "/saves") +os.makedirs(SAVES_DIR, exist_ok=True) + +def safe_filename(name): + name = re.sub(r"[^\w\-. ]", "", name).strip() + if not name: + abort(400, "Invalid save name") + return name + ".puC" + + +@app.route("/saves", methods=["GET"]) +def list_saves(): + files = [ + f[:-4] for f in os.listdir(SAVES_DIR) + if f.endswith(".puC") + ] + return jsonify(sorted(files)) + + +@app.route("/saves/", methods=["GET"]) +def get_save(name): + filename = safe_filename(name) + path = os.path.join(SAVES_DIR, filename) + if not os.path.exists(path): + abort(404) + return send_from_directory(SAVES_DIR, filename, as_attachment=True) + + +@app.route("/saves/", methods=["POST"]) +def store_save(name): + filename = safe_filename(name) + data = request.get_data() + if not data: + abort(400, "No data") + with open(os.path.join(SAVES_DIR, filename), "wb") as f: + f.write(data) + return jsonify({"ok": True, "name": name}) + + +@app.route("/saves/", methods=["DELETE"]) +def delete_save(name): + filename = safe_filename(name) + path = os.path.join(SAVES_DIR, filename) + if not os.path.exists(path): + abort(404) + os.remove(path) + return jsonify({"ok": True}) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bc8412f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + pu-saves: + build: . + container_name: pu-saves + restart: unless-stopped + volumes: + - ./saves:/saves + networks: + - caddy_default + +networks: + caddy_default: + external: true diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7fbb9cf --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask==3.1.0 +flask-cors==5.0.1 +gunicorn==23.0.0