-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathindex.ts
134 lines (121 loc) · 4.99 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { CloudflareEnv, Env, getCloudflareEnv } from "@backend/env";
import { Toucan } from "toucan-js";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { Context } from "toucan-js/dist/types";
import { t } from "./trpc";
import { professorRouter } from "./routers/professor";
import { ratingsRouter } from "./routers/rating";
import { adminRouter } from "./routers/admin";
import { authRouter } from "./routers/auth";
import { professorParser, truncatedProfessorParser } from "./types/schema";
import { ALL_PROFESSOR_KEY } from "./utils/const";
import { AnonymousIdDao } from "./dao/anonymous-id-dao";
export const appRouter = t.router({
professors: professorRouter,
ratings: ratingsRouter,
admin: adminRouter,
auth: authRouter,
});
export type AppRouter = typeof appRouter;
const CORS_HEADERS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "*",
"Access-Control-Allow-Headers": "*",
};
export default {
async fetch(request: Request, rawEnv: Record<string, unknown>, cloudflareCtx: Context) {
if (request.method === "OPTIONS") {
return new Response(null, { headers: CORS_HEADERS });
}
// In actually deployed instances (including `wrangler dev`) Cloudflare includes the CF-Ray header
// this does not get populated in Miniflare during actual local instances.
const isDeployed = request.headers.get("CF-Ray") != null;
const HASHED_IP = await AnonymousIdDao.hashIp(
request.headers.get("CF-Connecting-IP") ?? "",
);
const cloudflareEnv = getCloudflareEnv({ HASHED_IP, IS_DEPLOYED: isDeployed, ...rawEnv });
const polyratingsEnv = new Env(cloudflareEnv);
if (!cloudflareEnv.IS_DEPLOYED) {
await ensureLocalDb(cloudflareEnv, polyratingsEnv);
}
const sentry = new Toucan({
dsn: "https://[email protected]/6319110",
context: cloudflareCtx,
requestDataOptions: {
allowedHeaders: ["user-agent"],
allowedSearchParams: /(.*)/,
},
});
return fetchRequestHandler({
endpoint: "",
req: request,
router: appRouter,
batching: {
enabled: false,
},
createContext: async ({ req }) => {
const authHeader = req.headers.get("Authorization");
const user = await polyratingsEnv.authStrategy.verify(authHeader);
return { env: polyratingsEnv, user };
},
responseMeta: () => ({
headers: {
"Access-Control-Max-Age": "1728000",
"Content-Encoding": "gzip",
Vary: "Accept-Encoding",
...CORS_HEADERS,
},
}),
onError: (errorState) => {
if (errorState.error.code === "INTERNAL_SERVER_ERROR") {
sentry.captureException(errorState.error);
}
},
});
},
};
// Allows for singleton execution of ensureLocalDb
let initPromise: Promise<void>;
async function ensureLocalDb(cloudflareEnv: CloudflareEnv, polyratingsEnv: Env) {
if (initPromise) {
return initPromise;
}
let initPromiseResolver;
initPromise = new Promise((resolve) => {
initPromiseResolver = resolve;
});
// Check to find the all professor key
const allProfessorKey = await cloudflareEnv.POLYRATINGS_TEACHERS.get(ALL_PROFESSOR_KEY);
if (allProfessorKey) {
// It will be defined. Typescript does not understand promise escaping
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
initPromiseResolver!();
return initPromise;
}
const reqUrl =
"https://raw.githubusercontent.com/Polyratings/polyratings-data/data/professor-dump.json";
// eslint-disable-next-line no-console
console.log(`Retrieving professor data from ${reqUrl}`);
const githubReq = await fetch(reqUrl);
const githubJson = await githubReq.json();
// Verify that professors are formed correctly
const parsedProfessors = professorParser.array().parse(githubJson);
const truncatedProfessors = truncatedProfessorParser.array().parse(parsedProfessors);
await cloudflareEnv.POLYRATINGS_TEACHERS.put(
ALL_PROFESSOR_KEY,
JSON.stringify(truncatedProfessors),
);
for (const professor of parsedProfessors) {
// eslint-disable-next-line no-await-in-loop
await cloudflareEnv.POLYRATINGS_TEACHERS.put(professor.id, JSON.stringify(professor));
}
const password = await polyratingsEnv.authStrategy.hashPassword("password");
polyratingsEnv.kvDao.putUser({
username: "local",
password,
});
// It will be defined. Typescript does not understand promise escaping
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
initPromiseResolver!();
return initPromise;
}