mail notification
This commit is contained in:
@@ -53,6 +53,8 @@ class SyncUser(Base):
|
||||
mywhoosh_password_enc: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
garmin_email_enc: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
garmin_password_enc: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
notify_email_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
notification_email: Mapped[str | None] = mapped_column(String(255))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
|
||||
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy import create_engine, inspect, text
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from app.db.models import Base
|
||||
|
||||
# Columns added to existing tables after their initial release. create_all()
|
||||
# only creates missing tables, never adds columns to tables that already
|
||||
# exist, so a column added to a model here must also be listed below or an
|
||||
# already-deployed database will never receive it and the app will crash
|
||||
# reading/writing that column.
|
||||
_ADDITIVE_COLUMNS: dict[str, list[tuple[str, str]]] = {
|
||||
"sync_users": [
|
||||
("notify_email_enabled", "BOOLEAN NOT NULL DEFAULT 0"),
|
||||
("notification_email", "VARCHAR(255)"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def create_db_engine(database_url: str) -> Engine:
|
||||
connect_args = {"check_same_thread": False} if database_url.startswith("sqlite") else {}
|
||||
@@ -16,3 +28,24 @@ def create_session_factory(engine: Engine) -> sessionmaker[Session]:
|
||||
|
||||
def initialize_schema(engine: Engine) -> None:
|
||||
Base.metadata.create_all(engine)
|
||||
_apply_additive_migrations(engine)
|
||||
|
||||
|
||||
def _apply_additive_migrations(engine: Engine) -> None:
|
||||
if engine.dialect.name != "sqlite":
|
||||
# ALTER TABLE ... ADD COLUMN syntax/type names below are only
|
||||
# verified against sqlite, the only backend this app is deployed
|
||||
# against; a fresh create_all() on another backend already has every
|
||||
# current column, so skipping here only matters for a pre-existing
|
||||
# non-sqlite database, which does not exist in practice.
|
||||
return
|
||||
inspector = inspect(engine)
|
||||
existing_tables = set(inspector.get_table_names())
|
||||
with engine.begin() as conn:
|
||||
for table, columns in _ADDITIVE_COLUMNS.items():
|
||||
if table not in existing_tables:
|
||||
continue
|
||||
existing_columns = {col["name"] for col in inspector.get_columns(table)}
|
||||
for name, ddl_type in columns:
|
||||
if name not in existing_columns:
|
||||
conn.execute(text(f"ALTER TABLE {table} ADD COLUMN {name} {ddl_type}"))
|
||||
|
||||
Reference in New Issue
Block a user