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
|
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
|
username, email, salt(request.form["password"]), group_id, status_id
|
||||||
):
|
):
|
||||||
id, status = res
|
if user.status_id == -2:
|
||||||
if status == -1:
|
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(
|
flash(
|
||||||
"Please check your email for a verification request",
|
"Please check your email for a verification request",
|
||||||
category="success",
|
category="success",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
----------------------------------------------------------------------------------------------------
|
||||||
|
-- User structures
|
||||||
CREATE TABLE IF NOT EXISTS groups (
|
CREATE TABLE IF NOT EXISTS groups (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
, name TEXT
|
, name TEXT
|
||||||
|
@ -100,3 +102,14 @@ CREATE TABLE IF NOT EXISTS jobs (
|
||||||
, FOREIGN KEY(file_id) REFERENCES files(id)
|
, FOREIGN KEY(file_id) REFERENCES files(id)
|
||||||
, FOREIGN KEY(printer_id) REFERENCES printer(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.update(password.encode("utf-8"))
|
||||||
digest = digest.hexdigest()
|
digest = digest.hexdigest()
|
||||||
return self._conn.execute(
|
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],
|
[username, email, digest, group_id, status_id],
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
|
@ -521,3 +521,33 @@ class Store(object):
|
||||||
@requires_conn
|
@requires_conn
|
||||||
def delete_job(self, uid: int, jid: int):
|
def delete_job(self, uid: int, jid: int):
|
||||||
self._conn.execute("DELETE FROM jobs WHERE user_id = ? and id = ?", [uid, jid])
|
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 }}!
|
Welcome {{ username }}!
|
||||||
</p>
|
</p>
|
||||||
<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>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
{{ base_url }}/user/verify?token={{token_id}}
|
{{ base_url }}user/verify?token={{token_id}}
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -254,35 +254,21 @@ def pull_jobs(app: App, store: Store) -> None:
|
||||||
log.exception("Oop")
|
log.exception("Oop")
|
||||||
|
|
||||||
|
|
||||||
def send_verifications(app, store: Store):
|
def send_emails(app, store: Store):
|
||||||
with closing(
|
with closing(
|
||||||
FastMailSMTP(
|
FastMailSMTP(
|
||||||
app.config.get("fastmail", {}).get("username"),
|
app.config.get("fastmail", {}).get("username"),
|
||||||
app.config.get("fastmail", {}).get("key"),
|
app.config.get("fastmail", {}).get("key"),
|
||||||
)
|
)
|
||||||
) as fm:
|
) as fm:
|
||||||
for user in store.list_unverified_users():
|
for message in store.poll_spool():
|
||||||
fm.send_message(
|
fm.send_message(
|
||||||
from_addr="root@tirefireind.us",
|
from_addr="root@tirefireind.us",
|
||||||
to_addrs=[user.email],
|
to_addrs=[message.to],
|
||||||
subject="Email verification from tentacles",
|
subject=message.subject,
|
||||||
msg=render_template(
|
msg=message.body,
|
||||||
"verification_email.html.j2",
|
|
||||||
base_url=app.config.get("base_url"),
|
|
||||||
username=user.username,
|
|
||||||
token_id=user.verification_token,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
store.send_email(message.id)
|
||||||
|
|
||||||
def send_approvals(app, store: Store):
|
|
||||||
with closing(
|
|
||||||
FastMailSMTP(
|
|
||||||
app.config.get("fastmail", {}).get("username"),
|
|
||||||
app.config.get("fastmail", {}).get("key"),
|
|
||||||
)
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@corn_job(timedelta(seconds=5))
|
@corn_job(timedelta(seconds=5))
|
||||||
|
@ -293,8 +279,7 @@ def run_worker(app: App, db_factory):
|
||||||
push_jobs(app, store)
|
push_jobs(app, store)
|
||||||
revoke_jobs(app, store)
|
revoke_jobs(app, store)
|
||||||
pull_jobs(app, store)
|
pull_jobs(app, store)
|
||||||
send_verifications(app, store)
|
send_emails(app, store)
|
||||||
send_approvals(app, store)
|
|
||||||
|
|
||||||
|
|
||||||
def create_workers(app, db_factory: Callable[[App], Store]) -> Event:
|
def create_workers(app, db_factory: Callable[[App], Store]) -> Event:
|
||||||
|
|
Loading…
Reference in a new issue