chore: include authentication services
This commit is contained in:
@@ -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
|
||||||
@@ -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()
|
||||||
Reference in New Issue
Block a user