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"