Simplify emails with a spool
This commit is contained in:
parent
2a59c41903
commit
d9db59b1db
5 changed files with 67 additions and 29 deletions
|
@ -84,11 +84,21 @@ 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",
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue