Spaces:
Runtime error
Runtime error
| """ | |
| 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": ( | |
| "<div style='font-family:sans-serif;max-width:480px;margin:auto;padding:24px'>" | |
| "<h2 style='color:#333'>Verification Code</h2>" | |
| "<p>Your verification code is:</p>" | |
| "<div style='font-size:32px;font-weight:bold;letter-spacing:6px;padding:16px;background:#f5f5f5;text-align:center;border-radius:8px'>{{otp}}</div>" | |
| "<p style='color:#666;font-size:13px'>Valid for {{expiry_minutes}} minutes. Do not share this code.</p>" | |
| "</div>" | |
| ), | |
| "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": ( | |
| "<div style='font-family:sans-serif;max-width:480px;margin:auto;padding:24px'>" | |
| "<h2 style='color:#333'>Staff Login OTP</h2>" | |
| "<p>Your one-time password for staff login:</p>" | |
| "<div style='font-size:32px;font-weight:bold;letter-spacing:6px;padding:16px;background:#f5f5f5;text-align:center;border-radius:8px'>{{otp}}</div>" | |
| "<p style='color:#666;font-size:13px'>Valid for {{expiry_minutes}} minutes.</p>" | |
| "</div>" | |
| ), | |
| "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": ( | |
| "<div style='font-family:sans-serif;max-width:480px;margin:auto;padding:24px'>" | |
| "<h2 style='color:#333'>OTP Verification</h2>" | |
| "<p>Your one-time password:</p>" | |
| "<div style='font-size:32px;font-weight:bold;letter-spacing:6px;padding:16px;background:#f5f5f5;text-align:center;border-radius:8px'>{{otp}}</div>" | |
| "<p style='color:#666;font-size:13px'>Valid for {{expiry_minutes}} minutes.</p>" | |
| "</div>" | |
| ), | |
| "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": ( | |
| "<div style='font-family:sans-serif;max-width:520px;margin:auto;padding:24px'>" | |
| "<h2 style='color:#333'>Welcome, {{name}}!</h2>" | |
| "<p>Your account has been created. Here are your login credentials:</p>" | |
| "<table style='width:100%;border-collapse:collapse;margin:16px 0'>" | |
| "<tr><td style='padding:8px 12px;background:#f5f5f5;font-weight:bold;width:120px'>Username</td><td style='padding:8px 12px;background:#f5f5f5'>{{username}}</td></tr>" | |
| "<tr><td style='padding:8px 12px;font-weight:bold'>Password</td><td style='padding:8px 12px'>{{password}}</td></tr>" | |
| "</table>" | |
| "<p style='color:#c00;font-size:13px'>Please change your password after your first login.</p>" | |
| "<p style='color:#999;font-size:12px'>{{merchant_name}}</p>" | |
| "</div>" | |
| ), | |
| "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": ( | |
| "<div style='font-family:sans-serif;max-width:520px;margin:auto;padding:24px'>" | |
| "<h2 style='color:#333'>Password Reset</h2>" | |
| "<p>Hi {{name}}, your password has been reset.</p>" | |
| "<table style='width:100%;border-collapse:collapse;margin:16px 0'>" | |
| "<tr><td style='padding:8px 12px;background:#f5f5f5;font-weight:bold;width:140px'>New Password</td><td style='padding:8px 12px;background:#f5f5f5'>{{new_password}}</td></tr>" | |
| "</table>" | |
| "<p style='color:#c00;font-size:13px'>Please change your password immediately after login.</p>" | |
| "<p style='color:#999;font-size:12px'>{{merchant_name}}</p>" | |
| "</div>" | |
| ), | |
| "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": ( | |
| "<div style='font-family:sans-serif;max-width:520px;margin:auto;padding:24px'>" | |
| "<h2 style='color:#333'>Reset Your Password</h2>" | |
| "<p>Hi {{name}},</p>" | |
| "<p>Click the button below to reset your password:</p>" | |
| "<p style='text-align:center;margin:24px 0'>" | |
| "<a href='{{reset_link}}' style='background:#2563eb;color:#fff;padding:12px 32px;border-radius:6px;text-decoration:none;font-weight:bold'>Reset Password</a>" | |
| "</p>" | |
| "<p style='color:#666;font-size:13px'>This link expires in 60 minutes. If you did not request this, ignore this email.</p>" | |
| "<p style='color:#999;font-size:12px'>{{merchant_name}}</p>" | |
| "</div>" | |
| ), | |
| "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)) | |