Skip to content

Commit 413949c

Browse files
committed
trpc
1 parent 3881b5a commit 413949c

File tree

10 files changed

+199
-61
lines changed

10 files changed

+199
-61
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"**/.vercel": true,
1212
"**/out": true,
1313
"**/build": true,
14-
"**/node_modules": true,
14+
"**/node_modules": false,
1515
"pnpm-lock.yaml": false,
1616
"postcss.config.js": true,
1717
".prettierrc": true,

app/api/incidents.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

app/lib/trpc.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Resource } from "sst";
2+
import type { Router } from "../server";
3+
import { createTRPCReact, httpBatchLink } from "@trpc/react-query";
4+
5+
export const trpc = createTRPCReact<Router>();
6+
7+
export const api = trpc.createClient({
8+
links: [
9+
httpBatchLink({
10+
url: Resource.Trpc.url,
11+
}),
12+
],
13+
});

app/routes/__root.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { QueryClient } from "@tanstack/react-query";
1+
import { QueryClientProvider, type QueryClient } from "@tanstack/react-query";
22
import { createRootRouteWithContext, Outlet, ScrollRestoration } from "@tanstack/react-router";
33
import { createServerFn, Meta, Scripts } from "@tanstack/start";
44
import { getWebRequest } from "vinxi/http";
@@ -8,6 +8,7 @@ import { getAuth } from "@clerk/tanstack-start/server";
88
import { db } from "@/database/db";
99
import { eq } from "drizzle-orm";
1010
import { UserTable } from "@/database/schema.sql";
11+
import { trpc, api } from "@/lib/trpc";
1112

1213
const getUser = createServerFn({ method: "GET" }).handler(async () => {
1314
const { userId } = await getAuth(getWebRequest());
@@ -32,22 +33,28 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()(
3233
],
3334
component: RootComponent,
3435
beforeLoad: async () => await getUser(),
36+
loader: ({ context }) => ({ queryClient: context.queryClient }),
3537
links: () => [{ rel: "stylesheet", href: appCss }],
3638
});
3739

3840
function RootComponent() {
41+
const { queryClient } = Route.useLoaderData();
3942
return (
40-
<ClerkProvider>
41-
<html>
42-
<head>
43-
<Meta />
44-
</head>
45-
<body>
46-
<Outlet />
47-
<ScrollRestoration />
48-
<Scripts />
49-
</body>
50-
</html>
51-
</ClerkProvider>
43+
<trpc.Provider client={api} queryClient={queryClient}>
44+
<QueryClientProvider client={queryClient}>
45+
<ClerkProvider>
46+
<html>
47+
<head>
48+
<Meta />
49+
</head>
50+
<body>
51+
<Outlet />
52+
<ScrollRestoration />
53+
<Scripts />
54+
</body>
55+
</html>
56+
</ClerkProvider>
57+
</QueryClientProvider>
58+
</trpc.Provider>
5259
);
5360
}

app/routes/_authed/config.tsx

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,43 +30,44 @@ import { eq, getTableColumns, sql, and } from "drizzle-orm";
3030
import { db } from "@/database/db";
3131
import { ConfigTable, SlackInstallationTable } from "@/database/schema.sql";
3232
import { useQuery } from "@tanstack/react-query";
33+
import { trpc } from "@/lib/trpc";
3334

34-
const feeds = createServerFn({ method: "GET" })
35-
.validator(z.object({ teamId: z.string() }))
36-
.handler(async ({ data }) => {
37-
// const { userId } = await getAuth(getWebRequest());
38-
// const clerk = await clerkClient({
39-
// secretKey: Resource.ClerkSecretKey.value,
40-
// });
41-
// const accessTokens = (await clerk.users.getUserOauthAccessToken(userId!, "oauth_slack")).data;
42-
// const token = accessTokens[0].token || "";
43-
// const userInfo = await client.openid.connect.userInfo({
44-
// token,
45-
// });
46-
const teamConfigs = await db
47-
.select({ ...getTableColumns(ConfigTable), teamId: SlackInstallationTable.teamId })
48-
.from(ConfigTable)
49-
.innerJoin(SlackInstallationTable, eq(ConfigTable.installationId, SlackInstallationTable.id))
50-
.where(eq(SlackInstallationTable.teamId, data.teamId));
51-
const products = await getFeeds();
52-
const services = await products.reduce(
53-
async (acc, product) => {
54-
const services = await product.getServices();
55-
return {
56-
...(await acc),
57-
[product.name]: services,
58-
};
59-
},
60-
Promise.resolve({} as Record<string, IService[]>),
61-
);
62-
const productsWithServices = products.map((product) => ({
63-
name: product.name,
64-
displayName: product.displayName,
65-
logo: product.logo,
66-
services: services[product.name],
67-
}));
68-
return { products: productsWithServices, teamConfigs };
69-
});
35+
// const feeds = createServerFn({ method: "GET" })
36+
// .validator(z.object({ teamId: z.string() }))
37+
// .handler(async ({ data }) => {
38+
// // const { userId } = await getAuth(getWebRequest());
39+
// // const clerk = await clerkClient({
40+
// // secretKey: Resource.ClerkSecretKey.value,
41+
// // });
42+
// // const accessTokens = (await clerk.users.getUserOauthAccessToken(userId!, "oauth_slack")).data;
43+
// // const token = accessTokens[0].token || "";
44+
// // const userInfo = await client.openid.connect.userInfo({
45+
// // token,
46+
// // });
47+
// const teamConfigs = await db
48+
// .select({ ...getTableColumns(ConfigTable), teamId: SlackInstallationTable.teamId })
49+
// .from(ConfigTable)
50+
// .innerJoin(SlackInstallationTable, eq(ConfigTable.installationId, SlackInstallationTable.id))
51+
// .where(eq(SlackInstallationTable.teamId, data.teamId));
52+
// const products = await getFeeds();
53+
// const services = await products.reduce(
54+
// async (acc, product) => {
55+
// const services = await product.getServices();
56+
// return {
57+
// ...(await acc),
58+
// [product.name]: services,
59+
// };
60+
// },
61+
// Promise.resolve({} as Record<string, IService[]>),
62+
// );
63+
// const productsWithServices = products.map((product) => ({
64+
// name: product.name,
65+
// displayName: product.displayName,
66+
// logo: product.logo,
67+
// services: services[product.name],
68+
// }));
69+
// return { products: productsWithServices, teamConfigs };
70+
// });
7071

7172
const toggleService = createServerFn({ method: "POST" })
7273
.validator(z.object({ product: z.string(), service: z.string(), installationId: z.number() }))
@@ -104,7 +105,7 @@ const toggleService = createServerFn({ method: "POST" })
104105

105106
export const Route = createFileRoute("/_authed/config")({
106107
component: ConfigComponent,
107-
loader: ({ context }) => feeds({ data: { teamId: context.teamId } }),
108+
loader: ({ context }) => ({ teamId: context.teamId }),
108109
});
109110

110111
// This would typically come from your API
@@ -137,7 +138,9 @@ export const Route = createFileRoute("/_authed/config")({
137138
// ];
138139

139140
function ConfigComponent() {
140-
const { products, teamConfigs } = Route.useLoaderData();
141+
// const { products, teamConfigs } = Route.useLoaderData();
142+
const { teamId } = Route.useLoaderData();
143+
const configQuery = trpc.getFeedsAndConfigs.useQuery({ teamId });
141144
const [expandedProducts, setExpandedProducts] = useState<Set<string>>(new Set());
142145
const [enabledServices, setEnabledServices] = useState<Set<string>>(new Set());
143146
const [searchQuery, setSearchQuery] = useState("");
@@ -155,13 +158,13 @@ function ConfigComponent() {
155158

156159
const toggleService = (productId: string, serviceId: string) => {
157160
const productKey = `${productId}-${serviceId}`;
158-
const newEnabled = new Set(enabledProducts);
159-
if (newEnabled.has(productKey)) {
160-
newEnabled.delete(productKey);
161-
} else {
162-
newEnabled.add(productKey);
163-
}
164-
setEnabledProducts(newEnabled);
161+
// const newEnabled = new Set(enabledProducts);
162+
// if (newEnabled.has(productKey)) {
163+
// newEnabled.delete(productKey);
164+
// } else {
165+
// newEnabled.add(productKey);
166+
// }
167+
// setEnabledProducts(newEnabled);
165168
};
166169

167170
const getIconForProduct = (productId: string, serviceId: string) => {
@@ -195,6 +198,7 @@ function ConfigComponent() {
195198
return <Cloud className="h-5 w-5" />;
196199
};
197200

201+
const products = useMemo(() => configQuery.data?.products || [], [configQuery.data]);
198202
const filteredProducts = useMemo(
199203
() =>
200204
products.filter(

app/server/index.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { db } from "@/database/db";
2+
import { ConfigTable, SlackInstallationTable } from "@/database/schema.sql";
3+
import { getFeeds } from "@/feeds";
4+
import { eq, getTableColumns } from "drizzle-orm";
5+
import { initTRPC } from "@trpc/server";
6+
import { awsLambdaRequestHandler, CreateAWSLambdaContextOptions } from "@trpc/server/adapters/aws-lambda";
7+
import { APIGatewayProxyEvent, APIGatewayProxyEventV2 } from "aws-lambda";
8+
import { z } from "zod";
9+
import { IService } from "@/lib/interfaces";
10+
11+
const t = initTRPC
12+
.context<CreateAWSLambdaContextOptions<APIGatewayProxyEvent | APIGatewayProxyEventV2>>()
13+
.create();
14+
15+
const router = t.router({
16+
ping: t.procedure.query(() => {
17+
return "pong";
18+
}),
19+
getFeedsAndConfigs: t.procedure.input(z.object({ teamId: z.string() })).query(async ({ input }) => {
20+
const teamConfigs = await db
21+
.select({ ...getTableColumns(ConfigTable), teamId: SlackInstallationTable.teamId })
22+
.from(ConfigTable)
23+
.innerJoin(SlackInstallationTable, eq(ConfigTable.installationId, SlackInstallationTable.id))
24+
.where(eq(SlackInstallationTable.teamId, input.teamId));
25+
const products = await getFeeds();
26+
const services = await products.reduce(
27+
async (acc, product) => {
28+
const services = await product.getServices();
29+
return {
30+
...(await acc),
31+
[product.name]: services,
32+
};
33+
},
34+
Promise.resolve({} as Record<string, IService[]>),
35+
);
36+
const productsWithServices = products.map((product) => ({
37+
name: product.name,
38+
displayName: product.displayName,
39+
logo: product.logo,
40+
services: services[product.name],
41+
}));
42+
return { products: productsWithServices, teamConfigs };
43+
}),
44+
});
45+
46+
export type Router = typeof router;
47+
48+
export const handler = awsLambdaRequestHandler({
49+
router: router,
50+
createContext: (opts) => opts,
51+
});

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
"@tanstack/react-router": "^1.81.10",
6565
"@tanstack/react-router-with-query": "^1.81.10",
6666
"@tanstack/start": "^1.81.10",
67+
"@trpc/client": "^10.45.2",
68+
"@trpc/react-query": "^10.45.2",
69+
"@trpc/server": "^10.45.2",
6770
"@vitejs/plugin-react": "^4.3.3",
6871
"class-variance-authority": "^0.7.0",
6972
"clsx": "^2.1.1",
@@ -100,6 +103,7 @@
100103
"@eslint/js": "^9.14.0",
101104
"@tanstack/eslint-plugin-router": "^1.81.9",
102105
"@tanstack/router-devtools": "^1.87.1",
106+
"@types/aws-lambda": "^8.10.146",
103107
"@types/eslint__js": "^8.42.3",
104108
"@types/node": "^22.10.1",
105109
"@types/pg": "^8.11.10",

pnpm-lock.yaml

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sst-env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ declare module "sst" {
3939
"type": "sst.sst.Secret"
4040
"value": string
4141
}
42+
"Trpc": {
43+
"name": string
44+
"type": "sst.aws.Function"
45+
"url": string
46+
}
4247
"VPC": {
4348
"bastion": string
4449
"type": "sst.aws.Vpc"

sst.config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,16 @@ export default $config({
6161
// },
6262
// });
6363

64-
const webApp = new sst.aws.TanstackStart(`Web`, {
64+
const trpc = new sst.aws.Function(`Trpc`, {
65+
handler: "./app/server/index.handler",
6566
link: [database, clerkSecretKey, config, slackClientId, slackClientSecret, slackSigningSecret],
6667
vpc,
68+
url: true,
69+
});
70+
71+
const webApp = new sst.aws.TanstackStart(`Web`, {
72+
link: [database, trpc, clerkSecretKey, config, slackClientId, slackClientSecret, slackSigningSecret],
73+
vpc,
6774
dev: { command: "pnpm run dev:app" },
6875
});
6976

0 commit comments

Comments
 (0)