Files
webmail-client/backend/migrations/versions/0022_push_notifications.py
T

55 lines
2.0 KiB
Python
Raw Normal View History

"""push notifications about new mail (Web Push, RFC 8030/8291/8292)
``push_subscriptions`` holds one row per browser (or installed app) that asked
to be told about new mail: the push service URL it gave, and the two keys the
message is encrypted to. ``session_sid`` is the sign-in it was made under — once
that session is gone (signed out, revoked, expired) nothing is sent there any
more, so a signed-out device never shows a subject line again.
The server signs every push with its own key pair (VAPID). It is generated on
first use and kept in ``app_settings``; the private half is vault-encrypted
with MASTER_KEY like every other server secret.
Revision ID: 0022
Revises: 0021
Create Date: 2026-10-08
"""
from __future__ import annotations
from collections.abc import Sequence
from alembic import op
revision: str = "0022"
down_revision: str | None = "0021"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.execute(
"""
CREATE TABLE push_subscriptions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
endpoint text NOT NULL UNIQUE,
p256dh text NOT NULL,
auth text NOT NULL,
session_sid text NOT NULL,
user_agent text NOT NULL DEFAULT '',
created_at timestamptz NOT NULL DEFAULT now(),
last_used_at timestamptz
)
"""
)
op.execute("CREATE INDEX ix_push_subscriptions_user ON push_subscriptions (user_id)")
op.execute("ALTER TABLE app_settings ADD COLUMN vapid_public text NOT NULL DEFAULT ''")
op.execute("ALTER TABLE app_settings ADD COLUMN vapid_private_ct bytea")
def downgrade() -> None:
op.execute("ALTER TABLE app_settings DROP COLUMN IF EXISTS vapid_private_ct")
op.execute("ALTER TABLE app_settings DROP COLUMN IF EXISTS vapid_public")
op.execute("DROP TABLE IF EXISTS push_subscriptions")