From a7dbd7cd858983499e1ad2a77f8bdc6ffdeb3ab2 Mon Sep 17 00:00:00 2001 From: alma Date: Fri, 2 May 2025 09:10:52 +0200 Subject: [PATCH] courrier preview --- .env | 4 + app/api/courrier/microsoft/callback/route.ts | 125 +++++++++++++++++ app/api/courrier/microsoft/route.ts | 41 ++++++ app/ms/page.tsx | 87 ++++++++++++ components/email/EmailSidebar.tsx | 35 +++++ lib/services/email-service.ts | 88 +++++++++--- lib/services/microsoft-oauth.ts | 109 +++++++++++++++ lib/services/token-refresh.ts | 79 +++++++++++ lib/types.ts | 6 + node_modules/.package-lock.json | 126 +++++++++++++++-- package-lock.json | 127 ++++++++++++++++-- package.json | 1 + .../20240612_add_oauth_fields/migration.sql | 7 + prisma/schema.prisma | 8 +- yarn.lock | 70 +++++++++- 15 files changed, 872 insertions(+), 41 deletions(-) create mode 100644 app/api/courrier/microsoft/callback/route.ts create mode 100644 app/api/courrier/microsoft/route.ts create mode 100644 app/ms/page.tsx create mode 100644 lib/services/microsoft-oauth.ts create mode 100644 lib/services/token-refresh.ts create mode 100644 prisma/migrations/20240612_add_oauth_fields/migration.sql diff --git a/.env b/.env index 36f3843e..10b1c3bd 100644 --- a/.env +++ b/.env @@ -80,3 +80,7 @@ NEWS_API_URL="http://172.16.0.104:8000" REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD=mySecretPassword + +MICROSOFT_CLIENT_ID="afaffea5-4e10-462a-aa64-e73baf642c57" +MICROSOFT_CLIENT_SECRET="eIx8Q~N3ZnXTjTsVM3ECZio4G7t.BO6AYlD1-b2h" +MICROSOFT_REDIRECT_URI="https://lab.slm-lab.net/ms" \ No newline at end of file diff --git a/app/api/courrier/microsoft/callback/route.ts b/app/api/courrier/microsoft/callback/route.ts new file mode 100644 index 00000000..7a1c363a --- /dev/null +++ b/app/api/courrier/microsoft/callback/route.ts @@ -0,0 +1,125 @@ +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/app/api/auth/[...nextauth]/route'; +import { exchangeCodeForTokens } from '@/lib/services/microsoft-oauth'; +import { prisma } from '@/lib/prisma'; +import { testEmailConnection, saveUserEmailCredentials } from '@/lib/services/email-service'; +import { invalidateFolderCache } from '@/lib/redis'; + +export async function POST(request: Request) { + try { + // Authenticate user + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json( + { error: 'Unauthorized' }, + { status: 401 } + ); + } + + // Parse request body + const body = await request.json(); + const { code, state } = body; + + if (!code || !state) { + return NextResponse.json( + { error: 'Missing required parameters' }, + { status: 400 } + ); + } + + // Validate state parameter to prevent CSRF + try { + const decodedState = JSON.parse(Buffer.from(state, 'base64').toString()); + + // Check if state contains valid userId and is not expired (10 minutes) + if (decodedState.userId !== session.user.id || + Date.now() - decodedState.timestamp > 10 * 60 * 1000) { + return NextResponse.json( + { error: 'Invalid or expired state parameter' }, + { status: 400 } + ); + } + } catch (e) { + return NextResponse.json( + { error: 'Invalid state parameter' }, + { status: 400 } + ); + } + + // Exchange code for tokens + const tokens = await exchangeCodeForTokens(code); + + // Extract user email from token (would require token decoding in production) + // For this implementation, we'll use a temporary email + const userEmail = `${session.user.email || 'user'}@microsoft.com`; + + // Create credentials object for Microsoft account + const credentials = { + email: userEmail, + // Use Microsoft's IMAP server for Outlook/Office365 + host: 'outlook.office365.com', + port: 993, + secure: true, + + // OAuth specific fields + useOAuth: true, + accessToken: tokens.access_token, + refreshToken: tokens.refresh_token, + tokenExpiry: Date.now() + (tokens.expires_in * 1000), + + // Optional fields + display_name: `Microsoft ${userEmail}`, + color: '#0078D4', // Microsoft blue + + // SMTP settings for Microsoft + smtp_host: 'smtp.office365.com', + smtp_port: 587, + smtp_secure: false + }; + + // Test connection before saving + console.log(`Testing Microsoft OAuth connection for user ${session.user.id}`); + const testResult = await testEmailConnection(credentials); + + if (!testResult.imap) { + return NextResponse.json( + { error: `Connection test failed: ${testResult.error || 'Could not connect to Microsoft IMAP server'}` }, + { status: 400 } + ); + } + + // Save credentials to database and cache + console.log(`Saving Microsoft account for user: ${session.user.id}`); + await saveUserEmailCredentials(session.user.id, userEmail, credentials); + + // Fetch the created account from the database + const createdAccount = await prisma.mailCredentials.findFirst({ + where: { userId: session.user.id, email: userEmail }, + select: { + id: true, + email: true, + display_name: true, + color: true, + } + }); + + // Invalidate any existing folder caches + await invalidateFolderCache(session.user.id, userEmail, '*'); + + return NextResponse.json({ + success: true, + account: createdAccount, + message: 'Microsoft account added successfully' + }); + } catch (error) { + console.error('Error processing Microsoft callback:', error); + return NextResponse.json( + { + error: 'Failed to process Microsoft authentication', + details: error instanceof Error ? error.message : 'Unknown error' + }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/api/courrier/microsoft/route.ts b/app/api/courrier/microsoft/route.ts new file mode 100644 index 00000000..0caebc6c --- /dev/null +++ b/app/api/courrier/microsoft/route.ts @@ -0,0 +1,41 @@ +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/app/api/auth/[...nextauth]/route'; +import { getMicrosoftAuthUrl } from '@/lib/services/microsoft-oauth'; + +// Endpoint to initiate Microsoft OAuth flow +export async function GET(request: Request) { + try { + // Authenticate user + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json( + { error: 'Unauthorized' }, + { status: 401 } + ); + } + + // Create a state parameter with the user's ID to prevent CSRF + const state = Buffer.from(JSON.stringify({ + userId: session.user.id, + timestamp: Date.now() + })).toString('base64'); + + // Generate the authorization URL + const authUrl = getMicrosoftAuthUrl(state); + + return NextResponse.json({ + authUrl, + state + }); + } catch (error) { + console.error('Error initiating Microsoft OAuth flow:', error); + return NextResponse.json( + { + error: 'Failed to initiate Microsoft OAuth flow', + details: error instanceof Error ? error.message : 'Unknown error' + }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/ms/page.tsx b/app/ms/page.tsx new file mode 100644 index 00000000..a5d5e9bc --- /dev/null +++ b/app/ms/page.tsx @@ -0,0 +1,87 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter, useSearchParams } from 'next/navigation'; + +export default function MicrosoftCallbackPage() { + const router = useRouter(); + const searchParams = useSearchParams(); + const [status, setStatus] = useState('Processing authentication...'); + const [error, setError] = useState(null); + + useEffect(() => { + async function handleCallback() { + try { + // Extract code and state from URL + const code = searchParams.get('code'); + const state = searchParams.get('state'); + const errorMsg = searchParams.get('error'); + + if (errorMsg) { + setError(`Authentication error: ${errorMsg}`); + return; + } + + if (!code || !state) { + setError('Missing required parameters'); + return; + } + + setStatus('Authentication successful. Exchanging code for tokens...'); + + // Send code to our API to exchange for tokens + const response = await fetch('/api/courrier/microsoft/callback', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ code, state }), + }); + + const data = await response.json(); + + if (!response.ok) { + setError(data.error || 'Failed to process authentication'); + return; + } + + setStatus('Account connected successfully!'); + + // Redirect back to email client after a short delay + setTimeout(() => { + router.push('/courrier'); + }, 2000); + } catch (err) { + setError('An unexpected error occurred'); + console.error('Callback processing error:', err); + } + } + + handleCallback(); + }, [router, searchParams]); + + return ( +
+
+

Microsoft Account Connection

+ + {error ? ( +
+

Error

+

{error}

+ +
+ ) : ( +
+

{status}

+
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/components/email/EmailSidebar.tsx b/components/email/EmailSidebar.tsx index a3c6faae..b016d083 100644 --- a/components/email/EmailSidebar.tsx +++ b/components/email/EmailSidebar.tsx @@ -227,6 +227,23 @@ export default function EmailSidebar({ ); }; + // Add Microsoft button logic + const handleConnectMicrosoft = async () => { + try { + const response = await fetch('/api/courrier/microsoft'); + const data = await response.json(); + + if (response.ok && data.authUrl) { + // Redirect to Microsoft's authorization page + window.location.href = data.authUrl; + } else { + console.error('Failed to initiate Microsoft authentication:', data.error); + } + } catch (error) { + console.error('Error connecting Microsoft account:', error); + } + }; + return (
{/* Courrier Title */} @@ -441,6 +458,24 @@ export default function EmailSidebar({
+ + {/* Add Microsoft option */} +
+ +
)} diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index 159ec43e..3fc73ebd 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -18,6 +18,7 @@ import { invalidateEmailContentCache } from '@/lib/redis'; import { EmailCredentials, EmailMessage, EmailAddress, EmailAttachment } from '@/lib/types'; +import { ensureFreshToken } from './token-refresh'; // Types specific to this service export interface EmailListResult { @@ -250,6 +251,22 @@ export async function getImapConnection( await cacheEmailCredentials(userId, accountId, credentials); } + // If using OAuth, ensure we have a fresh token + if (credentials.useOAuth) { + try { + console.log(`Ensuring fresh token for OAuth account ${credentials.email}`); + const { accessToken, success } = await ensureFreshToken(userId, credentials.email); + + if (success) { + credentials.accessToken = accessToken; + } else { + console.error(`Failed to refresh token for ${credentials.email}`); + } + } catch (err) { + console.error(`Error refreshing token for ${credentials.email}:`, err); + } + } + // Initialize connection tracking connectionPool[connectionKey] = { client: null as any, @@ -314,14 +331,23 @@ export async function getImapConnection( * Helper function to create a new IMAP connection */ async function createImapConnection(credentials: EmailCredentials, connectionKey: string): Promise { + // Configure auth based on whether we're using OAuth or password + const auth = credentials.useOAuth + ? { + user: credentials.email, + // Use XOAUTH2 authentication for Microsoft accounts + accessToken: credentials.accessToken + } + : { + user: credentials.email, + pass: credentials.password, + }; + const client = new ImapFlow({ host: credentials.host, port: credentials.port, secure: credentials.secure ?? true, - auth: { - user: credentials.email, - pass: credentials.password, - }, + auth, logger: false, emitLogs: false, tls: { @@ -962,15 +988,23 @@ export async function sendEmail( }; } + // Configure SMTP auth based on OAuth or password + const auth = credentials.useOAuth + ? { + user: credentials.email, + accessToken: credentials.accessToken + } + : { + user: credentials.email, + pass: credentials.password, + }; + // Create SMTP transporter with user's SMTP settings if available const transporter = nodemailer.createTransport({ host: credentials.smtp_host || 'smtp.infomaniak.com', port: credentials.smtp_port || 587, secure: credentials.smtp_secure || false, - auth: { - user: credentials.email, - pass: credentials.password, - }, + auth, tls: { rejectUnauthorized: false } @@ -1036,20 +1070,31 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis }> { console.log('Testing connection with:', { ...credentials, - password: '***' + password: credentials.password ? '***' : undefined, + accessToken: credentials.accessToken ? '***' : undefined, + refreshToken: credentials.refreshToken ? '***' : undefined }); // Test IMAP connection try { console.log(`Testing IMAP connection to ${credentials.host}:${credentials.port} for ${credentials.email}`); + + // Configure auth based on whether we're using OAuth or password + const auth = credentials.useOAuth + ? { + user: credentials.email, + accessToken: credentials.accessToken + } + : { + user: credentials.email, + pass: credentials.password, + }; + const client = new ImapFlow({ host: credentials.host, port: credentials.port, secure: credentials.secure ?? true, - auth: { - user: credentials.email, - pass: credentials.password, - }, + auth, logger: false, tls: { rejectUnauthorized: false @@ -1068,14 +1113,23 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis if (credentials.smtp_host && credentials.smtp_port) { try { console.log(`Testing SMTP connection to ${credentials.smtp_host}:${credentials.smtp_port}`); + + // Configure SMTP auth based on OAuth or password + const smtpAuth = credentials.useOAuth + ? { + user: credentials.email, + accessToken: credentials.accessToken + } + : { + user: credentials.email, + pass: credentials.password, + }; + const transporter = nodemailer.createTransport({ host: credentials.smtp_host, port: credentials.smtp_port, secure: credentials.smtp_secure ?? false, - auth: { - user: credentials.email, - pass: credentials.password, - }, + auth: smtpAuth, tls: { rejectUnauthorized: false } diff --git a/lib/services/microsoft-oauth.ts b/lib/services/microsoft-oauth.ts new file mode 100644 index 00000000..1a14aa1f --- /dev/null +++ b/lib/services/microsoft-oauth.ts @@ -0,0 +1,109 @@ +import axios from 'axios'; + +// Microsoft OAuth URLs +const MICROSOFT_AUTHORIZE_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'; +const MICROSOFT_TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; + +// Client configuration from environment variables +const clientId = process.env.MICROSOFT_CLIENT_ID; +const clientSecret = process.env.MICROSOFT_CLIENT_SECRET; +const redirectUri = process.env.MICROSOFT_REDIRECT_URI; + +// Required scopes for IMAP and SMTP access +const REQUIRED_SCOPES = [ + 'offline_access', + 'https://outlook.office.com/IMAP.AccessAsUser.All', + 'https://outlook.office.com/SMTP.Send' +].join(' '); + +/** + * Generates the authorization URL for Microsoft OAuth + */ +export function getMicrosoftAuthUrl(state: string): string { + const params = new URLSearchParams({ + client_id: clientId!, + response_type: 'code', + redirect_uri: redirectUri!, + scope: REQUIRED_SCOPES, + state, + response_mode: 'query' + }); + + return `${MICROSOFT_AUTHORIZE_URL}?${params.toString()}`; +} + +/** + * Exchange authorization code for tokens + */ +export async function exchangeCodeForTokens(code: string): Promise<{ + access_token: string; + refresh_token: string; + expires_in: number; +}> { + const params = new URLSearchParams({ + client_id: clientId!, + client_secret: clientSecret!, + code, + redirect_uri: redirectUri!, + grant_type: 'authorization_code' + }); + + try { + const response = await axios.post(MICROSOFT_TOKEN_URL, params.toString(), { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + return { + access_token: response.data.access_token, + refresh_token: response.data.refresh_token, + expires_in: response.data.expires_in + }; + } catch (error) { + console.error('Error exchanging code for tokens:', error); + throw new Error('Failed to exchange authorization code for tokens'); + } +} + +/** + * Refresh an access token using a refresh token + */ +export async function refreshAccessToken(refreshToken: string): Promise<{ + access_token: string; + refresh_token?: string; + expires_in: number; +}> { + const params = new URLSearchParams({ + client_id: clientId!, + client_secret: clientSecret!, + refresh_token: refreshToken, + grant_type: 'refresh_token', + scope: REQUIRED_SCOPES + }); + + try { + const response = await axios.post(MICROSOFT_TOKEN_URL, params.toString(), { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + return { + access_token: response.data.access_token, + refresh_token: response.data.refresh_token, + expires_in: response.data.expires_in + }; + } catch (error) { + console.error('Error refreshing token:', error); + throw new Error('Failed to refresh access token'); + } +} + +/** + * Create special XOAUTH2 string for IMAP authentication + */ +export function createXOAuth2Token(email: string, accessToken: string): string { + const auth = `user=${email}\x01auth=Bearer ${accessToken}\x01\x01`; + return Buffer.from(auth).toString('base64'); +} \ No newline at end of file diff --git a/lib/services/token-refresh.ts b/lib/services/token-refresh.ts new file mode 100644 index 00000000..68449bdb --- /dev/null +++ b/lib/services/token-refresh.ts @@ -0,0 +1,79 @@ +import { refreshAccessToken } from './microsoft-oauth'; +import { prisma } from '@/lib/prisma'; +import { getRedisClient, KEYS } from '@/lib/redis'; + +/** + * Check if a token is expired or about to expire (within 5 minutes) + */ +export function isTokenExpired(expiryTimestamp: number): boolean { + const fiveMinutesInMs = 5 * 60 * 1000; + return Date.now() + fiveMinutesInMs >= expiryTimestamp; +} + +/** + * Refresh an access token if it's expired or about to expire + */ +export async function ensureFreshToken( + userId: string, + email: string +): Promise<{ accessToken: string; success: boolean }> { + try { + // Get stored credentials using raw query until Prisma schema is updated + const credentials = await prisma.$queryRaw` + SELECT "useOAuth", "refreshToken", "accessToken", "tokenExpiry" + FROM "MailCredentials" + WHERE "userId" = ${userId} AND "email" = ${email} + LIMIT 1 + `; + + const credData = Array.isArray(credentials) && credentials.length > 0 + ? credentials[0] + : null; + + // If not OAuth or missing refresh token, return failure + if (!credData?.useOAuth || !credData.refreshToken) { + return { accessToken: '', success: false }; + } + + // If token is still valid, return current token + if (credData.tokenExpiry && credData.accessToken && + new Date(credData.tokenExpiry) > new Date(Date.now() + 5 * 60 * 1000)) { + return { accessToken: credData.accessToken, success: true }; + } + + // Token is expired or about to expire, refresh it + console.log(`Refreshing token for user ${userId}, account ${email}`); + const tokens = await refreshAccessToken(credData.refreshToken); + + // Update database with new token information using raw query + await prisma.$executeRaw` + UPDATE "MailCredentials" + SET + "accessToken" = ${tokens.access_token}, + "refreshToken" = ${tokens.refresh_token || credData.refreshToken}, + "tokenExpiry" = ${new Date(Date.now() + (tokens.expires_in * 1000))} + WHERE "userId" = ${userId} AND "email" = ${email} + `; + + // Update Redis cache + const redis = getRedisClient(); + const key = KEYS.CREDENTIALS(userId, email); + const credStr = await redis.get(key); + + if (credStr) { + const creds = JSON.parse(credStr); + creds.accessToken = tokens.access_token; + if (tokens.refresh_token) { + creds.refreshToken = tokens.refresh_token; + } + creds.tokenExpiry = Date.now() + (tokens.expires_in * 1000); + + await redis.set(key, JSON.stringify(creds), 'EX', 86400); // 24 hours + } + + return { accessToken: tokens.access_token, success: true }; + } catch (error) { + console.error(`Error refreshing token for user ${userId}:`, error); + return { accessToken: '', success: false }; + } +} \ No newline at end of file diff --git a/lib/types.ts b/lib/types.ts index ca6fed2d..7e4ffe52 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -7,6 +7,12 @@ export interface EmailCredentials { secure?: boolean; encryptedPassword?: string; + // OAuth Settings (for Microsoft accounts) + useOAuth?: boolean; + accessToken?: string; + refreshToken?: string; + tokenExpiry?: number; + // SMTP Settings smtp_host?: string; smtp_port?: number; diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index ed4288b4..bc5a27b0 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -2622,6 +2622,12 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -2683,6 +2689,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2841,7 +2858,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3028,6 +3044,18 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -3338,6 +3366,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -3461,7 +3498,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3537,7 +3573,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3547,7 +3582,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3557,7 +3591,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3566,6 +3599,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", @@ -3742,6 +3790,26 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3773,6 +3841,42 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -3836,7 +3940,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3869,7 +3972,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3913,7 +4015,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3939,7 +4040,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3952,7 +4052,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -4655,7 +4754,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5643,6 +5741,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package-lock.json b/package-lock.json index cce2bdba..d9c9a12c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "@types/xmldom": "^0.1.34", "@xmldom/xmldom": "^0.9.8", "autoprefixer": "^10.4.20", + "axios": "^1.9.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.4", @@ -3597,6 +3598,12 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -3658,6 +3665,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3816,7 +3834,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4003,6 +4020,18 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -4313,6 +4342,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -4436,7 +4474,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4512,7 +4549,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4522,7 +4558,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4532,7 +4567,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4541,6 +4575,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", @@ -4717,6 +4766,26 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -4748,6 +4817,42 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -4811,7 +4916,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4844,7 +4948,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -4888,7 +4991,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4914,7 +5016,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4927,7 +5028,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -5630,7 +5730,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6618,6 +6717,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 19462b04..de7b3f4f 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@types/xmldom": "^0.1.34", "@xmldom/xmldom": "^0.9.8", "autoprefixer": "^10.4.20", + "axios": "^1.9.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.4", diff --git a/prisma/migrations/20240612_add_oauth_fields/migration.sql b/prisma/migrations/20240612_add_oauth_fields/migration.sql new file mode 100644 index 00000000..e1d4a868 --- /dev/null +++ b/prisma/migrations/20240612_add_oauth_fields/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable to make password optional and add OAuth fields +ALTER TABLE "MailCredentials" + ALTER COLUMN "password" DROP NOT NULL, + ADD COLUMN "useOAuth" BOOLEAN NOT NULL DEFAULT false, + ADD COLUMN "refreshToken" TEXT, + ADD COLUMN "accessToken" TEXT, + ADD COLUMN "tokenExpiry" TIMESTAMP(3); \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 674a0417..46517d51 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -60,11 +60,17 @@ model MailCredentials { id String @id @default(uuid()) userId String email String - password String + password String? // Make password optional host String port Int secure Boolean @default(true) + // OAuth Settings + useOAuth Boolean @default(false) + refreshToken String? + accessToken String? + tokenExpiry DateTime? + // SMTP Settings smtp_host String? smtp_port Int? diff --git a/yarn.lock b/yarn.lock index f8591b60..7e432d2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1265,6 +1265,11 @@ async@^3: resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" @@ -1289,6 +1294,15 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +axios@^1.9.0: + version "1.9.0" + resolved "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz" + integrity sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -1469,6 +1483,13 @@ color@^4.2.3: color-convert "^2.0.1" color-string "^1.9.0" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" @@ -1663,6 +1684,11 @@ define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + denque@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz" @@ -1817,6 +1843,16 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + esbuild-register@3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz" @@ -1925,6 +1961,11 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + for-each@^0.3.5: version "0.3.5" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" @@ -1940,6 +1981,16 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz" + integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + mime-types "^2.1.12" + formdata-polyfill@^4.0.10: version "4.0.10" resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" @@ -1974,7 +2025,7 @@ function-bind@^1.1.2: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -get-intrinsic@^1.2.4, get-intrinsic@^1.3.0: +get-intrinsic@^1.2.4, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -2535,6 +2586,18 @@ mime-db@^1.54.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime-types@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz" @@ -3061,6 +3124,11 @@ prop-types@^15.6.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode.js@2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz"