Bolt up a WSGI server shim; tapping on verification

This commit is contained in:
Reid 'arrdem' McKenzie 2023-06-02 23:07:59 -06:00
parent 883c74bf62
commit b981e1255e
13 changed files with 140 additions and 37 deletions

View file

@ -0,0 +1,6 @@
py_project(
name = "cherry-shim",
lib_deps = [
py_requirement("cherrypy"),
]
)

View file

@ -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

View file

@ -4,6 +4,7 @@ py_project(
main_deps = [ main_deps = [
"//projects/anosql", "//projects/anosql",
"//projects/anosql-migrations", "//projects/anosql-migrations",
"//projects/cherry-shim",
py_requirement("click"), py_requirement("click"),
py_requirement("flask"), py_requirement("flask"),
py_requirement("jinja2"), py_requirement("jinja2"),

View file

@ -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

View file

@ -8,6 +8,8 @@ import tomllib
import click import click
from flask import Flask, request from flask import Flask, request
from cherry_shim import shim
from tentacles.blueprints import ( from tentacles.blueprints import (
api, api,
file_ui, file_ui,
@ -102,10 +104,11 @@ def serve(hostname: str, port: int, config: Path):
# Shove our middleware in there # Shove our middleware in there
app.wsgi_app = custom_ctx(app, app.wsgi_app) app.wsgi_app = custom_ctx(app, app.wsgi_app)
cherry = shim(app)
# And run the blame thing # And run the blame thing
try: try:
app.run(host=hostname, port=port) cherry.run(host=hostname, port=port)
finally: finally:
shutdown_event.set() shutdown_event.set()

View file

@ -15,6 +15,7 @@ class Ctx:
sid: str = None sid: str = None
username: str = None username: str = None
is_admin: bool = None is_admin: bool = None
base_url: str = None
_ctx = ContextVar("tentacles.ctx") _ctx = ContextVar("tentacles.ctx")

View file

@ -14,8 +14,9 @@ CREATE TABLE IF NOT EXISTS user_statuses (
, UNIQUE(name) , 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 (-2, 'unverified');
INSERT OR IGNORE INTO user_statuses (id, name) VALUES (-1, 'disabled');
INSERT OR IGNORE INTO user_statuses (id, name) VALUES (1, 'enabled'); INSERT OR IGNORE INTO user_statuses (id, name) VALUES (1, 'enabled');
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
@ -25,7 +26,9 @@ CREATE TABLE IF NOT EXISTS users (
, email TEXT , email TEXT
, hash TEXT , hash TEXT
, status_id INTEGER , status_id INTEGER
, verification_token TEXT DEFAULT (lower(hex(randomblob(32))))
, verified_at TEXT , verified_at TEXT
, approved_at TEXT
, enabled_at TEXT , enabled_at TEXT
, FOREIGN KEY(group_id) REFERENCES groups(id) , FOREIGN KEY(group_id) REFERENCES groups(id)
, FOREIGN KEY(status_id) REFERENCES user_statuses(id) , FOREIGN KEY(status_id) REFERENCES user_statuses(id)
@ -61,6 +64,7 @@ CREATE TABLE IF NOT EXISTS printers (
id INTEGER PRIMARY KEY AUTOINCREMENT id INTEGER PRIMARY KEY AUTOINCREMENT
, name TEXT , name TEXT
, url TEXT , url TEXT
, stream_url TEXT
, api_key TEXT , api_key TEXT
, status_id INTEGER , status_id INTEGER
, last_poll_date TEXT , last_poll_date TEXT

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<html lang="en">
<head>
<link rel="stylesheet" href="{{ base_url }}/static/css/style.css" />
</head>
<body>
<nav class="container navbar">
<span class="logo">
<a class="row" href="/">
<img src="{{ base_url }}/static/tentacles.svg" alt="Tentacles">
<span class="name color-yellow">Tentacles</span>
</a>
</span>
</nav>
<div class="container content">
<div class="row">
<p>
Welcome {{ username }}!
</p>
<p>
Before you can use your account, please confirm your email address by clicking <a href="{{ base_url }}/user/verify?token={{token_id}}">this link</a> or pasting the following text into your browser's navigation bar.
</p>
<pre>
<code>
{{ base_url }}/user/verify?token={{token_id}}
</code>
</pre>
<p>
Once your email has been verified, one of our administrators will review and approve your account. Then you can get to printing!
</p>
</div>
</div>
</body>
<footer>
</footer>
</html>

View file

@ -265,7 +265,13 @@ def send_verifications(app, store: Store):
fm.send_message( fm.send_message(
from_addr="root@tirefireind.us", from_addr="root@tirefireind.us",
to_addrs=[user.email], 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)) @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: with app.app_context(), closing(db_factory(app)) as store:
poll_printers(app, store) poll_printers(app, store)
assign_jobs(app, store) assign_jobs(app, store)
@ -291,7 +297,7 @@ def run_worker(app, db_factory):
send_approvals(app, store) 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() Thread(target=run_worker, args=[app, db_factory]).start()
return SHUTDOWN return SHUTDOWN

View file

@ -0,0 +1,2 @@
# jaraco.text==git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d
cherrypy==18.8.0

View file

@ -44,7 +44,7 @@ def py_pytest(name, srcs, deps, main=None, python_version=None, args=None, **kwa
deps = sets.to_list(sets.make([ deps = sets.to_list(sets.make([
py_requirement("pytest"), py_requirement("pytest"),
py_requirement("pytest-pudb"), # py_requirement("pytest-pudb"),
py_requirement("pytest-cov"), py_requirement("pytest-cov"),
py_requirement("pytest-timeout"), py_requirement("pytest-timeout"),
] + deps)) ] + deps))

View file

@ -1,3 +1,5 @@
-c constraints.in
ExifRead ExifRead
aiohttp aiohttp
aiohttp_basicauth aiohttp_basicauth
@ -7,6 +9,7 @@ autoflake
beautifulsoup4 beautifulsoup4
black black
cachetools cachetools
cherrypy
click click
colored colored
flake8 flake8
@ -40,6 +43,7 @@ redis
requests requests
requests requests
retry retry
pip-tools
smbus2 smbus2
sphinx sphinx
sphinxcontrib-openapi sphinxcontrib-openapi

View file

@ -5,34 +5,42 @@ alabaster==0.7.13
async-lru==2.0.2 async-lru==2.0.2
async-timeout==4.0.2 async-timeout==4.0.2
attrs==23.1.0 attrs==23.1.0
autocommand==2.2.2
autoflake==2.1.1 autoflake==2.1.1
Babel==2.12.1 Babel==2.12.1
beautifulsoup4==4.12.2 beautifulsoup4==4.12.2
black==23.3.0 black==23.3.0
blinker==1.6.2 blinker==1.6.2
cachetools==5.3.0 build==0.10.0
certifi==2022.12.7 cachetools==5.3.1
certifi==2023.5.7
charset-normalizer==3.1.0 charset-normalizer==3.1.0
cheroot==10.0.0
CherryPy==18.8.0
click==8.1.3 click==8.1.3
colored==1.4.4 colored==1.4.4
commonmark==0.9.1 commonmark==0.9.1
coverage==7.2.2 coverage==7.2.7
decorator==5.1.1 decorator==5.1.1
deepmerge==1.1.0 deepmerge==1.1.0
docutils==0.19 docutils==0.20.1
exceptiongroup==1.1.1
ExifRead==3.0.0 ExifRead==3.0.0
flake8==6.0.0 flake8==6.0.0
Flask==2.3.2 Flask==2.3.2
frozenlist==1.3.3 frozenlist==1.3.3
hypothesis==6.75.5 hypothesis==6.75.9
ibis==3.2.0 ibis==3.2.0
icmplib==3.0.3 icmplib==3.0.3
idna==3.4 idna==3.4
imagesize==1.4.1 imagesize==1.4.1
inflect==6.0.4
iniconfig==2.0.0 iniconfig==2.0.0
isort==5.12.0 isort==5.12.0
itsdangerous==2.1.2 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 jedi==0.18.2
Jinja2==3.1.2 Jinja2==3.1.2
jsonschema==4.17.3 jsonschema==4.17.3
@ -43,52 +51,60 @@ libsass==0.22.0
livereload==2.6.3 livereload==2.6.3
lxml==4.9.2 lxml==4.9.2
Markdown==3.4.3 Markdown==3.4.3
MarkupSafe==2.1.2 MarkupSafe==2.1.3
mccabe==0.7.0 mccabe==0.7.0
meraki==1.33.0 meraki==1.33.0
mirakuru==2.5.1 mirakuru==2.5.1
mistune==2.0.5 mistune==2.0.5
more-itertools==9.1.0
multidict==6.0.4 multidict==6.0.4
mypy-extensions==1.0.0 mypy-extensions==1.0.0
octorest==0.4 octorest==0.4
openapi-schema-validator==0.4.4 openapi-schema-validator==0.4.4
openapi-spec-validator==0.5.6 openapi-spec-validator==0.5.6
packaging==23.0 packaging==23.1
parso==0.8.3 parso==0.8.3
pathable==0.4.3 pathable==0.4.3
pathspec==0.11.1 pathspec==0.11.1
picobox==2.2.0 picobox==3.0.0
platformdirs==3.2.0 pip==23.1.2
pip-tools==6.13.0
platformdirs==3.5.1
pluggy==1.0.0 pluggy==1.0.0
port-for==0.6.3 port-for==0.6.3
portend==3.1.0
prompt-toolkit==3.0.38 prompt-toolkit==3.0.38
proquint==0.2.1 proquint==0.2.1
psutil==5.9.4 psutil==5.9.5
psycopg==3.1.9 psycopg==3.1.9
psycopg2==2.9.6 psycopg2==2.9.6
pudb==2022.1.3 pudb==2022.1.3
py==1.11.0 py==1.11.0
pycodestyle==2.10.0 pycodestyle==2.10.0
pycryptodome==3.18.0 pycryptodome==3.18.0
pydantic==1.10.8
pyflakes==3.0.1 pyflakes==3.0.1
Pygments==2.14.0 Pygments==2.15.1
pyproject_hooks==1.0.0
pyrsistent==0.19.3 pyrsistent==0.19.3
pytest==7.2.2 pytest==7.3.1
pytest-cov==4.1.0 pytest-cov==4.1.0
pytest-postgresql==5.0.0 pytest-postgresql==5.0.0
pytest-pudb==0.7.0 pytest-pudb==0.7.0
pytest-timeout==2.1.0 pytest-timeout==2.1.0
pytz==2023.3
PyYAML==6.0 PyYAML==6.0
recommonmark==0.7.1 recommonmark==0.7.1
redis==4.5.5 redis==4.5.5
requests==2.31.0 requests==2.31.0
retry==0.9.2 retry==0.9.2
rfc3339-validator==0.1.4 rfc3339-validator==0.1.4
setuptools==67.7.2
six==1.16.0 six==1.16.0
smbus2==0.4.2 smbus2==0.4.2
snowballstemmer==2.2.0 snowballstemmer==2.2.0
sortedcontainers==2.4.0 sortedcontainers==2.4.0
soupsieve==2.4 soupsieve==2.4.1
Sphinx==7.0.1 Sphinx==7.0.1
sphinx_mdinclude==0.5.3 sphinx_mdinclude==0.5.3
sphinxcontrib-applehelp==1.0.4 sphinxcontrib-applehelp==1.0.4
@ -100,19 +116,21 @@ sphinxcontrib-openapi==0.8.1
sphinxcontrib-programoutput==0.17 sphinxcontrib-programoutput==0.17
sphinxcontrib-qthelp==1.0.3 sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5 sphinxcontrib-serializinghtml==1.1.5
termcolor==2.2.0 tempora==5.2.2
termcolor==2.3.0
toml==0.10.2 toml==0.10.2
tomli==2.0.1 tornado==6.3.2
tornado==6.2 typing_extensions==4.6.3
typing_extensions==4.5.0
unify==0.5 unify==0.5
untokenize==0.1.1 untokenize==0.1.1
urllib3==1.26.15 urllib3==2.0.2
urwid==2.1.2 urwid==2.1.2
urwid-readline==0.13 urwid-readline==0.13
wcwidth==0.2.6 wcwidth==0.2.6
websocket-client==1.5.1 websocket-client==1.5.2
Werkzeug==2.3.4 Werkzeug==2.3.4
wheel==0.40.0
yamllint==1.32.0 yamllint==1.32.0
yarl==1.8.2 yarl==1.9.2
yaspin==2.3.0 yaspin==2.3.0
zc.lockfile==3.0.post1