Skip to content

Commit 83c95ab

Browse files
MrgSubahmetskilincBlankParticleripgrimcoderabbitai[bot]
authored
Staging (Mail-0#750)
* draft fixes: - added cc and bcc when saving drafts - save drafts less aggresively * some fixes for saving attachments to draft * fix for empty draft loading * fix draft list recipient name/address * also show 'No Recipient' if empty * remove comments * switch to mimetext for draft saving to keep formatting consistent * add message title to draft list * feat: single api for oauth connections * fix: add extra error handling * chore: simplify and fix the dev env * Ai generate security (Mail-0#706) * Create prompts with XML formatting * Include XML formatted prompts in generate func * remove unused regex and add helper functions/warnings * error handling * Update apps/mail/lib/prompts.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * lint issues * Update prompts.ts * Mail-0#706 (comment) Coderabbit fix 1 * erabbitai bot 3 days ago ⚠️ Potential issue errorOccurred state is stale inside finally React state setters (setErrorOccurred) are asynchronous; the errorOccurred value captured at render time will not yet reflect changes made earlier in the same event loop. Consequently, the logic deciding whether to collapse/expand may run with an outdated flag. - } finally { - setIsLoading(false); - if (!errorOccurred || isAskingQuestion) { - setIsExpanded(true); - } else { - setIsExpanded(false); // Collapse on errors - } - } + } finally { + setIsLoading(false); + // Use a local flag to track errors deterministically + const hadError = isAskingQuestion ? false : !!errorFlagRef.current; + setIsExpanded(!hadError); + } You can create const errorFlagRef = useRef(false); and update errorFlagRef.current = true every time an error is detected, ensuring reliable behaviour irrespective of React batching. Committable suggestion skipped: line range outside the PR's diff. * Mail-0#706 (comment) * Mail-0#706 (comment) * Mail-0#706 (comment) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add a new Vietnamese translation file to support Vietnamese language users (Mail-0#726) * feat(i18n): add Vietnamese language support Add Vietnamese ('vi') to the list of supported languages in the i18n configuration and JSON file to expand language options. * Add a new Vietnamese translation file to support Vietnamese language users. * Clear Vietnamese translation strings * Update es.json (Mail-0#710) Co-authored-by: needle <122770437+needleXO@users.noreply.github.com> * Update app manifest and add new icons for PWA (Mail-0#739) * feat: allow sending from email aliases added through gmail (Mail-0#743) * Refactor IP handling in early-access routes * Add unauthorized error handling in sign out function * Redirect from Home Page on Session (Mail-0#701) * Updated lockfile * Updated home page session validation --------- Co-authored-by: Adam <x_1337@outlook.com> * Refactor settings handling and golden ticket logic * Feat: og:image Generation on /compose route (Mail-0#730) * Create route og image * resolve coderabbit nitpicks --------- Co-authored-by: Adam <x_1337@outlook.com> * Update session check to include user id before redirecting * Fix unauthorized error handling in multiple actions * Enable shortcuts settings in navigation * Refactor error handling to return unauthorized gracefully * Update Hero component with new imports and link adjustments * Update redirect URL to use hostname from req object * Fix redirect URL formatting and add log for missing user ID * Fix error handling in API routes for unauthorized requests * Refactor throwUnauthorizedGracefully function for readability * Fix error handling in driver routes * Handle unauthorized gracefully when getting connections * Refactor mail actions for better error handling * Refactor deleteActiveConnection function for readability * fixed (Mail-0#752) * Refactor error handling in mail actions to return null or specific error messages instead of throwing unauthorized errors. This improves readability and maintains functionality across various actions. * Update Google auth provider configuration * Delete connection and update hero text * Refactor error handling to use StandardizedError class * Refactor error handling for Google API driver --------- Co-authored-by: Ahmet Kilinc <akx9@icloud.com> Co-authored-by: BlankParticle <blankparticle@gmail.com> Co-authored-by: grim <75869731+ripgrim@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Chánh Đại <dai@chanhdai.com> Co-authored-by: Dani B. <danibaldomirm@gmail.com> Co-authored-by: needle <122770437+needleXO@users.noreply.github.com> Co-authored-by: Humber Nieto <56887259+humbernieto@users.noreply.github.com> Co-authored-by: Atharva Deosthale <atharva.deosthale17@gmail.com> Co-authored-by: Nikita Drokin <86173808+nikitadrokin@users.noreply.github.com>
1 parent cc0ffd5 commit 83c95ab

36 files changed

Lines changed: 894 additions & 1035 deletions

File tree

apps/mail/actions/ai-reply.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use server';
22

3-
import { throwUnauthorizedGracefully } from '@/app/api/utils';
43
import { generateCompletions } from '@/lib/groq';
54
import { headers } from 'next/headers';
65
import { auth } from '@/lib/auth';
@@ -13,9 +12,7 @@ export async function generateAIResponse(
1312
const headersList = await headers();
1413
const session = await auth.api.getSession({ headers: headersList });
1514

16-
if (!session?.user) {
17-
return throwUnauthorizedGracefully();
18-
}
15+
if (!session?.user) return '';
1916

2017
if (!process.env.GROQ_API_KEY) {
2118
throw new Error('Groq API key is not configured');

apps/mail/actions/brain.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use server';
2-
import { throwUnauthorizedGracefully } from '@/app/api/utils';
32
import { getActiveConnection } from './utils';
43
import axios from 'axios';
54

@@ -11,7 +10,7 @@ export const EnableBrain = async () => {
1110
const connection = await getActiveConnection();
1211

1312
if (!connection?.accessToken || !connection.refreshToken) {
14-
return throwUnauthorizedGracefully();
13+
return;
1514
}
1615

1716
return await axios.put(process.env.BRAIN_URL + `/subscribe/${connection.providerId}`, {

apps/mail/actions/connections.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use server';
22

3-
import { getAuthenticatedUserId, throwUnauthorizedGracefully } from '@/app/api/utils';
3+
import { getAuthenticatedUserId } from '@/app/api/utils';
44
import { connection, user } from '@zero/db/schema';
5-
import { type IConnection } from '@/types';
65
import { headers } from 'next/headers';
76
import { and, eq } from 'drizzle-orm';
87
import { auth } from '@/lib/auth';
@@ -14,13 +13,13 @@ export async function deleteConnection(connectionId: string) {
1413
const session = await auth.api.getSession({ headers: headersList });
1514

1615
if (!session) {
17-
return throwUnauthorizedGracefully();
16+
return { success: false, error: 'Session not found' };
1817
}
1918

2019
const userId = session?.user?.id;
2120

2221
if (!userId) {
23-
return throwUnauthorizedGracefully();
22+
return { success: false, error: 'User not found' };
2423
}
2524

2625
await db
@@ -42,18 +41,8 @@ export async function deleteConnection(connectionId: string) {
4241

4342
export async function putConnection(connectionId: string) {
4443
try {
45-
const headersList = await headers();
46-
const session = await auth.api.getSession({ headers: headersList });
47-
48-
if (!session) {
49-
return throwUnauthorizedGracefully();
50-
}
51-
52-
const userId = session?.user?.id;
53-
54-
if (!userId) {
55-
return throwUnauthorizedGracefully();
56-
}
44+
const userId = await getAuthenticatedUserId();
45+
if (!userId) return { success: false, error: 'User not found' };
5746

5847
const [foundConnection] = await db
5948
.select()
@@ -62,7 +51,7 @@ export async function putConnection(connectionId: string) {
6251
.limit(1);
6352

6453
if (!foundConnection) {
65-
throw new Error('Connection not found');
54+
return { success: false, error: 'Connection not found' };
6655
}
6756

6857
await db
@@ -75,6 +64,6 @@ export async function putConnection(connectionId: string) {
7564
return { success: true };
7665
} catch (error) {
7766
console.error('Failed to update connection:', error);
78-
throw new Error('Failed to update connection');
67+
return { success: false, error: String(error) };
7968
}
8069
}

apps/mail/actions/drafts.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use server';
2-
import { throwUnauthorizedGracefully } from '@/app/api/utils';
32
import { getActiveDriver } from './utils';
43

54
export const getDrafts = async ({
@@ -16,32 +15,21 @@ export const getDrafts = async ({
1615
return await driver.listDrafts(q, max, pageToken);
1716
} catch (error) {
1817
console.error('Error getting threads:', error);
19-
await throwUnauthorizedGracefully();
20-
// throw error;
21-
return { messages: [], nextPageToken: null };
18+
throw error;
2219
}
2320
};
2421

2522
export const createDraft = async (data: any) => {
2623
try {
2724
const driver = await getActiveDriver();
28-
29-
const res = await driver.createDraft(data);
30-
31-
return { success: true, id: res.id };
25+
return await driver.createDraft(data);
3226
} catch (error) {
3327
console.error('Error creating draft:', error);
34-
return { success: false, error: String(error) };
28+
throw error;
3529
}
3630
};
3731

3832
export const getDraft = async (id: string) => {
39-
if (!id) {
40-
throw new Error('Missing draft ID');
41-
}
42-
43-
console.log('getting email:', id);
44-
4533
try {
4634
const driver = await getActiveDriver();
4735
return await driver.getDraft(id);

apps/mail/actions/email-aliases.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

apps/mail/actions/getSummary.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use server';
2-
import { throwUnauthorizedGracefully } from '@/app/api/utils';
32
import { connection, summary } from '@zero/db/schema';
43
import { headers } from 'next/headers';
54
import { and, eq } from 'drizzle-orm';
@@ -12,7 +11,7 @@ export const GetSummary = async (threadId: string) => {
1211
const session = await auth.api.getSession({ headers: headersList });
1312

1413
if (!session || !session.connectionId) {
15-
return throwUnauthorizedGracefully();
14+
return null;
1615
}
1716

1817
const [_connection] = await db
@@ -21,7 +20,7 @@ export const GetSummary = async (threadId: string) => {
2120
.where(and(eq(connection.userId, session.user.id), eq(connection.id, session.connectionId)));
2221

2322
if (!_connection) {
24-
return throwUnauthorizedGracefully();
23+
return null;
2524
}
2625

2726
try {

apps/mail/actions/mail.ts

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,17 @@
11
'use server';
2-
import { deleteActiveConnection, FatalErrors, getActiveDriver } from './utils';
3-
import { throwUnauthorizedGracefully } from '@/app/api/utils';
42
import { IGetThreadResponse } from '@/app/api/driver/types';
3+
import { getActiveDriver } from './utils';
54
import { ParsedMessage } from '@/types';
65

7-
export const getMails = async ({
8-
folder,
9-
q,
10-
max,
11-
labelIds,
12-
pageToken,
13-
}: {
14-
folder: string;
15-
q?: string;
16-
max?: number;
17-
labelIds?: string[];
18-
pageToken: string | number | undefined;
19-
}) => {
20-
if (!folder) {
21-
throw new Error('Missing required fields');
22-
}
23-
24-
try {
25-
const driver = await getActiveDriver();
26-
return await driver.list(folder, q, max, labelIds, pageToken);
27-
} catch (error) {
28-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
29-
console.error('Error getting threads:', error);
30-
// throw error;
31-
await throwUnauthorizedGracefully();
32-
return { messages: [], nextPageToken: null };
33-
}
34-
};
35-
36-
export const getMail = async ({ id }: { id: string }): Promise<IGetThreadResponse> => {
6+
export const getMail = async ({ id }: { id: string }): Promise<IGetThreadResponse | null> => {
377
if (!id) {
388
throw new Error('Missing required fields');
399
}
4010
try {
4111
const driver = await getActiveDriver();
4212
const mailData = await driver.get(id);
43-
44-
if (!mailData) {
45-
throw new Error('Mail data not found');
46-
}
47-
4813
return mailData;
4914
} catch (error) {
50-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
5115
console.error('Error getting mail:', error);
5216
throw error;
5317
}
@@ -59,7 +23,6 @@ export const markAsRead = async ({ ids }: { ids: string[] }) => {
5923
await driver.markAsRead(ids);
6024
return { success: true };
6125
} catch (error) {
62-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
6326
console.error('Error marking message as read:', error);
6427
throw error;
6528
}
@@ -71,23 +34,11 @@ export const markAsUnread = async ({ ids }: { ids: string[] }) => {
7134
await driver.markAsUnread(ids);
7235
return { success: true };
7336
} catch (error) {
74-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
7537
console.error('Error marking message as unread:', error);
7638
throw error;
7739
}
7840
};
7941

80-
export const mailCount = async () => {
81-
try {
82-
const driver = await getActiveDriver();
83-
return await driver.count();
84-
} catch (error) {
85-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
86-
console.error('Error getting mail count:', error);
87-
throw error;
88-
}
89-
};
90-
9142
export const modifyLabels = async ({
9243
threadId,
9344
addLabels = [],
@@ -117,7 +68,6 @@ export const modifyLabels = async ({
11768
console.log('Server: No label changes specified');
11869
return { success: false, error: 'No label changes specified' };
11970
} catch (error) {
120-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
12171
console.error('Error updating thread labels:', error);
12272
throw error;
12373
}
@@ -159,7 +109,6 @@ export const toggleStar = async ({ ids }: { ids: string[] }) => {
159109

160110
return { success: true };
161111
} catch (error) {
162-
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
163112
console.error('Error toggling star:', error);
164113
throw error;
165114
}

apps/mail/actions/send.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use server';
22

3-
import { throwUnauthorizedGracefully } from '@/app/api/utils';
43
import { createDriver } from '@/app/api/driver';
54
import { getActiveConnection } from './utils';
65
import { Sender } from '@/types';
@@ -33,7 +32,7 @@ export async function sendEmail({
3332
const connection = await getActiveConnection();
3433

3534
if (!connection?.accessToken || !connection.refreshToken) {
36-
return throwUnauthorizedGracefully();
35+
return null;
3736
}
3837

3938
const driver = await createDriver(connection.providerId, {

0 commit comments

Comments
 (0)