database wf 9
This commit is contained in:
parent
58f0b37476
commit
ed6a33b8d7
@ -58,6 +58,35 @@ export const authOptions: NextAuthOptions = {
|
|||||||
strategy: "jwt",
|
strategy: "jwt",
|
||||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
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: {
|
callbacks: {
|
||||||
async jwt({ token, account, profile }) {
|
async jwt({ token, account, profile }) {
|
||||||
if (account && profile) {
|
if (account && profile) {
|
||||||
@ -121,11 +150,13 @@ export const authOptions: NextAuthOptions = {
|
|||||||
|
|
||||||
session.accessToken = token.accessToken;
|
session.accessToken = token.accessToken;
|
||||||
session.user = {
|
session.user = {
|
||||||
...session.user,
|
|
||||||
id: token.sub as string,
|
id: token.sub as string,
|
||||||
|
name: token.name,
|
||||||
|
email: token.email,
|
||||||
|
image: null,
|
||||||
|
username: token.username ?? '',
|
||||||
first_name: token.first_name ?? '',
|
first_name: token.first_name ?? '',
|
||||||
last_name: token.last_name ?? '',
|
last_name: token.last_name ?? '',
|
||||||
username: token.username ?? '',
|
|
||||||
role: token.role ?? [],
|
role: token.role ?? [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import Imap from 'imap';
|
import Imap from 'imap';
|
||||||
|
import { cookies } from 'next/headers';
|
||||||
|
|
||||||
interface StoredCredentials {
|
interface StoredCredentials {
|
||||||
user: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
host: string;
|
host: string;
|
||||||
port: string;
|
port: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let storedCredentials: StoredCredentials | null = null;
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const { email, password, host, port } = await request.json();
|
const { email, password, host, port } = await request.json();
|
||||||
@ -40,8 +39,25 @@ export async function POST(request: Request) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
imap.once('ready', () => {
|
imap.once('ready', () => {
|
||||||
imap.end();
|
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 }));
|
resolve(NextResponse.json({ success: true }));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -78,14 +94,25 @@ export async function POST(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
if (!storedCredentials) {
|
const cookieStore = cookies();
|
||||||
|
const credentialsCookie = cookieStore.get('imap_credentials');
|
||||||
|
|
||||||
|
if (!credentialsCookie?.value) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'No stored credentials found' },
|
{ error: 'No stored credentials found' },
|
||||||
{ status: 404 }
|
{ status: 404 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return credentials without password
|
try {
|
||||||
const { password, ...safeCredentials } = storedCredentials;
|
const credentials = JSON.parse(credentialsCookie.value);
|
||||||
|
// Return credentials without password for security
|
||||||
|
const { password, ...safeCredentials } = credentials;
|
||||||
return NextResponse.json(safeCredentials);
|
return NextResponse.json(safeCredentials);
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Invalid credentials format' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,362 +1,52 @@
|
|||||||
import { NextResponse } from 'next/server';
|
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 { cookies } from 'next/headers';
|
||||||
|
import { ImapFlow } from 'imapflow';
|
||||||
|
|
||||||
interface StoredCredentials {
|
export async function GET() {
|
||||||
email: string;
|
try {
|
||||||
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 cookieStore = cookies();
|
||||||
|
const credentials = cookieStore.get('imap_credentials');
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const credentials = JSON.parse(credentialsCookie.value);
|
|
||||||
console.log('Parsed credentials:', {
|
|
||||||
...credentials,
|
|
||||||
password: '***'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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) {
|
if (!credentials) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'No stored credentials found' },
|
{ error: 'No credentials found' },
|
||||||
{ status: 401 }
|
{ status: 401 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get pagination parameters from URL
|
const { email, password, host, port } = JSON.parse(credentials.value);
|
||||||
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;
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
const client = new ImapFlow({
|
||||||
const imap = new Imap({
|
host,
|
||||||
user: credentials.email,
|
port: parseInt(port),
|
||||||
password: credentials.password,
|
secure: true,
|
||||||
host: credentials.host,
|
auth: {
|
||||||
port: credentials.port,
|
user: email,
|
||||||
tls: true,
|
pass: password,
|
||||||
tlsOptions: { rejectUnauthorized: false },
|
},
|
||||||
authTimeout: 30000,
|
|
||||||
connTimeout: 30000
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
await client.connect();
|
||||||
console.error('IMAP connection timeout');
|
const mailbox = await client.mailboxOpen('INBOX');
|
||||||
imap.end();
|
const messages = await client.fetch('1:10', { envelope: true });
|
||||||
resolve(NextResponse.json({
|
|
||||||
emails: [],
|
|
||||||
error: 'Connection timeout'
|
|
||||||
}));
|
|
||||||
}, 30000);
|
|
||||||
|
|
||||||
imap.once('error', (err: Error) => {
|
const result = [];
|
||||||
console.error('IMAP error:', err);
|
for await (const message of messages) {
|
||||||
clearTimeout(timeout);
|
result.push({
|
||||||
resolve(NextResponse.json({
|
id: message.uid,
|
||||||
emails: [],
|
subject: message.envelope.subject,
|
||||||
error: 'IMAP connection error'
|
from: message.envelope.from[0].address,
|
||||||
}));
|
date: message.envelope.date,
|
||||||
});
|
});
|
||||||
|
|
||||||
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(
|
await client.logout();
|
||||||
box => !['Starred', 'Archives'].includes(box)
|
return NextResponse.json(result);
|
||||||
);
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in GET /api/mail:', error);
|
console.error('Mail API error:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Failed to fetch emails' },
|
{ error: 'Failed to fetch emails' },
|
||||||
{ status: 500 }
|
{ 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 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { setCookie } from 'cookies-next';
|
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -43,34 +42,24 @@ export default function LoginPage() {
|
|||||||
throw new Error(testData.error || 'Failed to connect to email server');
|
throw new Error(testData.error || 'Failed to connect to email server');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store all credentials in a single cookie
|
// Store credentials using the API endpoint
|
||||||
const credentials = {
|
const loginResponse = await fetch('/api/mail/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
host,
|
host,
|
||||||
port: parseInt(port),
|
port,
|
||||||
};
|
}),
|
||||||
|
|
||||||
console.log('Storing credentials in cookie:', {
|
|
||||||
...credentials,
|
|
||||||
password: '***'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store as a single cookie with proper options
|
const loginData = await loginResponse.json();
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify cookie was set
|
if (!loginResponse.ok) {
|
||||||
const stored = document.cookie.split(';').find(c => c.trim().startsWith('imap_credentials='));
|
throw new Error(loginData.error || 'Failed to store credentials');
|
||||||
console.log('Cookie verification:', stored ? 'Cookie found' : 'Cookie not found');
|
|
||||||
|
|
||||||
if (!stored) {
|
|
||||||
throw new Error('Failed to store credentials');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to mail page
|
// Redirect to mail page
|
||||||
|
|||||||
@ -18,6 +18,14 @@ export default withAuth(
|
|||||||
if (req.nextUrl.pathname === "/" || req.nextUrl.pathname === "/signin") {
|
if (req.nextUrl.pathname === "/" || req.nextUrl.pathname === "/signin") {
|
||||||
return true;
|
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;
|
return !!token;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
10
node_modules/.package-lock.json
generated
vendored
10
node_modules/.package-lock.json
generated
vendored
@ -2214,6 +2214,16 @@
|
|||||||
"@types/node": "*"
|
"@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": {
|
"node_modules/@types/mailparser": {
|
||||||
"version": "3.4.5",
|
"version": "3.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz",
|
||||||
|
|||||||
21
node_modules/@types/imapflow/LICENSE
generated
vendored
Normal file
21
node_modules/@types/imapflow/LICENSE
generated
vendored
Normal file
@ -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
|
||||||
15
node_modules/@types/imapflow/README.md
generated
vendored
Normal file
15
node_modules/@types/imapflow/README.md
generated
vendored
Normal file
@ -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).
|
||||||
393
node_modules/@types/imapflow/index.d.ts
generated
vendored
Normal file
393
node_modules/@types/imapflow/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
|
||||||
|
import { EventEmitter } from "stream";
|
||||||
|
|
||||||
|
export type Readable = import("stream").Readable;
|
||||||
|
|
||||||
|
export class ImapFlow extends EventEmitter {
|
||||||
|
constructor(options: ImapFlowOptions);
|
||||||
|
authenticated: string | boolean;
|
||||||
|
capabilities: Map<string, (boolean | number)>;
|
||||||
|
emitLogs: boolean;
|
||||||
|
enabled: Set<string>;
|
||||||
|
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<AppendResonseObject>;
|
||||||
|
|
||||||
|
connect(): Promise<void>;
|
||||||
|
logout(): Promise<void>;
|
||||||
|
close(): void;
|
||||||
|
download(
|
||||||
|
range: SequenceString,
|
||||||
|
part?: string,
|
||||||
|
options?: { uid?: boolean; maxBytes?: number; chunkSize?: number },
|
||||||
|
): Promise<DownloadObject>;
|
||||||
|
|
||||||
|
getMailboxLock(path: string, options?: null | { readonly?: boolean }): Promise<MailboxLockObject>;
|
||||||
|
|
||||||
|
getQuota(path: string): Promise<QuotaResponse | boolean>;
|
||||||
|
|
||||||
|
idle(): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see {@link https://imapflow.com/module-imapflow-ImapFlow.html#list}
|
||||||
|
*/
|
||||||
|
list(options?: {
|
||||||
|
statusQuery?: StatusQuery;
|
||||||
|
specialUseHints?: SpecialUseHints;
|
||||||
|
}): Promise<ListResponse[]>;
|
||||||
|
|
||||||
|
listTree(): Promise<ListTreeResponse>;
|
||||||
|
|
||||||
|
mailboxClose(): Promise<boolean>;
|
||||||
|
|
||||||
|
mailboxCreate(path: string | any[]): Promise<MailboxCreateResponse>;
|
||||||
|
|
||||||
|
mailboxDelete(path: string | any[]): Promise<MailboxDeleteResponse>;
|
||||||
|
|
||||||
|
mailboxOpen(path: string | any[], options?: { readOnly?: boolean }): Promise<MailboxObject>;
|
||||||
|
|
||||||
|
mailboxRename(path: string | any[], newPath: string | any[]): Promise<MailboxRenameResponse>;
|
||||||
|
|
||||||
|
mailboxSubscribe(path: string | any[]): Promise<boolean>;
|
||||||
|
|
||||||
|
mailboxUnsubscribe(path: string | any[]): Promise<boolean>;
|
||||||
|
|
||||||
|
messageCopy(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
destination: string,
|
||||||
|
options?: { uid?: boolean },
|
||||||
|
): Promise<CopyResponseObject>;
|
||||||
|
|
||||||
|
messageDelete(range: SequenceString | number[] | SearchObject, options?: { uid?: boolean }): Promise<boolean>;
|
||||||
|
|
||||||
|
messageFlagsAdd(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
Array: string[],
|
||||||
|
options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
messageFlagsRemove(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
Array: string[],
|
||||||
|
options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
messageFlagsSet(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
Array: string[],
|
||||||
|
options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean },
|
||||||
|
): Promise<boolean>;
|
||||||
|
|
||||||
|
messageMove(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
destination: string,
|
||||||
|
options?: { uid?: boolean },
|
||||||
|
): Promise<CopyResponseObject>;
|
||||||
|
|
||||||
|
fetchOne(
|
||||||
|
seq: SequenceString,
|
||||||
|
query: FetchQueryObject,
|
||||||
|
options?: {
|
||||||
|
uid?: boolean;
|
||||||
|
},
|
||||||
|
): Promise<FetchMessageObject>;
|
||||||
|
|
||||||
|
noop(): Promise<void>;
|
||||||
|
|
||||||
|
search(query: SearchObject, options?: { uid?: boolean }): Promise<number[]>;
|
||||||
|
|
||||||
|
status(
|
||||||
|
path: string,
|
||||||
|
query: {
|
||||||
|
messages?: boolean;
|
||||||
|
recent?: boolean;
|
||||||
|
uidNext?: boolean;
|
||||||
|
uidValidity?: boolean;
|
||||||
|
unseen?: boolean;
|
||||||
|
highestModseq?: boolean;
|
||||||
|
},
|
||||||
|
): Promise<StatusObject>;
|
||||||
|
|
||||||
|
fetch(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
query: FetchQueryObject,
|
||||||
|
options?: { uid?: boolean; changedSince?: bigint; binary?: boolean },
|
||||||
|
): AsyncGenerator<FetchMessageObject, never, void>;
|
||||||
|
|
||||||
|
fetchAll(
|
||||||
|
range: SequenceString | number[] | SearchObject,
|
||||||
|
query: FetchQueryObject,
|
||||||
|
options?: { uid?: boolean; changedSince?: bigint; binary?: boolean },
|
||||||
|
): Promise<FetchMessageObject[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<number, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<string>;
|
||||||
|
specialUse: string;
|
||||||
|
listed: boolean;
|
||||||
|
subscribed: boolean;
|
||||||
|
permanentFlags: Set<string>;
|
||||||
|
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<string>;
|
||||||
|
size: number;
|
||||||
|
flags: Set<string>;
|
||||||
|
envelope: MessageEnvelopeObject;
|
||||||
|
bodyStructure: MessageStructureObject;
|
||||||
|
internalDate: Date;
|
||||||
|
bodyParts: Map<string, Buffer>;
|
||||||
|
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<string>;
|
||||||
|
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;
|
||||||
|
}
|
||||||
43
node_modules/@types/imapflow/package.json
generated
vendored
Normal file
43
node_modules/@types/imapflow/package.json
generated
vendored
Normal file
@ -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"
|
||||||
|
}
|
||||||
11
package-lock.json
generated
11
package-lock.json
generated
@ -75,6 +75,7 @@
|
|||||||
"vaul": "^0.9.6"
|
"vaul": "^0.9.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/imapflow": "^1.0.20",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.14.1",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
@ -2831,6 +2832,16 @@
|
|||||||
"@types/node": "*"
|
"@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": {
|
"node_modules/@types/mailparser": {
|
||||||
"version": "3.4.5",
|
"version": "3.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz",
|
||||||
|
|||||||
@ -76,6 +76,7 @@
|
|||||||
"vaul": "^0.9.6"
|
"vaul": "^0.9.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/imapflow": "^1.0.20",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.14.1",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
|||||||
@ -1014,6 +1014,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@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":
|
"@types/mailparser@^3.4.5":
|
||||||
version "3.4.5"
|
version "3.4.5"
|
||||||
resolved "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz"
|
resolved "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.5.tgz"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user