diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
index 9d107f88..e98a4394 100644
--- a/app/api/auth/[...nextauth]/route.ts
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -58,6 +58,35 @@ export const authOptions: NextAuthOptions = {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
},
+ cookies: {
+ sessionToken: {
+ name: `__Secure-next-auth.session-token`,
+ options: {
+ httpOnly: true,
+ sameSite: 'lax',
+ path: '/',
+ secure: true
+ }
+ },
+ callbackUrl: {
+ name: `__Secure-next-auth.callback-url`,
+ options: {
+ httpOnly: true,
+ sameSite: 'lax',
+ path: '/',
+ secure: true
+ }
+ },
+ csrfToken: {
+ name: `__Host-next-auth.csrf-token`,
+ options: {
+ httpOnly: true,
+ sameSite: 'lax',
+ path: '/',
+ secure: true
+ }
+ }
+ },
callbacks: {
async jwt({ token, account, profile }) {
if (account && profile) {
@@ -121,11 +150,13 @@ export const authOptions: NextAuthOptions = {
session.accessToken = token.accessToken;
session.user = {
- ...session.user,
id: token.sub as string,
+ name: token.name,
+ email: token.email,
+ image: null,
+ username: token.username ?? '',
first_name: token.first_name ?? '',
last_name: token.last_name ?? '',
- username: token.username ?? '',
role: token.role ?? [],
};
diff --git a/app/api/mail/login/route.ts b/app/api/mail/login/route.ts
index 92f9b333..6fa2d744 100644
--- a/app/api/mail/login/route.ts
+++ b/app/api/mail/login/route.ts
@@ -1,15 +1,14 @@
import { NextResponse } from 'next/server';
import Imap from 'imap';
+import { cookies } from 'next/headers';
interface StoredCredentials {
- user: string;
+ email: string;
password: string;
host: string;
- port: string;
+ port: number;
}
-export let storedCredentials: StoredCredentials | null = null;
-
export async function POST(request: Request) {
try {
const { email, password, host, port } = await request.json();
@@ -40,8 +39,25 @@ export async function POST(request: Request) {
return new Promise((resolve, reject) => {
imap.once('ready', () => {
imap.end();
- // Store credentials
- storedCredentials = { user: email, password, host, port };
+
+ // Store credentials in cookie
+ const cookieStore = cookies();
+ const credentials: StoredCredentials = {
+ email,
+ password,
+ host,
+ port: parseInt(port)
+ };
+
+ // Set the cookie with proper security options
+ cookieStore.set('imap_credentials', JSON.stringify(credentials), {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ sameSite: 'lax',
+ path: '/',
+ maxAge: 30 * 24 * 60 * 60 // 30 days
+ });
+
resolve(NextResponse.json({ success: true }));
});
@@ -78,14 +94,25 @@ export async function POST(request: Request) {
}
export async function GET() {
- if (!storedCredentials) {
+ const cookieStore = cookies();
+ const credentialsCookie = cookieStore.get('imap_credentials');
+
+ if (!credentialsCookie?.value) {
return NextResponse.json(
{ error: 'No stored credentials found' },
{ status: 404 }
);
}
- // Return credentials without password
- const { password, ...safeCredentials } = storedCredentials;
- return NextResponse.json(safeCredentials);
+ try {
+ const credentials = JSON.parse(credentialsCookie.value);
+ // Return credentials without password for security
+ const { password, ...safeCredentials } = credentials;
+ return NextResponse.json(safeCredentials);
+ } catch (error) {
+ return NextResponse.json(
+ { error: 'Invalid credentials format' },
+ { status: 400 }
+ );
+ }
}
\ No newline at end of file
diff --git a/app/api/mail/route.ts b/app/api/mail/route.ts
index 6a08520b..ac1f3ddd 100644
--- a/app/api/mail/route.ts
+++ b/app/api/mail/route.ts
@@ -1,362 +1,52 @@
import { NextResponse } from 'next/server';
-import Imap from 'imap';
-import nodemailer from 'nodemailer';
-import { parseEmailHeaders, decodeEmailBody } from '@/lib/email-parser';
import { cookies } from 'next/headers';
+import { ImapFlow } from 'imapflow';
-interface StoredCredentials {
- email: string;
- password: string;
- host: string;
- port: number;
-}
-
-interface Email {
- id: string;
- from: string;
- subject: string;
- date: Date;
- read: boolean;
- starred: boolean;
- body: string;
- to?: string;
- folder: string;
-}
-
-interface ImapBox {
- messages: {
- total: number;
- };
-}
-
-interface ImapMessage {
- on: (event: string, callback: (data: any) => void) => void;
- once: (event: string, callback: (data: any) => void) => void;
- attributes: {
- uid: number;
- flags: string[];
- size: number;
- };
- body: {
- [key: string]: {
- on: (event: string, callback: (data: any) => void) => void;
- };
- };
-}
-
-interface ImapConfig {
- user: string;
- password: string;
- host: string;
- port: number;
- tls: boolean;
- authTimeout: number;
- connTimeout: number;
- debug?: (info: string) => void;
-}
-
-function getStoredCredentials(): StoredCredentials | null {
- const cookieStore = cookies();
-
- const credentialsCookie = cookieStore.get('imap_credentials');
- console.log('Retrieved credentials cookie:', credentialsCookie ? 'Found' : 'Not found');
-
- if (!credentialsCookie?.value) {
- console.log('No credentials cookie found');
- return null;
- }
-
+export async function GET() {
try {
- const credentials = JSON.parse(credentialsCookie.value);
- console.log('Parsed credentials:', {
- ...credentials,
- password: '***'
- });
+ const cookieStore = cookies();
+ const credentials = cookieStore.get('imap_credentials');
- // Validate required fields
- if (!credentials.email || !credentials.password || !credentials.host || !credentials.port) {
- console.error('Missing required credentials fields');
- return null;
- }
-
- return {
- email: credentials.email,
- password: credentials.password,
- host: credentials.host,
- port: credentials.port
- };
- } catch (error) {
- console.error('Error parsing credentials cookie:', error);
- return null;
- }
-}
-
-export async function GET(request: Request) {
- try {
- const credentials = getStoredCredentials();
if (!credentials) {
return NextResponse.json(
- { error: 'No stored credentials found' },
+ { error: 'No credentials found' },
{ status: 401 }
);
}
- // Get pagination parameters from URL
- const url = new URL(request.url);
- const folder = url.searchParams.get('folder') || 'INBOX';
- const page = parseInt(url.searchParams.get('page') || '1');
- const limit = parseInt(url.searchParams.get('limit') || '24');
- const offset = (page - 1) * limit;
+ const { email, password, host, port } = JSON.parse(credentials.value);
- 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
- });
-
- const timeout = setTimeout(() => {
- console.error('IMAP connection timeout');
- imap.end();
- resolve(NextResponse.json({
- emails: [],
- error: 'Connection timeout'
- }));
- }, 30000);
-
- imap.once('error', (err: Error) => {
- console.error('IMAP error:', err);
- clearTimeout(timeout);
- resolve(NextResponse.json({
- emails: [],
- error: 'IMAP connection error'
- }));
- });
-
- imap.once('ready', () => {
- imap.getBoxes((err, boxes) => {
- if (err) {
- console.error('Error getting mailboxes:', err);
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({ emails: [], error: 'Failed to get mailboxes' }));
- return;
- }
-
- const availableMailboxes = Object.keys(boxes).filter(
- box => !['Starred', 'Archives'].includes(box)
- );
- console.log('Available mailboxes:', availableMailboxes);
-
- // Only process the requested folder
- imap.openBox(folder, false, (err, box) => {
- if (err) {
- console.error(`Error opening box ${folder}:`, err);
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({ emails: [], error: `Failed to open folder ${folder}` }));
- return;
- }
-
- // Get the specified folder
- const totalMessages = box.messages.total;
-
- // Calculate the range of messages to fetch
- const start = Math.max(1, totalMessages - offset - limit + 1);
- const end = totalMessages - offset;
-
- if (start > end) {
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({
- emails: [],
- folders: availableMailboxes,
- mailUrl: process.env.NEXT_PUBLIC_IFRAME_MAIL_URL
- }));
- return;
- }
-
- // Fetch messages in the calculated range
- imap.search(['ALL'], (err, results) => {
- if (err) {
- console.error(`Error searching in ${folder}:`, err);
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({ emails: [], error: `Failed to search in ${folder}` }));
- return;
- }
-
- if (!results || results.length === 0) {
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({
- emails: [],
- folders: availableMailboxes,
- mailUrl: process.env.NEXT_PUBLIC_IFRAME_MAIL_URL
- }));
- return;
- }
-
- // Take only the most recent emails up to the limit
- const recentResults = results.slice(-limit);
- const emails: any[] = [];
-
- const fetch = imap.fetch(recentResults, {
- bodies: ['HEADER', 'TEXT'],
- struct: true
- });
-
- fetch.on('message', (msg) => {
- let header = '';
- let text = '';
- let messageId: number | null = null;
- let messageFlags: string[] = [];
-
- msg.once('attributes', (attrs) => {
- messageId = attrs.uid;
- messageFlags = attrs.flags || [];
- });
-
- msg.on('body', (stream, info) => {
- let buffer = '';
- stream.on('data', (chunk) => {
- buffer += chunk.toString('utf8');
- });
- stream.on('end', () => {
- if (info.which === 'HEADER') {
- header = buffer;
- } else if (info.which === 'TEXT') {
- text = buffer;
- }
- });
- });
-
- msg.on('end', () => {
- if (!messageId) {
- console.error('No message ID found for email');
- return;
- }
-
- const parsedHeader = Imap.parseHeader(header);
- const email = {
- id: messageId,
- from: parsedHeader.from?.[0] || '',
- to: parsedHeader.to?.[0] || '',
- subject: parsedHeader.subject?.[0] || '(No subject)',
- date: parsedHeader.date?.[0] || new Date().toISOString(),
- body: text,
- folder: folder,
- flags: messageFlags,
- read: messageFlags.includes('\\Seen'),
- starred: messageFlags.includes('\\Flagged')
- };
- emails.push(email);
- });
- });
-
- fetch.on('error', (err) => {
- console.error(`Error fetching emails from ${folder}:`, err);
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({ emails: [], error: `Failed to fetch emails from ${folder}` }));
- });
-
- fetch.on('end', () => {
- // Sort emails by date (most recent first)
- emails.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
-
- clearTimeout(timeout);
- imap.end();
- resolve(NextResponse.json({
- emails,
- folders: availableMailboxes,
- mailUrl: process.env.NEXT_PUBLIC_IFRAME_MAIL_URL
- }));
- });
- });
- });
- });
- });
-
- imap.connect();
+ const client = new ImapFlow({
+ host,
+ port: parseInt(port),
+ secure: true,
+ auth: {
+ user: email,
+ pass: password,
+ },
});
+
+ await client.connect();
+ const mailbox = await client.mailboxOpen('INBOX');
+ const messages = await client.fetch('1:10', { envelope: true });
+
+ const result = [];
+ for await (const message of messages) {
+ result.push({
+ id: message.uid,
+ subject: message.envelope.subject,
+ from: message.envelope.from[0].address,
+ date: message.envelope.date,
+ });
+ }
+
+ await client.logout();
+ return NextResponse.json(result);
} catch (error) {
- console.error('Error in GET /api/mail:', error);
+ console.error('Mail API error:', error);
return NextResponse.json(
{ error: 'Failed to fetch emails' },
{ status: 500 }
);
}
-}
-
-export async function POST(request: Request) {
- try {
- const credentials = getStoredCredentials();
- if (!credentials) {
- return NextResponse.json(
- { error: 'No stored credentials found' },
- { status: 401 }
- );
- }
-
- let body;
- try {
- body = await request.json();
- } catch (error) {
- return NextResponse.json(
- { error: 'Invalid JSON in request body' },
- { status: 400 }
- );
- }
-
- const { to, subject, body: emailBody, attachments } = body;
-
- if (!to || !subject || !emailBody) {
- return NextResponse.json(
- { error: 'Missing required fields: to, subject, or body' },
- { status: 400 }
- );
- }
-
- const transporter = nodemailer.createTransport({
- host: credentials.host,
- port: credentials.port,
- secure: true,
- auth: {
- user: credentials.email,
- pass: credentials.password,
- },
- });
-
- const mailOptions = {
- from: credentials.email,
- to,
- subject,
- text: emailBody,
- attachments: attachments || [],
- };
-
- const info = await transporter.sendMail(mailOptions);
- console.log('Email sent:', info.messageId);
-
- return NextResponse.json({
- success: true,
- messageId: info.messageId,
- message: 'Email sent successfully'
- });
- } catch (error) {
- console.error('Error sending email:', error);
- return NextResponse.json(
- {
- error: error instanceof Error ? error.message : 'Failed to send email',
- details: error instanceof Error ? error.stack : undefined
- },
- { status: 500 }
- );
- }
}
\ No newline at end of file
diff --git a/app/mail/login/page.tsx b/app/mail/login/page.tsx
index 2867a7e0..6179d145 100644
--- a/app/mail/login/page.tsx
+++ b/app/mail/login/page.tsx
@@ -6,7 +6,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
-import { setCookie } from 'cookies-next';
export default function LoginPage() {
const router = useRouter();
@@ -43,34 +42,24 @@ export default function LoginPage() {
throw new Error(testData.error || 'Failed to connect to email server');
}
- // Store all credentials in a single cookie
- const credentials = {
- email,
- password,
- host,
- port: parseInt(port),
- };
-
- console.log('Storing credentials in cookie:', {
- ...credentials,
- password: '***'
+ // Store credentials using the API endpoint
+ const loginResponse = await fetch('/api/mail/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ email,
+ password,
+ host,
+ port,
+ }),
});
- // Store as a single cookie with proper options
- setCookie('imap_credentials', JSON.stringify(credentials), {
- maxAge: 60 * 60 * 24, // 1 day
- path: '/',
- sameSite: 'lax',
- secure: process.env.NODE_ENV === 'production',
- httpOnly: false // Allow access from JavaScript
- });
+ const loginData = await loginResponse.json();
- // Verify cookie was set
- const stored = document.cookie.split(';').find(c => c.trim().startsWith('imap_credentials='));
- console.log('Cookie verification:', stored ? 'Cookie found' : 'Cookie not found');
-
- if (!stored) {
- throw new Error('Failed to store credentials');
+ if (!loginResponse.ok) {
+ throw new Error(loginData.error || 'Failed to store credentials');
}
// Redirect to mail page
diff --git a/middleware.ts b/middleware.ts
index a0b8055e..0e16f91c 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -18,6 +18,14 @@ export default withAuth(
if (req.nextUrl.pathname === "/" || req.nextUrl.pathname === "/signin") {
return true;
}
+
+ // Check if the request is for an API route
+ if (req.nextUrl.pathname.startsWith('/api/')) {
+ // For API routes, require a valid token
+ return !!token;
+ }
+
+ // For all other routes, require a valid token
return !!token;
},
},
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json
index 249fa932..790f6f07 100644
--- a/node_modules/.package-lock.json
+++ b/node_modules/.package-lock.json
@@ -2214,6 +2214,16 @@
"@types/node": "*"
}
},
+ "node_modules/@types/imapflow": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/@types/imapflow/-/imapflow-1.0.20.tgz",
+ "integrity": "sha512-kmBeiV815byuxYlu2lomAx3VY3SsyOYg4rDsK5vT6CGZ6Sow2AUEZwieql8uAizO6+p4sQchw/g3vQX76XBIpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/mailparser": {
"version": "3.4.5",
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz",
diff --git a/node_modules/@types/imapflow/LICENSE b/node_modules/@types/imapflow/LICENSE
new file mode 100644
index 00000000..9e841e7a
--- /dev/null
+++ b/node_modules/@types/imapflow/LICENSE
@@ -0,0 +1,21 @@
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
diff --git a/node_modules/@types/imapflow/README.md b/node_modules/@types/imapflow/README.md
new file mode 100644
index 00000000..7feca44a
--- /dev/null
+++ b/node_modules/@types/imapflow/README.md
@@ -0,0 +1,15 @@
+# Installation
+> `npm install --save @types/imapflow`
+
+# Summary
+This package contains type definitions for imapflow (https://imapflow.com/).
+
+# Details
+Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/imapflow.
+
+### Additional Details
+ * Last updated: Mon, 03 Mar 2025 07:02:16 GMT
+ * Dependencies: [@types/node](https://npmjs.com/package/@types/node)
+
+# Credits
+These definitions were written by [Jeffrey Ratton](https://github.com/jeffreyratton98), [Martin Badin](https://github.com/martin-badin), [Northern Star](https://github.com/grayson-code), and [Zachary Nawar](https://github.com/remscar).
diff --git a/node_modules/@types/imapflow/index.d.ts b/node_modules/@types/imapflow/index.d.ts
new file mode 100644
index 00000000..9535cc2a
--- /dev/null
+++ b/node_modules/@types/imapflow/index.d.ts
@@ -0,0 +1,393 @@
+///
+
+import { EventEmitter } from "stream";
+
+export type Readable = import("stream").Readable;
+
+export class ImapFlow extends EventEmitter {
+ constructor(options: ImapFlowOptions);
+ authenticated: string | boolean;
+ capabilities: Map;
+ emitLogs: boolean;
+ enabled: Set;
+ id: string;
+ idling: boolean;
+ mailbox: MailboxObject | boolean;
+ secureConnection: boolean;
+ serverInfo: IdInfoObject;
+ usable: boolean;
+
+ append(
+ path: string,
+ content: string | Buffer,
+ flags?: string[],
+ idate?: Date | string,
+ ): Promise;
+
+ connect(): Promise;
+ logout(): Promise;
+ close(): void;
+ download(
+ range: SequenceString,
+ part?: string,
+ options?: { uid?: boolean; maxBytes?: number; chunkSize?: number },
+ ): Promise;
+
+ getMailboxLock(path: string, options?: null | { readonly?: boolean }): Promise;
+
+ getQuota(path: string): Promise;
+
+ idle(): Promise;
+
+ /**
+ * @see {@link https://imapflow.com/module-imapflow-ImapFlow.html#list}
+ */
+ list(options?: {
+ statusQuery?: StatusQuery;
+ specialUseHints?: SpecialUseHints;
+ }): Promise;
+
+ listTree(): Promise;
+
+ mailboxClose(): Promise;
+
+ mailboxCreate(path: string | any[]): Promise;
+
+ mailboxDelete(path: string | any[]): Promise;
+
+ mailboxOpen(path: string | any[], options?: { readOnly?: boolean }): Promise;
+
+ mailboxRename(path: string | any[], newPath: string | any[]): Promise;
+
+ mailboxSubscribe(path: string | any[]): Promise;
+
+ mailboxUnsubscribe(path: string | any[]): Promise;
+
+ messageCopy(
+ range: SequenceString | number[] | SearchObject,
+ destination: string,
+ options?: { uid?: boolean },
+ ): Promise;
+
+ messageDelete(range: SequenceString | number[] | SearchObject, options?: { uid?: boolean }): Promise;
+
+ messageFlagsAdd(
+ range: SequenceString | number[] | SearchObject,
+ Array: string[],
+ options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
+ ): Promise;
+
+ messageFlagsRemove(
+ range: SequenceString | number[] | SearchObject,
+ Array: string[],
+ options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
+ ): Promise;
+
+ messageFlagsSet(
+ range: SequenceString | number[] | SearchObject,
+ Array: string[],
+ options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
+ ): Promise;
+
+ messageMove(
+ range: SequenceString | number[] | SearchObject,
+ destination: string,
+ options?: { uid?: boolean },
+ ): Promise;
+
+ fetchOne(
+ seq: SequenceString,
+ query: FetchQueryObject,
+ options?: {
+ uid?: boolean;
+ },
+ ): Promise;
+
+ noop(): Promise;
+
+ search(query: SearchObject, options?: { uid?: boolean }): Promise;
+
+ status(
+ path: string,
+ query: {
+ messages?: boolean;
+ recent?: boolean;
+ uidNext?: boolean;
+ uidValidity?: boolean;
+ unseen?: boolean;
+ highestModseq?: boolean;
+ },
+ ): Promise;
+
+ fetch(
+ range: SequenceString | number[] | SearchObject,
+ query: FetchQueryObject,
+ options?: { uid?: boolean; changedSince?: bigint; binary?: boolean },
+ ): AsyncGenerator;
+
+ fetchAll(
+ range: SequenceString | number[] | SearchObject,
+ query: FetchQueryObject,
+ options?: { uid?: boolean; changedSince?: bigint; binary?: boolean },
+ ): Promise;
+}
+
+export interface ImapFlowOptions {
+ host: string;
+ port: number;
+ auth: {
+ user: string;
+ pass?: string;
+ accessToken?: string;
+ };
+ secure?: boolean;
+ servername?: string;
+ disableCompression?: boolean;
+ clientInfo?: IdInfoObject;
+ disableAutoIdle?: boolean;
+ tls?: object;
+ logger?: Logger | false;
+ emitLogs?: boolean;
+ verifyOnly?: boolean;
+ logRaw?: boolean;
+ proxy?: string;
+ qresync?: boolean;
+ maxIdleTime?: number;
+ missingIdleCommand?: string;
+ disableBinary?: boolean;
+ disableAutoEnable?: boolean;
+ connectionTimeout?: number;
+ greetingTimeout?: number;
+ socketTimeout?: number;
+}
+
+export interface AppendResonseObject {
+ path: string;
+ uidValidity?: bigint;
+ uid?: number;
+ seq?: number;
+}
+
+export interface CopyResponseObject {
+ path: string;
+ destination: string;
+ uidValidity?: bigint;
+ uidMap?: Map;
+}
+
+export interface DownloadObject {
+ content: Readable;
+ meta: {
+ expectedSize: number;
+ contentType: string;
+ charset?: string;
+ disposition?: string;
+ filename?: string;
+ };
+}
+
+export interface MailboxObject {
+ path: string;
+ delimeter: string;
+ flags: Set;
+ specialUse: string;
+ listed: boolean;
+ subscribed: boolean;
+ permanentFlags: Set;
+ mailboxId: string;
+ highestModseq: BigInt;
+ uidValidity: BigInt;
+ uidNext: number;
+ exists: number;
+}
+
+export interface MailboxLockObject {
+ path: string;
+ release: () => void;
+}
+
+export interface FetchMessageObject {
+ seq: number;
+ uid: number;
+ source: Buffer;
+ modseq: BigInt;
+ emailId: string;
+ threadId?: string;
+ labels: Set;
+ size: number;
+ flags: Set;
+ envelope: MessageEnvelopeObject;
+ bodyStructure: MessageStructureObject;
+ internalDate: Date;
+ bodyParts: Map;
+ headers: Buffer;
+}
+
+export interface FetchQueryObject {
+ uid?: boolean;
+ flags?: boolean;
+ bodyStructure?: boolean;
+ envelope?: boolean;
+ internalDate?: boolean;
+ size?: boolean;
+ source?: boolean | object;
+ threadId?: boolean;
+ labels?: boolean;
+ headers?: boolean | string[];
+ bodyParts?: string[];
+}
+
+export interface MailboxRenameResponse {
+ path: string;
+ newPath: string;
+}
+
+export interface MessageAddressObject {
+ name?: string;
+ address?: string;
+}
+
+export interface MessageEnvelopeObject {
+ date: Date;
+ subject: string;
+ messageId: string;
+ inReplyTo: string;
+ from: MessageAddressObject[];
+ sender: MessageAddressObject[];
+ replyTo: MessageAddressObject[];
+ to: MessageAddressObject[];
+ cc: MessageAddressObject[];
+ bcc: MessageAddressObject[];
+}
+
+export interface QuotaResponse {
+ path: string;
+ storage?: object;
+ messages?: object;
+}
+
+export type SequenceString = string;
+
+export interface SearchObject {
+ seq?: SequenceString;
+ answered?: boolean;
+ deleted?: boolean;
+ draft?: boolean;
+ flagged?: boolean;
+ seen?: boolean;
+ all?: boolean;
+ new?: boolean;
+ old?: boolean;
+ recent?: boolean;
+ from?: string;
+ to?: string;
+ cc?: string;
+ bcc?: string;
+ body?: string;
+ subject?: string;
+ larger?: number;
+ smaller?: number;
+ uid?: SequenceString;
+ modseq?: bigint;
+ emailId?: string;
+ threadId?: string;
+ before?: Date | string;
+ on?: Date | string;
+ since?: Date | string;
+ sentBefore?: Date | string;
+ sentOn?: Date | string;
+ sentSince?: Date | string;
+ keyword?: string;
+ unKeyword?: string;
+ header?: { [key: string]: boolean | string };
+ or?: SearchObject[];
+}
+
+export interface StatusObject {
+ path: string;
+ messages?: number;
+ recent?: number;
+ uidNext?: number;
+ uidValidity?: bigint;
+ unseen?: number;
+ highestModseq?: bigint;
+}
+
+export interface IdInfoObject {
+ name?: string;
+ version?: string;
+ os?: string;
+ vendor?: string;
+ "support-url"?: string;
+ date?: Date;
+}
+
+export interface ListResponse {
+ path: string;
+ name: string;
+ delimiter: string;
+ flags: Set;
+ specialUse: string;
+ listed: boolean;
+ subscribed: boolean;
+ status?: StatusObject;
+}
+
+export interface ListTreeResponse {
+ root: boolean;
+ path: string;
+ name: string;
+ delimiter: string;
+ flags: [];
+ specialUse: string;
+ listed: boolean;
+ subscribed: boolean;
+ disabled: boolean;
+ folders: ListTreeResponse[];
+}
+
+export interface MailboxCreateResponse {
+ path: string;
+ mailboxId?: string;
+ created: boolean;
+}
+
+export interface MailboxDeleteResponse {
+ path: string;
+}
+
+export interface MessageStructureObject {
+ part: string;
+ type: string;
+ parameters: string;
+ id: string;
+ encoding: string;
+ size: number;
+ envelope: MessageEnvelopeObject;
+ disposition: string;
+ dispositionParameters: string;
+ childNodes: MessageStructureObject[];
+}
+
+export interface Logger {
+ debug: (obj: object) => void;
+ info: (obj: object) => void;
+ warn: (obj: object) => void;
+ error: (obj: object) => void;
+}
+
+export interface StatusQuery {
+ messages?: boolean;
+ recent?: boolean;
+ uidNext?: boolean;
+ uidValidity?: boolean;
+ unseen?: boolean;
+ highestModseq?: boolean;
+}
+
+export interface SpecialUseHints {
+ sent: string;
+ trash: string;
+ junk: string;
+ drafts: string;
+}
diff --git a/node_modules/@types/imapflow/package.json b/node_modules/@types/imapflow/package.json
new file mode 100644
index 00000000..1613b3a2
--- /dev/null
+++ b/node_modules/@types/imapflow/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "@types/imapflow",
+ "version": "1.0.20",
+ "description": "TypeScript definitions for imapflow",
+ "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/imapflow",
+ "license": "MIT",
+ "contributors": [
+ {
+ "name": "Jeffrey Ratton",
+ "githubUsername": "jeffreyratton98",
+ "url": "https://github.com/jeffreyratton98"
+ },
+ {
+ "name": "Martin Badin",
+ "githubUsername": "martin-badin",
+ "url": "https://github.com/martin-badin"
+ },
+ {
+ "name": "Northern Star",
+ "githubUsername": "grayson-code",
+ "url": "https://github.com/grayson-code"
+ },
+ {
+ "name": "Zachary Nawar",
+ "githubUsername": "remscar",
+ "url": "https://github.com/remscar"
+ }
+ ],
+ "main": "",
+ "types": "index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+ "directory": "types/imapflow"
+ },
+ "scripts": {},
+ "dependencies": {
+ "@types/node": "*"
+ },
+ "peerDependencies": {},
+ "typesPublisherContentHash": "5a7661a9dee2cf2fab17d371ef006249b141fe3d09adc38c21661ce457b282ac",
+ "typeScriptVersion": "5.0"
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 818ddcad..bdc98d83 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -75,6 +75,7 @@
"vaul": "^0.9.6"
},
"devDependencies": {
+ "@types/imapflow": "^1.0.20",
"@types/node": "^22.14.1",
"@types/react": "^18",
"@types/react-dom": "^18",
@@ -2831,6 +2832,16 @@
"@types/node": "*"
}
},
+ "node_modules/@types/imapflow": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/@types/imapflow/-/imapflow-1.0.20.tgz",
+ "integrity": "sha512-kmBeiV815byuxYlu2lomAx3VY3SsyOYg4rDsK5vT6CGZ6Sow2AUEZwieql8uAizO6+p4sQchw/g3vQX76XBIpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/mailparser": {
"version": "3.4.5",
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz",
diff --git a/package.json b/package.json
index 08968df5..f7a1b372 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,7 @@
"vaul": "^0.9.6"
},
"devDependencies": {
+ "@types/imapflow": "^1.0.20",
"@types/node": "^22.14.1",
"@types/react": "^18",
"@types/react-dom": "^18",
diff --git a/yarn.lock b/yarn.lock
index 42d0890c..4da5eaf8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1014,6 +1014,13 @@
dependencies:
"@types/node" "*"
+"@types/imapflow@^1.0.20":
+ version "1.0.20"
+ resolved "https://registry.npmjs.org/@types/imapflow/-/imapflow-1.0.20.tgz"
+ integrity sha512-kmBeiV815byuxYlu2lomAx3VY3SsyOYg4rDsK5vT6CGZ6Sow2AUEZwieql8uAizO6+p4sQchw/g3vQX76XBIpA==
+ dependencies:
+ "@types/node" "*"
+
"@types/mailparser@^3.4.5":
version "3.4.5"
resolved "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz"