""" Seed default notification templates for SMS and Email channels. Templates are company-level (not per-merchant). Usage: python -m scripts.seed_templates python -m scripts.seed_templates --mongodb-uri mongodb://prod-host:27017 Templates seeded: - cust_otp (customer OTP) - staff_otp_login (staff OTP) - otp_verification (generic / service partner OTP) - welcome_credentials (employee & merchant welcome password) - password_reset (password reset) - password_reset_link (password reset with link) """ import asyncio import argparse import uuid import sys import os from datetime import datetime # Allow running from project root sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from motor.motor_asyncio import AsyncIOMotorClient COLLECTION = "notification_templates" # ── Template definitions ───────────────────────────────────────────────────── TEMPLATES = [ # ── Customer OTP ───────────────────────────────────────────────────── { "channel": "sms", "name": "cust_otp", "subject": None, "body": "{{otp}} is your verification code. Valid for {{expiry_minutes}} minutes. Do not share this code with anyone.", "html_body": None, "dlt_template_id": "", "variables": ["otp", "expiry_minutes"], }, { "channel": "email", "name": "cust_otp", "subject": "Your verification code - {{otp}}", "body": "Your verification code is {{otp}}. It is valid for {{expiry_minutes}} minutes. Do not share this code with anyone.", "html_body": ( "
" "

Verification Code

" "

Your verification code is:

" "
{{otp}}
" "

Valid for {{expiry_minutes}} minutes. Do not share this code.

" "
" ), "dlt_template_id": None, "variables": ["otp", "expiry_minutes"], }, # ── Staff OTP ──────────────────────────────────────────────────────── { "channel": "sms", "name": "staff_otp_login", "subject": None, "body": "{{otp}} is your staff login OTP. Valid for {{expiry_minutes}} minutes. Do not share.", "html_body": None, "dlt_template_id": "", "variables": ["otp", "expiry_minutes"], }, { "channel": "email", "name": "staff_otp_login", "subject": "Staff Login OTP - {{otp}}", "body": "Your staff login OTP is {{otp}}. It is valid for {{expiry_minutes}} minutes.", "html_body": ( "
" "

Staff Login OTP

" "

Your one-time password for staff login:

" "
{{otp}}
" "

Valid for {{expiry_minutes}} minutes.

" "
" ), "dlt_template_id": None, "variables": ["otp", "expiry_minutes"], }, # ── Generic / Service Partner OTP ──────────────────────────────────── { "channel": "sms", "name": "otp", "subject": None, "body": "{{otp}} is your OTP. Valid for {{expiry_minutes}} minutes. Do not share this code.", "html_body": None, "dlt_template_id": "", "variables": ["otp", "expiry_minutes"], }, { "channel": "email", "name": "otp", "subject": "Your OTP - {{otp}}", "body": "Your OTP is {{otp}}. It is valid for {{expiry_minutes}} minutes. Do not share this code with anyone.", "html_body": ( "
" "

OTP Verification

" "

Your one-time password:

" "
{{otp}}
" "

Valid for {{expiry_minutes}} minutes.

" "
" ), "dlt_template_id": None, "variables": ["otp", "expiry_minutes"], }, # ── Welcome Credentials (Employee / Merchant) ──────────────────────── { "channel": "sms", "name": "welcome_credentials", "subject": None, "body": "Welcome {{name}}! Your login: Username: {{username}}, Password: {{password}}. Please change your password after first login.", "html_body": None, "dlt_template_id": "", "variables": ["name", "username", "password", "merchant_name"], }, { "channel": "email", "name": "welcome_credentials", "subject": "Welcome {{name}} - Your Login Credentials", "body": "Hi {{name}},\n\nYour account has been created.\n\nUsername: {{username}}\nPassword: {{password}}\n\nPlease change your password after your first login.\n\nRegards,\n{{merchant_name}}", "html_body": ( "
" "

Welcome, {{name}}!

" "

Your account has been created. Here are your login credentials:

" "" "" "" "
Username{{username}}
Password{{password}}
" "

Please change your password after your first login.

" "

{{merchant_name}}

" "
" ), "dlt_template_id": None, "variables": ["name", "username", "password", "merchant_name"], }, # ── Password Reset ─────────────────────────────────────────────────── { "channel": "sms", "name": "password_reset", "subject": None, "body": "Hi {{name}}, your password has been reset. New password: {{new_password}}. Please change it after login.", "html_body": None, "dlt_template_id": "", "variables": ["name", "new_password", "merchant_name"], }, { "channel": "email", "name": "password_reset", "subject": "Password Reset - Action Required", "body": "Hi {{name}},\n\nYour password has been reset.\n\nNew password: {{new_password}}\n\nPlease change it immediately after login.\n\nRegards,\n{{merchant_name}}", "html_body": ( "
" "

Password Reset

" "

Hi {{name}}, your password has been reset.

" "" "" "
New Password{{new_password}}
" "

Please change your password immediately after login.

" "

{{merchant_name}}

" "
" ), "dlt_template_id": None, "variables": ["name", "new_password", "merchant_name"], }, # ── Password Reset Link ────────────────────────────────────────────── { "channel": "sms", "name": "password_reset_link", "subject": None, "body": "Hi {{name}}, reset your password here: {{reset_link}}. Link expires in 60 minutes.", "html_body": None, "dlt_template_id": "", "variables": ["name", "reset_link", "merchant_name"], }, { "channel": "email", "name": "password_reset_link", "subject": "Reset Your Password", "body": "Hi {{name}},\n\nClick the link below to reset your password:\n{{reset_link}}\n\nThis link expires in 60 minutes.\n\nIf you did not request this, please ignore this email.\n\nRegards,\n{{merchant_name}}", "html_body": ( "
" "

Reset Your Password

" "

Hi {{name}},

" "

Click the button below to reset your password:

" "

" "Reset Password" "

" "

This link expires in 60 minutes. If you did not request this, ignore this email.

" "

{{merchant_name}}

" "
" ), "dlt_template_id": None, "variables": ["name", "reset_link", "merchant_name"], }, ] # ── Main ───────────────────────────────────────────────────────────────────── async def main(mongodb_uri: str, db_name: str): """Connect to MongoDB and upsert company-level seed templates.""" client = AsyncIOMotorClient(mongodb_uri) db = client[db_name] col = db[COLLECTION] # Ensure unique index on (channel, name) — company-level await col.create_index( [("channel", 1), ("name", 1)], unique=True, name="ux_channel_name", ) created = 0 skipped = 0 now = datetime.utcnow() for tpl in TEMPLATES: filt = {"channel": tpl["channel"], "name": tpl["name"]} existing = await col.find_one(filt, {"_id": 1}) if existing: print(f" SKIP {tpl['channel']:5s} {tpl['name']} (already exists)") skipped += 1 continue doc = { "template_id": str(uuid.uuid4()), "channel": tpl["channel"], "name": tpl["name"], "subject": tpl.get("subject"), "body": tpl["body"], "html_body": tpl.get("html_body"), "dlt_template_id": tpl.get("dlt_template_id"), "variables": tpl.get("variables", []), "status": "active", "created_by": "seed_script", "created_at": now, "updated_by": None, "updated_at": None, } await col.insert_one(doc) print(f" NEW {tpl['channel']:5s} {tpl['name']} → {doc['template_id']}") created += 1 print(f"\nDone — created: {created}, skipped: {skipped}, total templates: {len(TEMPLATES)}") client.close() if __name__ == "__main__": parser = argparse.ArgumentParser(description="Seed default notification templates (company-level)") parser.add_argument( "--mongodb-uri", default=os.getenv("MONGODB_URI", "mongodb://localhost:27017"), help="MongoDB connection URI (default: $MONGODB_URI or localhost)", ) parser.add_argument( "--db-name", default=os.getenv("MONGODB_DB_NAME", "cuatrolabs"), help="MongoDB database name (default: $MONGODB_DB_NAME or cuatrolabs)", ) args = parser.parse_args() print(f"Seeding company-level notification templates") print(f"MongoDB: {args.mongodb_uri} / {args.db_name}\n") asyncio.run(main(args.mongodb_uri, args.db_name))