Fix portal account creation and improve email handling
Build And Push Image / docker (push) Successful in 2m56s
Details
Build And Push Image / docker (push) Successful in 2m56s
Details
- Add explicit POST method to portal account creation API call - Improve error handling with specific messages for different failure cases - Remove SMTP verification step that was causing issues with some servers - Make email sending non-critical to portal account creation success - Add better response data handling for keycloak_id - Add integration review documentation
This commit is contained in:
parent
8d872f9a04
commit
c4a0230f42
|
|
@ -0,0 +1,280 @@
|
||||||
|
# MonacoUSA Portal - Integration Review & Troubleshooting Guide
|
||||||
|
|
||||||
|
## SMTP Email Integration Points
|
||||||
|
|
||||||
|
### 1. Email Configuration Storage
|
||||||
|
- **Location**: `server/utils/admin-config.ts`
|
||||||
|
- **Storage**: Encrypted in `/app/data/admin-config.json` (Docker) or `./data/admin-config.json` (local)
|
||||||
|
- **Fields**: host, port, secure, username, password, fromAddress, fromName
|
||||||
|
|
||||||
|
### 2. Email Service Implementation
|
||||||
|
- **Location**: `server/utils/email.ts`
|
||||||
|
- **Features**:
|
||||||
|
- Auto-detects security settings based on port
|
||||||
|
- Increased timeouts (60 seconds) for slow servers
|
||||||
|
- Supports STARTTLS (port 587) and SSL/TLS (port 465)
|
||||||
|
- Authentication type set to 'login' for compatibility
|
||||||
|
- Accepts self-signed certificates
|
||||||
|
|
||||||
|
### 3. Email Usage Points
|
||||||
|
- **Registration**: `server/api/registration.post.ts` - Sends welcome email with verification link
|
||||||
|
- **Portal Account Creation**: `server/api/members/[id]/create-portal-account.post.ts` - Sends welcome email
|
||||||
|
- **Password Reset**: `server/api/auth/forgot-password.post.ts` - Sends password reset link
|
||||||
|
- **Email Verification Resend**: `server/api/auth/send-verification-email.post.ts`
|
||||||
|
- **Test Email**: `server/api/admin/test-email.post.ts` - Admin panel test
|
||||||
|
|
||||||
|
### 4. Common SMTP Issues & Solutions
|
||||||
|
|
||||||
|
#### Issue: "500 plugin timeout" / EAUTH errors
|
||||||
|
**Solutions**:
|
||||||
|
1. **Port 587 (STARTTLS)**:
|
||||||
|
- Set SSL/TLS: OFF
|
||||||
|
- Username: Full email address (noreply@monacousa.org)
|
||||||
|
- Password: Your SMTP password (not email password if different)
|
||||||
|
|
||||||
|
2. **Port 465 (SSL/TLS)**:
|
||||||
|
- Set SSL/TLS: ON
|
||||||
|
- Same credentials as above
|
||||||
|
|
||||||
|
3. **Port 25 (Unencrypted)**:
|
||||||
|
- Set SSL/TLS: OFF
|
||||||
|
- May not require authentication
|
||||||
|
- Not recommended for production
|
||||||
|
|
||||||
|
4. **Alternative Configuration** for mail.monacousa.org:
|
||||||
|
- Try port 587 with SSL/TLS OFF
|
||||||
|
- Try port 465 with SSL/TLS ON
|
||||||
|
- Ensure username is full email address
|
||||||
|
- Some servers require app-specific passwords
|
||||||
|
|
||||||
|
#### Issue: Connection timeouts
|
||||||
|
**Solutions**:
|
||||||
|
- Timeouts already increased to 60 seconds
|
||||||
|
- Check firewall rules allow outbound connections on SMTP port
|
||||||
|
- Verify DNS resolution of mail server
|
||||||
|
|
||||||
|
#### Issue: Certificate errors
|
||||||
|
**Solutions**:
|
||||||
|
- Self-signed certificates are already accepted
|
||||||
|
- TLS minimum version set to TLSv1 for compatibility
|
||||||
|
|
||||||
|
### 5. Testing SMTP Without Email
|
||||||
|
If SMTP cannot be configured, the system gracefully handles email failures:
|
||||||
|
- Portal accounts are still created
|
||||||
|
- Users can use "Forgot Password" to set initial password
|
||||||
|
- Admin sees appropriate messages about email status
|
||||||
|
|
||||||
|
## Keycloak Integration Points
|
||||||
|
|
||||||
|
### 1. Authentication Flow
|
||||||
|
- **Login**: `server/api/auth/keycloak/login.get.ts` - Redirects to Keycloak
|
||||||
|
- **Callback**: `server/api/auth/keycloak/callback.get.ts` - Handles OAuth callback
|
||||||
|
- **Session**: `server/utils/session.ts` - Manages encrypted sessions
|
||||||
|
- **Logout**: `server/api/auth/logout.post.ts` - Clears session and Keycloak logout
|
||||||
|
|
||||||
|
### 2. User Management
|
||||||
|
- **Admin Client**: `server/utils/keycloak-admin.ts`
|
||||||
|
- **Features**:
|
||||||
|
- Create users with role-based registration
|
||||||
|
- Update user attributes (membership data)
|
||||||
|
- Password reset functionality
|
||||||
|
- Email verification tokens
|
||||||
|
- User search by email
|
||||||
|
|
||||||
|
### 3. Role-Based Access
|
||||||
|
- **Tiers**: admin, board, user
|
||||||
|
- **Middleware**:
|
||||||
|
- `middleware/auth.ts` - General authentication
|
||||||
|
- `middleware/auth-admin.ts` - Admin only
|
||||||
|
- `middleware/auth-board.ts` - Board and admin
|
||||||
|
- `middleware/auth-user.ts` - All authenticated users
|
||||||
|
|
||||||
|
### 4. Member-Portal Sync
|
||||||
|
- **Dual Database System**:
|
||||||
|
- NocoDB: Member records (source of truth)
|
||||||
|
- Keycloak: Authentication and portal accounts
|
||||||
|
- **Sync Points**:
|
||||||
|
- Registration creates both records
|
||||||
|
- Portal account creation links existing member to Keycloak
|
||||||
|
- Member updates sync to Keycloak attributes
|
||||||
|
|
||||||
|
### 5. Common Keycloak Issues & Solutions
|
||||||
|
|
||||||
|
#### Issue: Login redirect loops
|
||||||
|
**Solutions**:
|
||||||
|
- Check `NUXT_KEYCLOAK_CALLBACK_URL` matches actual domain
|
||||||
|
- Verify Keycloak client redirect URIs include callback URL
|
||||||
|
- Ensure session secret is set and consistent
|
||||||
|
|
||||||
|
#### Issue: User creation failures
|
||||||
|
**Solutions**:
|
||||||
|
- Check Keycloak admin credentials in environment
|
||||||
|
- Verify realm exists and is accessible
|
||||||
|
- Ensure email is unique in Keycloak
|
||||||
|
|
||||||
|
#### Issue: Role assignment not working
|
||||||
|
**Solutions**:
|
||||||
|
- Verify realm roles exist: user, board, admin
|
||||||
|
- Check client scope mappings include roles
|
||||||
|
- Ensure token includes role claims
|
||||||
|
|
||||||
|
## Environment Variables Required
|
||||||
|
|
||||||
|
### Keycloak Configuration
|
||||||
|
```env
|
||||||
|
NUXT_KEYCLOAK_ISSUER=https://auth.monacousa.org/realms/monacousa-portal
|
||||||
|
NUXT_KEYCLOAK_CLIENT_ID=monacousa-portal
|
||||||
|
NUXT_KEYCLOAK_CLIENT_SECRET=your-client-secret
|
||||||
|
NUXT_KEYCLOAK_CALLBACK_URL=https://monacousa.org/auth/callback
|
||||||
|
NUXT_KEYCLOAK_ADMIN_USERNAME=admin
|
||||||
|
NUXT_KEYCLOAK_ADMIN_PASSWORD=admin-password
|
||||||
|
```
|
||||||
|
|
||||||
|
### Session Security
|
||||||
|
```env
|
||||||
|
NUXT_SESSION_SECRET=48-character-secret-key
|
||||||
|
NUXT_ENCRYPTION_KEY=32-character-encryption-key
|
||||||
|
```
|
||||||
|
|
||||||
|
### Public Configuration
|
||||||
|
```env
|
||||||
|
NUXT_PUBLIC_DOMAIN=monacousa.org
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Check Endpoints
|
||||||
|
|
||||||
|
### System Health
|
||||||
|
- **Endpoint**: `GET /api/health`
|
||||||
|
- **Checks**:
|
||||||
|
- Database connectivity (NocoDB)
|
||||||
|
- Keycloak connectivity
|
||||||
|
- Session management
|
||||||
|
- File storage (if configured)
|
||||||
|
|
||||||
|
## Troubleshooting Workflow
|
||||||
|
|
||||||
|
### For SMTP Issues:
|
||||||
|
1. Try port 587 with SSL/TLS OFF first
|
||||||
|
2. If fails, try port 465 with SSL/TLS ON
|
||||||
|
3. Check credentials (use full email as username)
|
||||||
|
4. Test with personal Gmail/Outlook account to verify code works
|
||||||
|
5. Check firewall/network restrictions
|
||||||
|
6. Review server logs for specific error messages
|
||||||
|
|
||||||
|
### For Keycloak Issues:
|
||||||
|
1. Verify all environment variables are set
|
||||||
|
2. Check Keycloak server is accessible
|
||||||
|
3. Test with direct Keycloak login first
|
||||||
|
4. Review browser console for redirect issues
|
||||||
|
5. Check server logs for token/session errors
|
||||||
|
6. Verify realm and client configuration in Keycloak admin
|
||||||
|
|
||||||
|
## Manual SMTP Testing
|
||||||
|
|
||||||
|
To manually test SMTP settings without the portal:
|
||||||
|
|
||||||
|
### Using OpenSSL (for connection test):
|
||||||
|
```bash
|
||||||
|
# For STARTTLS (port 587)
|
||||||
|
openssl s_client -starttls smtp -connect mail.monacousa.org:587
|
||||||
|
|
||||||
|
# For SSL/TLS (port 465)
|
||||||
|
openssl s_client -connect mail.monacousa.org:465
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Telnet (for basic connectivity):
|
||||||
|
```bash
|
||||||
|
telnet mail.monacousa.org 587
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using swaks (comprehensive SMTP test):
|
||||||
|
```bash
|
||||||
|
swaks --to test@example.com \
|
||||||
|
--from noreply@monacousa.org \
|
||||||
|
--server mail.monacousa.org:587 \
|
||||||
|
--auth LOGIN \
|
||||||
|
--auth-user noreply@monacousa.org \
|
||||||
|
--auth-password yourpassword \
|
||||||
|
--tls
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alternative Email Solutions
|
||||||
|
|
||||||
|
If SMTP continues to fail:
|
||||||
|
|
||||||
|
### 1. Use Gmail with App Password:
|
||||||
|
- Enable 2FA on Gmail account
|
||||||
|
- Generate app-specific password
|
||||||
|
- Use smtp.gmail.com:587
|
||||||
|
- Username: your gmail address
|
||||||
|
- Password: app-specific password
|
||||||
|
|
||||||
|
### 2. Use SendGrid (Free tier available):
|
||||||
|
- Sign up at sendgrid.com
|
||||||
|
- Create API key
|
||||||
|
- Use smtp.sendgrid.net:587
|
||||||
|
- Username: apikey (literal string)
|
||||||
|
- Password: your API key
|
||||||
|
|
||||||
|
### 3. Use Local Mail Server (Development):
|
||||||
|
- Install MailHog or MailCatcher
|
||||||
|
- No authentication required
|
||||||
|
- Captures all emails locally
|
||||||
|
- Perfect for testing
|
||||||
|
|
||||||
|
## System Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌──────────────┐ ┌─────────────┐
|
||||||
|
│ │────▶│ │────▶│ │
|
||||||
|
│ Frontend │ │ Nuxt API │ │ Keycloak │
|
||||||
|
│ (Vue/Vuetify) │◀────│ Routes │◀────│ Server │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
└─────────────────┘ └──────────────┘ └─────────────┘
|
||||||
|
│ ▲
|
||||||
|
│ │
|
||||||
|
▼ │
|
||||||
|
┌──────────────┐ │
|
||||||
|
│ │ │
|
||||||
|
│ NocoDB │─────────────┘
|
||||||
|
│ Database │
|
||||||
|
│ │
|
||||||
|
└──────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ │
|
||||||
|
│ SMTP │
|
||||||
|
│ Server │
|
||||||
|
│ │
|
||||||
|
└──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Checklist
|
||||||
|
|
||||||
|
- [ ] All environment variables set correctly
|
||||||
|
- [ ] SSL certificates valid and configured
|
||||||
|
- [ ] Keycloak realm and client configured
|
||||||
|
- [ ] NocoDB database accessible and configured
|
||||||
|
- [ ] SMTP credentials tested and working
|
||||||
|
- [ ] Session secrets are strong and unique
|
||||||
|
- [ ] Firewall rules allow necessary ports
|
||||||
|
- [ ] Backup strategy in place
|
||||||
|
- [ ] Monitoring and logging configured
|
||||||
|
- [ ] Health check endpoint monitored
|
||||||
|
|
||||||
|
## Support Resources
|
||||||
|
|
||||||
|
- **Keycloak Documentation**: https://www.keycloak.org/documentation
|
||||||
|
- **NocoDB Documentation**: https://docs.nocodb.com
|
||||||
|
- **Nodemailer Documentation**: https://nodemailer.com
|
||||||
|
- **Nuxt 3 Documentation**: https://nuxt.com
|
||||||
|
|
||||||
|
## Contact for Issues
|
||||||
|
|
||||||
|
If you continue to experience issues after following this guide:
|
||||||
|
1. Check server logs for detailed error messages
|
||||||
|
2. Test each component independently
|
||||||
|
3. Verify network connectivity and DNS resolution
|
||||||
|
4. Review firewall and security group rules
|
||||||
|
5. Consider using alternative email providers
|
||||||
|
|
@ -507,23 +507,41 @@ const createPortalAccount = async (member: Member) => {
|
||||||
creatingPortalAccountIds.value.push(member.Id);
|
creatingPortalAccountIds.value.push(member.Id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await $fetch(`/api/members/${member.Id}/create-portal-account`) as any;
|
const response = await $fetch<any>(`/api/members/${member.Id}/create-portal-account`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
if (response?.success) {
|
if (response?.success) {
|
||||||
// Update the member in the local array to reflect the new keycloak_id
|
// Update the member in the local array to reflect the new keycloak_id
|
||||||
const index = members.value.findIndex(m => m.Id === member.Id);
|
const index = members.value.findIndex(m => m.Id === member.Id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
members.value[index] = { ...members.value[index], keycloak_id: response.keycloak_id };
|
// Get keycloak_id from response.data
|
||||||
|
members.value[index] = { ...members.value[index], keycloak_id: response.data?.keycloak_id };
|
||||||
}
|
}
|
||||||
|
|
||||||
showSuccess.value = true;
|
showSuccess.value = true;
|
||||||
successMessage.value = `Portal account created successfully for ${member.FullName}. They will receive an email with login instructions.`;
|
successMessage.value = response.message || `Portal account created successfully for ${member.FullName}.`;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response?.message || 'Failed to create portal account');
|
throw new Error(response?.message || 'Failed to create portal account');
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('Error creating portal account:', err);
|
console.error('Error creating portal account:', err);
|
||||||
error.value = err.data?.message || err.message || 'Failed to create portal account. Please try again.';
|
|
||||||
|
// Better error handling
|
||||||
|
let errorMessage = 'Failed to create portal account. Please try again.';
|
||||||
|
if (err.statusCode === 409) {
|
||||||
|
errorMessage = 'This member already has a portal account or a user with this email already exists.';
|
||||||
|
} else if (err.statusCode === 400) {
|
||||||
|
errorMessage = 'Member must have email, first name, and last name to create a portal account.';
|
||||||
|
} else if (err.data?.message) {
|
||||||
|
errorMessage = err.data.message;
|
||||||
|
} else if (err.message) {
|
||||||
|
errorMessage = err.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show error in snackbar
|
||||||
|
showSuccess.value = true; // Reuse success snackbar for errors
|
||||||
|
successMessage.value = errorMessage;
|
||||||
} finally {
|
} finally {
|
||||||
// Remove from creating array
|
// Remove from creating array
|
||||||
const index = creatingPortalAccountIds.value.indexOf(member.Id);
|
const index = creatingPortalAccountIds.value.indexOf(member.Id);
|
||||||
|
|
|
||||||
|
|
@ -51,18 +51,11 @@ export default defineEventHandler(async (event) => {
|
||||||
const { getEmailService } = await import('~/server/utils/email');
|
const { getEmailService } = await import('~/server/utils/email');
|
||||||
const emailService = await getEmailService();
|
const emailService = await getEmailService();
|
||||||
|
|
||||||
// Try to verify connection but don't fail if verification doesn't work
|
// Skip verification entirely and just try to send
|
||||||
// Some SMTP servers have issues with verify() but work fine for sending
|
// Many SMTP servers don't support the VERIFY command
|
||||||
try {
|
console.log('[api/admin/test-email.post] Attempting to send test email without verification...');
|
||||||
const connectionOk = await emailService.verifyConnection();
|
|
||||||
if (connectionOk) {
|
// Attempt to send test email directly
|
||||||
console.log('[api/admin/test-email.post] SMTP connection verified successfully');
|
|
||||||
}
|
|
||||||
} catch (verifyError: any) {
|
|
||||||
console.warn('[api/admin/test-email.post] SMTP verification failed, attempting to send anyway:', verifyError.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to send test email regardless of verification result
|
|
||||||
await emailService.sendTestEmail(body.testEmail);
|
await emailService.sendTestEmail(body.testEmail);
|
||||||
|
|
||||||
console.log('[api/admin/test-email.post] ✅ Test email sent successfully');
|
console.log('[api/admin/test-email.post] ✅ Test email sent successfully');
|
||||||
|
|
|
||||||
|
|
@ -117,12 +117,13 @@ export default defineEventHandler(async (event) => {
|
||||||
await updateMember(memberId, { keycloak_id: keycloakId });
|
await updateMember(memberId, { keycloak_id: keycloakId });
|
||||||
|
|
||||||
// 9. Send welcome/verification email using our custom email system
|
// 9. Send welcome/verification email using our custom email system
|
||||||
console.log('[api/members/[id]/create-portal-account.post] Sending welcome/verification email...');
|
console.log('[api/members/[id]/create-portal-account.post] Attempting to send welcome/verification email...');
|
||||||
|
let emailSent = false;
|
||||||
try {
|
try {
|
||||||
const { getEmailService } = await import('~/server/utils/email');
|
const { getEmailService } = await import('~/server/utils/email');
|
||||||
const { generateEmailVerificationToken } = await import('~/server/utils/email-tokens');
|
const { generateEmailVerificationToken } = await import('~/server/utils/email-tokens');
|
||||||
|
|
||||||
const emailService = await getEmailService();
|
const emailService = await getEmailService();
|
||||||
const verificationToken = await generateEmailVerificationToken(keycloakId, member.email);
|
const verificationToken = await generateEmailVerificationToken(keycloakId, member.email);
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
const verificationLink = `${config.public.domain}/api/auth/verify-email?token=${verificationToken}`;
|
const verificationLink = `${config.public.domain}/api/auth/verify-email?token=${verificationToken}`;
|
||||||
|
|
@ -134,6 +135,7 @@ export default defineEventHandler(async (event) => {
|
||||||
memberId: memberId
|
memberId: memberId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
emailSent = true;
|
||||||
console.log('[api/members/[id]/create-portal-account.post] Welcome email sent successfully');
|
console.log('[api/members/[id]/create-portal-account.post] Welcome email sent successfully');
|
||||||
} catch (emailError: any) {
|
} catch (emailError: any) {
|
||||||
console.error('[api/members/[id]/create-portal-account.post] Failed to send welcome email:', emailError.message);
|
console.error('[api/members/[id]/create-portal-account.post] Failed to send welcome email:', emailError.message);
|
||||||
|
|
@ -144,12 +146,15 @@ export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Portal account created successfully. The member will receive an email to verify their account and set their password.',
|
message: emailSent
|
||||||
|
? 'Portal account created successfully. The member will receive an email to verify their account and set their password.'
|
||||||
|
: 'Portal account created successfully. Email sending is not configured - the member will need to request a password reset to access their account.',
|
||||||
data: {
|
data: {
|
||||||
keycloak_id: keycloakId,
|
keycloak_id: keycloakId,
|
||||||
member_id: memberId,
|
member_id: memberId,
|
||||||
email: member.email,
|
email: member.email,
|
||||||
name: `${member.first_name} ${member.last_name}`
|
name: `${member.first_name} ${member.last_name}`,
|
||||||
|
email_sent: emailSent
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,14 @@ export class EmailService {
|
||||||
host: this.config.host,
|
host: this.config.host,
|
||||||
port: this.config.port,
|
port: this.config.port,
|
||||||
secure: useSecure,
|
secure: useSecure,
|
||||||
// Connection timeout settings
|
// Increased timeout settings to handle slow servers
|
||||||
connectionTimeout: 30000, // 30 seconds
|
connectionTimeout: 60000, // 60 seconds
|
||||||
greetingTimeout: 30000,
|
greetingTimeout: 60000,
|
||||||
socketTimeout: 30000,
|
socketTimeout: 60000,
|
||||||
// Debug logging
|
// Pool configuration for better connection management
|
||||||
|
pool: false,
|
||||||
|
maxConnections: 1,
|
||||||
|
// Debug logging (can be enabled for troubleshooting)
|
||||||
logger: false,
|
logger: false,
|
||||||
debug: false
|
debug: false
|
||||||
};
|
};
|
||||||
|
|
@ -96,20 +99,32 @@ export class EmailService {
|
||||||
// Add requireTLS if needed (for STARTTLS)
|
// Add requireTLS if needed (for STARTTLS)
|
||||||
if (requireTLS && !useSecure) {
|
if (requireTLS && !useSecure) {
|
||||||
transporterOptions.requireTLS = true;
|
transporterOptions.requireTLS = true;
|
||||||
|
transporterOptions.opportunisticTLS = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure TLS options
|
// Configure TLS options
|
||||||
transporterOptions.tls = {
|
transporterOptions.tls = {
|
||||||
rejectUnauthorized: false, // Accept self-signed certificates
|
rejectUnauthorized: false, // Accept self-signed certificates
|
||||||
// Don't specify minVersion or ciphers to allow auto-negotiation
|
// Allow various TLS versions for compatibility
|
||||||
|
minVersion: 'TLSv1',
|
||||||
|
// Don't specify ciphers to allow auto-negotiation
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add authentication only if credentials are provided
|
// Add authentication only if credentials are provided
|
||||||
if (this.config.username && this.config.password) {
|
if (this.config.username && this.config.password) {
|
||||||
transporterOptions.auth = {
|
transporterOptions.auth = {
|
||||||
user: this.config.username,
|
user: this.config.username,
|
||||||
pass: this.config.password
|
pass: this.config.password,
|
||||||
|
// Try different auth methods for compatibility
|
||||||
|
type: 'login' // Can be 'oauth2', 'login', or omitted for auto-detection
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For some servers, disabling STARTTLS can help
|
||||||
|
if (this.config.port === 587) {
|
||||||
|
transporterOptions.ignoreTLS = false;
|
||||||
|
transporterOptions.secure = false;
|
||||||
|
transporterOptions.requireTLS = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.transporter = nodemailer.createTransport(transporterOptions);
|
this.transporter = nodemailer.createTransport(transporterOptions);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue