diff --git a/projects/tentacles/src/tentacles/__main__.py b/projects/tentacles/src/tentacles/__main__.py index 2834899..53d1186 100644 --- a/projects/tentacles/src/tentacles/__main__.py +++ b/projects/tentacles/src/tentacles/__main__.py @@ -21,15 +21,21 @@ from tentacles.db import Db from tentacles.globals import _ctx, Ctx, ctx from tentacles import workers from contextlib import closing +from tentacles.blueprints.util import salt + + +log = logging.getLogger(__name__) def db_factory(app): - store = Db( - Path( - app.config["ROOT_FOLDER"], - app.config.get("db", {}).get("uri"), - ) + p = Path( + app.config["ROOT_FOLDER"], + app.config.get("db", {}).get("uri"), ) + + # log.info("Using db from %r", p) + + store = Db(p) store.connect() return store @@ -117,6 +123,8 @@ def serve(hostname: str, port: int, config: Path, trace: bool): if trace: logging.getLogger("tentacles.db").setLevel(logging.TRACE) + logging.getLogger("aiosql.adapters.sqlite3").setLevel(logging.TRACE) + app = make_app() if config: @@ -124,8 +132,6 @@ def serve(hostname: str, port: int, config: Path, trace: bool): app.config.update(tomllib.load(fp)) app.config["ROOT_FOLDER"] = str(Path(config).absolute().parent) - print(app.config) - # Run migrations once at startup rather than when connecting with closing(db_factory(app)) as db: db.migrate() diff --git a/projects/tentacles/src/tentacles/blueprints/api.py b/projects/tentacles/src/tentacles/blueprints/api.py index b728b9b..1f9b9a5 100644 --- a/projects/tentacles/src/tentacles/blueprints/api.py +++ b/projects/tentacles/src/tentacles/blueprints/api.py @@ -99,10 +99,14 @@ def create_file(location: Optional[str] = None): if os.path.exists(sanitized_path): return {"error": "file exists already"}, 409 - # FIXME: Explicitly interpolating the path here kinda rots - file.save( - sanitized_path.replace("$ROOT_FOLDER", current_app.config["ROOT_FOLDER"]) + real_path = sanitized_path.replace( + "$ROOT_FOLDER", current_app.config["ROOT_FOLDER"] ) + + print(file.filename, real_path) + + # FIXME: Explicitly interpolating the path here kinda rots + file.save(real_path) row = ctx.db.create_file( uid=ctx.uid, filename=file.filename, @@ -110,7 +114,12 @@ def create_file(location: Optional[str] = None): ) if request.form.get("print", "").lower() == "true": - ctx.db.create_job(uid=ctx.uid, fid=row.id) + ctx.db.create_job( + uid=ctx.uid, + fid=row.id, + cid=None, + pid=None, + ) return {"status": "ok"}, 202 diff --git a/projects/tentacles/src/tentacles/blueprints/file_ui.py b/projects/tentacles/src/tentacles/blueprints/file_ui.py index 377673b..b5f05da 100644 --- a/projects/tentacles/src/tentacles/blueprints/file_ui.py +++ b/projects/tentacles/src/tentacles/blueprints/file_ui.py @@ -43,7 +43,10 @@ def manipulate_files(): return render_template("files.html.j2"), code case "download": - file = ctx.db.fetch_file(uid=ctx.uid, fid=int(request.form.get("file_id"))) + file = ctx.db.fetch_file( + uid=ctx.uid, + fid=int(request.form.get("file_id")), + ) if file: return send_file( file.path, diff --git a/projects/tentacles/src/tentacles/blueprints/job_ui.py b/projects/tentacles/src/tentacles/blueprints/job_ui.py index 7d3c5fc..05a5b07 100644 --- a/projects/tentacles/src/tentacles/blueprints/job_ui.py +++ b/projects/tentacles/src/tentacles/blueprints/job_ui.py @@ -18,6 +18,11 @@ log = logging.getLogger(__name__) BLUEPRINT = Blueprint("jobs", __name__) +def maybe(f, x): + if x is not None: + return f(x) + + @BLUEPRINT.route("/jobs", methods=["GET"]) @requires_auth def list_jobs(): @@ -29,7 +34,12 @@ def list_jobs(): def manipulate_jobs(): match request.form.get("action"): case "enqueue": - ctx.db.create_job(uid=ctx.uid, fid=int(request.form.get("file_id"))) + job = ctx.db.create_job( + uid=ctx.uid, + fid=int(request.form.get("file_id")), + cid=maybe(int, request.form.get("color_id")), + pid=maybe(int, request.form.get("printer_id")), + ) flash("Job created!", category="info") case "duplicate": diff --git a/projects/tentacles/src/tentacles/db.py b/projects/tentacles/src/tentacles/db.py index a374296..dafb25b 100644 --- a/projects/tentacles/src/tentacles/db.py +++ b/projects/tentacles/src/tentacles/db.py @@ -165,7 +165,13 @@ class Db(Queries): ################################################################################ # Wrappers for doing Python type mapping - def create_key(self, *, uid: int, name: str, ttl: Optional[timedelta]): + def create_key( + self, + *, + uid: int, + name: str, + ttl: Optional[timedelta], + ): return super().create_key( uid=uid, name=name, @@ -173,7 +179,11 @@ class Db(Queries): ) def try_login( - self, *, username: str, password: str, ttl: timedelta + self, + *, + username: str, + password: str, + ttl: timedelta, ) -> Optional[str]: """Given a username and an (unsecured) password, attempt to authenticate the named user. @@ -190,7 +200,13 @@ class Db(Queries): return self.create_key(uid=res.id, name="web session", ttl=ttl) def try_create_user( - self, *, username: str, email: str, password: str, gid: int = 1, sid: int = -3 + self, + *, + username: str, + email: str, + password: str, + gid: int = 1, + sid: int = -3, ): digest = sha3_256() digest.update(password.encode("utf-8")) @@ -202,6 +218,25 @@ class Db(Queries): sid=sid, ) + def force_create_user( + self, + *, + username: str, + email: str, + password: str, + gid: int = 1, + sid: int = -3, + ): + digest = sha3_256() + digest.update(password.encode("utf-8")) + return super().force_create_user( + name=username, + email=email, + hash=digest.hexdigest(), + gid=gid, + sid=sid, + ) + def refresh_key(self, *, kid: str, ttl: timedelta): """Automagically renew an API key which is still in use. diff --git a/projects/tentacles/src/tentacles/sql/color.sql b/projects/tentacles/src/tentacles/sql/color.sql index bd0a403..9609cf2 100644 --- a/projects/tentacles/src/tentacles/sql/color.sql +++ b/projects/tentacles/src/tentacles/sql/color.sql @@ -54,7 +54,7 @@ WHERE id = :cid ; --- name: list-color +-- name: list-colors SELECT * FROM filament_color ; diff --git a/projects/tentacles/src/tentacles/sql/jobs.sql b/projects/tentacles/src/tentacles/sql/jobs.sql index 81bcfc5..ea737f1 100644 --- a/projects/tentacles/src/tentacles/sql/jobs.sql +++ b/projects/tentacles/src/tentacles/sql/jobs.sql @@ -29,17 +29,27 @@ CREATE TABLE IF NOT EXISTS jobs ( -- name: migration-0001-jobs-add-print-time# ALTER TABLE jobs ADD COLUMN time_left INTEGER DEFAULT (0); +-- name: migration-0002-jobs-add-print-color# +ALTER TABLE jobs ADD COLUMN color_id INTEGER DEFAULT (0); + +-- name: migration-0003-jobs-add-queued-at# +ALTER TABLE jobs ADD COLUMN queued_at TEXT DEFAULT (datetime('now')); + -- name: create-job^ INSERT INTO jobs ( user_id , file_id + , color_id + , printer_id ) VALUES ( :uid , :fid + , :cid + , :pid ) RETURNING - id + * ; -- name: fetch-job^ SELECT @@ -73,6 +83,7 @@ WHERE SELECT j.id as id , j.file_id + , coalesce(j.color_id, fa.color_id) as color_id , fa.id as analysis_id , fa.max_x , fa.max_y @@ -81,7 +92,6 @@ SELECT , fa.max_end , fa.nozzle_diameter , fa.filament_id - , fa.color_id , (SELECT name FROM filament WHERE id = fa.filament_id) AS filament_name , (SELECT name AS name FROM filament_color WHERE id = fa.color_id) AS color_name , (SELECT name FROM job_statuses WHERE id = j.status_id) AS status diff --git a/projects/tentacles/src/tentacles/sql/printers.sql b/projects/tentacles/src/tentacles/sql/printers.sql index a7233d6..a2bed9b 100644 --- a/projects/tentacles/src/tentacles/sql/printers.sql +++ b/projects/tentacles/src/tentacles/sql/printers.sql @@ -71,7 +71,6 @@ INSERT OR IGNORE INTO filament (name) VALUES ('PETG'); ALTER TABLE printers ADD filament_id INTEGER REFERENCES filament(id) DEFAULT 1; - -- name: migration-0004-create-printer-enabled# ALTER TABLE printers ADD enabled BOOLEAN DEFAULT TRUE; @@ -81,17 +80,24 @@ ALTER TABLE printers ADD nozzle_diameter FLOAT DEFAULT 0.4; -- name: migration-0006-create-printer-level-date# ALTER TABLE printers ADD last_level_date TEXT DEFAULT NULL; --- name: migration-0006-create-printer-filament-color# +-- name: migration-0007-create-printer-filament-color# ALTER TABLE printers ADD color_id INTEGER REFERENCES filament_color(id) DEFAULT 1; -- name: try-create-printer^ INSERT INTO printers ( name , url + , stream_url , api_key , status_id ) -VALUES (:name, :url, :api_key, :sid) +VALUES ( + :name + , :url + , :stream_url + , :api_key + , :sid +) RETURNING id ; diff --git a/projects/tentacles/src/tentacles/sql/user_keys.sql b/projects/tentacles/src/tentacles/sql/user_keys.sql index 9d841ef..d418b63 100644 --- a/projects/tentacles/src/tentacles/sql/user_keys.sql +++ b/projects/tentacles/src/tentacles/sql/user_keys.sql @@ -81,7 +81,7 @@ WHERE OR u.group_id = 0) -- or the user is a root ; --- name: refresh-key +-- name: refresh-key! UPDATE user_keys SET expiration = :expiration @@ -89,7 +89,7 @@ WHERE id = :kid ; --- name: delete-key +-- name: delete-key! DELETE FROM user_keys WHERE user_id = :uid diff --git a/projects/tentacles/src/tentacles/sql/users.sql b/projects/tentacles/src/tentacles/sql/users.sql index dab1464..ff431c6 100644 --- a/projects/tentacles/src/tentacles/sql/users.sql +++ b/projects/tentacles/src/tentacles/sql/users.sql @@ -43,8 +43,20 @@ CREATE TABLE IF NOT EXISTS users ( , UNIQUE(email) ); +-- name: force-create-user^ +INSERT OR REPLACE INTO users ( + name + , email + , hash + , group_id + , status_id +) +VALUES (:name, :email, :hash, :gid, :sid) +RETURNING * +; + -- name: try-create-user^ -INSERT INTO users ( +INSERT OR IGNORE INTO users ( name , email , hash diff --git a/projects/tentacles/src/tentacles/templates/edit_printer.html.j2 b/projects/tentacles/src/tentacles/templates/edit_printer.html.j2 index cecad76..0f13eb6 100644 --- a/projects/tentacles/src/tentacles/templates/edit_printer.html.j2 +++ b/projects/tentacles/src/tentacles/templates/edit_printer.html.j2 @@ -47,7 +47,7 @@