NeahFront9/app/api/mail/route.ts

420 lines
11 KiB
TypeScript

import { NextResponse } from 'next/server';
import Imap from 'imap';
import { simpleParser } from 'mailparser';
// Debug logging for environment variables
console.log('Environment Variables:', {
IMAP_USER: process.env.IMAP_USER,
IMAP_HOST: process.env.IMAP_HOST,
IMAP_PORT: process.env.IMAP_PORT,
IMAP_PASSWORD: process.env.IMAP_PASSWORD ? '***' : undefined,
NODE_ENV: process.env.NODE_ENV
});
// Store for credentials (in memory, you might want to use a proper storage solution)
let storedCredentials: {
user: string;
password: string;
host: string;
port: string;
} | null = null;
// Helper function to get stored credentials
function getStoredCredentials() {
// Server-side: use stored credentials
if (typeof window === 'undefined') {
return storedCredentials;
}
// Client-side: use localStorage
const stored = localStorage.getItem('imapCredentials');
return stored ? JSON.parse(stored) : null;
}
// IMAP configuration
const getImapConfig = () => {
const credentials = getStoredCredentials();
if (!credentials?.user || !credentials?.password) {
throw new Error('Email credentials not found. Please log in first.');
}
return {
user: credentials.user,
password: credentials.password,
host: credentials.host || 'mail.infomaniak.com',
port: parseInt(credentials.port || '993', 10),
tls: true,
tlsOptions: {
rejectUnauthorized: false,
servername: credentials.host || 'mail.infomaniak.com'
},
authTimeout: 10000,
connTimeout: 10000,
debug: console.log
};
};
// Debug logging for IMAP configuration
console.log('IMAP Configuration:', {
user: getImapConfig().user,
host: getImapConfig().host,
port: getImapConfig().port,
tls: getImapConfig().tls,
hasPassword: !!getImapConfig().password,
authTimeout: getImapConfig().authTimeout,
connTimeout: getImapConfig().connTimeout
});
interface ImapMessage {
header: {
from?: string[];
to?: string[];
subject?: string[];
date?: string[];
[key: string]: string[] | undefined;
};
body: string;
attributes: {
flags: string[];
};
}
interface ImapError extends Error {
type?: string;
textCode?: string;
source?: string;
}
interface Email {
id: string;
from: string;
subject: string;
date: string;
body: string;
read: boolean;
starred: boolean;
}
interface EmailHeaders {
from: string;
subject: string;
date: string;
}
function parseEmailHeaders(buffer: string): EmailHeaders {
const headers: EmailHeaders = {
from: '',
subject: '',
date: ''
};
const lines = buffer.split('\r\n');
for (const line of lines) {
if (line.toLowerCase().startsWith('from:')) {
headers.from = line.substring(5).trim();
} else if (line.toLowerCase().startsWith('subject:')) {
headers.subject = line.substring(8).trim();
} else if (line.toLowerCase().startsWith('date:')) {
headers.date = line.substring(5).trim();
}
}
return headers;
}
// Helper function to create a promise-based IMAP connection
function createImapConnection(config: any): Promise<Imap> {
return new Promise((resolve, reject) => {
const imap = new Imap({
user: config.user,
password: config.password,
host: config.host,
port: config.port,
tls: true,
tlsOptions: {
rejectUnauthorized: false,
servername: config.host
},
authTimeout: 10000,
connTimeout: 10000,
debug: console.log,
autotls: 'always',
keepalive: true
});
imap.once('ready', () => {
console.log('IMAP connection established successfully');
resolve(imap);
});
imap.once('error', (err: ImapError) => {
console.error('IMAP connection error:', err);
console.error('Error details:', {
type: err.type,
textCode: err.textCode,
source: err.source
});
reject(err);
});
imap.once('end', () => console.log('[connection] Ended'));
imap.once('close', () => console.log('[connection] Closed'));
try {
console.log('Attempting to connect to IMAP server...');
console.log('Using credentials:', {
user: config.user,
passwordLength: config.password.length,
host: config.host,
port: config.port
});
imap.connect();
} catch (err) {
console.error('Error during IMAP connection:', err);
reject(err);
}
});
}
// Helper function to promisify the message fetching
function fetchMessages(imap: Imap, box: string): Promise<ImapMessage[]> {
return new Promise((resolve, reject) => {
imap.openBox(box, false, (err, mailbox) => {
if (err) {
reject(err);
return;
}
// Search for all messages
imap.search(['ALL'], (err, results) => {
if (err) {
reject(err);
return;
}
// No messages found
if (!results || !results.length) {
resolve([]);
return;
}
const fetch = imap.fetch(results, {
bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', 'TEXT'],
struct: true
});
const messages: ImapMessage[] = [];
fetch.on('message', (msg) => {
const message: Partial<ImapMessage> = {};
msg.on('body', (stream, info) => {
let buffer = '';
stream.on('data', (chunk) => {
buffer += chunk.toString('utf8');
});
stream.once('end', () => {
if (info.which === 'TEXT') {
message.body = buffer;
} else {
message.header = Imap.parseHeader(buffer);
}
});
});
msg.once('attributes', (attrs) => {
message.attributes = attrs;
});
msg.once('end', () => {
messages.push(message as ImapMessage);
});
});
fetch.once('error', (err) => {
reject(err);
});
fetch.once('end', () => {
resolve(messages);
});
});
});
});
}
export async function GET() {
try {
// Get stored credentials
const credentials = getStoredCredentials();
if (!credentials) {
return NextResponse.json(
{ error: 'No IMAP credentials found. Please configure your email account.' },
{ status: 401 }
);
}
console.log('Starting email fetch process...');
console.log('IMAP Configuration:', {
user: credentials.user,
host: credentials.host,
port: credentials.port,
tls: true,
hasPassword: !!credentials.password
});
// Create IMAP connection
const imap = await createImapConnection({
user: credentials.user,
password: credentials.password,
host: credentials.host,
port: parseInt(credentials.port),
tls: true
});
return new Promise((resolve, reject) => {
imap.once('ready', () => {
console.log('IMAP connection ready');
imap.openBox('INBOX', false, (err, box) => {
if (err) {
console.error('Error opening inbox:', err);
imap.end();
reject(new Error('Failed to open inbox'));
return;
}
const fetch = imap.seq.fetch('1:10', {
bodies: ['HEADER', 'TEXT'],
struct: true
});
const messages: Email[] = [];
fetch.on('message', (msg) => {
const email: Email = {
id: '',
from: '',
subject: '',
date: '',
body: '',
read: false,
starred: false
};
msg.on('body', (stream) => {
let buffer = '';
stream.on('data', (chunk) => {
buffer += chunk.toString('utf8');
});
stream.on('end', () => {
const headers = parseEmailHeaders(buffer);
email.from = headers.from;
email.subject = headers.subject;
email.date = headers.date;
});
});
msg.once('attributes', (attrs) => {
email.id = attrs.uid.toString();
email.read = !attrs.flags.includes('\\Unseen');
email.starred = attrs.flags.includes('\\Flagged');
});
msg.once('end', () => {
messages.push(email);
});
});
fetch.once('error', (err) => {
console.error('Fetch error:', err);
imap.end();
reject(new Error('Failed to fetch messages'));
});
fetch.once('end', () => {
imap.end();
resolve(NextResponse.json(messages));
});
});
});
imap.once('error', (err: ImapError) => {
console.error('IMAP connection error:', err);
console.error('Error details:', {
type: err.type,
textCode: err.textCode,
source: err.source
});
imap.end();
reject(new Error(err.message));
});
imap.connect();
});
} catch (error) {
console.error('Error in GET handler:', error);
if (error instanceof Error) {
if (error.message.includes('Invalid login or password')) {
return NextResponse.json(
{ error: 'Invalid login or password', details: error.message },
{ status: 401 }
);
}
return NextResponse.json(
{ error: 'Failed to fetch emails', details: error.message },
{ status: 500 }
);
}
return NextResponse.json(
{ error: 'Unknown error occurred' },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
const body = await request.json();
const { email, password, host, port } = body;
if (!email || !password || !host || !port) {
return NextResponse.json(
{ error: 'Missing required fields' },
{ status: 400 }
);
}
// Store credentials
storedCredentials = {
user: email,
password,
host,
port
};
// Test the connection
const imap = await createImapConnection(storedCredentials);
imap.end();
return NextResponse.json({ success: true });
} catch (error) {
console.error('Error in POST handler:', error);
if (error instanceof Error) {
if (error.message.includes('Invalid login or password')) {
return NextResponse.json(
{ error: 'Invalid login or password', details: error.message },
{ status: 401 }
);
}
return NextResponse.json(
{ error: 'Failed to connect to email server', details: error.message },
{ status: 500 }
);
}
return NextResponse.json(
{ error: 'Unknown error occurred' },
{ status: 500 }
);
}
}