Skip to content
Permalink
Newer
Older
100644 300 lines (272 sloc) 7.61 KB
October 19, 2023 16:49
1
function arrBufToB64Url(buf: ArrayBuffer) {
2
const b64data = btoa(String.fromCharCode(...new Uint8Array(buf)));
3
return b64data.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
4
}
5
6
function stringToBuffer(str: string) {
7
const buffer = new ArrayBuffer(str.length);
8
const ui8 = new Uint8Array(buffer);
9
for (let i = 0, bufLen = str.length; i < bufLen; i++) {
10
ui8[i] = str.charCodeAt(i);
11
}
12
return buffer;
13
}
14
15
export async function GenerateUploadURL(
16
env: Env,
17
path: string,
18
size: number,
19
fileExt: string,
20
origin: string,
October 19, 2023 16:49
21
): Promise<string> {
22
const accessToken = await GetAccessToken(env);
23
const contentTypes: { [k: string]: string } = {
24
gif: "image/gif",
25
m4v: "video/x-m4v",
26
mkv: "video/x-matroska",
October 19, 2023 16:50
27
mov: "video/mp4",
October 19, 2023 16:49
28
mp4: "video/mp4",
29
webm: "video/webm",
30
wmv: "video/x-ms-wmv",
31
};
32
33
const resumableUploadReq = await fetch(
34
`https://storage.googleapis.com/upload/storage/v1/b/portal-carcrushers-cc/o?uploadType=resumable&name=${encodeURIComponent(
October 19, 2023 16:49
36
)}`,
37
{
38
headers: {
39
authorization: `Bearer ${accessToken}`,
40
origin,
October 19, 2023 16:49
41
"x-upload-content-type": contentTypes[fileExt],
42
"x-upload-content-length": size.toString(),
43
},
44
method: "POST",
October 19, 2023 16:49
46
);
47
48
if (!resumableUploadReq.ok)
49
throw new Error(
50
`Failed to create resumable upload: ${await resumableUploadReq.text()}`,
October 19, 2023 16:49
51
);
52
53
const url = resumableUploadReq.headers.get("location");
54
55
if (!url) throw new Error("No location header returned");
56
57
return url;
58
}
59
October 19, 2023 16:50
60
export async function GetAccessToken(env: Env): Promise<string> {
October 19, 2023 16:49
61
const claimSet = btoa(
62
JSON.stringify({
63
aud: "https://oauth2.googleapis.com/token",
64
exp: Math.floor(Date.now() / 1000) + 120,
65
iat: Math.floor(Date.now() / 1000),
66
iss: env.WORKER_GSERVICEACCOUNT,
67
scope: "https://www.googleapis.com/auth/cloud-platform",
October 19, 2023 16:49
69
)
70
.replaceAll("+", "-")
71
.replaceAll("/", "_")
72
.replaceAll("=", "");
73
74
const signingKey = await crypto.subtle.importKey(
75
"pkcs8",
October 19, 2023 16:49
76
stringToBuffer(atob(env.STORAGE_ACCOUNT_KEY.replace(/(\r\n|\n|\r)/gm, ""))),
October 19, 2023 16:49
77
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
78
false,
79
["sign"],
October 19, 2023 16:49
80
);
81
const signature = await crypto.subtle.sign(
82
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
83
signingKey,
84
stringToBuffer(`eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.${claimSet}`),
October 19, 2023 16:49
85
);
86
const assertion = `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.${claimSet}.${arrBufToB64Url(
87
signature,
October 19, 2023 16:49
88
)}`;
89
const tokenRequest = await fetch("https://oauth2.googleapis.com/token", {
90
body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${assertion}`,
91
headers: {
92
"content-type": "application/x-www-form-urlencoded",
93
},
94
method: "POST",
95
});
96
97
if (!tokenRequest.ok)
98
throw new Error(`Failed to get access token: ${await tokenRequest.text()}`);
99
100
return ((await tokenRequest.json()) as { [k: string]: any }).access_token;
101
}
October 19, 2023 16:49
102
103
async function getKeyIDs(
104
access_token: string,
105
projectId: string,
106
keys: { partitionId: { projectId: string }; path: { kind: string }[] }[],
October 19, 2023 16:49
107
) {
108
const keyRequest = await fetch(
109
`https://datastore.googleapis.com/v1/projects/${projectId}:allocateIds`,
110
{
111
body: JSON.stringify({ keys }),
112
headers: {
113
authorization: `Bearer ${access_token}`,
114
"content-type": "application/json",
115
},
116
method: "POST",
October 19, 2023 16:49
118
);
119
120
if (!keyRequest.ok) {
121
console.log(await keyRequest.json());
122
throw new Error("Failed to allocate key IDs");
123
}
124
125
return ((await keyRequest.json()) as { keys: { [k: string]: any }[] }).keys;
126
}
127
128
export async function insertLogs(
129
userActionMap: { [k: string]: number },
130
reportId: string,
131
context: RequestContext,
October 19, 2023 16:49
132
) {
133
const accessToken = await GetAccessToken(context.env);
134
const actionBaseURLs: { [k: number]: string } = {
135
1: "https://portal.carcrushers.cc/view-closed-report/",
136
2: "https://portal.carcrushers.cc/view-closed-report/",
137
3: "",
138
4: "https://portal.carcrushers.cc/game-appeals/",
139
};
140
const actionIntegers: { [k: number]: string } = {
141
1: "blacklist",
142
2: "ban",
143
3: "revoke",
144
4: "accept_appeal",
145
};
146
const incompleteLogKey = {
147
partitionId: {
148
projectId: context.env.DATASTORE_PROJECT,
149
},
150
path: [
151
{
152
kind: "log",
153
},
154
],
155
};
156
const payload: { mode: string; mutations: { [k: string]: any }[] } = {
157
mode: "NON_TRANSACTIONAL",
158
mutations: [],
159
};
160
const preAllocatedLogKeys = [];
161
162
while (preAllocatedLogKeys.length < Object.keys(userActionMap).length)
163
preAllocatedLogKeys.push(incompleteLogKey);
164
165
const keys = await getKeyIDs(
166
accessToken,
167
context.env.DATASTORE_PROJECT,
168
preAllocatedLogKeys,
October 19, 2023 16:49
169
);
170
171
for (const [user, action] of Object.entries(userActionMap)) {
172
payload.mutations.push({
173
insert: {
174
key: keys.pop(),
175
properties: {
176
action: {
177
stringValue: actionIntegers[action],
178
},
179
evidence: {
180
stringValue: actionBaseURLs[action] + reportId,
181
},
182
executed_at: {
183
integerValue: Date.now(),
184
},
185
executor: {
186
stringValue: context.data.current_user.id,
187
},
188
target: {
189
integerValue: user,
190
},
191
},
192
},
193
});
194
}
195
196
const mutationRequest = await fetch(
197
`https://datastore.googleapis.com/v1/projects/${context.env.DATASTORE_PROJECT}:commit`,
198
{
199
body: JSON.stringify(payload),
200
headers: {
201
authorization: `Bearer ${accessToken}`,
202
"content-type": "application/json",
203
},
204
method: "POST",
October 19, 2023 16:49
206
);
207
208
if (!mutationRequest.ok) {
209
console.log(await mutationRequest.json());
210
throw new Error("Failed to commit mutation");
211
}
212
213
return await mutationRequest.json();
214
}
215
216
export async function queryLogs(user: number, context: RequestContext) {
217
const accessToken = await GetAccessToken(context.env);
218
219
const queryRequest = await fetch(
220
`https://datastore.googleapis.com/v1/projects/${context.env.DATASTORE_PROJECT}:runQuery`,
221
{
222
body: JSON.stringify({
223
partitionId: {
224
projectId: context.env.DATASTORE_PROJECT,
225
},
226
query: {
227
filter: {
228
propertyFilter: {
229
op: "EQUAL",
230
property: {
231
name: "target",
232
},
233
value: {
234
integerValue: user.toString(),
235
},
236
},
237
},
238
kind: [
239
{
240
name: "log",
241
},
242
],
243
},
244
}),
245
headers: {
246
authorization: `Bearer ${accessToken}`,
247
"content-type": "application/json",
248
},
249
method: "POST",
October 19, 2023 16:49
251
);
252
253
if (!queryRequest.ok) {
254
console.log(await queryRequest.json());
255
throw new Error("Failed to query logs");
256
}
257
258
return (
October 19, 2023 16:49
259
(
260
(await queryRequest.json()) as {
261
batch: {
262
entityResults: {
263
cursor: string;
264
entity: { [k: string]: any };
265
version: string;
266
}[];
267
};
October 19, 2023 16:49
268
}
269
).batch?.entityResults ?? []
270
);
October 19, 2023 16:49
271
}
October 19, 2023 16:51
272
273
export async function sendPushNotification(
274
env: Env,
275
title: string,
276
body: string,
277
token?: string,
278
) {
October 19, 2023 16:51
279
const message = {
October 19, 2023 16:51
280
notification: {
281
body,
282
title,
283
},
284
token,
October 19, 2023 16:51
285
};
October 19, 2023 16:51
286
287
const notifResp = await fetch(
288
"https://fcm.googleapis.com/v1/projects/car-crushers-mobile/messages:send",
289
{
290
body: JSON.stringify({ message }),
291
headers: {
292
authorization: `Bearer ${await GetAccessToken(env)}`,
293
"content-type": "application/json",
294
},
295
method: "POST",
296
},
297
);
298
October 19, 2023 16:51
299
if (!notifResp.ok) throw new Error(await notifResp.text());
October 19, 2023 16:51
300
}