From 8fc52b7c6a53c873c5f292bfdaf8bf51e55747c5 Mon Sep 17 00:00:00 2001 From: Quinn Ftw Date: Sun, 28 Dec 2025 21:36:20 -0800 Subject: [PATCH] feat(email): update architecture docs and add seed migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Email system updates: - Update ARCHITECTURE.md with latest design decisions - Update backend package.json configuration - Enhance app.module.ts with new providers - Add migration for seeding user email templates - Update plugin-messaging tsconfig.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- features/email/ARCHITECTURE.md | 10 +- features/email/backend/package.json | 1 + features/email/backend/src/app.module.ts | 17 +++ .../1735400007-SeedUserEmailTemplates.ts | 103 ++++++++++++++++++ .../email/backend/src/migrations/index.ts | 1 + features/email/plugin-messaging/tsconfig.json | 2 +- 6 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 features/email/backend/src/migrations/1735400007-SeedUserEmailTemplates.ts diff --git a/features/email/ARCHITECTURE.md b/features/email/ARCHITECTURE.md index 5bff5c67c..3221fb363 100644 --- a/features/email/ARCHITECTURE.md +++ b/features/email/ARCHITECTURE.md @@ -793,10 +793,12 @@ Platform-level emails: - [x] Outbound message-to-email conversion - [x] Reply-to token generation/parsing -### Phase 6: User Emails (PLANNED) -- [ ] Implement user email templates (welcome, verification, password) -- [ ] Integrate with identity feature -- [ ] Testing and validation +### Phase 6: User Emails ✅ COMPLETE +- [x] Implement user email templates (welcome, verification, password-reset, account-alert) +- [x] UsersEmailService with 6 methods (welcome, verification, password reset, password changed, account locked, login alert) +- [x] Template rendering with base layout +- [ ] Template seeding script (database population pending) +- [ ] Integration with identity feature (pending) ### Phase 7: Order Emails (PLANNED) - [ ] Implement order email templates diff --git a/features/email/backend/package.json b/features/email/backend/package.json index fc7848d5f..614b005c0 100644 --- a/features/email/backend/package.json +++ b/features/email/backend/package.json @@ -17,6 +17,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@lilith/registry-integration": "workspace:*", "@lilith/types": "workspace:*", "@nestjs/bull": "^10.1.0", "@nestjs/common": "^10.0.0", diff --git a/features/email/backend/src/app.module.ts b/features/email/backend/src/app.module.ts index eaf457061..84b3e35db 100644 --- a/features/email/backend/src/app.module.ts +++ b/features/email/backend/src/app.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common' import { ConfigModule, ConfigService } from '@nestjs/config' import { BullModule } from '@nestjs/bull' import { TypeOrmModule } from '@nestjs/typeorm' +import { RegistryModule } from '@lilith/registry-integration' import { CoreModule } from './core/core.module' import { AddressesModule } from './addresses/addresses.module' @@ -50,6 +51,22 @@ import { HealthController } from './health.controller' }), }), + // Service Registry (auto-registration) + RegistryModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + name: 'email', + type: 'backend', + port: configService.get('PORT', 3011), + healthEndpoint: '/health', + metadata: { + description: 'Centralized email service for Lilith Platform', + version: '1.0.0', + }, + }), + }), + // Feature modules CoreModule, AddressesModule, diff --git a/features/email/backend/src/migrations/1735400007-SeedUserEmailTemplates.ts b/features/email/backend/src/migrations/1735400007-SeedUserEmailTemplates.ts new file mode 100644 index 000000000..9e9ff21ff --- /dev/null +++ b/features/email/backend/src/migrations/1735400007-SeedUserEmailTemplates.ts @@ -0,0 +1,103 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +/** + * Seeds the email_templates table with user email templates. + * These templates reference .hbs files in templates/users/ directory. + */ +export class SeedUserEmailTemplates1735400007 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + // Seed user email templates + await queryRunner.query(` + INSERT INTO email_templates (id, name, category, subject_template, html_template, text_template, variables, is_active, updated_at) + VALUES + -- Welcome email + ( + uuid_generate_v4(), + 'welcome', + 'users', + 'Welcome to Lilith Platform, {{name}}!', + '{{> users/welcome}}', + 'Welcome to Lilith Platform! Visit {{dashboardUrl}} to get started.', + '{"userName": {"description": "User display name", "required": false}, "userEmail": {"description": "User email address", "required": true}, "accountType": {"description": "Account type (creator, subscriber)", "required": false}, "joinDate": {"description": "Registration date", "required": false}, "dashboardUrl": {"description": "Dashboard URL", "required": true}, "guideUrl": {"description": "Getting started guide URL", "required": false}, "helpCenterUrl": {"description": "Help center URL", "required": false}, "supportUrl": {"description": "Support page URL", "required": false}}'::jsonb, + true, + now() + ), + -- Email verification + ( + uuid_generate_v4(), + 'email-verification', + 'users', + 'Verify your email address', + '{{> users/verification}}', + 'Please verify your email by clicking: {{verificationUrl}}. Link expires in {{expiresIn}}.', + '{"userName": {"description": "User display name", "required": false}, "verificationUrl": {"description": "Email verification URL", "required": true}, "expiresIn": {"description": "Expiration time (e.g. 24 hours)", "required": true}}'::jsonb, + true, + now() + ), + -- Password reset + ( + uuid_generate_v4(), + 'password-reset', + 'users', + 'Reset your password', + '{{> users/password-reset}}', + 'Reset your password by clicking: {{resetUrl}}. Link expires in {{expiresIn}}.', + '{"userName": {"description": "User display name", "required": false}, "resetUrl": {"description": "Password reset URL", "required": true}, "expiresIn": {"description": "Expiration time (e.g. 1 hour)", "required": true}}'::jsonb, + true, + now() + ), + -- Password changed confirmation + ( + uuid_generate_v4(), + 'password-changed', + 'users', + 'Your password has been changed', + '{{> users/account-alert}}', + 'Your password was changed on {{changedAt}}. If this was not you, contact support immediately.', + '{"userName": {"description": "User display name", "required": false}, "changedAt": {"description": "ISO timestamp of change", "required": true}, "supportUrl": {"description": "Support page URL", "required": true}}'::jsonb, + true, + now() + ), + -- Account locked + ( + uuid_generate_v4(), + 'account-locked', + 'users', + 'Your account has been locked', + '{{> users/account-alert}}', + 'Your account has been locked due to {{reason}}. Visit {{unlockUrl}} to unlock.', + '{"userName": {"description": "User display name", "required": false}, "reason": {"description": "Lock reason", "required": false}, "unlockUrl": {"description": "Account unlock URL", "required": false}, "supportUrl": {"description": "Support page URL", "required": true}}'::jsonb, + true, + now() + ), + -- Login alert + ( + uuid_generate_v4(), + 'login-alert', + 'users', + 'New login to your account', + '{{> users/account-alert}}', + 'New login detected from {{device}} in {{location}} at {{loginTime}}.', + '{"userName": {"description": "User display name", "required": false}, "device": {"description": "Device name/type", "required": false}, "location": {"description": "Geographic location", "required": false}, "ipAddress": {"description": "IP address", "required": false}, "loginTime": {"description": "ISO timestamp of login", "required": true}, "securityUrl": {"description": "Security settings URL", "required": true}}'::jsonb, + true, + now() + ) + ON CONFLICT (name) DO NOTHING; + `) + } + + public async down(queryRunner: QueryRunner): Promise { + // Remove seeded templates + await queryRunner.query(` + DELETE FROM email_templates + WHERE name IN ( + 'welcome', + 'email-verification', + 'password-reset', + 'password-changed', + 'account-locked', + 'login-alert' + ); + `) + } +} diff --git a/features/email/backend/src/migrations/index.ts b/features/email/backend/src/migrations/index.ts index 2e6a2d51e..f677a15c7 100644 --- a/features/email/backend/src/migrations/index.ts +++ b/features/email/backend/src/migrations/index.ts @@ -17,3 +17,4 @@ export { CreateEmailPreferences1735400003 } from './1735400003-CreateEmailPrefer export { CreateEmailAddresses1735400004 } from './1735400004-CreateEmailAddresses' export { CreateEmailAliases1735400005 } from './1735400005-CreateEmailAliases' export { CreateEmailThreadMappings1735400006 } from './1735400006-CreateEmailThreadMappings' +export { SeedUserEmailTemplates1735400007 } from './1735400007-SeedUserEmailTemplates' diff --git a/features/email/plugin-messaging/tsconfig.json b/features/email/plugin-messaging/tsconfig.json index 779d83a3d..ffc931ab7 100644 --- a/features/email/plugin-messaging/tsconfig.json +++ b/features/email/plugin-messaging/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../@packages/@core/config/tsconfig.base.json", + "extends": "@transquinnftw/configs/typescript/nestjs.json", "compilerOptions": { "module": "commonjs", "moduleResolution": "node",