Skip to content

Commit eb60383

Browse files
atrakhConvex, Inc.
authored and
Convex, Inc.
committed
Update contact-form to support plain tiers (#36389)
GitOrigin-RevId: ec8776e468a39482011dc4a8c457e6f03f5ed3d3
1 parent 9e8b74a commit eb60383

File tree

2 files changed

+346
-205
lines changed

2 files changed

+346
-205
lines changed
Lines changed: 18 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,7 @@
1-
import { inspect } from "util";
2-
import {
3-
PlainClient,
4-
PlainSDKError,
5-
ThreadFieldSchemaType,
6-
UpsertCustomerInput,
7-
} from "@team-plain/typescript-sdk";
8-
import type { NextApiRequest, NextApiResponse } from "next";
91
import { z } from "zod";
102
import { auth0 } from "server/auth0";
113
import { captureException, captureMessage } from "@sentry/nextjs";
12-
import { retryingFetch } from "lib/ssr";
13-
import { Team, ProjectDetails, DeploymentResponse } from "generatedApi";
14-
15-
const apiKey = process.env.PLAIN_API_KEY;
16-
17-
if (!apiKey) {
18-
throw new Error("PLAIN_API_KEY environment variable is not set");
19-
}
20-
21-
const client = new PlainClient({
22-
apiKey,
23-
});
4+
import type { NextApiRequest, NextApiResponse } from "next";
245

256
export type ResponseData = {
267
error: string | null;
@@ -68,196 +49,28 @@ export default async function handler(
6849
return res.status(400).json({ error: error.message });
6950
}
7051

71-
const profileDataResp = await retryingFetch(
72-
`${process.env.NEXT_PUBLIC_BIG_BRAIN_URL}/api/dashboard/profile`,
73-
{
74-
headers: {
75-
authorization: `Bearer ${session.accessToken}`,
76-
},
77-
},
78-
);
79-
if (!profileDataResp.ok) {
80-
const responseText = await profileDataResp.text();
81-
captureMessage(`Couldn't fetch profile data: ${responseText}`);
82-
return {
83-
error: "Internal server Error",
84-
};
85-
}
86-
87-
const { id, email: profileEmail } = await profileDataResp.json();
52+
try {
53+
// Get the host from the request headers
54+
const protocol = req.headers["x-forwarded-proto"] || "http";
55+
const host = req.headers["x-forwarded-host"] || req.headers.host;
56+
const baseUrl = `${protocol}://${host}`;
8857

89-
const memberDataResp = await retryingFetch(
90-
`${process.env.NEXT_PUBLIC_BIG_BRAIN_URL}/api/dashboard/member_data`,
91-
{
58+
void fetch(`${baseUrl}/api/send-plain-message`, {
59+
method: "POST",
9260
headers: {
93-
authorization: `Bearer ${session.accessToken}`,
61+
"Content-Type": "application/json",
62+
"X-Plain-Api-Key": process.env.PLAIN_API_KEY || "",
63+
"X-Convex-Access-Token": session.accessToken || "",
9464
},
95-
},
96-
);
97-
if (!memberDataResp.ok) {
98-
const responseText = await memberDataResp.text();
99-
captureMessage(`Couldn't fetch member data: ${responseText}`);
100-
return {
101-
error: "Internal server Error",
102-
};
103-
}
104-
const {
105-
teams,
106-
projects,
107-
deployments,
108-
}: {
109-
teams: Team[];
110-
projects: ProjectDetails[];
111-
deployments: DeploymentResponse[];
112-
} = await memberDataResp.json();
113-
const { teamId, projectId, deploymentName } = body;
114-
115-
let customerId: string | null = null;
116-
117-
const upsertCustomerRes = await upsertPlainCustomer(
118-
{
119-
externalId: id.toString(),
120-
},
121-
id,
122-
profileEmail,
123-
validatedUser,
124-
);
125-
126-
if (upsertCustomerRes.error) {
127-
const customerAlreadyExists =
128-
upsertCustomerRes.error.type === "mutation_error" &&
129-
upsertCustomerRes.error.errorDetails.code ===
130-
"customer_already_exists_with_email";
131-
if (customerAlreadyExists) {
132-
const upsertCustomerWithEmailIdentifierRes = await upsertPlainCustomer(
133-
{ emailAddress: profileEmail },
134-
id,
135-
profileEmail,
136-
validatedUser,
137-
);
138-
139-
if (upsertCustomerWithEmailIdentifierRes.error) {
140-
return failedToUpsertPlainCustomer(
141-
upsertCustomerWithEmailIdentifierRes.error,
142-
res,
143-
);
144-
}
145-
customerId = upsertCustomerWithEmailIdentifierRes.data.customer.id;
146-
} else {
147-
return failedToUpsertPlainCustomer(upsertCustomerRes.error, res);
148-
}
149-
} else {
150-
customerId = upsertCustomerRes.data.customer.id;
151-
}
152-
153-
const team = teams.find((t) => t.id === teamId);
154-
const project = projects.find((p) => p.id === projectId);
155-
const deployment = deployments.find((d) => d.name === deploymentName);
156-
157-
const threadFields = [];
158-
if (team) {
159-
threadFields.push({
160-
key: "team_id",
161-
stringValue: team.id.toString(),
162-
type: ThreadFieldSchemaType.String,
163-
});
164-
threadFields.push({
165-
key: "team_slug",
166-
stringValue: team.slug,
167-
type: ThreadFieldSchemaType.String,
168-
});
169-
}
170-
171-
if (project) {
172-
threadFields.push({
173-
key: "project_id",
174-
stringValue: project.id.toString(),
175-
type: ThreadFieldSchemaType.String,
176-
});
177-
threadFields.push({
178-
key: "project_slug",
179-
stringValue: project.slug,
180-
type: ThreadFieldSchemaType.String,
65+
body: JSON.stringify({
66+
...body,
67+
user: validatedUser,
68+
}),
18169
});
182-
}
18370

184-
if (deployment) {
185-
threadFields.push({
186-
key: "deployment_name",
187-
stringValue: deployment.name,
188-
type: ThreadFieldSchemaType.String,
189-
});
190-
}
191-
192-
const createThreadRes = await client.createThread({
193-
customerIdentifier: {
194-
customerId,
195-
},
196-
title: body.subject,
197-
threadFields,
198-
components: [
199-
{
200-
componentText: {
201-
text: body.message,
202-
},
203-
},
204-
],
205-
});
206-
207-
if (createThreadRes.error) {
208-
console.error(
209-
inspect(createThreadRes.error, {
210-
showHidden: false,
211-
depth: null,
212-
colors: true,
213-
}),
214-
);
215-
captureMessage(createThreadRes.error.message);
71+
res.status(200).json({ error: null });
72+
} catch (error: any) {
73+
captureException(error);
21674
return res.status(500).json({ error: "Internal Server Error" });
21775
}
218-
219-
res.status(200).json({ error: null });
220-
}
221-
222-
function upsertPlainCustomer(
223-
customerIdentifier: UpsertCustomerInput["identifier"],
224-
memberId: number,
225-
profileEmail: string,
226-
validatedUser: z.infer<typeof UserSchema>,
227-
) {
228-
return client.upsertCustomer({
229-
identifier: customerIdentifier,
230-
onCreate: {
231-
fullName: validatedUser.name || validatedUser.nickname || profileEmail,
232-
externalId: memberId.toString(),
233-
email: {
234-
email: profileEmail,
235-
isVerified: true,
236-
},
237-
},
238-
onUpdate: {
239-
email: {
240-
email: profileEmail,
241-
isVerified: true,
242-
},
243-
fullName: {
244-
value: validatedUser.name || validatedUser.nickname || profileEmail,
245-
},
246-
},
247-
});
248-
}
249-
250-
function failedToUpsertPlainCustomer(
251-
error: PlainSDKError,
252-
res: NextApiResponse,
253-
) {
254-
console.error(
255-
inspect(error, {
256-
showHidden: false,
257-
depth: null,
258-
colors: true,
259-
}),
260-
);
261-
captureMessage(error.message);
262-
return res.status(500).json({ error: "Internal Server Error" });
26376
}

0 commit comments

Comments
 (0)