메일 알림 시스템 추가

This commit is contained in:
Yun Chan 2026-06-29 17:07:01 +09:00
parent ddf12a851c
commit 3bf38c50df
22 changed files with 1769 additions and 31 deletions

View file

@ -207,12 +207,41 @@ async def check_database(database_url: str, *, require_app_role: bool) -> Check:
to_regclass('app.session_evaluation') IS NOT NULL AS has_session_evaluation,
to_regclass('app.live_coach_events') IS NOT NULL AS has_live_coach_events,
to_regclass('app.session_share_link') IS NOT NULL AS has_session_share_link,
to_regclass('app.notification_event') IS NOT NULL AS has_notification_event,
to_regclass('app.notification_delivery') IS NOT NULL AS has_notification_delivery,
to_regclass('app.sessions') IS NOT NULL AS has_sessions,
to_regclass('app.turns') IS NOT NULL AS has_turns,
to_regclass('app.session_review_status') IS NOT NULL AS has_session_review_status,
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'app'
AND table_name = 'persona_card'
AND column_name = 'triggers'
) AS has_persona_triggers,
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'app'
AND table_name = 'turns'
AND column_name = 'provider_events'
) AS has_turn_provider_events,
EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'app'
AND table_name = 'session_review_status'
AND column_name IN (
'worksheet_status',
'worksheet_note',
'worksheet_reviewed_at'
)
GROUP BY table_schema, table_name
HAVING count(*) = 3
) AS has_session_review_worksheet_columns,
EXISTS (
SELECT 1 FROM pg_indexes
WHERE schemaname = 'app'
AND tablename = 'notification_delivery'
AND indexname = 'idx_notification_delivery_queue'
) AS has_notification_delivery_queue_index,
EXISTS (
SELECT 1 FROM pg_policies
WHERE schemaname = 'app'
@ -245,7 +274,15 @@ async def check_database(database_url: str, *, require_app_role: bool) -> Check:
"has_session_evaluation",
"has_live_coach_events",
"has_session_share_link",
"has_notification_event",
"has_notification_delivery",
"has_sessions",
"has_turns",
"has_session_review_status",
"has_persona_triggers",
"has_turn_provider_events",
"has_session_review_worksheet_columns",
"has_notification_delivery_queue_index",
"has_session_write_policies",
)
if not row[key]

View file

@ -0,0 +1,44 @@
"""Drain queued Vignette email notifications once.
Use from Task Scheduler, cron, or a one-shot ops shell after SMTP env is loaded.
"""
from __future__ import annotations
import argparse
import asyncio
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
API_ROOT = ROOT / "apps" / "api"
if str(API_ROOT) not in sys.path:
sys.path.insert(0, str(API_ROOT))
async def main_async(argv: list[str]) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--limit", type=int, default=25, help="Maximum deliveries to process.")
args = parser.parse_args(argv)
from app.db import close_pool, init_pool
from app.services.notifications import ensure_notification_tables, process_queued_email_notifications
await init_pool()
try:
await ensure_notification_tables()
result = await process_queued_email_notifications(limit=args.limit)
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
return 1 if result.get("failed", 0) else 0
finally:
await close_pool()
def main() -> int:
return asyncio.run(main_async(sys.argv[1:]))
if __name__ == "__main__":
raise SystemExit(main())