Skip to content

Commit d9b2f38

Browse files
committed
Format ported ingestion files
1 parent 4199589 commit d9b2f38

6 files changed

Lines changed: 1707 additions & 525 deletions

File tree

apps/dashboard/convex/gmailOAuth.ts

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { v } from "convex/values";
22
import { internal } from "./_generated/api";
3-
import { httpAction, internalMutation, internalQuery, mutation, query } from "./_generated/server";
3+
import {
4+
httpAction,
5+
internalMutation,
6+
internalQuery,
7+
mutation,
8+
query,
9+
} from "./_generated/server";
410
import { requireAdminToken } from "./_shared/adminToken";
511

612
declare const process: { env: Record<string, string | undefined> };
@@ -12,7 +18,8 @@ const GMAIL_SCOPES = [
1218
];
1319
const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
1420
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
15-
const GMAIL_PROFILE_URL = "https://gmail.googleapis.com/gmail/v1/users/me/profile";
21+
const GMAIL_PROFILE_URL =
22+
"https://gmail.googleapis.com/gmail/v1/users/me/profile";
1623

1724
type GoogleTokenResponse = {
1825
access_token?: string;
@@ -75,7 +82,8 @@ export const consumeAdminNonce = internalMutation({
7582
.query("gmailOAuthStates")
7683
.withIndex("by_state", (q) => q.eq("state", args.nonce))
7784
.unique();
78-
if (!row || !row.adminNonce || row.usedAt || row.expiresAt < Date.now()) return false;
85+
if (!row || !row.adminNonce || row.usedAt || row.expiresAt < Date.now())
86+
return false;
7987
await ctx.db.patch(row._id, { usedAt: Date.now() });
8088
return true;
8189
},
@@ -89,8 +97,12 @@ export const start = httpAction(async (ctx, request) => {
8997
// compatibility, but the frontend should always use the nonce path.
9098
const nonce = url.searchParams.get("nonce");
9199
if (nonce) {
92-
const valid = await ctx.runMutation(internal.gmailOAuth.consumeAdminNonce, { nonce });
93-
if (!valid) return textResponse("Nonce is invalid, already used, or expired.", 403);
100+
const valid = await ctx.runMutation(
101+
internal.gmailOAuth.consumeAdminNonce,
102+
{ nonce },
103+
);
104+
if (!valid)
105+
return textResponse("Nonce is invalid, already used, or expired.", 403);
94106
} else {
95107
const token = url.searchParams.get("token") ?? "";
96108
requireAdminToken(token);
@@ -122,19 +134,36 @@ export const callback = httpAction(async (ctx, request) => {
122134
const code = url.searchParams.get("code") ?? "";
123135
const oauthError = url.searchParams.get("error");
124136

125-
if (oauthError) return htmlResponse(renderResultPage("Gmail connection failed", oauthError), 400);
126-
if (!state || !code) return htmlResponse(renderResultPage("Gmail connection failed", "Missing OAuth state or code."), 400);
137+
if (oauthError)
138+
return htmlResponse(
139+
renderResultPage("Gmail connection failed", oauthError),
140+
400,
141+
);
142+
if (!state || !code)
143+
return htmlResponse(
144+
renderResultPage(
145+
"Gmail connection failed",
146+
"Missing OAuth state or code.",
147+
),
148+
400,
149+
);
127150

128151
try {
129-
const stateRow = await ctx.runQuery(internal.gmailOAuth.getValidOAuthState, { state });
152+
const stateRow = await ctx.runQuery(
153+
internal.gmailOAuth.getValidOAuthState,
154+
{ state },
155+
);
130156
if (!stateRow) throw new Error("OAuth state is invalid or expired.");
131157

132158
const redirectUri = getRedirectUri(request);
133159
const tokenResponse = await exchangeCodeForTokens(code, redirectUri);
134160
if (!tokenResponse.refresh_token) {
135-
throw new Error("Google did not return a refresh token. Try reconnecting and approving consent again.");
161+
throw new Error(
162+
"Google did not return a refresh token. Try reconnecting and approving consent again.",
163+
);
136164
}
137-
if (!tokenResponse.access_token) throw new Error("Google did not return an access token.");
165+
if (!tokenResponse.access_token)
166+
throw new Error("Google did not return an access token.");
138167

139168
const profile = await getGmailProfile(tokenResponse.access_token);
140169
const email = profile.emailAddress?.toLowerCase();
@@ -147,9 +176,17 @@ export const callback = httpAction(async (ctx, request) => {
147176
scopes: tokenResponse.scope?.split(" ") ?? GMAIL_SCOPES,
148177
});
149178

150-
return htmlResponse(renderResultPage("Gmail connected", `Connected ${email}. You can close this tab.`));
179+
return htmlResponse(
180+
renderResultPage(
181+
"Gmail connected",
182+
`Connected ${email}. You can close this tab.`,
183+
),
184+
);
151185
} catch (error) {
152-
return htmlResponse(renderResultPage("Gmail connection failed", formatError(error)), 400);
186+
return htmlResponse(
187+
renderResultPage("Gmail connection failed", formatError(error)),
188+
400,
189+
);
153190
}
154191
});
155192

@@ -247,7 +284,9 @@ async function exchangeCodeForTokens(code: string, redirectUri: string) {
247284
const body = (await response.json()) as GoogleTokenResponse;
248285

249286
if (!response.ok) {
250-
throw new Error(body.error_description ?? body.error ?? "Google token exchange failed.");
287+
throw new Error(
288+
body.error_description ?? body.error ?? "Google token exchange failed.",
289+
);
251290
}
252291

253292
return body;
@@ -257,7 +296,8 @@ async function getGmailProfile(accessToken: string) {
257296
const response = await fetch(GMAIL_PROFILE_URL, {
258297
headers: { Authorization: `Bearer ${accessToken}` },
259298
});
260-
if (!response.ok) throw new Error(`Gmail profile request failed: ${await response.text()}`);
299+
if (!response.ok)
300+
throw new Error(`Gmail profile request failed: ${await response.text()}`);
261301
return (await response.json()) as GmailProfileResponse;
262302
}
263303

@@ -268,11 +308,17 @@ function getRequiredEnv(name: string) {
268308
}
269309

270310
function textResponse(text: string, status = 200) {
271-
return new Response(text, { status, headers: { "Content-Type": "text/plain; charset=utf-8" } });
311+
return new Response(text, {
312+
status,
313+
headers: { "Content-Type": "text/plain; charset=utf-8" },
314+
});
272315
}
273316

274317
function htmlResponse(html: string, status = 200) {
275-
return new Response(html, { status, headers: { "Content-Type": "text/html; charset=utf-8" } });
318+
return new Response(html, {
319+
status,
320+
headers: { "Content-Type": "text/html; charset=utf-8" },
321+
});
276322
}
277323

278324
function renderResultPage(title: string, message: string) {

0 commit comments

Comments
 (0)