courrier multi account
This commit is contained in:
parent
1aa9608722
commit
7eb6dd5b81
@ -1,7 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getServerSession } from 'next-auth';
|
||||
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
||||
import { saveUserEmailCredentials, testEmailConnection } from '@/lib/services/email-service';
|
||||
import { saveUserEmailCredentials } from '@/lib/services/email-service';
|
||||
|
||||
// Define EmailCredentials interface inline since we're having import issues
|
||||
interface EmailCredentials {
|
||||
@ -29,7 +29,17 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
const body = await request.json();
|
||||
const body = await request.json().catch(e => {
|
||||
console.error('Error parsing request body:', e);
|
||||
return {};
|
||||
});
|
||||
|
||||
// Log the request (but hide password)
|
||||
console.log('Adding account:', {
|
||||
...body,
|
||||
password: body.password ? '***' : undefined
|
||||
});
|
||||
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
@ -44,18 +54,33 @@ export async function POST(request: Request) {
|
||||
} = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!email || !password || !host || !port) {
|
||||
const missingFields = [];
|
||||
if (!email) missingFields.push('email');
|
||||
if (!password) missingFields.push('password');
|
||||
if (!host) missingFields.push('host');
|
||||
if (port === undefined) missingFields.push('port');
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
console.error(`Missing required fields: ${missingFields.join(', ')}`);
|
||||
return NextResponse.json(
|
||||
{ error: 'Required fields missing: email, password, host, port' },
|
||||
{ error: `Required fields missing: ${missingFields.join(', ')}` },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Fix common hostname errors - strip http/https prefixes
|
||||
let cleanHost = host;
|
||||
if (cleanHost.startsWith('http://')) {
|
||||
cleanHost = cleanHost.substring(7);
|
||||
} else if (cleanHost.startsWith('https://')) {
|
||||
cleanHost = cleanHost.substring(8);
|
||||
}
|
||||
|
||||
// Create credentials object
|
||||
const credentials: EmailCredentials = {
|
||||
email,
|
||||
password,
|
||||
host,
|
||||
host: cleanHost,
|
||||
port: typeof port === 'string' ? parseInt(port) : port,
|
||||
secure: secure ?? true,
|
||||
// Optional SMTP settings
|
||||
@ -67,40 +92,14 @@ export async function POST(request: Request) {
|
||||
...(color && { color })
|
||||
};
|
||||
|
||||
// Test connection before saving
|
||||
const connectionTest = await testEmailConnection(credentials);
|
||||
|
||||
if (!connectionTest.imap) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Failed to connect to IMAP server with provided credentials',
|
||||
details: connectionTest.error
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// If SMTP details provided but connection failed
|
||||
if (smtp_host && smtp_port && !connectionTest.smtp) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'IMAP connection successful, but SMTP connection failed',
|
||||
details: connectionTest.error
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Connection test is no longer needed here since we validate with the test-connection endpoint first
|
||||
// Save credentials to database and cache
|
||||
await saveUserEmailCredentials(session.user.id, credentials);
|
||||
console.log(`Email account successfully added for user ${session.user.id}`);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Email account added successfully',
|
||||
connectionStatus: {
|
||||
imap: connectionTest.imap,
|
||||
smtp: connectionTest.smtp
|
||||
}
|
||||
message: 'Email account added successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error adding email account:', error);
|
||||
|
||||
137
app/api/courrier/test-connection/route.ts
Normal file
137
app/api/courrier/test-connection/route.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { ImapFlow } from 'imapflow';
|
||||
import nodemailer from 'nodemailer';
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
// Parse request body
|
||||
const body = await request.json().catch(e => {
|
||||
console.error('Error parsing request body:', e);
|
||||
return {};
|
||||
});
|
||||
|
||||
// Log request but hide password
|
||||
console.log('Testing connection with:', {
|
||||
...body,
|
||||
password: body.password ? '***' : undefined
|
||||
});
|
||||
|
||||
const { email, password, host, port, secure = true } = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!email || !password || !host || !port) {
|
||||
const missing = [];
|
||||
if (!email) missing.push('email');
|
||||
if (!password) missing.push('password');
|
||||
if (!host) missing.push('host');
|
||||
if (!port) missing.push('port');
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Missing required fields: ${missing.join(', ')}` },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Fix common hostname errors - strip http/https prefixes
|
||||
let cleanHost = host;
|
||||
if (cleanHost.startsWith('http://')) {
|
||||
cleanHost = cleanHost.substring(7);
|
||||
} else if (cleanHost.startsWith('https://')) {
|
||||
cleanHost = cleanHost.substring(8);
|
||||
}
|
||||
|
||||
console.log(`Testing IMAP connection to ${cleanHost}:${port} for ${email}`);
|
||||
|
||||
// Test IMAP connection
|
||||
const client = new ImapFlow({
|
||||
host: cleanHost,
|
||||
port: typeof port === 'string' ? parseInt(port) : port,
|
||||
secure: secure === true || secure === 'true',
|
||||
auth: {
|
||||
user: email,
|
||||
pass: password,
|
||||
},
|
||||
logger: false,
|
||||
tls: {
|
||||
rejectUnauthorized: false
|
||||
},
|
||||
// Set timeout to prevent long waits
|
||||
connectionTimeout: 10000
|
||||
});
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
console.log(`IMAP connection successful for ${email}`);
|
||||
|
||||
// Try to list mailboxes
|
||||
const mailboxes = await client.list();
|
||||
const folderNames = mailboxes.map(mailbox => mailbox.path);
|
||||
console.log(`Found ${folderNames.length} folders:`, folderNames.slice(0, 5));
|
||||
|
||||
try {
|
||||
await client.logout();
|
||||
} catch (e) {
|
||||
// Ignore logout errors
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'IMAP connection successful',
|
||||
details: {
|
||||
host: cleanHost,
|
||||
port,
|
||||
folderCount: folderNames.length,
|
||||
sampleFolders: folderNames.slice(0, 5)
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('IMAP connection test failed:', error);
|
||||
|
||||
let friendlyMessage = 'Connection failed';
|
||||
let errorDetails = '';
|
||||
|
||||
if (error instanceof Error) {
|
||||
errorDetails = error.message;
|
||||
|
||||
if (error.message.includes('Invalid login') || error.message.includes('authentication failed')) {
|
||||
friendlyMessage = 'Invalid username or password';
|
||||
} else if (error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) {
|
||||
friendlyMessage = 'Cannot connect to server - check host and port';
|
||||
} else if (error.message.includes('certificate')) {
|
||||
friendlyMessage = 'SSL/TLS certificate issue';
|
||||
} else if (error.message.includes('timeout')) {
|
||||
friendlyMessage = 'Connection timed out';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await client.logout();
|
||||
} catch (e) {
|
||||
// Ignore logout errors
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: friendlyMessage,
|
||||
details: errorDetails,
|
||||
debug: {
|
||||
providedHost: host,
|
||||
cleanHost,
|
||||
port,
|
||||
secure
|
||||
}
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error testing connection:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Failed to test connection',
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -458,48 +458,79 @@ export default function CourrierPage() {
|
||||
setLoading(true);
|
||||
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const newAccount = {
|
||||
email: formData.get('email'),
|
||||
password: formData.get('password'),
|
||||
host: formData.get('host'),
|
||||
port: parseInt(formData.get('port') as string),
|
||||
|
||||
// Pull values from form with proper type handling
|
||||
const formValues = {
|
||||
email: formData.get('email')?.toString() || '',
|
||||
password: formData.get('password')?.toString() || '',
|
||||
host: formData.get('host')?.toString() || '',
|
||||
port: parseInt(formData.get('port')?.toString() || '993'),
|
||||
secure: formData.get('secure') === 'on',
|
||||
display_name: formData.get('display_name') || formData.get('email'),
|
||||
color: formData.get('color') || '#0082c9',
|
||||
smtp_host: formData.get('smtp_host'),
|
||||
smtp_port: formData.get('smtp_port') ? parseInt(formData.get('smtp_port') as string) : undefined,
|
||||
display_name: formData.get('display_name')?.toString() || '',
|
||||
smtp_host: formData.get('smtp_host')?.toString() || '',
|
||||
smtp_port: formData.get('smtp_port')?.toString() ?
|
||||
parseInt(formData.get('smtp_port')?.toString() || '587') : undefined,
|
||||
smtp_secure: formData.get('smtp_secure') === 'on'
|
||||
};
|
||||
|
||||
// If display_name is empty, use email
|
||||
if (!formValues.display_name) {
|
||||
formValues.display_name = formValues.email;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/courrier/account', {
|
||||
// First test the connection
|
||||
const testResponse = await fetch('/api/courrier/test-connection', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(newAccount)
|
||||
body: JSON.stringify({
|
||||
email: formValues.email,
|
||||
password: formValues.password,
|
||||
host: formValues.host,
|
||||
port: formValues.port,
|
||||
secure: formValues.secure
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
const testResult = await testResponse.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result.error || 'Failed to add account');
|
||||
if (!testResponse.ok) {
|
||||
throw new Error(testResult.error || 'Connection test failed');
|
||||
}
|
||||
|
||||
console.log('Connection test successful:', testResult);
|
||||
|
||||
// If connection test is successful, save the account
|
||||
const saveResponse = await fetch('/api/courrier/account', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formValues)
|
||||
});
|
||||
|
||||
const saveResult = await saveResponse.json();
|
||||
|
||||
if (!saveResponse.ok) {
|
||||
throw new Error(saveResult.error || 'Failed to add account');
|
||||
}
|
||||
|
||||
// Update accounts list
|
||||
const newAccountObj = {
|
||||
id: Date.now(), // temporary ID
|
||||
name: newAccount.display_name as string,
|
||||
email: newAccount.email as string,
|
||||
name: formValues.display_name,
|
||||
email: formValues.email,
|
||||
color: `bg-blue-500`, // Default color class
|
||||
folders: ['INBOX', 'Sent', 'Drafts', 'Trash'] // Default folders
|
||||
folders: testResult.details.sampleFolders || ['INBOX', 'Sent', 'Drafts', 'Trash'] // Use discovered folders or defaults
|
||||
};
|
||||
|
||||
setAccounts(prev => [...prev, newAccountObj]);
|
||||
setShowAddAccountForm(false);
|
||||
toast({
|
||||
title: "Account added successfully",
|
||||
description: `Your email account ${newAccount.email} has been added.`,
|
||||
description: `Your email account ${formValues.email} has been added.`,
|
||||
duration: 5000
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user