Updated backend with api implementation

This commit is contained in:
tejas.prakash 2025-08-20 12:45:21 +05:30
parent 2fc8c9c960
commit 2e972f1157
17 changed files with 980 additions and 101 deletions

66
.env.backup Normal file
View File

@ -0,0 +1,66 @@
# Database Configuration
POSTGRES_HOST=localhost
POSTGRES_PORT=5433
POSTGRES_DB=dev_pipeline
POSTGRES_USER=pipeline_admin
POSTGRES_PASSWORD=secure_pipeline_2024
# Redis Configuration
REDIS_PASSWORD=redis_secure_2024
# MongoDB Configuration
MONGO_INITDB_ROOT_USERNAME=pipeline_admin
MONGO_INITDB_ROOT_PASSWORD=mongo_secure_2024
# RabbitMQ Configuration
RABBITMQ_DEFAULT_USER=pipeline_admin
RABBITMQ_DEFAULT_PASS=rabbit_secure_2024
# n8n Configuration
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=admin_n8n_2024
N8N_ENCRYPTION_KEY=very_secure_encryption_key_2024
# Jenkins Configuration
JENKINS_ADMIN_ID=admin
JENKINS_ADMIN_PASSWORD=jenkins_secure_2024
# Gitea Configuration
GITEA_ADMIN_USER=admin
GITEA_ADMIN_PASSWORD=gitea_secure_2024
# API Keys (add your actual keys later)
CLAUDE_API_KEY=your_claude_api_key_here
OPENAI_API_KEY=your_openai_api_key_here
CLOUDTOPIAA_API_KEY=your_cloudtopiaa_api_key_here
CLOUDTOPIAA_API_URL=https://api.cloudtopiaa.com
# JWT Configuration
JWT_SECRET=ultra_secure_jwt_secret_2024
# Environment
ENVIRONMENT=development
NODE_ENV=development
PYTHONPATH=/app/src
# Monitoring
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=grafana_secure_2024
RABBITMQ_PASSWORD=rabbit_secure_2024
MONGODB_PASSWORD=pipeline_password
MONGODB_PASSWORD=pipeline_password
CLAUDE_API_KEY=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
CLAUDE_API_KEY=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
# SMTP Configuration (Option 1)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=frontendtechbiz@gmail.com
SMTP_PASS=oidhhjeasgzbqptq
SMTP_FROM=frontendtechbiz@gmail.com
# Gmail Configuration (Option 2 - Alternative to SMTP)
GMAIL_USER=frontendtechbiz@gmail.com
GMAIL_APP_PASSWORD=oidhhjeasgzbqptq

View File

@ -794,7 +794,17 @@ services:
- JWT_REFRESH_SECRET=refresh-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
- JWT_ACCESS_EXPIRY=15m
- JWT_REFRESH_EXPIRY=7d
- FRONTEND_URL=http://localhost:61004
- FRONTEND_URL=http://localhost:3001
# Email Configuration
- SMTP_HOST=${SMTP_HOST:-smtp.gmail.com}
- SMTP_PORT=${SMTP_PORT:-587}
- SMTP_SECURE=${SMTP_SECURE:-false}
- SMTP_USER=${SMTP_USER:-frontendtechbiz@gmail.com}
- SMTP_PASS=${SMTP_PASS:-oidhhjeasgzbqptq}
- SMTP_FROM=${SMTP_FROM:-frontendtechbiz@gmail.com}
- GMAIL_USER=${GMAIL_USER:-frontendtechbiz@gmail.com}
- GMAIL_APP_PASSWORD=${GMAIL_APP_PASSWORD:-oidhhjeasgzbqptq}
- AUTH_PUBLIC_URL=http://localhost:8011
networks:
- pipeline_network
depends_on:

View File

@ -229,7 +229,8 @@ router.delete('/:id', async (req, res) => {
}
// Soft delete by updating the instance
await template.update({ is_active: false });
// await template.update({ is_active: false });
await Template.delete(id);
res.json({
success: true,

View File

@ -0,0 +1,181 @@
# Email Setup for User Auth Service
## Overview
The User Auth Service sends verification emails when users register. This document explains how to configure email functionality.
## Problem
If emails are not being sent, it's likely due to missing email configuration. The service will show errors like:
- "Email configuration is missing"
- "Failed to create email transporter"
- "Failed to send verification email"
## Quick Fix
### Option 1: Use the Setup Script (Recommended)
```bash
cd automated-dev-pipeline/services/user-auth
./setup-email.sh
```
This interactive script will guide you through the setup process.
### Option 2: Manual Configuration
1. **Create .env file**:
```bash
cd automated-dev-pipeline/services/user-auth
cp env.example .env
```
2. **Edit .env file** with your email credentials
## Email Configuration Options
### Gmail (Recommended for Development)
1. **Enable 2-Step Verification** on your Google account
2. **Generate App Password**:
- Go to https://myaccount.google.com/security
- Click "App passwords"
- Generate password for "Mail"
3. **Update .env file**:
```env
GMAIL_USER=your-email@gmail.com
GMAIL_APP_PASSWORD=your-16-char-app-password
SMTP_FROM=your-email@gmail.com
```
### Custom SMTP Server
```env
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-password
SMTP_FROM=your-email@gmail.com
```
### Development Mode (Mock Emails)
If you don't want to set up real emails in development:
```env
NODE_ENV=development
# No email credentials needed
```
The service will use mock emails and log what would be sent.
## Environment Variables
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `GMAIL_USER` | Gmail email address | If using Gmail | - |
| `GMAIL_APP_PASSWORD` | Gmail app password | If using Gmail | - |
| `SMTP_HOST` | SMTP server hostname | If using SMTP | - |
| `SMTP_PORT` | SMTP server port | If using SMTP | 587 |
| `SMTP_USER` | SMTP username | If using SMTP | - |
| `SMTP_PASS` | SMTP password | If using SMTP | - |
| `SMTP_FROM` | From email address | Yes | GMAIL_USER |
| `NODE_ENV` | Environment mode | No | development |
## Testing Email Configuration
### 1. Check Service Logs
```bash
# View email-related logs
docker-compose logs user-auth | grep -E "(Email|SMTP|Gmail|Mock)"
# View all logs
docker-compose logs user-auth
```
### 2. Test Registration
1. Visit `http://localhost:3001/signup`
2. Register a new user
3. Check logs for email status
### 3. Expected Log Output
**Success (Real Email)**:
```
✅ Email transporter created successfully
📧 Using Gmail configuration
✅ Verification email sent successfully to user@example.com
✉️ Email sent to user@example.com. MessageID: <message-id>
```
**Development Mode (Mock Email)**:
```
⚠️ No email configuration found. Using mock transporter for development.
📧 [MOCK] Email would be sent: { to: 'user@example.com', subject: '...', from: '...' }
```
## Troubleshooting
### Common Issues
1. **"Email configuration is missing"**
- Solution: Set up email credentials in .env file
2. **"Authentication failed"**
- Solution: Check username/password, enable 2FA for Gmail
3. **"Connection timeout"**
- Solution: Check firewall, use correct port (587 for Gmail)
4. **"Invalid credentials"**
- Solution: Use App Password, not regular Gmail password
### Debug Steps
1. **Check environment variables**:
```bash
docker-compose exec user-auth env | grep -E "(SMTP|GMAIL|EMAIL)"
```
2. **Test email service directly**:
```bash
docker-compose exec user-auth node -e "
require('dotenv').config();
const { sendMail } = require('./src/utils/email');
sendMail('test@example.com', 'Test', 'Test email', '<h1>Test</h1>')
.then(() => console.log('Email sent'))
.catch(err => console.error('Email failed:', err.message));
"
```
3. **Verify network connectivity**:
```bash
docker-compose exec user-auth ping smtp.gmail.com
```
## Security Notes
- **Never commit .env files** to version control
- **Use App Passwords** for Gmail, not regular passwords
- **Restrict SMTP access** to trusted IPs in production
- **Rotate credentials** regularly
## Production Considerations
- Use dedicated email service (SendGrid, Mailgun, etc.)
- Set up proper SPF/DKIM records
- Monitor email delivery rates
- Implement email templates
- Add retry logic for failed emails
## Support
If you continue having issues:
1. Check the service logs for detailed error messages
2. Verify your email credentials are correct
3. Test with a simple email client first
4. Check if your email provider blocks automated emails

View File

@ -0,0 +1,38 @@
# Email Configuration for User Auth Service
# Copy this file to .env and fill in your actual values
# SMTP Configuration (Option 1)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM=your-email@gmail.com
# Gmail Configuration (Option 2 - Alternative to SMTP)
GMAIL_USER=your-email@gmail.com
GMAIL_APP_PASSWORD=your-app-password
# Service Configuration
PORT=8011
NODE_ENV=development
FRONTEND_URL=http://localhost:3001
AUTH_PUBLIC_URL=http://localhost:8011
# Database Configuration
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=dev_pipeline
POSTGRES_USER=pipeline_admin
POSTGRES_PASSWORD=your_database_password
# Redis Configuration
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password
# JWT Configuration
JWT_ACCESS_SECRET=your_access_secret_key
JWT_REFRESH_SECRET=your_refresh_secret_key
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d

View File

@ -19,6 +19,7 @@
"joi": "^17.7.0",
"jsonwebtoken": "^9.0.0",
"morgan": "^1.10.0",
"nodemailer": "^7.0.5",
"pg": "^8.8.0",
"redis": "^4.6.0",
"uuid": "^9.0.0"
@ -72,22 +73,22 @@
}
},
"node_modules/@babel/core": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.0",
"@babel/generator": "^7.28.3",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.27.3",
"@babel/helpers": "^7.27.6",
"@babel/parser": "^7.28.0",
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helpers": "^7.28.3",
"@babel/parser": "^7.28.3",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.0",
"@babel/types": "^7.28.0",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@ -128,14 +129,14 @@
"license": "MIT"
},
"node_modules/@babel/generator": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.0",
"@babel/types": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/types": "^7.28.2",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@ -186,15 +187,15 @@
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/traverse": "^7.27.3"
"@babel/traverse": "^7.28.3"
},
"engines": {
"node": ">=6.9.0"
@ -244,9 +245,9 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz",
"integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -258,13 +259,13 @@
}
},
"node_modules/@babel/parser": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.0"
"@babel/types": "^7.28.2"
},
"bin": {
"parser": "bin/babel-parser.js"
@ -528,18 +529,18 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.0",
"@babel/generator": "^7.28.3",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.0",
"@babel/types": "^7.28.2",
"debug": "^4.3.1"
},
"engines": {
@ -927,9 +928,9 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.12",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -948,16 +949,16 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.29",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
"version": "0.3.30",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1184,9 +1185,9 @@
}
},
"node_modules/@types/node": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1525,9 +1526,9 @@
}
},
"node_modules/browserslist": {
"version": "4.25.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
"version": "4.25.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz",
"integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==",
"dev": true,
"funding": [
{
@ -1545,8 +1546,8 @@
],
"license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001726",
"electron-to-chromium": "^1.5.173",
"caniuse-lite": "^1.0.30001735",
"electron-to-chromium": "^1.5.204",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.3"
},
@ -1639,9 +1640,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001731",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz",
"integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==",
"version": "1.0.30001735",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz",
"integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==",
"dev": true,
"funding": [
{
@ -2075,9 +2076,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.199",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.199.tgz",
"integrity": "sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==",
"version": "1.5.206",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.206.tgz",
"integrity": "sha512-/eucXSTaI8L78l42xPurxdBzPTjAkMVCQO7unZCWk9LnZiwKcSvQUhF4c99NWQLwMQXxjlfoQy0+8m9U2yEDQQ==",
"dev": true,
"license": "ISC"
},
@ -2999,9 +3000,9 @@
"license": "MIT"
},
"node_modules/istanbul-reports": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
"integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@ -4044,6 +4045,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/nodemailer": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz",
"integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/nodemon": {
"version": "2.0.22",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",

View File

@ -22,6 +22,7 @@
"joi": "^17.7.0",
"jsonwebtoken": "^9.0.0",
"morgan": "^1.10.0",
"nodemailer": "^7.0.5",
"pg": "^8.8.0",
"redis": "^4.6.0",
"uuid": "^9.0.0"

View File

@ -0,0 +1,88 @@
#!/bin/bash
echo "🔧 Setting up email configuration for User Auth Service"
echo "======================================================"
# Check if .env file exists
if [ -f ".env" ]; then
echo "✅ .env file already exists"
echo "📧 Current email configuration:"
grep -E "^(SMTP_|GMAIL_|EMAIL_)" .env 2>/dev/null || echo " No email configuration found"
else
echo "📝 Creating .env file from template..."
cp env.example .env
echo "✅ .env file created from env.example"
fi
echo ""
echo "📧 Email Configuration Options:"
echo "1. Gmail (Recommended for development)"
echo "2. Custom SMTP Server"
echo "3. Skip email setup (use mock emails)"
read -p "Choose an option (1-3): " choice
case $choice in
1)
echo ""
echo "📧 Gmail Configuration"
echo "====================="
echo "You'll need to create an App Password for your Gmail account:"
echo "1. Go to https://myaccount.google.com/security"
echo "2. Enable 2-Step Verification if not already enabled"
echo "3. Go to 'App passwords' and generate a new app password"
echo "4. Use that password below (not your regular Gmail password)"
echo ""
read -p "Enter your Gmail address: " gmail_user
read -s -p "Enter your Gmail App Password: " gmail_pass
echo ""
# Update .env file
sed -i.bak "s/GMAIL_USER=.*/GMAIL_USER=$gmail_user/" .env
sed -i.bak "s/GMAIL_APP_PASSWORD=.*/GMAIL_APP_PASSWORD=$gmail_pass/" .env
sed -i.bak "s/SMTP_FROM=.*/SMTP_FROM=$gmail_user/" .env
echo "✅ Gmail configuration updated in .env file"
;;
2)
echo ""
echo "📧 Custom SMTP Configuration"
echo "============================"
read -p "Enter SMTP host (e.g., smtp.gmail.com): " smtp_host
read -p "Enter SMTP port (e.g., 587): " smtp_port
read -p "Enter SMTP username/email: " smtp_user
read -s -p "Enter SMTP password: " smtp_pass
echo ""
read -p "Enter from email address: " smtp_from
# Update .env file
sed -i.bak "s/SMTP_HOST=.*/SMTP_HOST=$smtp_host/" .env
sed -i.bak "s/SMTP_PORT=.*/SMTP_PORT=$smtp_port/" .env
sed -i.bak "s/SMTP_USER=.*/SMTP_USER=$smtp_user/" .env
sed -i.bak "s/SMTP_PASS=.*/SMTP_PASS=$smtp_pass/" .env
sed -i.bak "s/SMTP_FROM=.*/SMTP_FROM=$smtp_from/" .env
echo "✅ SMTP configuration updated in .env file"
;;
3)
echo ""
echo "⚠️ Email setup skipped. Mock emails will be used in development."
echo " Users will still be able to register and login, but verification emails won't be sent."
;;
*)
echo "❌ Invalid option. Email setup skipped."
;;
esac
echo ""
echo "🔧 Next steps:"
echo "1. Review and edit .env file if needed"
echo "2. Restart the user-auth service: docker-compose restart user-auth"
echo "3. Check logs for email configuration status"
echo ""
echo "📧 To test email configuration:"
echo " docker-compose logs user-auth | grep -E '(Email|SMTP|Gmail)'"
echo ""
echo "✅ Email setup complete!"

View File

@ -1,4 +1,5 @@
require('dotenv').config();
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '../../.env') });
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
@ -44,7 +45,7 @@ const corsOptions = {
origin: function (origin, callback) {
// Allow requests from your web-dashboard and other services
const allowedOrigins = [
'http://localhost:3001', // Web dashboard
'http://localhost:3001', // Web dashboard (changed from 3000 to 3001 to avoid conflict with other services)
'http://localhost:8008', // Dashboard service
'http://localhost:8000', // API Gateway
'http://localhost:3000', // Development React

View File

@ -4,7 +4,7 @@ class Database {
constructor() {
this.pool = new Pool({
host: process.env.POSTGRES_HOST || 'localhost',
port: process.env.POSTGRES_PORT || 5433,
port: process.env.POSTGRES_PORT || 5433, // changed from 5432 to 5433 to avoid conflict with other services
database: process.env.POSTGRES_DB || 'dev_pipeline',
user: process.env.POSTGRES_USER || 'pipeline_admin',
password: process.env.POSTGRES_PASSWORD || 'secure_pipeline_2024',

View File

@ -6,6 +6,7 @@ DROP TABLE IF EXISTS user_feature_preferences CASCADE;
DROP TABLE IF EXISTS user_sessions CASCADE;
DROP TABLE IF EXISTS refresh_tokens CASCADE;
DROP TABLE IF EXISTS users CASCADE;
DROP TABLE IF EXISTS user_projects CASCADE;
-- Enable UUID extension if not already enabled
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

View File

@ -0,0 +1,47 @@
-- Email Verification Database Schema Addition
-- Adds email verification tokens table and cleanup function
-- Enable UUID extension if not already enabled (safe to rerun)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Email verification tokens table - For storing verification tokens
CREATE TABLE IF NOT EXISTS email_verification_tokens (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash VARCHAR(255) NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
is_used BOOLEAN DEFAULT FALSE,
used_at TIMESTAMP
);
-- Indexes for performance
CREATE INDEX IF NOT EXISTS idx_email_verification_tokens_user_id ON email_verification_tokens(user_id);
CREATE INDEX IF NOT EXISTS idx_email_verification_tokens_token_hash ON email_verification_tokens(token_hash);
-- Cleanup function for expired or used verification tokens
CREATE OR REPLACE FUNCTION cleanup_expired_verification_tokens()
RETURNS INTEGER AS $$
DECLARE
deleted_count INTEGER;
BEGIN
DELETE FROM email_verification_tokens
WHERE expires_at < NOW() OR is_used = true;
GET DIAGNOSTICS deleted_count = ROW_COUNT;
RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;
-- Success message
SELECT 'Email Verification schema added successfully!' as message;
-- Display added table
SELECT
schemaname,
tablename,
tableowner
FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'email_verification_tokens';

View File

@ -1,32 +1,102 @@
// require('dotenv').config();
// const fs = require('fs');
// const path = require('path');
// const database = require('../config/database');
// async function runMigrations() {
// console.log('🚀 Starting User Auth database migration...');
// try {
// // Read the SQL migration file
// const migrationPath = path.join(__dirname, '001_user_auth_schema.sql');
// const migrationSQL = fs.readFileSync(migrationPath, 'utf8');
// console.log('📄 Running migration: 001_user_auth_schema.sql');
// // Execute the migration
// await database.query(migrationSQL);
// console.log('✅ User Auth migration completed successfully!');
// console.log('📊 Database schema created:');
// console.log(' - users table (with admin and test users)');
// console.log(' - refresh_tokens table');
// console.log(' - user_sessions table');
// console.log(' - user_feature_preferences table');
// console.log(' - user_projects table');
// console.log(' - indexes and triggers');
// console.log(' - cleanup functions');
// // Verify tables were created
// const result = await database.query(`
// SELECT
// schemaname,
// tablename,
// tableowner
// FROM pg_tables
// WHERE schemaname = 'public'
// AND tablename IN ('users', 'refresh_tokens', 'user_sessions', 'user_feature_preferences', 'user_projects')
// ORDER BY tablename
// `);
// console.log('🔍 Verified tables:');
// result.rows.forEach(row => {
// console.log(` - ${row.tablename} (owner: ${row.tableowner})`);
// });
// // Test initial users
// const userCount = await database.query('SELECT COUNT(*) as count FROM users');
// console.log(`👥 Initial users created: ${userCount.rows[0].count}`);
// console.log('🔐 Default credentials:');
// console.log(' Admin: admin@tech4biz.com / admin123');
// console.log(' Test: test@tech4biz.com / admin123');
// console.log(' ⚠️ Change passwords in production!');
// } catch (error) {
// console.error('❌ Migration failed:', error.message);
// console.error('📍 Error details:', error);
// process.exit(1);
// } finally {
// await database.close();
// }
// }
// // Run migration if called directly
// if (require.main === module) {
// runMigrations();
// }
// module.exports = { runMigrations };
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const database = require('../config/database');
async function runMigrations() {
console.log('🚀 Starting User Auth database migration...');
console.log('🚀 Starting database migrations...');
const migrations = [
'001_user_auth_schema.sql',
'002_email_verification_schema.sql'
];
try {
// Read the SQL migration file
const migrationPath = path.join(__dirname, '001_user_auth_schema.sql');
for (const migrationFile of migrations) {
const migrationPath = path.join(__dirname, migrationFile);
if (!fs.existsSync(migrationPath)) {
console.warn(`⚠️ Migration file ${migrationFile} not found, skipping...`);
continue;
}
const migrationSQL = fs.readFileSync(migrationPath, 'utf8');
console.log(`📄 Running migration: ${migrationFile}`);
console.log('📄 Running migration: 001_user_auth_schema.sql');
// Execute the migration
await database.query(migrationSQL);
console.log(`✅ Migration ${migrationFile} completed!`);
}
console.log('✅ User Auth migration completed successfully!');
console.log('📊 Database schema created:');
console.log(' - users table (with admin and test users)');
console.log(' - refresh_tokens table');
console.log(' - user_sessions table');
console.log(' - user_feature_preferences table');
console.log(' - user_projects table');
console.log(' - indexes and triggers');
console.log(' - cleanup functions');
// Verify tables were created
// Verify all tables
const result = await database.query(`
SELECT
schemaname,
@ -34,7 +104,7 @@ async function runMigrations() {
tableowner
FROM pg_tables
WHERE schemaname = 'public'
AND tablename IN ('users', 'refresh_tokens', 'user_sessions', 'user_feature_preferences', 'user_projects')
AND tablename IN ('users', 'refresh_tokens', 'user_sessions', 'user_feature_preferences', 'user_projects', 'email_verification_tokens')
ORDER BY tablename
`);
@ -43,21 +113,12 @@ async function runMigrations() {
console.log(` - ${row.tablename} (owner: ${row.tableowner})`);
});
// Test initial users
const userCount = await database.query('SELECT COUNT(*) as count FROM users');
console.log(`👥 Initial users created: ${userCount.rows[0].count}`);
console.log('🔐 Default credentials:');
console.log(' Admin: admin@tech4biz.com / admin123');
console.log(' Test: test@tech4biz.com / admin123');
console.log(' ⚠️ Change passwords in production!');
} catch (error) {
console.error('❌ Migration failed:', error.message);
console.error('📍 Error details:', error);
process.exit(1);
} finally {
await database.close();
await database.close(); // Close connection via Database wrapper
}
}

View File

@ -43,7 +43,7 @@ router.post('/register', /*registerRateLimit,*/ validateRegistration, async (req
success: true,
data: {
user: user.toJSON(),
message: 'User registered successfully'
message: 'User registered successfully. Please check your email to verify your account.'
},
message: 'Registration completed successfully'
});
@ -57,6 +57,29 @@ router.post('/register', /*registerRateLimit,*/ validateRegistration, async (req
}
});
// GET /api/auth/verify-email - Verify email via token and redirect to login
router.get('/verify-email', async (req, res) => {
try {
const { token } = req.query;
await authService.verifyEmailToken(token);
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3001';
const redirectUrl = `${frontendUrl}/signin?verified=true`;
// JSON fallback if not a browser navigation
if (req.get('Accept') && req.get('Accept').includes('application/json')) {
return res.json({ success: true, message: 'Email verified successfully', redirect: redirectUrl });
}
return res.redirect(302, redirectUrl);
} catch (error) {
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3001';
const redirectUrl = `${frontendUrl}/signin?error=${encodeURIComponent(error.message)}`;
if (req.get('Accept') && req.get('Accept').includes('application/json')) {
return res.status(400).json({ success: false, message: error.message });
}
return res.redirect(302, redirectUrl);
}
});
// POST /api/auth/login - User login
router.post('/login', /*loginRateLimit , */validateLogin, async (req, res) => {
try {

View File

@ -1,9 +1,10 @@
//const bcrypt = require('bcryptjs');
const bcrypt = require('bcryptjs');
const crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');
const database = require('../config/database');
const jwtConfig = require('../config/jwt');
const User = require('../models/user');
const { sendMail } = require('../utils/email');
class AuthService {
// Register new user
@ -45,6 +46,27 @@ class AuthService {
});
console.log(`👤 New user registered: ${newUser.email}`);
// Send verification email (non-blocking but awaited to surface errors in dev)
try {
await this.sendVerificationEmail(newUser);
console.log(`✅ Verification email sent successfully to ${newUser.email}`);
} catch (err) {
console.error('❌ Failed to send verification email:', {
error: err.message,
user: newUser.email,
stack: err.stack
});
// In development, don't fail the registration if email fails
if (process.env.NODE_ENV === 'development') {
console.warn('⚠️ Registration completed but verification email failed. User can still login.');
} else {
// In production, this might be more critical
console.error('🚨 Critical: Verification email failed in production environment');
}
}
return newUser;
}
@ -63,6 +85,11 @@ class AuthService {
throw new Error('Invalid email or password');
}
// Require email to be verified before allowing login
if (!user.email_verified) {
throw new Error('Please verify your email before logging in');
}
// Verify password
const isPasswordValid = await user.verifyPassword(password);
if (!isPasswordValid) {
@ -94,6 +121,88 @@ class AuthService {
};
}
// ===============================
// Email Verification
// ===============================
generateRandomToken() {
return crypto.randomBytes(32).toString('hex');
}
hashDeterministic(value) {
return crypto.createHash('sha256').update(value).digest('hex');
}
async createEmailVerificationToken(userId) {
const rawToken = this.generateRandomToken();
const tokenDigest = this.hashDeterministic(rawToken);
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
const query = `
INSERT INTO email_verification_tokens (user_id, token_hash, expires_at)
VALUES ($1, $2, $3)
RETURNING id
`;
await database.query(query, [userId, tokenDigest, expiresAt]);
return rawToken;
}
async sendVerificationEmail(user) {
const token = await this.createEmailVerificationToken(user.id);
const serviceBaseUrl = process.env.AUTH_PUBLIC_URL || `http://localhost:${process.env.PORT || 8011}`;
const verifyUrl = `${serviceBaseUrl}/api/auth/verify-email?token=${encodeURIComponent(token)}`;
const today = new Date();
const dateString = today.toLocaleDateString('en-US');
const subject = 'Verify your email - Tech4biz';
const text = `Hi ${user.first_name || user.username}, please verify your email by visiting: ${verifyUrl}`;
const html = `
<div style="font-family:Arial,Helvetica,sans-serif;max-width:640px;margin:0 auto;padding:24px;border:1px solid #eee;border-radius:8px;">
<h2>👋 Welcome to Codenuk, ${user.first_name || user.username}!</h2>
<p>We're excited to have you on board 🎉</p>
<p>Hi <b>${user.first_name || user.username}</b>, thanks for registering with us on <b>${dateString}</b>.
Please confirm your email address by clicking the button below:</p>
<div style="text-align:center;margin:24px 0;">
<a href="${verifyUrl}" style="background:#1677ff;color:#fff;text-decoration:none;padding:12px 20px;border-radius:6px;display:inline-block;font-weight:600;">Verify Email</a>
</div>
<p>This link is valid for <b>24 hours</b>.</p>
<p>If you didnt create this account, please ignore this email.</p>
</div>
`;
await sendMail(user.email, subject, text, html);
console.log(`✉️ Verification email sent to ${user.email}`);
}
async verifyEmailToken(rawToken) {
if (!rawToken) {
throw new Error('Verification token is required');
}
const tokenDigest = this.hashDeterministic(rawToken);
return await database.transaction(async (client) => {
const findQuery = `
SELECT * FROM email_verification_tokens
WHERE token_hash = $1 AND is_used = false AND expires_at > NOW()
ORDER BY created_at DESC
LIMIT 1
`;
const found = await client.query(findQuery, [tokenDigest]);
if (found.rows.length === 0) {
throw new Error('Invalid or expired verification link');
}
const record = found.rows[0];
// Mark user as verified
await client.query('UPDATE users SET email_verified = true, updated_at = NOW() WHERE id = $1', [record.user_id]);
// Mark token as used
await client.query('UPDATE email_verification_tokens SET is_used = true, used_at = NOW() WHERE id = $1', [record.id]);
return { userId: record.user_id };
});
}
// Refresh access token
async refreshToken(refreshToken) {
if (!refreshToken) {
@ -271,12 +380,9 @@ class AuthService {
}
// Hash token for storage
// async hashToken(token) {
// const saltRounds = 10;
// return await bcrypt.hash(token, saltRounds);
// }
hashToken(token) {
return crypto.createHash('sha256').update(token).digest('hex');
async hashToken(token) {
const saltRounds = 10;
return await bcrypt.hash(token, saltRounds);
}
// Cleanup expired tokens and sessions

View File

@ -0,0 +1,170 @@
const nodemailer = require('nodemailer');
const path = require('path');
// Don't load .env here - load it lazily when needed
let _envLoaded = false;
let _envPath = null;
const loadEnvIfNeeded = () => {
if (!_envLoaded) {
_envPath = path.join(__dirname, '../../../../.env'); // Go up 4 levels: utils -> src -> user-auth -> services -> automated-dev-pipeline
require('dotenv').config({ path: _envPath });
_envLoaded = true;
console.log('📧 Environment variables loaded from:', _envPath);
}
};
// Support env-configurable SMTP; fail if no valid configuration is provided
const createTransporter = () => {
// Load environment variables when this function is called
loadEnvIfNeeded();
const {
SMTP_HOST,
SMTP_PORT,
SMTP_SECURE,
SMTP_USER,
SMTP_PASS,
SMTP_FROM,
GMAIL_USER,
GMAIL_APP_PASSWORD,
} = process.env;
console.log('🔧 Email configuration check:', {
SMTP_HOST: SMTP_HOST ? '✓ Set' : '✗ Missing',
SMTP_USER: SMTP_USER ? '✓ Set' : '✗ Missing',
SMTP_PASS: SMTP_PASS ? '✓ Set' : '✗ Missing',
GMAIL_USER: GMAIL_USER ? '✓ Set' : '✗ Missing',
GMAIL_APP_PASSWORD: GMAIL_APP_PASSWORD ? '✓ Set' : '✗ Missing',
});
// Validate SMTP configuration
if (SMTP_HOST && SMTP_USER && SMTP_PASS) {
console.log('📧 Using SMTP configuration');
return nodemailer.createTransport({
host: SMTP_HOST,
port: SMTP_PORT ? Number(SMTP_PORT) : 587,
secure: SMTP_SECURE ? SMTP_SECURE === 'true' : false,
auth: { user: SMTP_USER, pass: SMTP_PASS },
socketTimeout: 300000,
connectionTimeout: 300000,
greetingTimeout: 300000,
});
}
// Validate Gmail configuration
if (GMAIL_USER && GMAIL_APP_PASSWORD) {
console.log('📧 Using Gmail configuration');
return nodemailer.createTransport({
service: 'gmail',
auth: {
user: GMAIL_USER,
pass: GMAIL_APP_PASSWORD,
},
socketTimeout: 300000,
connectionTimeout: 300000,
greetingTimeout: 300000,
});
}
// Fallback to mock transporter in development mode
if (process.env.NODE_ENV === 'development') {
console.warn('⚠️ No email configuration found. Using mock transporter for development.');
console.log('📧 To enable real emails, set GMAIL_USER and GMAIL_APP_PASSWORD in the .env file.');
return {
sendMail: async (mailOptions) => {
console.log('📧 [MOCK] Email would be sent:', {
to: mailOptions.to,
subject: mailOptions.subject,
from: mailOptions.from,
});
console.log('📧 [MOCK] Email content:', mailOptions.text);
console.log('📧 [MOCK] Email HTML:', mailOptions.html);
return {
messageId: 'mock-' + Date.now(),
response: 'Mock email sent successfully',
};
},
};
}
throw new Error(
`Email configuration is missing. Please set one of the following:
Gmail Configuration:
GMAIL_USER=your-email@gmail.com
GMAIL_APP_PASSWORD=your-app-password
Current environment: ${process.env.NODE_ENV || 'development'}`
);
};
// Lazy transporter creation
let _transporter = null;
const getTransporter = () => {
if (!_transporter) {
try {
_transporter = createTransporter();
console.log('✅ Email transporter created successfully');
} catch (err) {
console.error('❌ Failed to create email transporter:', err.message);
if (process.env.NODE_ENV === 'development') {
console.log('🔄 Creating mock transporter for development...');
_transporter = {
sendMail: async (mailOptions) => {
console.log('📧 [MOCK] Email would be sent:', {
to: mailOptions.to,
subject: mailOptions.subject,
from: mailOptions.from,
});
return {
messageId: 'mock-' + Date.now(),
response: 'Mock email sent successfully',
};
},
};
} else {
throw err;
}
}
}
return _transporter;
};
exports.sendMail = async (to, subject, text, html) => {
// Load environment variables when this function is called
loadEnvIfNeeded();
console.log('🔍 sendMail called with environment variables:');
console.log(' SMTP_FROM:', process.env.SMTP_FROM);
console.log(' GMAIL_USER:', process.env.GMAIL_USER);
console.log(' NODE_ENV:', process.env.NODE_ENV);
const fromAddress = process.env.SMTP_FROM || process.env.GMAIL_USER;
if (!fromAddress) {
console.error('❌ No from address configured. Available env vars:', Object.keys(process.env).filter((k) => k.includes('SMTP') || k.includes('GMAIL')));
throw new Error('No from address configured. Please set SMTP_FROM or GMAIL_USER.');
}
try {
const transporter = getTransporter();
const info = await transporter.sendMail({
from: fromAddress,
to,
subject,
text,
html,
});
console.log(`✉️ Email sent to ${to}. MessageID: ${info.messageId}`);
return info;
} catch (err) {
console.error('❌ Failed to send email:', {
message: err.message,
code: err.code,
response: err.response,
stack: err.stack,
});
throw err;
}
};

View File

@ -0,0 +1,75 @@
#!/usr/bin/env node
require('dotenv').config();
const { sendMail } = require('./src/utils/email');
async function testEmail() {
console.log('🧪 Testing Email Configuration');
console.log('================================');
// Check environment variables
console.log('\n🔧 Environment Variables:');
console.log('NODE_ENV:', process.env.NODE_ENV || 'development');
console.log('SMTP_HOST:', process.env.SMTP_HOST || 'Not set');
console.log('SMTP_USER:', process.env.SMTP_USER || 'Not set');
console.log('SMTP_PASS:', process.env.SMTP_PASS ? '***Set***' : 'Not set');
console.log('GMAIL_USER:', process.env.GMAIL_USER || 'Not set');
console.log('GMAIL_APP_PASSWORD:', process.env.GMAIL_APP_PASSWORD ? '***Set***' : 'Not set');
console.log('SMTP_FROM:', process.env.SMTP_FROM || 'Not set');
// Test email sending
console.log('\n📧 Testing Email Sending...');
const testEmail = {
to: 'test@example.com',
subject: 'Test Email - User Auth Service',
text: 'This is a test email to verify email configuration.',
html: `
<div style="font-family: Arial, sans-serif; padding: 20px;">
<h2>🧪 Test Email</h2>
<p>This is a test email to verify email configuration for the User Auth Service.</p>
<p><strong>Timestamp:</strong> ${new Date().toISOString()}</p>
<p><strong>Environment:</strong> ${process.env.NODE_ENV || 'development'}</p>
<hr>
<p style="color: #666; font-size: 12px;">
If you receive this email, your email configuration is working correctly!
</p>
</div>
`
};
try {
const result = await sendMail(
testEmail.to,
testEmail.subject,
testEmail.text,
testEmail.html
);
console.log('✅ Email test successful!');
console.log('Message ID:', result.messageId);
console.log('Response:', result.response);
} catch (error) {
console.error('❌ Email test failed:');
console.error('Error:', error.message);
if (error.code) {
console.error('Error Code:', error.code);
}
if (error.response) {
console.error('SMTP Response:', error.response);
}
console.error('\n🔧 Troubleshooting Tips:');
console.error('1. Check your email credentials');
console.error('2. Verify 2FA is enabled for Gmail');
console.error('3. Use App Password, not regular password');
console.error('4. Check firewall/network settings');
console.error('5. Verify SMTP port (587 for Gmail)');
}
}
// Run the test
testEmail().catch(console.error);