From 0de53ddb6effb1339c6d54a1686615e201fce522 Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 16 Apr 2025 20:37:03 +0200 Subject: [PATCH] Neah version mail remove mail correction 2 ?? --- app/api/mail/bulk-actions/route.ts | 179 ++++++++++++++++++++++------- app/api/mail/mark-read/route.ts | 153 +++++++++++++++++++----- 2 files changed, 261 insertions(+), 71 deletions(-) diff --git a/app/api/mail/bulk-actions/route.ts b/app/api/mail/bulk-actions/route.ts index 0739a0d..d265682 100644 --- a/app/api/mail/bulk-actions/route.ts +++ b/app/api/mail/bulk-actions/route.ts @@ -1,5 +1,32 @@ import { NextResponse } from 'next/server'; -import { getImapClient } from '@/lib/imap'; +import { cookies } from 'next/headers'; +import Imap from 'imap'; + +interface StoredCredentials { + email: string; + password: string; + host: string; + port: number; +} + +function getStoredCredentials(): StoredCredentials | null { + const cookieStore = cookies(); + const credentialsCookie = cookieStore.get('imap_credentials'); + + if (!credentialsCookie?.value) { + return null; + } + + try { + const credentials = JSON.parse(credentialsCookie.value); + if (!credentials.email || !credentials.password || !credentials.host || !credentials.port) { + return null; + } + return credentials; + } catch (error) { + return null; + } +} export async function POST(request: Request) { try { @@ -16,53 +43,125 @@ export async function POST(request: Request) { const url = new URL(request.url); const folder = url.searchParams.get('folder') || 'INBOX'; - // Get the IMAP client using the same method as the mail endpoint - const imap = await getImapClient(); - if (!imap) { + // Get stored credentials + const credentials = getStoredCredentials(); + if (!credentials) { return NextResponse.json( - { error: 'Failed to connect to mail server' }, - { status: 500 } + { error: 'No stored credentials found' }, + { status: 401 } ); } - // Convert string IDs to numbers - const numericIds = emailIds.map(id => parseInt(id, 10)); - - // Get mailbox lock for the current folder - const lock = await imap.getMailboxLock(folder); - - try { - // Fetch messages to get their UIDs - const messages = await imap.fetch({ - seq: numericIds.map(id => id.toString()), - uid: true, - envelope: true + return new Promise((resolve) => { + const imap = new Imap({ + user: credentials.email, + password: credentials.password, + host: credentials.host, + port: credentials.port, + tls: true, + tlsOptions: { rejectUnauthorized: false }, + authTimeout: 30000, + connTimeout: 30000 }); - // Process each message based on the action - for await (const message of messages) { - if (!message.uid) continue; + const timeout = setTimeout(() => { + console.error('IMAP connection timeout'); + imap.end(); + resolve(NextResponse.json({ error: 'Connection timeout' })); + }, 30000); - switch (action) { - case 'delete': - await imap.messageMove(message.uid, 'Trash'); - break; - case 'mark-read': - await imap.messageFlagsAdd(message.uid, ['\\Seen'], { uid: true }); - break; - case 'mark-unread': - await imap.messageFlagsRemove(message.uid, ['\\Seen'], { uid: true }); - break; - case 'archive': - await imap.messageMove(message.uid, 'Archive'); - break; - } - } + imap.once('error', (err: Error) => { + console.error('IMAP error:', err); + clearTimeout(timeout); + resolve(NextResponse.json({ error: 'IMAP connection error' })); + }); - return NextResponse.json({ success: true }); - } finally { - lock.release(); - } + imap.once('ready', () => { + imap.openBox(folder, false, (err, box) => { + if (err) { + console.error(`Error opening box ${folder}:`, err); + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ error: `Failed to open folder ${folder}` })); + return; + } + + // Convert string IDs to numbers + const numericIds = emailIds.map(id => parseInt(id, 10)); + + // Process each email + let processedCount = 0; + const totalEmails = numericIds.length; + + const processNextEmail = (index: number) => { + if (index >= totalEmails) { + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ success: true })); + return; + } + + const id = numericIds[index]; + const fetch = imap.fetch(id.toString(), { + bodies: '', + struct: true + }); + + fetch.on('message', (msg) => { + msg.once('attributes', (attrs) => { + const uid = attrs.uid; + if (!uid) { + processedCount++; + processNextEmail(index + 1); + return; + } + + switch (action) { + case 'delete': + imap.move(uid, 'Trash', (err) => { + if (err) console.error('Error moving to trash:', err); + processedCount++; + processNextEmail(index + 1); + }); + break; + case 'mark-read': + imap.addFlags(uid, ['\\Seen'], (err) => { + if (err) console.error('Error marking as read:', err); + processedCount++; + processNextEmail(index + 1); + }); + break; + case 'mark-unread': + imap.removeFlags(uid, ['\\Seen'], (err) => { + if (err) console.error('Error marking as unread:', err); + processedCount++; + processNextEmail(index + 1); + }); + break; + case 'archive': + imap.move(uid, 'Archive', (err) => { + if (err) console.error('Error moving to archive:', err); + processedCount++; + processNextEmail(index + 1); + }); + break; + } + }); + }); + + fetch.on('error', (err) => { + console.error('Error fetching email:', err); + processedCount++; + processNextEmail(index + 1); + }); + }; + + processNextEmail(0); + }); + }); + + imap.connect(); + }); } catch (error) { console.error('Error in bulk actions:', error); return NextResponse.json( diff --git a/app/api/mail/mark-read/route.ts b/app/api/mail/mark-read/route.ts index 21399e5..c91b6ca 100644 --- a/app/api/mail/mark-read/route.ts +++ b/app/api/mail/mark-read/route.ts @@ -1,5 +1,32 @@ import { NextResponse } from 'next/server'; -import { getImapClient } from '@/lib/imap'; +import { cookies } from 'next/headers'; +import Imap from 'imap'; + +interface StoredCredentials { + email: string; + password: string; + host: string; + port: number; +} + +function getStoredCredentials(): StoredCredentials | null { + const cookieStore = cookies(); + const credentialsCookie = cookieStore.get('imap_credentials'); + + if (!credentialsCookie?.value) { + return null; + } + + try { + const credentials = JSON.parse(credentialsCookie.value); + if (!credentials.email || !credentials.password || !credentials.host || !credentials.port) { + return null; + } + return credentials; + } catch (error) { + return null; + } +} export async function POST(request: Request) { try { @@ -16,44 +43,108 @@ export async function POST(request: Request) { const url = new URL(request.url); const folder = url.searchParams.get('folder') || 'INBOX'; - // Get the IMAP client using the same method as the mail endpoint - const imap = await getImapClient(); - if (!imap) { + // Get stored credentials + const credentials = getStoredCredentials(); + if (!credentials) { return NextResponse.json( - { error: 'Failed to connect to mail server' }, - { status: 500 } + { error: 'No stored credentials found' }, + { status: 401 } ); } - // Convert string ID to number - const numericId = parseInt(emailId, 10); - - // Get mailbox lock for the current folder - const lock = await imap.getMailboxLock(folder); - - try { - // Fetch message to get its UID - const messages = await imap.fetch({ - seq: numericId.toString(), - uid: true, - envelope: true + return new Promise((resolve) => { + const imap = new Imap({ + user: credentials.email, + password: credentials.password, + host: credentials.host, + port: credentials.port, + tls: true, + tlsOptions: { rejectUnauthorized: false }, + authTimeout: 30000, + connTimeout: 30000 }); - // Process the message - for await (const message of messages) { - if (!message.uid) continue; + const timeout = setTimeout(() => { + console.error('IMAP connection timeout'); + imap.end(); + resolve(NextResponse.json({ error: 'Connection timeout' })); + }, 30000); - if (isRead) { - await imap.messageFlagsAdd(message.uid, ['\\Seen'], { uid: true }); - } else { - await imap.messageFlagsRemove(message.uid, ['\\Seen'], { uid: true }); - } - } + imap.once('error', (err: Error) => { + console.error('IMAP error:', err); + clearTimeout(timeout); + resolve(NextResponse.json({ error: 'IMAP connection error' })); + }); - return NextResponse.json({ success: true }); - } finally { - lock.release(); - } + imap.once('ready', () => { + imap.openBox(folder, false, (err, box) => { + if (err) { + console.error(`Error opening box ${folder}:`, err); + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ error: `Failed to open folder ${folder}` })); + return; + } + + // Convert string ID to number + const numericId = parseInt(emailId, 10); + + const fetch = imap.fetch(numericId.toString(), { + bodies: '', + struct: true + }); + + fetch.on('message', (msg) => { + msg.once('attributes', (attrs) => { + const uid = attrs.uid; + if (!uid) { + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ error: 'No UID found for email' })); + return; + } + + if (isRead) { + imap.addFlags(uid, ['\\Seen'], (err) => { + if (err) { + console.error('Error marking as read:', err); + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ error: 'Failed to mark as read' })); + return; + } + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ success: true })); + }); + } else { + imap.removeFlags(uid, ['\\Seen'], (err) => { + if (err) { + console.error('Error marking as unread:', err); + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ error: 'Failed to mark as unread' })); + return; + } + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ success: true })); + }); + } + }); + }); + + fetch.on('error', (err) => { + console.error('Error fetching email:', err); + clearTimeout(timeout); + imap.end(); + resolve(NextResponse.json({ error: 'Failed to fetch email' })); + }); + }); + }); + + imap.connect(); + }); } catch (error) { console.error('Error marking email as read:', error); return NextResponse.json(