From 630252e2ff4b27a70a7a1ce196fca313b61f561e Mon Sep 17 00:00:00 2001 From: Huarch Date: Mon, 3 Aug 2026 18:49:16 +0800 Subject: [PATCH] chore: include authentication services --- app/email.py | 39 +++++++++++++++++++++++++++++++++++++++ app/migrations.py | 22 ++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 app/email.py create mode 100644 app/migrations.py diff --git a/app/email.py b/app/email.py new file mode 100644 index 0000000..16d7b77 --- /dev/null +++ b/app/email.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import logging +from typing import Any + +import resend +from flask import current_app + + +class EmailConfigurationError(RuntimeError): + """Raised when transactional email has not been configured.""" + + +class EmailDeliveryError(RuntimeError): + """Raised when Resend rejects or cannot deliver an email request.""" + + +def send_transactional_email(*, to: str, subject: str, html: str) -> dict[str, Any]: + """Send one application-generated email through Resend.""" + api_key = current_app.config["RESEND_API_KEY"] + from_email = current_app.config["RESEND_FROM_EMAIL"] + if not api_key or not from_email: + raise EmailConfigurationError( + "邮件服务尚未配置,请设置 RESEND_API_KEY 和 RESEND_FROM_EMAIL。" + ) + + resend.api_key = api_key + try: + return resend.Emails.send( + { + "from": from_email, + "to": [to], + "subject": subject, + "html": html, + } + ) + except Exception as exc: + logging.exception("Resend 邮件发送失败: %s", exc) + raise EmailDeliveryError("邮件发送失败,请稍后重试。") from exc diff --git a/app/migrations.py b/app/migrations.py new file mode 100644 index 0000000..3102fc1 --- /dev/null +++ b/app/migrations.py @@ -0,0 +1,22 @@ +"""Small, dependency-free schema migrations for the deployed SQLite database.""" +from sqlalchemy import inspect, text + +from .extensions import db + + +def upgrade_schema() -> None: + """Create new tables and add authentication columns without losing existing data.""" + db.create_all() + inspector = inspect(db.engine) + columns = {item["name"] for item in inspector.get_columns("users")} + additions = { + "email": "VARCHAR(254)", + "email_verified_at": "DATETIME", + "is_active_account": "BOOLEAN NOT NULL DEFAULT 0", + "auth_version": "INTEGER NOT NULL DEFAULT 1", + } + for name, sql_type in additions.items(): + if name not in columns: + db.session.execute(text(f"ALTER TABLE users ADD COLUMN {name} {sql_type}")) + db.session.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS ix_users_email_unique ON users (email) WHERE email IS NOT NULL")) + db.session.commit()