diff --git a/projects/cherry-shim/BUILD b/projects/cherry-shim/BUILD new file mode 100644 index 0000000..fbc4ca7 --- /dev/null +++ b/projects/cherry-shim/BUILD @@ -0,0 +1,6 @@ +py_project( + name = "cherry-shim", + lib_deps = [ + py_requirement("cherrypy"), + ] +) diff --git a/projects/cherry-shim/src/python/cherry_shim.py b/projects/cherry-shim/src/python/cherry_shim.py new file mode 100644 index 0000000..482ada6 --- /dev/null +++ b/projects/cherry-shim/src/python/cherry_shim.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +# Import CherryPy +import cherrypy + + +def shim(app): + # Mount the application + cherrypy.tree.graft(app, "/") + + # Unsubscribe the default server + cherrypy.server.unsubscribe() + + # Instantiate a new server object + server = cherrypy._cpserver.Server() + + def _run(host="0.0.0.0", port=8080, pool_size=16): + # Configure the server object + server.socket_host = host + server.socket_port = port + server.thread_pool = pool_size + server.subscribe() + cherrypy.engine.start() + cherrypy.engine.block() + + server.run = _run + + return server diff --git a/projects/tentacles/BUILD b/projects/tentacles/BUILD index 83b47df..4305c05 100644 --- a/projects/tentacles/BUILD +++ b/projects/tentacles/BUILD @@ -4,6 +4,7 @@ py_project( main_deps = [ "//projects/anosql", "//projects/anosql-migrations", + "//projects/cherry-shim", py_requirement("click"), py_requirement("flask"), py_requirement("jinja2"), diff --git a/projects/tentacles/config.toml b/projects/tentacles/config.toml deleted file mode 100644 index 2e4c34c..0000000 --- a/projects/tentacles/config.toml +++ /dev/null @@ -1,10 +0,0 @@ -SECRET_KEY = "SgvzxsO5oPBGInmqsyyGQWAJXkS9" -UPLOAD_FOLDER = "/home/arrdem/Documents/hobby/programming/source/projects/tentacles/tmp" - -[db] -uri = "/home/arrdem/Documents/hobby/programming/source/projects/tentacles/tentacles.sqlite3" - -[[users]] -email = "root@tirefireind.us" -group_id = 0 -status_id = 1 diff --git a/projects/tentacles/src/python/tentacles/__main__.py b/projects/tentacles/src/python/tentacles/__main__.py index 30c6bf3..506aa0a 100644 --- a/projects/tentacles/src/python/tentacles/__main__.py +++ b/projects/tentacles/src/python/tentacles/__main__.py @@ -8,6 +8,8 @@ import tomllib import click from flask import Flask, request + +from cherry_shim import shim from tentacles.blueprints import ( api, file_ui, @@ -102,10 +104,11 @@ def serve(hostname: str, port: int, config: Path): # Shove our middleware in there app.wsgi_app = custom_ctx(app, app.wsgi_app) + cherry = shim(app) # And run the blame thing try: - app.run(host=hostname, port=port) + cherry.run(host=hostname, port=port) finally: shutdown_event.set() diff --git a/projects/tentacles/src/python/tentacles/globals.py b/projects/tentacles/src/python/tentacles/globals.py index 896818a..5470d7c 100644 --- a/projects/tentacles/src/python/tentacles/globals.py +++ b/projects/tentacles/src/python/tentacles/globals.py @@ -15,6 +15,7 @@ class Ctx: sid: str = None username: str = None is_admin: bool = None + base_url: str = None _ctx = ContextVar("tentacles.ctx") diff --git a/projects/tentacles/src/python/tentacles/schema.sql b/projects/tentacles/src/python/tentacles/schema.sql index c6275f5..b2ec1db 100644 --- a/projects/tentacles/src/python/tentacles/schema.sql +++ b/projects/tentacles/src/python/tentacles/schema.sql @@ -14,8 +14,9 @@ CREATE TABLE IF NOT EXISTS user_statuses ( , UNIQUE(name) ); -INSERT OR IGNORE INTO user_statuses (id, name) VALUES (-1, 'disabled'); +INSERT OR IGNORE INTO user_statuses (id, name) VALUES (-3, 'unapproved'); INSERT OR IGNORE INTO user_statuses (id, name) VALUES (-2, 'unverified'); +INSERT OR IGNORE INTO user_statuses (id, name) VALUES (-1, 'disabled'); INSERT OR IGNORE INTO user_statuses (id, name) VALUES (1, 'enabled'); CREATE TABLE IF NOT EXISTS users ( @@ -25,7 +26,9 @@ CREATE TABLE IF NOT EXISTS users ( , email TEXT , hash TEXT , status_id INTEGER + , verification_token TEXT DEFAULT (lower(hex(randomblob(32)))) , verified_at TEXT + , approved_at TEXT , enabled_at TEXT , FOREIGN KEY(group_id) REFERENCES groups(id) , FOREIGN KEY(status_id) REFERENCES user_statuses(id) @@ -61,6 +64,7 @@ CREATE TABLE IF NOT EXISTS printers ( id INTEGER PRIMARY KEY AUTOINCREMENT , name TEXT , url TEXT + , stream_url TEXT , api_key TEXT , status_id INTEGER , last_poll_date TEXT diff --git a/projects/tentacles/src/python/tentacles/templates/verification_email.html.j2 b/projects/tentacles/src/python/tentacles/templates/verification_email.html.j2 new file mode 100644 index 0000000..814f148 --- /dev/null +++ b/projects/tentacles/src/python/tentacles/templates/verification_email.html.j2 @@ -0,0 +1,40 @@ + + + + + + + + + + +
+
+

+ Welcome {{ username }}! +

+

+ Before you can use your account, please confirm your email address by clicking this link or pasting the following text into your browser's navigation bar. +

+
+          
+            {{ base_url }}/user/verify?token={{token_id}}
+          
+        
+

+ Once your email has been verified, one of our administrators will review and approve your account. Then you can get to printing! +

+
+
+ + + diff --git a/projects/tentacles/src/python/tentacles/workers.py b/projects/tentacles/src/python/tentacles/workers.py index dd16ff5..0e89f6c 100644 --- a/projects/tentacles/src/python/tentacles/workers.py +++ b/projects/tentacles/src/python/tentacles/workers.py @@ -265,7 +265,13 @@ def send_verifications(app, store: Store): fm.send_message( from_addr="root@tirefireind.us", to_addrs=[user.email], - msg=render_template("verification_email.html.j2"), + subject="Email verification from tentacles", + msg=render_template( + "verification_email.html.j2", + base_url=app.config.get("base_url"), + username=user.username, + token_id=user.verification_token, + ), ) @@ -280,7 +286,7 @@ def send_approvals(app, store: Store): @corn_job(timedelta(seconds=5)) -def run_worker(app, db_factory): +def run_worker(app: App, db_factory): with app.app_context(), closing(db_factory(app)) as store: poll_printers(app, store) assign_jobs(app, store) @@ -291,7 +297,7 @@ def run_worker(app, db_factory): send_approvals(app, store) -def create_workers(app, db_factory: Callable[[], Store]) -> Event: +def create_workers(app, db_factory: Callable[[App], Store]) -> Event: Thread(target=run_worker, args=[app, db_factory]).start() return SHUTDOWN diff --git a/tools/python/constraints.in b/tools/python/constraints.in new file mode 100644 index 0000000..2b2d16f --- /dev/null +++ b/tools/python/constraints.in @@ -0,0 +1,2 @@ +# jaraco.text==git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d +cherrypy==18.8.0 diff --git a/tools/python/defs.bzl b/tools/python/defs.bzl index b631eea..abaac17 100644 --- a/tools/python/defs.bzl +++ b/tools/python/defs.bzl @@ -44,7 +44,7 @@ def py_pytest(name, srcs, deps, main=None, python_version=None, args=None, **kwa deps = sets.to_list(sets.make([ py_requirement("pytest"), - py_requirement("pytest-pudb"), + # py_requirement("pytest-pudb"), py_requirement("pytest-cov"), py_requirement("pytest-timeout"), ] + deps)) diff --git a/tools/python/requirements.in b/tools/python/requirements.in index 3692298..8705e45 100644 --- a/tools/python/requirements.in +++ b/tools/python/requirements.in @@ -1,3 +1,5 @@ +-c constraints.in + ExifRead aiohttp aiohttp_basicauth @@ -7,6 +9,7 @@ autoflake beautifulsoup4 black cachetools +cherrypy click colored flake8 @@ -40,6 +43,7 @@ redis requests requests retry +pip-tools smbus2 sphinx sphinxcontrib-openapi diff --git a/tools/python/requirements_lock.txt b/tools/python/requirements_lock.txt index 6f5a5ca..fd233f9 100644 --- a/tools/python/requirements_lock.txt +++ b/tools/python/requirements_lock.txt @@ -5,34 +5,42 @@ alabaster==0.7.13 async-lru==2.0.2 async-timeout==4.0.2 attrs==23.1.0 +autocommand==2.2.2 autoflake==2.1.1 Babel==2.12.1 beautifulsoup4==4.12.2 black==23.3.0 blinker==1.6.2 -cachetools==5.3.0 -certifi==2022.12.7 +build==0.10.0 +cachetools==5.3.1 +certifi==2023.5.7 charset-normalizer==3.1.0 +cheroot==10.0.0 +CherryPy==18.8.0 click==8.1.3 colored==1.4.4 commonmark==0.9.1 -coverage==7.2.2 +coverage==7.2.7 decorator==5.1.1 deepmerge==1.1.0 -docutils==0.19 -exceptiongroup==1.1.1 +docutils==0.20.1 ExifRead==3.0.0 flake8==6.0.0 Flask==2.3.2 frozenlist==1.3.3 -hypothesis==6.75.5 +hypothesis==6.75.9 ibis==3.2.0 icmplib==3.0.3 idna==3.4 imagesize==1.4.1 +inflect==6.0.4 iniconfig==2.0.0 isort==5.12.0 itsdangerous==2.1.2 +jaraco.collections==4.2.0 +jaraco.context==4.3.0 +jaraco.functools==3.7.0 +jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d jedi==0.18.2 Jinja2==3.1.2 jsonschema==4.17.3 @@ -43,52 +51,60 @@ libsass==0.22.0 livereload==2.6.3 lxml==4.9.2 Markdown==3.4.3 -MarkupSafe==2.1.2 +MarkupSafe==2.1.3 mccabe==0.7.0 meraki==1.33.0 mirakuru==2.5.1 mistune==2.0.5 +more-itertools==9.1.0 multidict==6.0.4 mypy-extensions==1.0.0 octorest==0.4 openapi-schema-validator==0.4.4 openapi-spec-validator==0.5.6 -packaging==23.0 +packaging==23.1 parso==0.8.3 pathable==0.4.3 pathspec==0.11.1 -picobox==2.2.0 -platformdirs==3.2.0 +picobox==3.0.0 +pip==23.1.2 +pip-tools==6.13.0 +platformdirs==3.5.1 pluggy==1.0.0 port-for==0.6.3 +portend==3.1.0 prompt-toolkit==3.0.38 proquint==0.2.1 -psutil==5.9.4 +psutil==5.9.5 psycopg==3.1.9 psycopg2==2.9.6 pudb==2022.1.3 py==1.11.0 pycodestyle==2.10.0 pycryptodome==3.18.0 +pydantic==1.10.8 pyflakes==3.0.1 -Pygments==2.14.0 +Pygments==2.15.1 +pyproject_hooks==1.0.0 pyrsistent==0.19.3 -pytest==7.2.2 +pytest==7.3.1 pytest-cov==4.1.0 pytest-postgresql==5.0.0 pytest-pudb==0.7.0 pytest-timeout==2.1.0 +pytz==2023.3 PyYAML==6.0 recommonmark==0.7.1 redis==4.5.5 requests==2.31.0 retry==0.9.2 rfc3339-validator==0.1.4 +setuptools==67.7.2 six==1.16.0 smbus2==0.4.2 snowballstemmer==2.2.0 sortedcontainers==2.4.0 -soupsieve==2.4 +soupsieve==2.4.1 Sphinx==7.0.1 sphinx_mdinclude==0.5.3 sphinxcontrib-applehelp==1.0.4 @@ -100,19 +116,21 @@ sphinxcontrib-openapi==0.8.1 sphinxcontrib-programoutput==0.17 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 -termcolor==2.2.0 +tempora==5.2.2 +termcolor==2.3.0 toml==0.10.2 -tomli==2.0.1 -tornado==6.2 -typing_extensions==4.5.0 +tornado==6.3.2 +typing_extensions==4.6.3 unify==0.5 untokenize==0.1.1 -urllib3==1.26.15 +urllib3==2.0.2 urwid==2.1.2 urwid-readline==0.13 wcwidth==0.2.6 -websocket-client==1.5.1 +websocket-client==1.5.2 Werkzeug==2.3.4 +wheel==0.40.0 yamllint==1.32.0 -yarl==1.8.2 +yarl==1.9.2 yaspin==2.3.0 +zc.lockfile==3.0.post1