Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions apps/web/utils/actions/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createScopedLogger } from "@/utils/logger";
import { deleteUser } from "@/utils/user/delete";
import prisma from "@/utils/prisma";
import { adminActionClient } from "@/utils/actions/safe-action";
import { SafeError } from "@/utils/error";

const logger = createScopedLogger("Admin Action");

Expand Down Expand Up @@ -40,14 +41,14 @@ export const adminDeleteAccountAction = adminActionClient
.action(async ({ parsedInput: { email } }) => {
try {
const userToDelete = await prisma.user.findUnique({ where: { email } });
if (!userToDelete) return { error: "User not found" };
if (!userToDelete) throw new SafeError("User not found");

await deleteUser({ userId: userToDelete.id });
} catch (error) {
logger.error("Failed to delete user", { email, error });
return {
error: `Failed to delete user: ${error instanceof Error ? error.message : String(error)}`,
};
throw new SafeError(
`Failed to delete user: ${error instanceof Error ? error.message : String(error)}`,
);
}

return { success: "User deleted" };
Expand Down
8 changes: 4 additions & 4 deletions apps/web/utils/actions/ai-rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export const approvePlanAction = actionClient
where: { id: executedRuleId },
include: { actionItems: true },
});
if (!executedRule) return { error: "Item not found" };
if (!executedRule) throw new SafeError("Plan not found");

await executeAct({
gmail,
Expand Down Expand Up @@ -275,7 +275,7 @@ export const saveRulesPromptAction = actionClient

if (!emailAccount) {
logger.error("Email account not found");
return { error: "Email account not found" };
throw new SafeError("Email account not found");
}

const oldPromptFile = emailAccount.rulesPrompt;
Expand Down Expand Up @@ -507,7 +507,7 @@ export const generateRulesPromptAction = actionClient
.action(async ({ ctx: { emailAccountId } }) => {
const emailAccount = await getEmailAccountWithAi({ emailAccountId });

if (!emailAccount) return { error: "Email account not found" };
if (!emailAccount) throw new SafeError("Email account not found");

const gmail = await getGmailClientForEmail({ emailAccountId });
const lastSent = await getMessages(gmail, {
Expand Down Expand Up @@ -564,7 +564,7 @@ export const generateRulesPromptAction = actionClient
userLabels: labelsWithCounts.map((label) => label.label),
});

if (!result) return { error: "Error generating rules prompt" };
if (!result) throw new SafeError("Error generating rules prompt");

return { rulesPrompt: result.join("\n\n") };
});
Expand Down
2 changes: 1 addition & 1 deletion apps/web/utils/actions/categorize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ async function upsertCategory({
}
} catch (error) {
if (isDuplicateError(error, "name"))
return { error: "Category with this name already exists" };
throw new SafeError("Category with this name already exists");

throw error;
}
Expand Down
5 changes: 3 additions & 2 deletions apps/web/utils/actions/generate-reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { emailToContent } from "@/utils/mail";
import { getReply, saveReply } from "@/utils/redis/reply";
import { actionClient } from "@/utils/actions/safe-action";
import { getEmailAccountWithAi } from "@/utils/user/get";
import { SafeError } from "@/utils/error";

export const generateNudgeReplyAction = actionClient
.metadata({ name: "generateNudgeReply" })
Expand All @@ -17,11 +18,11 @@ export const generateNudgeReplyAction = actionClient
}) => {
const emailAccount = await getEmailAccountWithAi({ emailAccountId });

if (!emailAccount) return { error: "User not found" };
if (!emailAccount) throw new SafeError("User not found");

const lastMessage = inputMessages.at(-1);

if (!lastMessage) return { error: "No message provided" };
if (!lastMessage) throw new SafeError("No message provided");

const reply = await getReply({
emailAccountId,
Expand Down
11 changes: 6 additions & 5 deletions apps/web/utils/actions/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@/utils/actions/group.validation";
import { addGroupItem, deleteGroupItem } from "@/utils/group/group-item";
import { actionClient } from "@/utils/actions/safe-action";
import { SafeError } from "@/utils/error";

export const createGroupAction = actionClient
.metadata({ name: "createGroup" })
Expand All @@ -18,7 +19,7 @@ export const createGroupAction = actionClient
select: { name: true, groupId: true },
});
if (rule?.groupId) return { groupId: rule.groupId };
if (!rule) return { error: "Rule not found" };
if (!rule) throw new SafeError("Rule not found");

const group = await prisma.group.create({
data: {
Expand All @@ -44,11 +45,11 @@ export const addGroupItemAction = actionClient
const group = await prisma.group.findUnique({
where: { id: groupId },
});
if (!group) return { error: "Group not found" };
if (!group) throw new SafeError("Learned patterns group not found");
if (group.emailAccountId !== emailAccountId)
return {
error: "You don't have permission to add items to this group",
};
throw new SafeError(
"You don't have permission to add this learned pattern",
);

await addGroupItem({ groupId, type, value, exclude });
},
Expand Down
22 changes: 13 additions & 9 deletions apps/web/utils/actions/mail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { sendEmailWithHtml, sendEmailBody } from "@/utils/gmail/mail";
import { actionClient } from "@/utils/actions/safe-action";
import { getGmailClientForEmail } from "@/utils/account";
import { SafeError } from "@/utils/error";

// do not return functions to the client or we'll get an error
const isStatusOk = (status: number) => status >= 200 && status < 300;
Expand All @@ -41,7 +42,8 @@ export const archiveThreadAction = actionClient
labelId,
});

if (!isStatusOk(res.status)) return { error: "Failed to archive thread" };
if (!isStatusOk(res.status))
throw new SafeError("Failed to archive thread");
},
);

Expand All @@ -62,7 +64,8 @@ export const trashThreadAction = actionClient
actionSource: "user",
});

if (!isStatusOk(res.status)) return { error: "Failed to delete thread" };
if (!isStatusOk(res.status))
throw new SafeError("Failed to delete thread");
},
);

Expand All @@ -74,7 +77,7 @@ export const trashThreadAction = actionClient

// const res = await trashMessage({ gmail, messageId });

// if (!isStatusOk(res.status)) return { error: "Failed to delete message" };
// if (!isStatusOk(res.status)) throw new SafeError("Failed to delete message");
// });

export const markReadThreadAction = actionClient
Expand All @@ -87,7 +90,7 @@ export const markReadThreadAction = actionClient
const res = await markReadThread({ gmail, threadId, read });

if (!isStatusOk(res.status))
return { error: "Failed to mark thread as read" };
throw new SafeError("Failed to mark thread as read");
},
);

Expand All @@ -104,7 +107,7 @@ export const markImportantMessageAction = actionClient
const res = await markImportantMessage({ gmail, messageId, important });

if (!isStatusOk(res.status))
return { error: "Failed to mark message as important" };
throw new SafeError("Failed to mark message as important");
},
);

Expand All @@ -117,7 +120,7 @@ export const markSpamThreadAction = actionClient
const res = await markSpam({ gmail, threadId });

if (!isStatusOk(res.status))
return { error: "Failed to mark thread as spam" };
throw new SafeError("Failed to mark thread as spam");
});

export const createAutoArchiveFilterAction = actionClient
Expand All @@ -133,7 +136,7 @@ export const createAutoArchiveFilterAction = actionClient
const res = await createAutoArchiveFilter({ gmail, from, gmailLabelId });

if (!isStatusOk(res.status))
return { error: "Failed to create auto archive filter" };
throw new SafeError("Failed to create auto archive filter");
},
);

Expand All @@ -153,7 +156,8 @@ export const createFilterAction = actionClient
addLabelIds: [gmailLabelId],
});

if (!isStatusOk(res.status)) return { error: "Failed to create filter" };
if (!isStatusOk(res.status))
throw new SafeError("Failed to create filter");

return res;
},
Expand All @@ -167,7 +171,7 @@ export const deleteFilterAction = actionClient

const res = await deleteFilter({ gmail, id });

if (!isStatusOk(res.status)) return { error: "Failed to delete filter" };
if (!isStatusOk(res.status)) throw new SafeError("Failed to delete filter");
});

export const createLabelAction = actionClient
Expand Down
15 changes: 8 additions & 7 deletions apps/web/utils/actions/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createScopedLogger } from "@/utils/logger";
import { actionClient, adminActionClient } from "@/utils/actions/safe-action";
import { getGmailAndAccessTokenForEmail } from "@/utils/account";
import prisma from "@/utils/prisma";
import { SafeError } from "@/utils/error";

const logger = createScopedLogger("actions/permissions");

Expand All @@ -17,13 +18,13 @@ export const checkPermissionsAction = actionClient
emailAccountId,
});

if (!accessToken) return { error: "No access token" };
if (!accessToken) throw new SafeError("No access token");

const { hasAllPermissions, error } = await handleGmailPermissionsCheck({
accessToken,
emailAccountId,
});
if (error) return { error };
if (error) throw new SafeError(error);

if (!hasAllPermissions) return { hasAllPermissions: false };

Expand All @@ -36,7 +37,7 @@ export const checkPermissionsAction = actionClient
emailAccountId,
error,
});
return { error: "Failed to check permissions" };
throw new SafeError("Failed to check permissions");
}
});

Expand All @@ -51,22 +52,22 @@ export const adminCheckPermissionsAction = adminActionClient
id: true,
},
});
if (!emailAccount) return { error: "Email account not found" };
if (!emailAccount) throw new SafeError("Email account not found");
const emailAccountId = emailAccount.id;

const { accessToken } = await getGmailAndAccessTokenForEmail({
emailAccountId,
});
if (!accessToken) return { error: "No Gmail access token" };
if (!accessToken) throw new SafeError("No Gmail access token");

const { hasAllPermissions, error } = await handleGmailPermissionsCheck({
accessToken,
emailAccountId,
});
if (error) return { error };
if (error) throw new SafeError(error);
return { hasAllPermissions };
} catch (error) {
logger.error("Admin failed to check permissions", { email, error });
return { error: "Failed to check permissions" };
throw new SafeError("Failed to check permissions");
}
});
Loading
Loading