Get PrusaSlicer working; fix some auth problems
This commit is contained in:
parent
f003351290
commit
198115860e
6 changed files with 86 additions and 29 deletions
|
@ -51,8 +51,20 @@ def create_j2_request_global(app):
|
|||
|
||||
|
||||
def user_session():
|
||||
if (session_id := request.cookies.get("sid", "")) and (
|
||||
uid := ctx.db.try_key(session_id)
|
||||
if (
|
||||
(
|
||||
(session_id := request.cookies.get("sid", ""))
|
||||
and (uid := ctx.db.try_key(session_id))
|
||||
)
|
||||
or (
|
||||
request.authorization
|
||||
and request.authorization.token
|
||||
and (uid := ctx.db.try_key(request.authorization.token))
|
||||
)
|
||||
or (
|
||||
(api_key := request.headers.get("x-api-key"))
|
||||
and (uid := ctx.db.try_key(api_key))
|
||||
)
|
||||
):
|
||||
ctx.sid = session_id
|
||||
ctx.uid = uid
|
||||
|
|
|
@ -7,29 +7,47 @@ import os
|
|||
from typing import Optional
|
||||
|
||||
from flask import Blueprint, current_app, request
|
||||
from tentacles.blueprints.util import (
|
||||
requires_admin,
|
||||
requires_auth,
|
||||
)
|
||||
from tentacles.globals import ctx
|
||||
|
||||
|
||||
BLUEPRINT = Blueprint("api", __name__, url_prefix="/api")
|
||||
|
||||
|
||||
def requires_admin(f):
|
||||
def _helper(*args, **kwargs):
|
||||
if not ctx.is_admin:
|
||||
return {"error": "unauthorized"}, 401
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
_helper.__name__ = f.__name__
|
||||
return _helper
|
||||
|
||||
|
||||
def requires_auth(f):
|
||||
def _helper(*args, **kwargs):
|
||||
if not ctx.uid:
|
||||
return {"error": "unauthorized"}, 401
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
_helper.__name__ = f.__name__
|
||||
return _helper
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# Printers
|
||||
#
|
||||
# The trick here is handling multipart uploads.
|
||||
@requires_admin
|
||||
@BLUEPRINT.route("/printers", methods=["POST"])
|
||||
@requires_admin
|
||||
def create_printer():
|
||||
pass
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/printers", methods=["GET"])
|
||||
@BLUEPRINT.route("/printers/", methods=["GET"])
|
||||
@requires_auth
|
||||
def list_printers():
|
||||
return {
|
||||
"printers": [
|
||||
|
@ -38,8 +56,8 @@ def list_printers():
|
|||
}, 200
|
||||
|
||||
|
||||
@requires_admin
|
||||
@BLUEPRINT.route("/printers", methods=["DELETE"])
|
||||
@requires_admin
|
||||
def delete_printer():
|
||||
pass
|
||||
|
||||
|
@ -48,9 +66,9 @@ def delete_printer():
|
|||
# Files
|
||||
#
|
||||
# The trick here is handling multipart uploads.
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/files", methods=["POST"])
|
||||
@BLUEPRINT.route("/files/<location>", methods=["POST"])
|
||||
@requires_auth
|
||||
def create_file(location: Optional[str] = None):
|
||||
# This is the important one, because it's the one that PrusaSlicer et all use to upload jobs.
|
||||
|
||||
|
@ -68,20 +86,29 @@ def create_file(location: Optional[str] = None):
|
|||
|
||||
else:
|
||||
digest = sha3_256()
|
||||
digest.update(
|
||||
f"$USER${ctx.uid}$".encode()
|
||||
) # Salt filenames per-user to avoid collisions
|
||||
digest.update(file.filename.encode())
|
||||
sanitized_filename = digest.hexdigest() + ".gcode"
|
||||
sanitized_path = os.path.join(
|
||||
current_app.config["UPLOAD_FOLDER"], sanitized_filename
|
||||
)
|
||||
if os.path.exists(sanitized_path):
|
||||
return {"error": "file exists already"}, 409
|
||||
|
||||
file.save(sanitized_path)
|
||||
ctx.db.create_file(ctx.uid, file.filename, sanitized_path)
|
||||
fid = ctx.db.create_file(ctx.uid, file.filename, sanitized_path)
|
||||
|
||||
if request.form.get("print", "").lower() == "true":
|
||||
ctx.db.create_job(ctx.uid, fid)
|
||||
|
||||
return {"status": "ok"}, 202
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/files", methods=["GET"])
|
||||
@BLUEPRINT.route("/files/", methods=["GET"])
|
||||
@requires_auth
|
||||
def get_files():
|
||||
return {
|
||||
"files": [
|
||||
|
@ -97,22 +124,22 @@ def get_files():
|
|||
}, 200
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/files", methods=["DELETE"])
|
||||
@requires_auth
|
||||
def delete_file():
|
||||
pass
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# Jobs
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/jobs", methods=["POST"])
|
||||
@requires_auth
|
||||
def create_job():
|
||||
pass
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/jobs", methods=["GET"])
|
||||
@requires_auth
|
||||
def get_jobs():
|
||||
return {
|
||||
"jobs": [
|
||||
|
@ -128,15 +155,27 @@ def get_jobs():
|
|||
}, 200
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/jobs", methods=["DELETE"])
|
||||
@requires_auth
|
||||
def delete_job():
|
||||
pass
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# API tokens
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/tokens", methods=["GET"])
|
||||
@requires_auth
|
||||
def get_tokens():
|
||||
pass
|
||||
|
||||
|
||||
@BLUEPRINT.route("/version", methods=["GET"])
|
||||
@requires_auth
|
||||
def get_version():
|
||||
"""OctoPrint compatability endpoint."""
|
||||
|
||||
return {
|
||||
"api": "0.1",
|
||||
"server": "1.3.10",
|
||||
"text": "OctoPrint 1.3.10 (Tentacles)",
|
||||
}, 200
|
||||
|
|
|
@ -20,8 +20,8 @@ log = logging.getLogger(__name__)
|
|||
BLUEPRINT = Blueprint("files", __name__)
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/files", methods=["GET"])
|
||||
@requires_auth
|
||||
def list_files():
|
||||
if request.method == "POST":
|
||||
flash("Not supported yet", category="warning")
|
||||
|
@ -29,8 +29,8 @@ def list_files():
|
|||
return render_template("files.html.j2")
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/files", methods=["POST"])
|
||||
@requires_auth
|
||||
def manipulate_files():
|
||||
match request.form.get("action"):
|
||||
case "upload":
|
||||
|
@ -43,11 +43,15 @@ def manipulate_files():
|
|||
|
||||
case "delete":
|
||||
file = ctx.db.fetch_file(ctx.uid, int(request.form.get("file_id")))
|
||||
if any(job.finished_at is None for job in ctx.db.list_jobs_by_file(file.id)):
|
||||
if any(
|
||||
job.finished_at is None for job in ctx.db.list_jobs_by_file(file.id)
|
||||
):
|
||||
flash("File is in use", category="error")
|
||||
return render_template("files.html.j2"), 400
|
||||
|
||||
if os.path.exists(file.path):
|
||||
os.unlink(file.path)
|
||||
|
||||
ctx.db.delete_file(ctx.uid, file.id)
|
||||
flash("File deleted", category="info")
|
||||
|
||||
|
|
|
@ -18,14 +18,14 @@ log = logging.getLogger(__name__)
|
|||
BLUEPRINT = Blueprint("jobs", __name__)
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/jobs", methods=["GET"])
|
||||
@requires_auth
|
||||
def list_jobs():
|
||||
return render_template("jobs.html.j2")
|
||||
|
||||
|
||||
@requires_auth
|
||||
@BLUEPRINT.route("/jobs", methods=["POST"])
|
||||
@requires_auth
|
||||
def manipulate_jobs():
|
||||
match request.form.get("action"):
|
||||
case "enqueue":
|
||||
|
|
|
@ -18,14 +18,14 @@ log = logging.getLogger(__name__)
|
|||
BLUEPRINT = Blueprint("printer", __name__)
|
||||
|
||||
|
||||
@requires_admin
|
||||
@BLUEPRINT.route("/printers")
|
||||
@requires_admin
|
||||
def printers():
|
||||
return render_template("printers.html.j2")
|
||||
|
||||
|
||||
@requires_admin
|
||||
@BLUEPRINT.route("/printers/add", methods=["get", "post"])
|
||||
@requires_admin
|
||||
def add_printer():
|
||||
if not is_logged_in():
|
||||
return redirect("/")
|
||||
|
@ -50,7 +50,7 @@ def add_printer():
|
|||
return render_template("add_printer.html.j2")
|
||||
|
||||
|
||||
@requires_admin
|
||||
@BLUEPRINT.route("/printers/delete")
|
||||
@requires_admin
|
||||
def delete_printer():
|
||||
return render_template("delete_printer.html.j2")
|
||||
|
|
|
@ -21,10 +21,11 @@ def requires_admin(f):
|
|||
def _helper(*args, **kwargs):
|
||||
if not ctx.is_admin:
|
||||
flash("Sorry, admins only", category="error")
|
||||
redirect("/")
|
||||
return redirect("/")
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
_helper.__name__ = f.__name__
|
||||
return _helper
|
||||
|
||||
|
||||
|
@ -32,8 +33,9 @@ def requires_auth(f):
|
|||
def _helper(*args, **kwargs):
|
||||
if not ctx.uid:
|
||||
flash("Please log in first", category="error")
|
||||
redirect("/")
|
||||
return redirect("/")
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
_helper.__name__ = f.__name__
|
||||
return _helper
|
||||
|
|
Loading…
Reference in a new issue