diff --git a/.gitignore b/.gitignore
index 37a916a..0c26cad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,6 @@
/configs/cloudflare/*.yaml
/configs/cloudflare/*.yml
-.excalidraw
\ No newline at end of file
+.excalidraw
+
+/.VSCodeCounter
\ No newline at end of file
diff --git a/config.sh b/config.sh
index cef59a5..d00651f 100755
--- a/config.sh
+++ b/config.sh
@@ -183,6 +183,7 @@ initialize_available_ports() {
["GITEA_WEB_PORT"]=3030
["GITEA_SSH_PORT"]=2222
["MAP_PORT"]=3000
+ ["INFLUENCE_PORT"]=3333
["MINI_QR_PORT"]=8089
)
@@ -248,6 +249,7 @@ HOMEPAGE_PORT=${HOMEPAGE_PORT:-3010}
GITEA_WEB_PORT=${GITEA_WEB_PORT:-3030}
GITEA_SSH_PORT=${GITEA_SSH_PORT:-2222}
MAP_PORT=${MAP_PORT:-3000}
+INFLUENCE_PORT=${INFLUENCE_PORT:-3333}
MINI_QR_PORT=${MINI_QR_PORT:-8089}
# Domain Configuration
@@ -317,6 +319,7 @@ EOL
echo "Gitea Web: ${GITEA_WEB_PORT:-3030}"
echo "Gitea SSH: ${GITEA_SSH_PORT:-2222}"
echo "Map: ${MAP_PORT:-3000}"
+ echo "Influence: ${INFLUENCE_PORT:-3333}"
echo "Mini QR: ${MINI_QR_PORT:-8089}"
echo "================================"
}
@@ -374,6 +377,7 @@ update_services_yaml() {
["Listmonk"]="listmonk.$new_domain"
["NocoDB"]="db.$new_domain"
["Map Server"]="map.$new_domain"
+ ["Influence"]="influence.$new_domain"
["Main Site"]="$new_domain"
["MkDocs (Live)"]="docs.$new_domain"
["Mini QR"]="qr.$new_domain"
@@ -527,6 +531,9 @@ ingress:
- hostname: map.$new_domain
service: http://localhost:${MAP_PORT:-3000}
+ - hostname: influence.$new_domain
+ service: http://localhost:${INFLUENCE_PORT:-3333}
+
- hostname: qr.$new_domain
service: http://localhost:${MINI_QR_PORT:-8089}
@@ -1203,6 +1210,7 @@ echo " - n8n: http://localhost:${N8N_PORT:-5678}"
echo " - NocoDB: http://localhost:${NOCODB_PORT:-8090}"
echo " - Gitea: http://localhost:${GITEA_WEB_PORT:-3030}"
echo " - Map: http://localhost:${MAP_PORT:-3000}"
+echo " - Influence: http://localhost:${INFLUENCE_PORT:-3333}"
echo " - Mini QR: http://localhost:${MINI_QR_PORT:-8089}"
echo ""
echo "3. When ready for production:"
diff --git a/configs/cloudflare/tunnel-config.yml b/configs/cloudflare/tunnel-config.yml
index 80a5078..d3d7862 100644
--- a/configs/cloudflare/tunnel-config.yml
+++ b/configs/cloudflare/tunnel-config.yml
@@ -24,4 +24,6 @@ ingress:
service: http://localhost:3000
- hostname: qr.cmlite.org
service: http://localhost:8089
+ - hostname: influence.cmlite.org
+ service: http://localhost:3333
- service: http_status:404
diff --git a/configs/homepage/services.yaml b/configs/homepage/services.yaml
index d30db17..6510fbe 100644
--- a/configs/homepage/services.yaml
+++ b/configs/homepage/services.yaml
@@ -8,12 +8,6 @@
href: "https://code.cmlite.org"
description: VS Code in the browser - Platform Editor
container: code-server-changemaker
-
- - Listmonk:
- icon: mdi-email-newsletter
- href: "https://listmonk.cmlite.org"
- description: Newsletter & mailing list manager
- container: listmonk_app
- NocoDB:
icon: mdi-database
@@ -27,6 +21,12 @@
description: Map server for geospatial data
container: nocodb-map-viewer
+ - Influence:
+ icon: mdi-account-group
+ href: "https://influence.cmlite.org"
+ description: Political influence and campaign management
+ container: influence-app-1
+
- Content & Documentation:
- Main Site:
@@ -46,8 +46,13 @@
href: "https://qr.cmlite.org"
description: QR code generator
container: mini-qr
+
+ - Listmonk:
+ icon: mdi-email-newsletter
+ href: "https://listmonk.cmlite.org"
+ description: Newsletter & mailing list manager
+ container: listmonk_app
-
- Automation & Infrastructure:
- n8n:
icon: mdi-robot-industrial
diff --git a/influence/README.MD b/influence/README.MD
index c1e5a68..1b0b472 100644
--- a/influence/README.MD
+++ b/influence/README.MD
@@ -1,4 +1,4 @@
-# Alberta Influence Campaign Tool
+# BNKops Influence Campaign Tool
A comprehensive web application that helps Alberta residents connect with their elected representatives across all levels of government. Users can find their representatives by postal code and send direct emails to advocate for important issues.
@@ -57,6 +57,111 @@ A comprehensive web application that helps Alberta residents connect with their
- Enter an Alberta postal code (e.g., T5N4B8)
- View your representatives and send emails
+## Development Mode
+
+### Email Testing with MailHog
+
+For development and testing, the application includes MailHog integration to safely test email functionality without sending real emails to elected officials.
+
+#### Quick Setup for Development
+
+1. **Use development configuration**:
+ ```bash
+ # Your .env should include these settings for development:
+ NODE_ENV=development
+ EMAIL_TEST_MODE=true
+ SMTP_HOST=mailhog
+ SMTP_PORT=1025
+ SMTP_USER=test
+ SMTP_PASS=test
+ TEST_EMAIL_RECIPIENT=your-email@example.com
+ ```
+
+2. **Start with MailHog included**:
+ ```bash
+ docker compose up --build
+ ```
+
+3. **Access development tools**:
+ - **Application**: http://localhost:3333
+ - **Email Testing Interface**: http://localhost:3333/email-test.html (admin login required)
+ - **MailHog Web UI**: http://localhost:8025 (view all caught emails)
+
+#### Email Testing Features
+
+**Test Mode Benefits:**
+- ✅ All emails redirected to your test recipient
+- ✅ Original recipient shown in subject line: `[TEST - Original: real@email.com] Subject`
+- ✅ Safe testing without spamming elected officials
+- ✅ Complete email logging with test mode indicators
+
+**Email Testing Interface** (`/email-test.html`):
+- **Quick Test**: Send test email with one click
+- **Email Preview**: See exactly how emails will look before sending
+- **Custom Composition**: Test with your own subject and message content
+- **Email Logs**: View all sent emails with test/live filtering
+- **SMTP Diagnostics**: Test connection and troubleshoot issues
+
+**MailHog Web Interface** (`http://localhost:8025`):
+- View all emails caught during development
+- Inspect email content, headers, and formatting
+- Search and filter caught emails
+- No emails leave your local environment
+
+#### Development Workflow
+
+1. **Safe Development**:
+ ```bash
+ # Ensure test mode is enabled
+ EMAIL_TEST_MODE=true
+
+ # Start development environment
+ docker compose up --build
+ ```
+
+2. **Test Email Functionality**:
+ - Use the main app to send emails (they'll be redirected)
+ - Check MailHog UI to see the actual email content
+ - Use `/email-test.html` for advanced testing and preview
+
+3. **Production Deployment**:
+ ```bash
+ # Switch to production SMTP settings
+ EMAIL_TEST_MODE=false
+ SMTP_HOST=smtp.your-provider.com
+ SMTP_USER=your-real-email@domain.com
+ SMTP_PASS=your-real-password
+
+ # Restart application
+ docker compose restart
+ ```
+
+#### Development Environment Variables
+
+```bash
+# Development Mode Configuration
+NODE_ENV=development
+EMAIL_TEST_MODE=true
+
+# MailHog SMTP (for development)
+SMTP_HOST=mailhog
+SMTP_PORT=1025
+SMTP_SECURE=false
+SMTP_USER=test
+SMTP_PASS=test
+SMTP_FROM_EMAIL=dev@albertainfluence.local
+SMTP_FROM_NAME="Alberta Influence Campaign (DEV)"
+
+# Email Testing
+TEST_EMAIL_RECIPIENT=developer@example.com
+
+# Production SMTP (commented out for dev)
+# SMTP_HOST=smtp.protonmail.ch
+# SMTP_PORT=587
+# SMTP_USER=your-production-email@domain.com
+# SMTP_PASS=your-production-password
+```
+
## Configuration
### Environment Variables (.env)
@@ -95,6 +200,11 @@ RATE_LIMIT_MAX_REQUESTS=100
- `POST /api/emails/send` - Send email to representative
- `GET /api/emails/logs` - Get email sending logs (with filters)
+### Email Testing (Development)
+- `POST /api/emails/preview` - Preview email without sending (admin only)
+- `POST /api/emails/test` - Send test email to configured recipient (admin only)
+- `GET /api/test-smtp` - Test SMTP connection (admin only)
+
### Health
- `GET /api/health` - Application health check
- `GET /api/test-represent` - Test Represent API connection
@@ -137,10 +247,11 @@ influence/
### Key Components
- **RepresentativesController**: Handles postal code lookups and caching
-- **EmailController**: Manages email composition and sending
+- **EmailController**: Manages email composition, sending, and testing
- **NocoDBService**: Database operations with error handling
- **RepresentAPI**: Integration with OpenNorth Represent API
-- **EmailService**: SMTP email functionality
+- **EmailService**: SMTP email functionality with test mode support
+- **Email Testing System**: Preview, test, and log email functionality for development
## Features in Detail
@@ -201,6 +312,8 @@ docker compose up --scale app=2
- Verify SMTP credentials in .env
- Check spam/junk folders
- Review email logs via API endpoint
+ - In development: Check MailHog UI at http://localhost:8025
+ - Use email testing interface at `/email-test.html` for diagnostics
3. **No Representatives Found**:
- Ensure postal code starts with 'T' (Alberta)
diff --git a/influence/app/controllers/campaigns.js b/influence/app/controllers/campaigns.js
index 0efe3f6..ce169df 100644
--- a/influence/app/controllers/campaigns.js
+++ b/influence/app/controllers/campaigns.js
@@ -399,21 +399,17 @@ class CampaignsController {
// Send email if SMTP method
if (emailMethod === 'smtp') {
- emailResult = await emailService.sendEmail({
- to: recipientEmail,
- from: {
- email: process.env.SMTP_FROM_EMAIL,
- name: process.env.SMTP_FROM_NAME
- },
- replyTo: userEmail,
- subject: subject,
- text: message,
- html: `
-
${message.replace(/\n/g, ' ')}
-
-
This message was sent via the BNKops Influence Campaign Tool by ${userName || 'A constituent'} (${userEmail}) from postal code ${postalCode} as part of the "${campaign['Campaign Title'] || campaign.title}" campaign.
+ To:
+ {{RECIPIENT_NAME}} ({{RECIPIENT_LEVEL}})
+
+ {{/if}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/influence/app/templates/email/campaign-email.txt b/influence/app/templates/email/campaign-email.txt
new file mode 100644
index 0000000..2ce6093
--- /dev/null
+++ b/influence/app/templates/email/campaign-email.txt
@@ -0,0 +1,16 @@
+{{CAMPAIGN_TITLE}} - Campaign Message
+
+{{MESSAGE}}
+
+---
+Campaign Participant Information:
+Name: {{USER_NAME}}
+Email: {{USER_EMAIL}}
+Postal Code: {{POSTAL_CODE}}
+{{#if RECIPIENT_NAME}}
+To: {{RECIPIENT_NAME}} ({{RECIPIENT_LEVEL}})
+{{/if}}
+
+---
+This message was sent via the {{APP_NAME}} as part of the "{{CAMPAIGN_TITLE}}" campaign at {{TIMESTAMP}}
+This platform enables constituents to participate in organized advocacy campaigns to communicate with their elected representatives.
\ No newline at end of file
diff --git a/influence/app/templates/email/representative-contact.html b/influence/app/templates/email/representative-contact.html
new file mode 100644
index 0000000..1cf6047
--- /dev/null
+++ b/influence/app/templates/email/representative-contact.html
@@ -0,0 +1,115 @@
+
+
+
+
+ Message from Constituent
+
+
+
+
+
+
{{APP_NAME}}
+
Constituent Communication Platform
+
+
+
+
+ {{MESSAGE}}
+
+
+
+
Constituent Information:
+
+ Name:
+ {{SENDER_NAME}}
+
+
+ Email:
+ {{SENDER_EMAIL}}
+
+
+ Postal Code:
+ {{POSTAL_CODE}}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/influence/app/templates/email/representative-contact.txt b/influence/app/templates/email/representative-contact.txt
new file mode 100644
index 0000000..ae054aa
--- /dev/null
+++ b/influence/app/templates/email/representative-contact.txt
@@ -0,0 +1,13 @@
+Message from Constituent - {{APP_NAME}}
+
+{{MESSAGE}}
+
+---
+Constituent Information:
+Name: {{SENDER_NAME}}
+Email: {{SENDER_EMAIL}}
+Postal Code: {{POSTAL_CODE}}
+
+---
+This message was sent via the {{APP_NAME}} at {{TIMESTAMP}}
+This platform enables constituents to communicate directly with their elected representatives.
\ No newline at end of file
diff --git a/influence/app/templates/email/test-email.html b/influence/app/templates/email/test-email.html
new file mode 100644
index 0000000..122cbee
--- /dev/null
+++ b/influence/app/templates/email/test-email.html
@@ -0,0 +1,101 @@
+
+
+
+
+ Test Email - {{APP_NAME}}
+
+
+
+
+
+
{{APP_NAME}}
+
TEST EMAIL
+
Email System Test
+
+
+
+
+
⚠️ This is a test email
+
This email was sent to verify the email system is working correctly.
+
+
+
+ {{MESSAGE}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/influence/app/templates/email/test-email.txt b/influence/app/templates/email/test-email.txt
new file mode 100644
index 0000000..13f483d
--- /dev/null
+++ b/influence/app/templates/email/test-email.txt
@@ -0,0 +1,10 @@
+TEST EMAIL - {{APP_NAME}}
+
+⚠️ This is a test email
+This email was sent to verify the email system is working correctly.
+
+{{MESSAGE}}
+
+---
+TEST EMAIL sent from {{APP_NAME}} at {{TIMESTAMP}}
+If you received this email, the email system is functioning properly.
\ No newline at end of file
diff --git a/influence/app/utils/rate-limiter.js b/influence/app/utils/rate-limiter.js
index 11209fb..2938827 100644
--- a/influence/app/utils/rate-limiter.js
+++ b/influence/app/utils/rate-limiter.js
@@ -1,5 +1,21 @@
const rateLimit = require('express-rate-limit');
+// In-memory store for per-recipient email tracking
+const emailTracker = new Map();
+
+// Helper function to clean up expired entries
+function cleanupExpiredEntries() {
+ const now = Date.now();
+ for (const [key, timestamp] of emailTracker.entries()) {
+ if (now - timestamp > 5 * 60 * 1000) { // 5 minutes
+ emailTracker.delete(key);
+ }
+ }
+}
+
+// Clean up expired entries every minute
+setInterval(cleanupExpiredEntries, 60 * 1000);
+
// General API rate limiter
const general = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
@@ -12,7 +28,7 @@ const general = rateLimit({
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
});
-// Email sending rate limiter
+// Email sending rate limiter (general - keeps existing behavior)
const email = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 10, // limit each IP to 10 emails per hour
@@ -25,6 +41,35 @@ const email = rateLimit({
skipSuccessfulRequests: false, // Don't skip counting successful requests
});
+// Custom middleware for per-recipient email rate limiting
+const perRecipientEmailLimiter = (req, res, next) => {
+ const clientIp = req.ip || req.connection.remoteAddress;
+ const recipientEmail = req.body.recipientEmail;
+
+ if (!recipientEmail) {
+ return next(); // Let validation middleware handle missing recipient
+ }
+
+ const trackingKey = `${clientIp}:${recipientEmail}`;
+ const now = Date.now();
+ const lastSent = emailTracker.get(trackingKey);
+
+ if (lastSent && (now - lastSent) < 5 * 60 * 1000) { // 5 minutes
+ const timeRemaining = Math.ceil((5 * 60 * 1000 - (now - lastSent)) / 1000);
+ return res.status(429).json({
+ success: false,
+ error: 'Rate limit exceeded',
+ message: `You can only send one email per representative every 5 minutes. Please wait ${Math.ceil(timeRemaining / 60)} more minutes before sending another email to this representative.`,
+ retryAfter: timeRemaining,
+ rateLimitType: 'per-recipient'
+ });
+ }
+
+ // Store the current timestamp for this IP-recipient combination
+ emailTracker.set(trackingKey, now);
+ next();
+};
+
// Represent API rate limiter (more restrictive)
const representAPI = rateLimit({
windowMs: 60 * 1000, // 1 minute
@@ -40,5 +85,6 @@ const representAPI = rateLimit({
module.exports = {
general,
email,
+ perRecipientEmailLimiter,
representAPI
};
\ No newline at end of file
diff --git a/influence/docker-compose.yml b/influence/docker-compose.yml
index ebc2b36..66ad864 100644
--- a/influence/docker-compose.yml
+++ b/influence/docker-compose.yml
@@ -5,11 +5,17 @@ services:
dockerfile: Dockerfile
ports:
- "3333:3333"
- environment:
- - NODE_ENV=production
env_file:
- .env
volumes:
- ./app:/usr/src/app
- /usr/src/app/node_modules
+ restart: unless-stopped
+
+ # MailHog for local email testing and development
+ mailhog:
+ image: mailhog/mailhog:latest
+ ports:
+ - "1025:1025" # SMTP server
+ - "8025:8025" # Web UI
restart: unless-stopped
\ No newline at end of file
diff --git a/influence/example.env b/influence/example.env
new file mode 100644
index 0000000..6b71105
--- /dev/null
+++ b/influence/example.env
@@ -0,0 +1,97 @@
+# Alberta Influence Campaign Tool - Environment Configuration Example
+# Copy this file to .env and update with your actual values
+
+# NocoDB Configuration
+# Your NocoDB instance URL and API configuration
+NOCODB_URL=https://your-nocodb-instance.com
+NOCODB_API_URL=https://your-nocodb-instance.com/api/v1
+NOCODB_API_TOKEN=your_nocodb_api_token_here
+NOCODB_PROJECT_ID=your_project_id
+
+# SMTP Configuration
+# Configure your email service provider settings
+SMTP_HOST=smtp.your-provider.com
+SMTP_PORT=587
+SMTP_SECURE=false
+SMTP_USER=your-email@domain.com
+SMTP_PASS=your_email_password_or_app_password
+SMTP_FROM_EMAIL=your-sender@domain.com
+SMTP_FROM_NAME="Your Campaign Name"
+
+# Admin Configuration
+# Set a strong password for admin access
+ADMIN_PASSWORD=change_this_to_a_strong_password
+
+# Represent API Configuration
+# Canadian electoral data API (usually no changes needed)
+REPRESENT_API_BASE=https://represent.opennorth.ca
+REPRESENT_API_RATE_LIMIT=60
+
+# App Configuration
+# Your application URL and basic settings
+APP_URL=http://localhost:3333
+PORT=3333
+SESSION_SECRET=generate_a_long_random_string_here_at_least_64_characters_long
+NODE_ENV=development
+
+# Email Testing Configuration
+# IMPORTANT: Set to true for development/testing, false for production
+EMAIL_TEST_MODE=true
+TEST_EMAIL_RECIPIENT=your-test-email@domain.com
+
+# NocoDB Table IDs
+# These will be auto-generated when you run build-nocodb.sh
+# DO NOT modify these manually - they are set by the setup script
+NOCODB_TABLE_REPRESENTATIVES=
+NOCODB_TABLE_EMAILS=
+NOCODB_TABLE_POSTAL_CODES=
+NOCODB_TABLE_CAMPAIGN_EMAILS=
+NOCODB_TABLE_CAMPAIGNS=
+NOCODB_TABLE_USERS=
+
+# Optional: Development Mode Settings
+# Uncomment and modify these for local development with MailHog
+# SMTP_HOST=mailhog
+# SMTP_PORT=1025
+# SMTP_SECURE=false
+# SMTP_USER=
+# SMTP_PASS=
+# SMTP_FROM_EMAIL=dev@albertainfluence.local
+# SMTP_FROM_NAME="Alberta Influence Campaign (DEV)"
+
+# Security Notes:
+# - Keep your .env file secure and never commit it to version control
+# - Use strong, unique passwords for ADMIN_PASSWORD
+# - Generate a secure random string for SESSION_SECRET
+# - For production, ensure EMAIL_TEST_MODE=false
+# - Use app passwords or API keys for SMTP_PASS, not your main email password
+
+# Common SMTP Provider Examples:
+#
+# Gmail:
+# SMTP_HOST=smtp.gmail.com
+# SMTP_PORT=587
+# SMTP_SECURE=false
+# SMTP_USER=your-email@gmail.com
+# SMTP_PASS=your_app_password
+#
+# ProtonMail:
+# SMTP_HOST=smtp.protonmail.ch
+# SMTP_PORT=587
+# SMTP_SECURE=false
+# SMTP_USER=your-email@protonmail.com
+# SMTP_PASS=your_app_password
+#
+# Outlook/Hotmail:
+# SMTP_HOST=smtp-mail.outlook.com
+# SMTP_PORT=587
+# SMTP_SECURE=false
+# SMTP_USER=your-email@outlook.com
+# SMTP_PASS=your_app_password
+#
+# SendGrid:
+# SMTP_HOST=smtp.sendgrid.net
+# SMTP_PORT=587
+# SMTP_SECURE=false
+# SMTP_USER=apikey
+# SMTP_PASS=your_sendgrid_api_key
\ No newline at end of file
diff --git a/influence/files-explainer.md b/influence/files-explainer.md
index 4f02986..8b2f458 100644
--- a/influence/files-explainer.md
+++ b/influence/files-explainer.md
@@ -9,9 +9,10 @@ The application includes a complete authentication system for admin panel access
## Root Directory Files
### Configuration Files
-- **`.env`** - Environment variables (database URLs, SMTP config, API keys)
+- **`.env`** - Environment variables (database URLs, SMTP config, API keys, email testing config)
+- **`.env.development`** - Development environment configuration with MailHog SMTP settings
- **`.env.example`** - Template for environment configuration
-- **`docker-compose.yml`** - Docker container orchestration for development/production
+- **`docker-compose.yml`** - Docker container orchestration with MailHog for email testing
- **`Dockerfile`** - Container definition for the Node.js application
- **`package.json`** - Node.js dependencies and scripts
- **`package-lock.json`** - Locked dependency versions for reproducible builds
@@ -43,9 +44,12 @@ Business logic layer that handles HTTP requests and responses:
- Handles caching logic and fallback to API when cache fails
- **`emails.js`** - Email composition and sending functionality
- - `send()` - Process and send emails to representatives
- - `getLogs()` - Retrieve email history with filtering
- - Integrates with SMTP service and logs to database
+ - `sendEmail()` - Process and send emails to representatives with test mode support
+ - `previewEmail()` - Generate email preview without sending for testing purposes
+ - `sendTestEmail()` - Send test emails to configured test recipient
+ - `getEmailLogs()` - Retrieve email history with filtering and pagination
+ - `testSMTPConnection()` - Test SMTP server connectivity for diagnostics
+ - Integrates with SMTP service and logs to database with test mode tracking
### Routes (`app/routes/`)
API endpoint definitions and request validation:
@@ -58,7 +62,9 @@ API endpoint definitions and request validation:
- **`api.js`** - Main API routes with validation middleware
- Representatives endpoints with postal code validation
- - Email endpoints with input sanitization
+ - Email endpoints with input sanitization and test mode support
+ - Email testing endpoints: `/api/emails/preview`, `/api/emails/test`, `/api/emails/logs`
+ - SMTP testing endpoint: `/api/test-smtp` for connection diagnostics
- Health check and testing endpoints
- Rate limiting and error handling middleware
@@ -67,23 +73,26 @@ External system integrations and data access layer:
- **`nocodb.js`** - NocoDB database integration
- User management methods: `getUserByEmail()`, `createUser()`, `updateUser()`, `getAllUsers()`
- - Handles users table operations for authentication system
- - CRUD operations for representatives, emails, postal codes
- - Table ID mapping and API client configuration
- - Error handling with graceful degradation
+ /* Lines 76-80 omitted */
- Caching logic with automatic retry mechanisms
- **`represent-api.js`** - Represent OpenNorth API integration
- Postal code lookup against Canadian electoral data
- - Representative data fetching and processing
- - API response transformation and error handling
+ /* Lines 84-86 omitted */
- Support for both concordance and centroid representative data
-- **`email.js`** - SMTP email service
- - Email composition and HTML template rendering
- - SMTP client configuration and sending
- - Delivery confirmation and error handling
- - Email logging and status tracking
+- **`emailTemplates.js`** - Email template service for managing HTML/text email templates
+ - Template loading and caching system with support for conditional blocks
+ - Variable replacement and template processing for dynamic content
+ - Template rendering for both HTML and text formats with fallback support
+ - Available templates: representative-contact, campaign-email, test-email
+
+- **`email.js`** - SMTP email service with comprehensive testing support
+ - Template-based email composition using emailTemplates service
+ - Legacy sendEmail method for backward compatibility
+ - New templated methods: sendRepresentativeEmail(), sendCampaignEmail(), sendTestEmail()
+ - Email preview functionality with template support
+ - SMTP connection testing for diagnostics and troubleshooting
### Utilities (`app/utils/`)
Helper functions and shared utilities:
@@ -110,6 +119,22 @@ Express.js middleware functions:
- Error logging and classification
- Production vs development error detail levels
+### Email Templates (`app/templates/email/`)
+Professional HTML and text email templates with variable substitution:
+
+- **`representative-contact.html/.txt`** - Template for direct constituent communications
+ - Variables: MESSAGE, SENDER_NAME, SENDER_EMAIL, POSTAL_CODE, APP_NAME, TIMESTAMP
+ - Professional styling with constituent information display and platform branding
+
+- **`campaign-email.html/.txt`** - Template for organized campaign emails
+ - Variables: MESSAGE, USER_NAME, USER_EMAIL, POSTAL_CODE, CAMPAIGN_TITLE, RECIPIENT_NAME, RECIPIENT_LEVEL
+ - Campaign-specific styling with participant information and campaign branding
+ - Conditional recipient information display
+
+- **`test-email.html/.txt`** - Template for email system testing
+ - Variables: MESSAGE, APP_NAME, TIMESTAMP
+ - Warning styling and test indicators for system verification emails
+
### Frontend Assets (`app/public/`)
#### HTML
@@ -131,6 +156,16 @@ Express.js middleware functions:
- Email composition modal
- Responsive design with accessibility features
+- **`email-test.html`** - Comprehensive email testing interface (admin-only)
+ - Protected admin interface for email testing and diagnostics
+ - Quick test email functionality with one-click testing
+ - Email preview system to see emails before sending
+ - Email composition form with real-time preview
+ - Email logs viewer with filtering by test/live mode
+ - SMTP connection testing and diagnostics
+ - Current configuration display showing test mode status
+ - Real-time feedback and error handling for all operations
+
#### Stylesheets (`app/public/css/`)
- **`styles.css`** - Complete application styling
- Responsive grid layouts for representative cards
@@ -175,6 +210,17 @@ Express.js middleware functions:
- Form validation and submission handling
- Success/error feedback to users
+- **`email-testing.js`** - Comprehensive email testing interface management
+ - `EmailTesting` class managing all testing functionality
+ - Quick test email sending with default content
+ - SMTP connection testing and diagnostics
+ - Email preview generation with real-time rendering
+ - Test email sending with form validation
+ - Email logs management with filtering and pagination
+ - Configuration status display and management
+ - Real-time UI updates and error handling
+ - Integration with authentication system for admin protection
+
## Scripts Directory (`scripts/`)
- **`build-nocodb.sh`** - Database setup automation
@@ -237,8 +283,24 @@ Express.js middleware functions:
### SMTP Integration
- **Security** → Secure authentication and encrypted connections
- **Reliability** → Error handling and delivery confirmation
-- **Logging** → Complete audit trail of email activity
-- **Configuration** → Flexible SMTP provider support
+- **Logging** → Complete audit trail of email activity with test mode tracking
+- **Configuration** → Flexible SMTP provider support with development/production modes
+- **Testing** → Comprehensive test mode with email redirection and preview capabilities
+
+### Email Testing System
+- **Test Mode** → Automatic email redirection to configured test recipient
+- **Preview System** → Generate email previews without sending for content review
+- **SMTP Diagnostics** → Connection testing and troubleshooting tools
+- **Email Logging** → Complete audit trail with test/live mode classification
+- **Development Tools** → MailHog integration for local email catching and review
+- **Admin Interface** → Dedicated testing interface accessible only to authenticated admins
+
+### Docker Configuration
+- **Production Mode** → Standard application container with external SMTP
+- **Development Mode** → Application + MailHog containers for local email testing
+- **Profile-based Deployment** → MailHog only runs in development profile
+- **Email Catching** → All development emails caught by MailHog web interface at port 8025
+- **Environment Flexibility** → Easy switching between development and production SMTP settings
## Development Patterns
diff --git a/influence/instruct.md b/influence/instruct.md
index 85ce560..802479f 100644
--- a/influence/instruct.md
+++ b/influence/instruct.md
@@ -1,6 +1,6 @@
# Instructions
-Welcome to the Influence project! Welcome to Influence, a tool for creating political change by targeting influential individuals within a community. This application is designed to help campaigns identify and engage with key figures who can sway public opinion and mobilize support.
+Welcome to the BNKops Influence project! Welcome to BNNKops Influence, a tool for creating political change by targeting influential individuals within a community. This application is designed to help campaigns identify and engage with key figures who can sway public opinion and mobilize support.
## Environment Setup
diff --git a/start-production.sh b/start-production.sh
index 2b2bcfe..9dc99be 100755
--- a/start-production.sh
+++ b/start-production.sh
@@ -321,6 +321,8 @@ ingress:
service: http://localhost:${GITEA_WEB_PORT:-3030}
- hostname: map.${CF_DOMAIN}
service: http://localhost:${MAP_PORT:-3000}
+ - hostname: influence.${CF_DOMAIN}
+ service: http://localhost:${INFLUENCE_PORT:-3333}
- hostname: qr.${CF_DOMAIN}
service: http://localhost:${MINI_QR_PORT:-8089}
- service: http_status:404
@@ -607,6 +609,7 @@ if [ "$CF_CREDS_VALID" = true ]; then
["db"]="NocoDB"
["git"]="Gitea"
["map"]="Map"
+ ["influence"]="Influence"
["qr"]="Mini QR"
)
@@ -651,6 +654,7 @@ echo " - n8n: https://n8n.$CF_DOMAIN"
echo " - NocoDB: https://db.$CF_DOMAIN"
echo " - Gitea: https://git.$CF_DOMAIN"
echo " - Map: https://map.$CF_DOMAIN"
+echo " - Influence: https://influence.$CF_DOMAIN"
echo " - Mini QR: https://qr.$CF_DOMAIN"
echo ""
echo "Protected services (requires login with $ADMIN_EMAIL):"