Simplify emails with a spool

This commit is contained in:
Reid 'arrdem' McKenzie 2023-06-02 23:58:29 -06:00
parent aa65b03d4a
commit 3ac56e2cbb
5 changed files with 67 additions and 29 deletions

View file

@ -84,16 +84,26 @@ def post_register():
break
if res := ctx.db.try_create_user(
if user := ctx.db.try_create_user(
username, email, salt(request.form["password"]), group_id, status_id
):
id, status = res
if status == -1:
if user.status_id == -2:
ctx.db.create_email(
user.id,
"Tentacles email confirmation",
render_template(
"verification_email.html.j2",
username=user.name,
token_id=user.verification_token,
base_url=request.root_url,
),
)
flash(
"Please check your email for a verification request",
category="success",
)
return render_template("register.html.j2")
return render_template("register.html.j2")
except Exception as e:
log.exception("Error encountered while registering a user...")

View file

@ -1,3 +1,5 @@
----------------------------------------------------------------------------------------------------
-- User structures
CREATE TABLE IF NOT EXISTS groups (
id INTEGER PRIMARY KEY AUTOINCREMENT
, name TEXT
@ -100,3 +102,14 @@ CREATE TABLE IF NOT EXISTS jobs (
, FOREIGN KEY(file_id) REFERENCES files(id)
, FOREIGN KEY(printer_id) REFERENCES printer(id)
);
----------------------------------------------------------------------------------------------------
-- Emails are used for notifications
CREATE TABLE IF NOT EXISTS email_spool (
id INTEGER PRIMARY KEY AUTOINCREMENT
, user_id INTEGER NOT NULL
, subject TEXT
, body TEXT
, sent_at TEXT
, FOREIGN KEY(user_id) REFERENCES users(id)
);

View file

@ -103,7 +103,7 @@ class Store(object):
digest.update(password.encode("utf-8"))
digest = digest.hexdigest()
return self._conn.execute(
"INSERT INTO users (name, email, hash, group_id, status_id) VALUES (?, ?, ?, ?, ?) RETURNING id, status_id",
"INSERT INTO users (name, email, hash, group_id, status_id) VALUES (?, ?, ?, ?, ?) RETURNING *",
[username, email, digest, group_id, status_id],
).fetchone()
@ -521,3 +521,33 @@ class Store(object):
@requires_conn
def delete_job(self, uid: int, jid: int):
self._conn.execute("DELETE FROM jobs WHERE user_id = ? and id = ?", [uid, jid])
################################################################################
# Emails (notifications)
@requires_conn
def create_email(self, uid: int, subject: str, body: str):
return self._conn.execute(
"INSERT INTO email_spool (user_id, subject, body) VALUES (?1, ?2, ?3) RETURNING id",
[uid, subject, body],
).fetchone()
@requires_conn
def send_email(self, eid: int):
return self._conn.execute(
"UPDATE email_spool SET sent_at = datetime('now') WHERE id = ?1", [eid]
)
@requires_conn
def poll_spool(self, limit: int = 16):
return self._conn.execute(
"""
SELECT s.id as `id`, u.email as `to`, subject, body
FROM email_spool s
INNER JOIN users u
ON s.user_id = u.id
WHERE s.sent_at IS NULL
LIMIT ?1
""",
[limit],
).fetchall()

View file

@ -21,11 +21,11 @@
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.
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}}
{{ base_url }}user/verify?token={{token_id}}
</code>
</pre>
<p>

View file

@ -254,35 +254,21 @@ def pull_jobs(app: App, store: Store) -> None:
log.exception("Oop")
def send_verifications(app, store: Store):
def send_emails(app, store: Store):
with closing(
FastMailSMTP(
app.config.get("fastmail", {}).get("username"),
app.config.get("fastmail", {}).get("key"),
)
) as fm:
for user in store.list_unverified_users():
for message in store.poll_spool():
fm.send_message(
from_addr="root@tirefireind.us",
to_addrs=[user.email],
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,
),
to_addrs=[message.to],
subject=message.subject,
msg=message.body,
)
def send_approvals(app, store: Store):
with closing(
FastMailSMTP(
app.config.get("fastmail", {}).get("username"),
app.config.get("fastmail", {}).get("key"),
)
):
pass
store.send_email(message.id)
@corn_job(timedelta(seconds=5))
@ -293,8 +279,7 @@ def run_worker(app: App, db_factory):
push_jobs(app, store)
revoke_jobs(app, store)
pull_jobs(app, store)
send_verifications(app, store)
send_approvals(app, store)
send_emails(app, store)
def create_workers(app, db_factory: Callable[[App], Store]) -> Event: