@server/next 0.27.0 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +686 -739
- package/package.json +3 -2
package/index.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
// src/polyfill.ts
|
|
2
|
-
globalThis.env = {};
|
|
3
|
-
if (typeof globalThis.Netlify !== "undefined") {
|
|
4
|
-
Object.assign(
|
|
5
|
-
globalThis.env,
|
|
6
|
-
globalThis.Netlify.env.toObject()
|
|
7
|
-
);
|
|
8
|
-
}
|
|
9
|
-
if (typeof process !== "undefined") {
|
|
10
|
-
Object.assign(globalThis.env, process.env);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
1
|
// src/ServerError.ts
|
|
14
2
|
var ServerError = class _ServerError extends Error {
|
|
15
3
|
code;
|
|
@@ -34,7 +22,6 @@ var ServerError = class _ServerError extends Error {
|
|
|
34
22
|
this.message = messageStr;
|
|
35
23
|
this.status = status2;
|
|
36
24
|
}
|
|
37
|
-
// Add error codes dynamically to the global object
|
|
38
25
|
static extend(errors) {
|
|
39
26
|
for (const code in errors) {
|
|
40
27
|
const error = errors[code];
|
|
@@ -46,31 +33,9 @@ var ServerError = class _ServerError extends Error {
|
|
|
46
33
|
}
|
|
47
34
|
return errors;
|
|
48
35
|
}
|
|
49
|
-
// Dynamically added error methods from errors/index.ts
|
|
50
|
-
static NO_STORE;
|
|
51
|
-
static NO_STORE_WRITE;
|
|
52
|
-
static NO_STORE_READ;
|
|
53
|
-
static AUTH_ARGON_NEEDED;
|
|
54
|
-
static AUTH_INVALID_TYPE;
|
|
55
|
-
static AUTH_INVALID_TOKEN;
|
|
56
|
-
static AUTH_INVALID_COOKIE;
|
|
57
|
-
static AUTH_NO_PROVIDER;
|
|
58
|
-
static AUTH_INVALID_PROVIDER;
|
|
59
|
-
static AUTH_NO_SESSION;
|
|
60
|
-
static AUTH_NO_USER;
|
|
61
|
-
static LOGIN_NO_EMAIL;
|
|
62
|
-
static LOGIN_INVALID_EMAIL;
|
|
63
|
-
static LOGIN_NO_PASSWORD;
|
|
64
|
-
static LOGIN_INVALID_PASSWORD;
|
|
65
|
-
static LOGIN_WRONG_EMAIL;
|
|
66
|
-
static LOGIN_WRONG_PASSWORD;
|
|
67
|
-
static REGISTER_NO_EMAIL;
|
|
68
|
-
static REGISTER_INVALID_EMAIL;
|
|
69
|
-
static REGISTER_NO_PASSWORD;
|
|
70
|
-
static REGISTER_INVALID_PASSWORD;
|
|
71
|
-
static REGISTER_EMAIL_EXISTS;
|
|
72
36
|
};
|
|
73
|
-
var
|
|
37
|
+
var TypedServerError = ServerError;
|
|
38
|
+
var ServerError_default = TypedServerError;
|
|
74
39
|
|
|
75
40
|
// src/errors/index.ts
|
|
76
41
|
ServerError_default.extend({
|
|
@@ -78,9 +43,10 @@ ServerError_default.extend({
|
|
|
78
43
|
NO_STORE_WRITE: "You need a 'store' to write 'ctx.session.{key}'",
|
|
79
44
|
NO_STORE_READ: "You need a 'store' to read 'ctx.session.{key}'",
|
|
80
45
|
AUTH_ARGON_NEEDED: "Argon2 is needed for the auth module, please install it with 'npm i argon2'",
|
|
81
|
-
AUTH_INVALID_TYPE: "Invalid Authorization type, '{type}'",
|
|
82
46
|
AUTH_INVALID_TOKEN: "Invalid Authorization token",
|
|
83
47
|
AUTH_INVALID_COOKIE: "Invalid Authorization cookie",
|
|
48
|
+
AUTH_INVALID_HEADER: "Invalid authorization header {type}, must send 'Bearer {TOKEN}' (with space)",
|
|
49
|
+
AUTH_INVALID_STRATEGY: "Invalid Authorization type '{strategy}', valid one is '{valid}'",
|
|
84
50
|
AUTH_NO_PROVIDER: "No provider passed to the option 'auth.provider'",
|
|
85
51
|
AUTH_INVALID_PROVIDER: "Invalid provider '{provider}', valid ones are: '{valid}'",
|
|
86
52
|
AUTH_NO_SESSION: { status: 401, message: "Invalid session" },
|
|
@@ -101,126 +67,109 @@ ServerError_default.extend({
|
|
|
101
67
|
REGISTER_EMAIL_EXISTS: "Email is already registered"
|
|
102
68
|
});
|
|
103
69
|
|
|
104
|
-
// src/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const { value, path: path2, expires } = val;
|
|
115
|
-
const pathPart = `;Path=${path2 || "/"}`;
|
|
116
|
-
const expiresPart = expires ? `;Expires=${expires}` : "";
|
|
117
|
-
return `${key}=${value || ""}${pathPart}${expiresPart}`;
|
|
118
|
-
});
|
|
70
|
+
// src/polyfill.ts
|
|
71
|
+
globalThis.env = {};
|
|
72
|
+
if (typeof globalThis.Netlify !== "undefined") {
|
|
73
|
+
Object.assign(
|
|
74
|
+
globalThis.env,
|
|
75
|
+
globalThis.Netlify.env.toObject()
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (typeof process !== "undefined") {
|
|
79
|
+
Object.assign(globalThis.env, process.env);
|
|
119
80
|
}
|
|
120
81
|
|
|
121
|
-
// src/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (typeof str !== "string") str = String(str);
|
|
126
|
-
let h1 = 3735928559 ^ seed;
|
|
127
|
-
let h2 = 1103547991 ^ seed;
|
|
128
|
-
for (let i = 0, ch; i < str.length; i++) {
|
|
129
|
-
ch = str.charCodeAt(i);
|
|
130
|
-
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
131
|
-
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
132
|
-
}
|
|
133
|
-
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
134
|
-
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
135
|
-
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
136
|
-
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
137
|
-
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
138
|
-
};
|
|
139
|
-
var hash = (str, size) => {
|
|
140
|
-
let chars = "";
|
|
141
|
-
let num = cyrb53(str);
|
|
142
|
-
for (let i = 0; i < size; i++) {
|
|
143
|
-
if (num < alphabet.length) num = cyrb53(str, i);
|
|
144
|
-
chars += alphabet[num % alphabet.length];
|
|
145
|
-
num = Math.floor(num / alphabet.length);
|
|
146
|
-
}
|
|
147
|
-
return chars;
|
|
148
|
-
};
|
|
149
|
-
var randomId = (size = 16) => {
|
|
150
|
-
let id = "";
|
|
151
|
-
const bytes = random(size);
|
|
152
|
-
while (size--) {
|
|
153
|
-
id += alphabet[bytes[size] & 61];
|
|
82
|
+
// src/auth/updateUser.ts
|
|
83
|
+
async function updateUser(user, auth2, store) {
|
|
84
|
+
if (auth2.provider === "email") {
|
|
85
|
+
return await store.set(auth2.email, user);
|
|
154
86
|
}
|
|
155
|
-
return id;
|
|
156
|
-
};
|
|
157
|
-
function createId(source, size = 16) {
|
|
158
|
-
if (source) return hash(source, size);
|
|
159
|
-
return randomId(size);
|
|
160
87
|
}
|
|
161
88
|
|
|
162
|
-
// src/
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
sockets.splice(sockets.indexOf(socket), 1);
|
|
174
|
-
handlers.socket?.filter((s) => s[1] === "close")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
|
|
175
|
-
}
|
|
89
|
+
// src/auth/providers/email.ts
|
|
90
|
+
var createSession = async (user, ctx) => {
|
|
91
|
+
const { strategy, session: session2, cleanUser, redirect: redirect2 = "/user" } = ctx.options.auth;
|
|
92
|
+
user = await cleanUser(user);
|
|
93
|
+
const id = createId();
|
|
94
|
+
const provider = "email";
|
|
95
|
+
ctx.user = {
|
|
96
|
+
id,
|
|
97
|
+
strategy,
|
|
98
|
+
provider,
|
|
99
|
+
email: user.email
|
|
176
100
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (id.length !== 16) {
|
|
186
|
-
throw ServerError_default.AUTH_INVALID_TOKEN();
|
|
101
|
+
await session2.set(
|
|
102
|
+
id,
|
|
103
|
+
{ id, strategy, provider, user: user.email },
|
|
104
|
+
{ expires: "1w" }
|
|
105
|
+
);
|
|
106
|
+
if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
|
|
107
|
+
if (strategy.includes("token")) {
|
|
108
|
+
return status(201).json({ ...user, token: id });
|
|
187
109
|
}
|
|
188
|
-
|
|
189
|
-
};
|
|
190
|
-
var validateCookie = (authorization) => {
|
|
191
|
-
if (authorization.length !== 16) {
|
|
192
|
-
throw ServerError_default.AUTH_INVALID_COOKIE();
|
|
110
|
+
if (strategy.includes("cookie")) {
|
|
111
|
+
return status(302).cookies({ authentication: id }).redirect(redirect2);
|
|
193
112
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
var findSessionId = (ctx) => {
|
|
197
|
-
const type2 = ctx.options.auth.type;
|
|
198
|
-
if (type2.includes("token")) {
|
|
199
|
-
if (!ctx.headers.authorization) return;
|
|
200
|
-
return validateToken(ctx.headers.authorization);
|
|
113
|
+
if (strategy.includes("jwt")) {
|
|
114
|
+
throw new Error("JWT auth not supported yet");
|
|
201
115
|
}
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
return validateCookie(ctx.cookies.authentication);
|
|
116
|
+
if (strategy.includes("key")) {
|
|
117
|
+
throw new Error("Key auth not supported yet");
|
|
205
118
|
}
|
|
206
|
-
throw new Error(
|
|
119
|
+
throw new Error("Unknown auth type");
|
|
207
120
|
};
|
|
208
|
-
async function
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (!
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (!
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
121
|
+
async function emailLogin(ctx) {
|
|
122
|
+
const { email, password } = ctx.body;
|
|
123
|
+
if (!email) throw ServerError_default.LOGIN_NO_EMAIL();
|
|
124
|
+
if (!/@/.test(email)) throw ServerError_default.LOGIN_INVALID_EMAIL();
|
|
125
|
+
if (!password) throw ServerError_default.LOGIN_NO_PASSWORD();
|
|
126
|
+
if (password.length < 8) throw ServerError_default.LOGIN_INVALID_PASSWORD();
|
|
127
|
+
const store = ctx.options.auth.store;
|
|
128
|
+
if (!await store.has(email)) throw ServerError_default.LOGIN_WRONG_EMAIL();
|
|
129
|
+
const user = await store.get(email);
|
|
130
|
+
const isValid = await verify(password, user.password);
|
|
131
|
+
if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
|
|
132
|
+
return createSession(user, ctx);
|
|
133
|
+
}
|
|
134
|
+
async function emailRegister(ctx) {
|
|
135
|
+
const { email, password, ...data } = ctx.body;
|
|
136
|
+
if (!email) throw ServerError_default.REGISTER_NO_EMAIL();
|
|
137
|
+
if (!/@/.test(email)) throw ServerError_default.REGISTER_INVALID_EMAIL();
|
|
138
|
+
if (!password) throw ServerError_default.REGISTER_NO_PASSWORD();
|
|
139
|
+
if (password.length < 8) throw ServerError_default.REGISTER_INVALID_PASSWORD();
|
|
140
|
+
const store = ctx.options.auth.store;
|
|
141
|
+
if (await store.has(email)) throw ServerError_default.REGISTER_EMAIL_EXISTS();
|
|
142
|
+
const time = (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "");
|
|
143
|
+
const user = {
|
|
144
|
+
id: createId(email),
|
|
145
|
+
strategy: ctx.options.auth.strategy,
|
|
146
|
+
provider: "email",
|
|
147
|
+
email,
|
|
148
|
+
password: await hash(password),
|
|
149
|
+
time,
|
|
150
|
+
...data
|
|
151
|
+
};
|
|
152
|
+
await store.set(email, user);
|
|
153
|
+
return createSession(user, ctx);
|
|
154
|
+
}
|
|
155
|
+
async function emailResetPassword() {
|
|
223
156
|
}
|
|
157
|
+
async function emailUpdatePassword(ctx) {
|
|
158
|
+
const passwords = ctx.body;
|
|
159
|
+
const fullUser = await ctx.options.auth.store.get(ctx.user.email);
|
|
160
|
+
if (!fullUser) throw ServerError_default.AUTH_NO_USER();
|
|
161
|
+
const isValid = await verify(passwords.previous, fullUser.password);
|
|
162
|
+
if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
|
|
163
|
+
fullUser.password = await hash(passwords.updated);
|
|
164
|
+
await updateUser(fullUser, ctx.user, ctx.options.auth.store);
|
|
165
|
+
return 200;
|
|
166
|
+
}
|
|
167
|
+
var email_default = {
|
|
168
|
+
login: emailLogin,
|
|
169
|
+
register: emailRegister,
|
|
170
|
+
reset: emailResetPassword,
|
|
171
|
+
password: emailUpdatePassword
|
|
172
|
+
};
|
|
224
173
|
|
|
225
174
|
// src/reply.ts
|
|
226
175
|
var Reply = class {
|
|
@@ -337,112 +286,6 @@ var file = (...args) => new Reply().file(...args);
|
|
|
337
286
|
var redirect = (...args) => new Reply().redirect(...args);
|
|
338
287
|
var view = (...args) => new Reply().view(...args);
|
|
339
288
|
|
|
340
|
-
// src/auth/logout.ts
|
|
341
|
-
async function logout(ctx) {
|
|
342
|
-
const { id, type: type2 } = ctx.auth;
|
|
343
|
-
await ctx.options.auth.session.del(id);
|
|
344
|
-
if (type2.includes("token")) {
|
|
345
|
-
return { token: null };
|
|
346
|
-
}
|
|
347
|
-
if (type2.includes("cookie")) {
|
|
348
|
-
return cookies({ authorization: null }).redirect("/");
|
|
349
|
-
}
|
|
350
|
-
if (type2.includes("jwt")) {
|
|
351
|
-
throw new Error("JWT auth not supported yet");
|
|
352
|
-
}
|
|
353
|
-
if (type2.includes("key")) {
|
|
354
|
-
throw new Error("Key auth not supported yet");
|
|
355
|
-
}
|
|
356
|
-
throw new Error("Unknown auth type");
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/auth/updateUser.ts
|
|
360
|
-
async function updateUser(user2, auth3, store) {
|
|
361
|
-
if (auth3.provider === "email") {
|
|
362
|
-
return await store.set(auth3.email, user2);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// src/auth/providers/email.ts
|
|
367
|
-
var createSession = async (user2, ctx) => {
|
|
368
|
-
const { type: type2, session: session2, cleanUser, redirect: redirect2 = "/user" } = ctx.options.auth;
|
|
369
|
-
user2 = cleanUser(user2);
|
|
370
|
-
const id = createId();
|
|
371
|
-
const provider = "email";
|
|
372
|
-
const time = (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "");
|
|
373
|
-
ctx.auth = {
|
|
374
|
-
id,
|
|
375
|
-
type: type2,
|
|
376
|
-
provider,
|
|
377
|
-
user: user2.email,
|
|
378
|
-
email: user2.email,
|
|
379
|
-
time
|
|
380
|
-
};
|
|
381
|
-
await session2.set(id, ctx.auth, { expires: "1w" });
|
|
382
|
-
if (type2.includes("token")) {
|
|
383
|
-
return status(201).json({ ...user2, token: id });
|
|
384
|
-
}
|
|
385
|
-
if (type2.includes("cookie")) {
|
|
386
|
-
return status(302).cookies({ authentication: id }).redirect(redirect2);
|
|
387
|
-
}
|
|
388
|
-
if (type2.includes("jwt")) {
|
|
389
|
-
throw new Error("JWT auth not supported yet");
|
|
390
|
-
}
|
|
391
|
-
if (type2.includes("key")) {
|
|
392
|
-
throw new Error("Key auth not supported yet");
|
|
393
|
-
}
|
|
394
|
-
throw new Error("Unknown auth type");
|
|
395
|
-
};
|
|
396
|
-
async function emailLogin(ctx) {
|
|
397
|
-
const { email, password } = ctx.body;
|
|
398
|
-
if (!email) throw ServerError_default.LOGIN_NO_EMAIL();
|
|
399
|
-
if (!/@/.test(email)) throw ServerError_default.LOGIN_INVALID_EMAIL();
|
|
400
|
-
if (!password) throw ServerError_default.LOGIN_NO_PASSWORD();
|
|
401
|
-
if (password.length < 8) throw ServerError_default.LOGIN_INVALID_PASSWORD();
|
|
402
|
-
const store = ctx.options.auth.store;
|
|
403
|
-
if (!await store.has(email)) throw ServerError_default.LOGIN_WRONG_EMAIL();
|
|
404
|
-
const user2 = await store.get(email);
|
|
405
|
-
const isValid = await verify(password, user2.password);
|
|
406
|
-
if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
|
|
407
|
-
return createSession(user2, ctx);
|
|
408
|
-
}
|
|
409
|
-
async function emailRegister(ctx) {
|
|
410
|
-
const { email, password, ...data } = ctx.body;
|
|
411
|
-
if (!email) throw ServerError_default.REGISTER_NO_EMAIL();
|
|
412
|
-
if (!/@/.test(email)) throw ServerError_default.REGISTER_INVALID_EMAIL();
|
|
413
|
-
if (!password) throw ServerError_default.REGISTER_NO_PASSWORD();
|
|
414
|
-
if (password.length < 8) throw ServerError_default.REGISTER_INVALID_PASSWORD();
|
|
415
|
-
const store = ctx.options.auth.store;
|
|
416
|
-
if (await store.has(email)) throw ServerError_default.REGISTER_EMAIL_EXISTS();
|
|
417
|
-
const time = (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "");
|
|
418
|
-
const user2 = {
|
|
419
|
-
id: createId(email),
|
|
420
|
-
email,
|
|
421
|
-
password: await hash2(password),
|
|
422
|
-
time,
|
|
423
|
-
...data
|
|
424
|
-
};
|
|
425
|
-
await store.set(email, user2);
|
|
426
|
-
return createSession(user2, ctx);
|
|
427
|
-
}
|
|
428
|
-
async function emailResetPassword() {
|
|
429
|
-
}
|
|
430
|
-
async function emailUpdatePassword(ctx) {
|
|
431
|
-
const { previous, updated } = ctx.body;
|
|
432
|
-
const fullUser = await ctx.options.auth.store.get(ctx.auth.user);
|
|
433
|
-
const isValid = await verify(previous, fullUser.password);
|
|
434
|
-
if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
|
|
435
|
-
fullUser.password = await hash2(updated);
|
|
436
|
-
await updateUser(fullUser, ctx.auth, ctx.options.auth.store);
|
|
437
|
-
return 200;
|
|
438
|
-
}
|
|
439
|
-
var email_default = {
|
|
440
|
-
login: emailLogin,
|
|
441
|
-
register: emailRegister,
|
|
442
|
-
reset: emailResetPassword,
|
|
443
|
-
password: emailUpdatePassword
|
|
444
|
-
};
|
|
445
|
-
|
|
446
289
|
// src/auth/providers/github.ts
|
|
447
290
|
var oauth = async (code) => {
|
|
448
291
|
const fch = async (url, { body, headers: headers2 = {}, ...rest } = {}) => {
|
|
@@ -481,17 +324,17 @@ var getUserProfile = async (code) => {
|
|
|
481
324
|
return { ...profile, email };
|
|
482
325
|
};
|
|
483
326
|
var callback = async (ctx) => {
|
|
484
|
-
const {
|
|
327
|
+
const { strategy, cleanUser, store, session: session2, redirect: redirect2 } = ctx.options.auth;
|
|
485
328
|
const profile = await getUserProfile(ctx.url.query.code);
|
|
486
|
-
const
|
|
329
|
+
const auth2 = {
|
|
487
330
|
id: createId(),
|
|
488
|
-
|
|
331
|
+
strategy,
|
|
489
332
|
provider: "github",
|
|
490
333
|
user: createId(profile.email),
|
|
491
334
|
email: profile.email,
|
|
492
335
|
time: (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "")
|
|
493
336
|
};
|
|
494
|
-
const
|
|
337
|
+
const user = cleanUser({
|
|
495
338
|
id: profile.id,
|
|
496
339
|
name: profile.name,
|
|
497
340
|
email: profile.email,
|
|
@@ -499,18 +342,18 @@ var callback = async (ctx) => {
|
|
|
499
342
|
location: profile.location,
|
|
500
343
|
created: profile.created_at
|
|
501
344
|
});
|
|
502
|
-
await store.set(
|
|
503
|
-
await session2.set(
|
|
504
|
-
if (
|
|
505
|
-
return status(201).json({ ...
|
|
345
|
+
await store.set(auth2.user, user);
|
|
346
|
+
await session2.set(auth2.id, auth2, { expires: "1w" });
|
|
347
|
+
if (auth2.strategy.includes("token")) {
|
|
348
|
+
return status(201).json({ ...user, token: auth2.id });
|
|
506
349
|
}
|
|
507
|
-
if (
|
|
508
|
-
return status(302).cookies({ authentication:
|
|
350
|
+
if (auth2.strategy.includes("cookie")) {
|
|
351
|
+
return status(302).cookies({ authentication: auth2.id }).redirect(redirect2);
|
|
509
352
|
}
|
|
510
|
-
if (
|
|
353
|
+
if (auth2.strategy.includes("jwt")) {
|
|
511
354
|
throw new Error("JWT auth not supported yet");
|
|
512
355
|
}
|
|
513
|
-
if (
|
|
356
|
+
if (auth2.strategy.includes("key")) {
|
|
514
357
|
throw new Error("Key auth not supported yet");
|
|
515
358
|
}
|
|
516
359
|
throw new Error("Unknown auth type");
|
|
@@ -520,149 +363,53 @@ var github_default = { login, callback };
|
|
|
520
363
|
// src/auth/providers/index.ts
|
|
521
364
|
var providers_default = { email: email_default, github: github_default };
|
|
522
365
|
|
|
523
|
-
// src/auth/
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (target[key]) return target[key];
|
|
530
|
-
if (key === "then") return target[key];
|
|
531
|
-
if (typeof key === "symbol") return target[key];
|
|
532
|
-
throw ServerError_default.NO_STORE_READ({ key: String(key) });
|
|
533
|
-
},
|
|
534
|
-
set(target, key, value) {
|
|
535
|
-
if (target[key] || key === "then" || typeof key === "symbol") {
|
|
536
|
-
target[key] = value;
|
|
537
|
-
return true;
|
|
538
|
-
} else {
|
|
539
|
-
throw ServerError_default.NO_STORE_WRITE({ key: String(key) });
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// src/auth/session.ts
|
|
546
|
-
async function session(ctx) {
|
|
547
|
-
const store = ctx.options.session?.store;
|
|
548
|
-
if (!store) return createNoSession();
|
|
549
|
-
if (ctx.cookies.session) {
|
|
550
|
-
const session2 = await store.get(ctx.cookies.session);
|
|
551
|
-
if (session2) return session2;
|
|
552
|
-
}
|
|
553
|
-
return {};
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// src/auth/user.ts
|
|
557
|
-
async function user(ctx) {
|
|
558
|
-
if (!ctx.auth) return;
|
|
559
|
-
const user2 = await ctx.options.auth.store.get(ctx.auth.user);
|
|
560
|
-
if (!user2) throw ServerError_default.AUTH_NO_USER();
|
|
561
|
-
return ctx.options.auth.cleanUser(user2);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// src/auth/index.ts
|
|
565
|
-
var parseOptions = (auth3, all) => {
|
|
566
|
-
if (!auth3) return null;
|
|
567
|
-
if (typeof auth3 === "string") {
|
|
568
|
-
const [type2, provider] = auth3.split(":");
|
|
569
|
-
auth3 = { type: type2, provider };
|
|
366
|
+
// src/auth/parseAuthOptions.ts
|
|
367
|
+
function parseAuthOptions(auth2, all) {
|
|
368
|
+
if (!auth2) return null;
|
|
369
|
+
if (typeof auth2 === "string") {
|
|
370
|
+
const [strategy, provider] = auth2.split(":");
|
|
371
|
+
auth2 = { strategy, provider };
|
|
570
372
|
}
|
|
571
|
-
if (typeof
|
|
572
|
-
|
|
373
|
+
if (typeof auth2.provider === "string") {
|
|
374
|
+
auth2.provider = auth2.provider.split("|").filter(Boolean);
|
|
573
375
|
}
|
|
574
|
-
if (
|
|
575
|
-
|
|
376
|
+
if (!auth2.strategy) {
|
|
377
|
+
throw new Error("Auth options needs a strategy");
|
|
576
378
|
}
|
|
577
|
-
if (!
|
|
578
|
-
throw new Error("Auth options needs a
|
|
379
|
+
if (!auth2.strategy.length) {
|
|
380
|
+
throw new Error("Auth options needs a strategy");
|
|
579
381
|
}
|
|
580
|
-
if (!
|
|
581
|
-
throw new Error("Auth options needs a type");
|
|
582
|
-
}
|
|
583
|
-
if (!auth3.provider || !auth3.provider.length) {
|
|
382
|
+
if (!auth2.provider || !auth2.provider.length) {
|
|
584
383
|
throw new Error("Auth options needs a provider");
|
|
585
384
|
}
|
|
586
|
-
const providerNotFound =
|
|
385
|
+
const providerNotFound = auth2.provider.find((p) => !providers_default[p]);
|
|
587
386
|
if (providerNotFound) {
|
|
588
387
|
throw new Error(
|
|
589
388
|
`Provider "${providerNotFound}" not found, available ones are "${Object.keys(providers_default).join('", "')}"`
|
|
590
389
|
);
|
|
591
390
|
}
|
|
592
|
-
if (!
|
|
593
|
-
|
|
391
|
+
if (!auth2.session && all.store) {
|
|
392
|
+
auth2.session = all.store.prefix("auth:");
|
|
594
393
|
}
|
|
595
|
-
if (!
|
|
596
|
-
|
|
394
|
+
if (!auth2.store && all.store) {
|
|
395
|
+
auth2.store = all.store.prefix("user:");
|
|
597
396
|
}
|
|
598
|
-
if (!
|
|
599
|
-
|
|
600
|
-
const { password: _password, ...
|
|
601
|
-
return
|
|
397
|
+
if (!auth2.cleanUser) {
|
|
398
|
+
auth2.cleanUser = (fullUser) => {
|
|
399
|
+
const { password: _password, ...user } = fullUser;
|
|
400
|
+
return user;
|
|
602
401
|
};
|
|
603
402
|
}
|
|
604
|
-
if (!
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
return auth3;
|
|
608
|
-
};
|
|
609
|
-
var load = async (ctx) => {
|
|
610
|
-
ctx.session = await session(ctx);
|
|
611
|
-
ctx.auth = await auth(ctx);
|
|
612
|
-
ctx.user = await user(ctx);
|
|
613
|
-
};
|
|
614
|
-
var middle = async (ctx) => {
|
|
615
|
-
if (ctx.options.auth) {
|
|
616
|
-
if (ctx.options.auth.provider.includes("github")) {
|
|
617
|
-
if (!env.GITHUB_ID) throw new Error("GITHUB_ID not defined");
|
|
618
|
-
if (!env.GITHUB_SECRET) throw new Error("GITHUB_SECRET not defined");
|
|
619
|
-
ctx.app.get(
|
|
620
|
-
"/auth/logout",
|
|
621
|
-
{ tags: "Auth", title: "Github logout" },
|
|
622
|
-
logout
|
|
623
|
-
);
|
|
624
|
-
ctx.app.get(
|
|
625
|
-
"/auth/login/github",
|
|
626
|
-
{ tags: "Auth" },
|
|
627
|
-
providers_default.github.login
|
|
628
|
-
);
|
|
629
|
-
ctx.app.get(
|
|
630
|
-
"/auth/callback/github",
|
|
631
|
-
{ tags: "Auth", title: "Github callback" },
|
|
632
|
-
providers_default.github.callback
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
if (ctx.options.auth.provider.includes("email")) {
|
|
636
|
-
ctx.app.post("/auth/logout", { tags: "Auth" }, logout);
|
|
637
|
-
ctx.app.post(
|
|
638
|
-
"/auth/register/email",
|
|
639
|
-
{ tags: "Auth" },
|
|
640
|
-
providers_default.email.register
|
|
641
|
-
);
|
|
642
|
-
ctx.app.post(
|
|
643
|
-
"/auth/login/email",
|
|
644
|
-
{ tags: "Auth" },
|
|
645
|
-
providers_default.email.login
|
|
646
|
-
);
|
|
647
|
-
ctx.app.put(
|
|
648
|
-
"/auth/password/email",
|
|
649
|
-
{ tags: "Auth" },
|
|
650
|
-
providers_default.email.password
|
|
651
|
-
);
|
|
652
|
-
ctx.app.put(
|
|
653
|
-
"/auth/reset/email",
|
|
654
|
-
{ tags: "Auth" },
|
|
655
|
-
providers_default.email.reset
|
|
656
|
-
);
|
|
657
|
-
}
|
|
403
|
+
if (!auth2.redirect) {
|
|
404
|
+
auth2.redirect = "/user";
|
|
658
405
|
}
|
|
659
|
-
|
|
660
|
-
|
|
406
|
+
return auth2;
|
|
407
|
+
}
|
|
661
408
|
|
|
662
409
|
// src/helpers/bucket.ts
|
|
663
410
|
import * as fs from "fs";
|
|
664
|
-
import * as path from "path";
|
|
665
411
|
import * as fsp from "fs/promises";
|
|
412
|
+
import * as path from "path";
|
|
666
413
|
function thinLocalBucket(root) {
|
|
667
414
|
const absolute = (name) => {
|
|
668
415
|
if (!name) throw new Error("File name is required");
|
|
@@ -737,6 +484,47 @@ function bucket_default(root) {
|
|
|
737
484
|
return root;
|
|
738
485
|
}
|
|
739
486
|
|
|
487
|
+
// src/helpers/createId.ts
|
|
488
|
+
var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
|
|
489
|
+
var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
|
|
490
|
+
var cyrb53 = (str, seed = 0) => {
|
|
491
|
+
if (typeof str !== "string") str = String(str);
|
|
492
|
+
let h1 = 3735928559 ^ seed;
|
|
493
|
+
let h2 = 1103547991 ^ seed;
|
|
494
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
495
|
+
ch = str.charCodeAt(i);
|
|
496
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
497
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
498
|
+
}
|
|
499
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
500
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
501
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
502
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
503
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
504
|
+
};
|
|
505
|
+
var hash2 = (str, size) => {
|
|
506
|
+
let chars = "";
|
|
507
|
+
let num = cyrb53(str);
|
|
508
|
+
for (let i = 0; i < size; i++) {
|
|
509
|
+
if (num < alphabet.length) num = cyrb53(str, i);
|
|
510
|
+
chars += alphabet[num % alphabet.length];
|
|
511
|
+
num = Math.floor(num / alphabet.length);
|
|
512
|
+
}
|
|
513
|
+
return chars;
|
|
514
|
+
};
|
|
515
|
+
var randomId = (size = 16) => {
|
|
516
|
+
let id = "";
|
|
517
|
+
const bytes = random(size);
|
|
518
|
+
while (size--) {
|
|
519
|
+
id += alphabet[bytes[size] & 61];
|
|
520
|
+
}
|
|
521
|
+
return id;
|
|
522
|
+
};
|
|
523
|
+
function createId(source, size = 16) {
|
|
524
|
+
if (source) return hash2(source, size);
|
|
525
|
+
return randomId(size);
|
|
526
|
+
}
|
|
527
|
+
|
|
740
528
|
// src/helpers/color.ts
|
|
741
529
|
var map = {
|
|
742
530
|
reset: 0,
|
|
@@ -846,7 +634,9 @@ function config(options = {}) {
|
|
|
846
634
|
(session2) => session2?.store?.name || "working",
|
|
847
635
|
"\u{1F510}"
|
|
848
636
|
);
|
|
849
|
-
|
|
637
|
+
if (options.auth || env2.AUTH) {
|
|
638
|
+
settings.auth = parseAuthOptions(options.auth || env2.AUTH || null, options);
|
|
639
|
+
}
|
|
850
640
|
if (options.openapi) {
|
|
851
641
|
if (options.openapi === true) {
|
|
852
642
|
settings.openapi = {};
|
|
@@ -869,6 +659,40 @@ function cors(config2, origin = "") {
|
|
|
869
659
|
return null;
|
|
870
660
|
}
|
|
871
661
|
|
|
662
|
+
// src/helpers/createCookies.ts
|
|
663
|
+
function createCookies(cookies2) {
|
|
664
|
+
if (!cookies2 || !Object.keys(cookies2).length) return [];
|
|
665
|
+
return Object.entries(cookies2).map(([key, val]) => {
|
|
666
|
+
if (!val) {
|
|
667
|
+
val = { value: "", expires: (/* @__PURE__ */ new Date(0)).toUTCString() };
|
|
668
|
+
}
|
|
669
|
+
if (typeof val === "string") {
|
|
670
|
+
val = { value: val };
|
|
671
|
+
}
|
|
672
|
+
const { value, path: path2, expires } = val;
|
|
673
|
+
const pathPart = `;Path=${path2 || "/"}`;
|
|
674
|
+
const expiresPart = expires ? `;Expires=${expires}` : "";
|
|
675
|
+
return `${key}=${value || ""}${pathPart}${expiresPart}`;
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/helpers/createWebsocket.ts
|
|
680
|
+
function createWebsocket(sockets, handlers) {
|
|
681
|
+
return {
|
|
682
|
+
message: async (socket, body) => {
|
|
683
|
+
handlers.socket?.filter((s) => s[1] === "message")?.map((s) => s[2]({ socket, sockets, body }));
|
|
684
|
+
},
|
|
685
|
+
open: (socket) => {
|
|
686
|
+
sockets.push(socket);
|
|
687
|
+
handlers.socket?.filter((s) => s[1] === "open")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
|
|
688
|
+
},
|
|
689
|
+
close: (socket) => {
|
|
690
|
+
sockets.splice(sockets.indexOf(socket), 1);
|
|
691
|
+
handlers.socket?.filter((s) => s[1] === "close")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
872
696
|
// src/helpers/define.ts
|
|
873
697
|
function define(obj, key, cb) {
|
|
874
698
|
Object.defineProperty(obj, key, {
|
|
@@ -990,6 +814,7 @@ async function parseResponse(out, ctx) {
|
|
|
990
814
|
|
|
991
815
|
// src/pathPattern.ts
|
|
992
816
|
function pathPattern(pattern, path2) {
|
|
817
|
+
if (pattern === "*" && path2 === "/") return {};
|
|
993
818
|
pattern = `/${pattern.replace(/^\//, "")}`;
|
|
994
819
|
pattern = pattern.replace(/\/$/, "") || "/";
|
|
995
820
|
path2 = path2.replace(/\/$/, "") || "/";
|
|
@@ -1062,7 +887,9 @@ function validate(ctx, schema) {
|
|
|
1062
887
|
}
|
|
1063
888
|
} catch (error) {
|
|
1064
889
|
if (error.name === "ZodError" || error.constructor.name === "ZodError") {
|
|
1065
|
-
const message = error.issues.map(
|
|
890
|
+
const message = error.issues.map(
|
|
891
|
+
({ path: path2, message: message2 }) => `[${base}.${path2.join(".")}]: ${message2}`
|
|
892
|
+
).sort().join("\n");
|
|
1066
893
|
throw new StatusError(message, 422);
|
|
1067
894
|
}
|
|
1068
895
|
throw error;
|
|
@@ -1072,9 +899,6 @@ function validate(ctx, schema) {
|
|
|
1072
899
|
// src/helpers/handleRequest.ts
|
|
1073
900
|
async function handleRequest(handlers, ctx) {
|
|
1074
901
|
try {
|
|
1075
|
-
if (ctx.error) {
|
|
1076
|
-
throw ctx.error;
|
|
1077
|
-
}
|
|
1078
902
|
for (const [method, matcher, ...cbs] of handlers[ctx.method]) {
|
|
1079
903
|
const match = pathPattern(matcher, ctx.url.pathname || "/");
|
|
1080
904
|
if (!match) continue;
|
|
@@ -1090,7 +914,7 @@ async function handleRequest(handlers, ctx) {
|
|
|
1090
914
|
}
|
|
1091
915
|
if (method !== "*") break;
|
|
1092
916
|
}
|
|
1093
|
-
if (ctx.
|
|
917
|
+
if (ctx.platform.provider === "netlify") return;
|
|
1094
918
|
return new Response("Not Found", { status: 404 });
|
|
1095
919
|
} catch (error) {
|
|
1096
920
|
return new Response(error.message || "", { status: error.status || 500 });
|
|
@@ -1101,7 +925,7 @@ async function handleRequest(handlers, ctx) {
|
|
|
1101
925
|
import * as crypto2 from "crypto";
|
|
1102
926
|
import { getRandomValues } from "crypto";
|
|
1103
927
|
import { promisify } from "util";
|
|
1104
|
-
async function
|
|
928
|
+
async function hash(password) {
|
|
1105
929
|
if ("argon2" in crypto2) {
|
|
1106
930
|
const argon23 = promisify(crypto2.argon2);
|
|
1107
931
|
const buf = await argon23("argon2id", {
|
|
@@ -1131,18 +955,6 @@ async function iterate(stream, cb) {
|
|
|
1131
955
|
}
|
|
1132
956
|
}
|
|
1133
957
|
|
|
1134
|
-
// src/helpers/iteratorToReadable.ts
|
|
1135
|
-
function iteratorToReadable(generator) {
|
|
1136
|
-
return new ReadableStream({
|
|
1137
|
-
async start(controller) {
|
|
1138
|
-
for await (const chunk of generator) {
|
|
1139
|
-
controller.enqueue(chunk);
|
|
1140
|
-
}
|
|
1141
|
-
controller.close();
|
|
1142
|
-
}
|
|
1143
|
-
});
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
958
|
// src/helpers/iteratorAsyncToReadable.ts
|
|
1147
959
|
function iteratorAsyncToReadable(asyncGenerator) {
|
|
1148
960
|
return new ReadableStream({
|
|
@@ -1165,6 +977,116 @@ function iteratorAsyncToReadable(asyncGenerator) {
|
|
|
1165
977
|
});
|
|
1166
978
|
}
|
|
1167
979
|
|
|
980
|
+
// src/helpers/iteratorToReadable.ts
|
|
981
|
+
function iteratorToReadable(generator) {
|
|
982
|
+
return new ReadableStream({
|
|
983
|
+
async start(controller) {
|
|
984
|
+
for await (const chunk of generator) {
|
|
985
|
+
controller.enqueue(chunk);
|
|
986
|
+
}
|
|
987
|
+
controller.close();
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// src/helpers/parseBody.ts
|
|
993
|
+
function getBoundary(header) {
|
|
994
|
+
if (!header) return null;
|
|
995
|
+
if (header.includes("multipart/form-data") && !header.includes("boundary=")) {
|
|
996
|
+
console.error("Do not set the `Content-Type` manually for FormData");
|
|
997
|
+
}
|
|
998
|
+
const items = header.split(";");
|
|
999
|
+
for (const item of items) {
|
|
1000
|
+
const trimmedItem = item.trim();
|
|
1001
|
+
if (trimmedItem.startsWith("boundary=")) {
|
|
1002
|
+
return trimmedItem.split("=")[1].trim();
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
function getMatching(string, regex) {
|
|
1008
|
+
const matches = string.match(regex);
|
|
1009
|
+
return matches?.[1] ?? "";
|
|
1010
|
+
}
|
|
1011
|
+
var saveFile = async (name, value, bucket) => {
|
|
1012
|
+
const ext = name.split(".").pop();
|
|
1013
|
+
const id = `${createId()}.${ext}`;
|
|
1014
|
+
await bucket.write(id, value);
|
|
1015
|
+
return id;
|
|
1016
|
+
};
|
|
1017
|
+
function splitBuffer(buffer, delimiter) {
|
|
1018
|
+
const result = [];
|
|
1019
|
+
let start = 0;
|
|
1020
|
+
let index = buffer.indexOf(delimiter);
|
|
1021
|
+
while (index !== -1) {
|
|
1022
|
+
result.push(buffer.slice(start, index));
|
|
1023
|
+
start = index + delimiter.length;
|
|
1024
|
+
index = buffer.indexOf(delimiter, start);
|
|
1025
|
+
}
|
|
1026
|
+
result.push(buffer.slice(start));
|
|
1027
|
+
return result;
|
|
1028
|
+
}
|
|
1029
|
+
var BREAK_BUFFER = Buffer.from("\r\n\r\n");
|
|
1030
|
+
function isProbablyText(buffer) {
|
|
1031
|
+
for (let i = 0; i < Math.min(buffer.length, 512); i++) {
|
|
1032
|
+
const byte = buffer[i];
|
|
1033
|
+
if (byte === 0) return false;
|
|
1034
|
+
if (byte < 7 || byte > 13 && byte < 32) return false;
|
|
1035
|
+
}
|
|
1036
|
+
return true;
|
|
1037
|
+
}
|
|
1038
|
+
async function parseBody(raw, contentType, bucket) {
|
|
1039
|
+
const contentTypeStr = Array.isArray(contentType) ? contentType[0] : contentType;
|
|
1040
|
+
if (!raw) return {};
|
|
1041
|
+
if (!contentTypeStr || /^text\//.test(contentTypeStr)) {
|
|
1042
|
+
return raw.toString("utf-8");
|
|
1043
|
+
}
|
|
1044
|
+
if (/application\/json/.test(contentTypeStr)) {
|
|
1045
|
+
return JSON.parse(raw.toString("utf-8"));
|
|
1046
|
+
}
|
|
1047
|
+
const boundary = getBoundary(contentTypeStr);
|
|
1048
|
+
if (!boundary) return null;
|
|
1049
|
+
const body = {};
|
|
1050
|
+
const boundaryBuffer = Buffer.from(`--${boundary}`);
|
|
1051
|
+
const parts = splitBuffer(raw, boundaryBuffer);
|
|
1052
|
+
for (const part of parts) {
|
|
1053
|
+
if (part.length === 0 || part.equals(Buffer.from("--\r\n"))) continue;
|
|
1054
|
+
const idx = part.indexOf(BREAK_BUFFER);
|
|
1055
|
+
if (idx === -1) continue;
|
|
1056
|
+
const headerStr = part.slice(0, idx).toString("utf-8");
|
|
1057
|
+
const contentBuf = part.slice(idx + BREAK_BUFFER.length, part.length - 2);
|
|
1058
|
+
const name = getMatching(headerStr, /name="(.+?)"/).trim().replace(/\[\]$/, "");
|
|
1059
|
+
if (!name) continue;
|
|
1060
|
+
const filename = getMatching(headerStr, /filename="(.+?)"/).trim();
|
|
1061
|
+
if (filename) {
|
|
1062
|
+
if (!bucket) throw new Error("Bucket is required to save files");
|
|
1063
|
+
body[name] = await saveFile(filename, contentBuf, bucket);
|
|
1064
|
+
} else {
|
|
1065
|
+
const value = isProbablyText(contentBuf) ? contentBuf.toString("utf-8").trim() : contentBuf;
|
|
1066
|
+
if (body[name]) {
|
|
1067
|
+
if (!Array.isArray(body[name])) body[name] = [body[name]];
|
|
1068
|
+
body[name].push(value);
|
|
1069
|
+
} else {
|
|
1070
|
+
body[name] = value;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return body;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// src/helpers/parseCookies.ts
|
|
1078
|
+
function parseCookies(cookies2) {
|
|
1079
|
+
if (!cookies2) return {};
|
|
1080
|
+
const cookieStr = Array.isArray(cookies2) ? cookies2[0] : cookies2;
|
|
1081
|
+
if (!cookieStr) return {};
|
|
1082
|
+
return Object.fromEntries(
|
|
1083
|
+
cookieStr.split(/;\s*/).map((part) => {
|
|
1084
|
+
const [key, ...rest] = part.split("=");
|
|
1085
|
+
return [key, decodeURIComponent(rest.join("="))];
|
|
1086
|
+
})
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1168
1090
|
// src/helpers/parseHeaders.ts
|
|
1169
1091
|
var parseHeaders_default = (raw) => {
|
|
1170
1092
|
const headers2 = {};
|
|
@@ -1328,34 +1250,117 @@ async function verify(password, hash3) {
|
|
|
1328
1250
|
});
|
|
1329
1251
|
}
|
|
1330
1252
|
|
|
1331
|
-
// src/
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
if (
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1253
|
+
// src/auth/findSessionId.ts
|
|
1254
|
+
var validateToken = (authorization) => {
|
|
1255
|
+
const [type2, id] = authorization.trim().split(" ");
|
|
1256
|
+
if (type2.toLowerCase() !== "bearer") {
|
|
1257
|
+
throw ServerError_default.AUTH_INVALID_HEADER({ type: type2 });
|
|
1258
|
+
}
|
|
1259
|
+
if (id.length !== 16) {
|
|
1260
|
+
throw ServerError_default.AUTH_INVALID_TOKEN();
|
|
1261
|
+
}
|
|
1262
|
+
return id;
|
|
1263
|
+
};
|
|
1264
|
+
var validateCookie = (authorization) => {
|
|
1265
|
+
if (authorization.length !== 16) {
|
|
1266
|
+
throw ServerError_default.AUTH_INVALID_COOKIE();
|
|
1267
|
+
}
|
|
1268
|
+
return authorization;
|
|
1269
|
+
};
|
|
1270
|
+
function findSessionId(ctx) {
|
|
1271
|
+
const strategy = ctx.options.auth.strategy;
|
|
1272
|
+
if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
|
|
1273
|
+
if (strategy.includes("token")) {
|
|
1274
|
+
if (!ctx.headers.authorization) return;
|
|
1275
|
+
return validateToken(ctx.headers.authorization);
|
|
1276
|
+
}
|
|
1277
|
+
if (strategy.includes("cookie")) {
|
|
1278
|
+
if (!ctx.cookies.authentication) return;
|
|
1279
|
+
return validateCookie(ctx.cookies.authentication);
|
|
1341
1280
|
}
|
|
1281
|
+
throw new Error(`Invalid auth type "${strategy}"`);
|
|
1342
1282
|
}
|
|
1343
1283
|
|
|
1344
|
-
// src/
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
};
|
|
1357
|
-
|
|
1358
|
-
|
|
1284
|
+
// src/auth/getUser.ts
|
|
1285
|
+
async function getUser(ctx) {
|
|
1286
|
+
if (!ctx.options.auth) return;
|
|
1287
|
+
const options = ctx.options.auth;
|
|
1288
|
+
const sessionId = findSessionId(ctx);
|
|
1289
|
+
if (!sessionId) return;
|
|
1290
|
+
const auth2 = await options.session.get(sessionId);
|
|
1291
|
+
if (!auth2) return;
|
|
1292
|
+
if (options.strategy !== auth2.strategy) {
|
|
1293
|
+
throw ServerError_default.AUTH_INVALID_STRATEGY({
|
|
1294
|
+
strategy: auth2.strategy || "undefined",
|
|
1295
|
+
valid: options.strategy
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
if (!options.provider.includes(auth2.provider)) {
|
|
1299
|
+
throw ServerError_default.AUTH_INVALID_PROVIDER({
|
|
1300
|
+
provider: auth2.provider,
|
|
1301
|
+
valid: options.provider
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
const user = await ctx.options.auth.store.get(auth2.user);
|
|
1305
|
+
if (!user) throw ServerError_default.AUTH_NO_USER();
|
|
1306
|
+
user.strategy = auth2.strategy;
|
|
1307
|
+
user.provider = auth2.provider;
|
|
1308
|
+
return ctx.options.auth.cleanUser(user);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/auth/logout.ts
|
|
1312
|
+
async function logout(ctx) {
|
|
1313
|
+
const session2 = findSessionId(ctx);
|
|
1314
|
+
const { strategy } = ctx.user;
|
|
1315
|
+
await ctx.options.auth.session.del(session2);
|
|
1316
|
+
if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
|
|
1317
|
+
if (strategy.includes("token")) {
|
|
1318
|
+
return { token: null };
|
|
1319
|
+
}
|
|
1320
|
+
if (strategy.includes("cookie")) {
|
|
1321
|
+
return cookies({ authorization: null }).redirect("/");
|
|
1322
|
+
}
|
|
1323
|
+
if (strategy.includes("jwt")) {
|
|
1324
|
+
throw new Error("JWT auth not supported yet");
|
|
1325
|
+
}
|
|
1326
|
+
if (strategy.includes("key")) {
|
|
1327
|
+
throw new Error("Key auth not supported yet");
|
|
1328
|
+
}
|
|
1329
|
+
throw new Error("Unknown auth type");
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// src/auth/index.ts
|
|
1333
|
+
function auth(app) {
|
|
1334
|
+
app.use(async function middle(ctx) {
|
|
1335
|
+
ctx.user = await getUser(ctx);
|
|
1336
|
+
});
|
|
1337
|
+
if (app.settings.auth.provider.includes("github")) {
|
|
1338
|
+
if (!env.GITHUB_ID) throw new Error("GITHUB_ID not defined");
|
|
1339
|
+
if (!env.GITHUB_SECRET) throw new Error("GITHUB_SECRET not defined");
|
|
1340
|
+
app.get("/auth/logout", logout);
|
|
1341
|
+
app.get("/auth/login/github", providers_default.github.login);
|
|
1342
|
+
app.get("/auth/callback/github", providers_default.github.callback);
|
|
1343
|
+
}
|
|
1344
|
+
if (app.settings.auth.provider.includes("email")) {
|
|
1345
|
+
app.post("/auth/logout", logout);
|
|
1346
|
+
app.post("/auth/register/email", providers_default.email.register);
|
|
1347
|
+
app.post("/auth/login/email", providers_default.email.login);
|
|
1348
|
+
app.put("/auth/password/email", providers_default.email.password);
|
|
1349
|
+
app.put("/auth/reset/email", providers_default.email.reset);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// src/middle/assets.ts
|
|
1354
|
+
async function assets(ctx) {
|
|
1355
|
+
if (!ctx.options.public) return;
|
|
1356
|
+
if (ctx.method !== "get") return;
|
|
1357
|
+
if (ctx.url.pathname === "/") return;
|
|
1358
|
+
try {
|
|
1359
|
+
const asset = await ctx.options.public.read(ctx.url.pathname);
|
|
1360
|
+
if (!asset) return;
|
|
1361
|
+
return type(ctx.url.pathname.split(".").pop()).send(asset);
|
|
1362
|
+
} catch {
|
|
1363
|
+
}
|
|
1359
1364
|
}
|
|
1360
1365
|
|
|
1361
1366
|
// src/middle/openapi.ts
|
|
@@ -1520,10 +1525,205 @@ var openapi_default = async (ctx) => {
|
|
|
1520
1525
|
</html> `;
|
|
1521
1526
|
};
|
|
1522
1527
|
|
|
1523
|
-
// src/middle/
|
|
1524
|
-
var
|
|
1528
|
+
// src/middle/timer.ts
|
|
1529
|
+
var createTime = () => {
|
|
1530
|
+
const times = [["init", performance.now()]];
|
|
1531
|
+
const time = (name) => times.push([name, performance.now()]);
|
|
1532
|
+
time.times = times;
|
|
1533
|
+
time.headers = () => {
|
|
1534
|
+
const r = (t) => Math.round(t);
|
|
1535
|
+
const times2 = time.times;
|
|
1536
|
+
const timing = times2.slice(1).map(([name, time2], i) => `${name};dur=${r(time2 - times2[i][1])}`).join(", ");
|
|
1537
|
+
return timing;
|
|
1538
|
+
};
|
|
1539
|
+
return time;
|
|
1540
|
+
};
|
|
1541
|
+
function timer(ctx) {
|
|
1542
|
+
ctx.time = createTime();
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// src/auth/NoSession.ts
|
|
1546
|
+
var NoSession = class {
|
|
1547
|
+
};
|
|
1548
|
+
function createNoSession() {
|
|
1549
|
+
return new Proxy(NoSession, {
|
|
1550
|
+
get(target, key) {
|
|
1551
|
+
if (target[key]) return target[key];
|
|
1552
|
+
if (key === "then") return target[key];
|
|
1553
|
+
if (typeof key === "symbol") return target[key];
|
|
1554
|
+
throw ServerError_default.NO_STORE_READ({ key: String(key) });
|
|
1555
|
+
},
|
|
1556
|
+
set(target, key, value) {
|
|
1557
|
+
if (target[key] || key === "then" || typeof key === "symbol") {
|
|
1558
|
+
target[key] = value;
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
throw ServerError_default.NO_STORE_WRITE({ key: String(key) });
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// src/auth/session.ts
|
|
1567
|
+
async function session(ctx) {
|
|
1568
|
+
const store = ctx.options.session?.store;
|
|
1569
|
+
if (!store) {
|
|
1570
|
+
ctx.session = createNoSession();
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
if (ctx.cookies.session) {
|
|
1574
|
+
const session2 = await store.get(ctx.cookies.session);
|
|
1575
|
+
ctx.session = session2;
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// src/context/node.ts
|
|
1581
|
+
import { TLSSocket } from "tls";
|
|
1582
|
+
|
|
1583
|
+
// src/context/createEvents.ts
|
|
1584
|
+
function createEvents() {
|
|
1585
|
+
const events = {};
|
|
1586
|
+
events.on = (name, callback2) => {
|
|
1587
|
+
events[name] = events[name] || [];
|
|
1588
|
+
events[name].push(callback2);
|
|
1589
|
+
};
|
|
1590
|
+
events.trigger = (name, data) => {
|
|
1591
|
+
if (!events[name]) return;
|
|
1592
|
+
for (const cb of events[name]) {
|
|
1593
|
+
cb(data);
|
|
1594
|
+
}
|
|
1595
|
+
};
|
|
1596
|
+
return events;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// src/context/isValidMethod.ts
|
|
1600
|
+
var methods = [
|
|
1601
|
+
"get",
|
|
1602
|
+
"post",
|
|
1603
|
+
"put",
|
|
1604
|
+
"patch",
|
|
1605
|
+
"delete",
|
|
1606
|
+
"head",
|
|
1607
|
+
"options",
|
|
1608
|
+
"socket"
|
|
1609
|
+
];
|
|
1610
|
+
function isValidMethod(method) {
|
|
1611
|
+
return methods.includes(method);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// src/context/node.ts
|
|
1615
|
+
var chunkArray = (arr) => arr.length > 2 ? [[arr[0], arr[1]], ...chunkArray(arr.slice(2))] : [arr];
|
|
1616
|
+
async function createNode(req, app) {
|
|
1617
|
+
const init = performance.now();
|
|
1618
|
+
const method = req.method?.toLowerCase() || "get";
|
|
1619
|
+
if (!isValidMethod(method)) {
|
|
1620
|
+
throw new Error(`Invalid HTTP method: ${method}`);
|
|
1621
|
+
}
|
|
1622
|
+
const chunks = chunkArray(req.rawHeaders);
|
|
1623
|
+
const headers2 = parseHeaders_default(new Headers(chunks));
|
|
1624
|
+
const cookies2 = parseCookies(headers2.cookie);
|
|
1625
|
+
const scheme = req.socket instanceof TLSSocket ? "https" : "http";
|
|
1626
|
+
const host = headers2.host || `localhost:${app.settings.port}`;
|
|
1627
|
+
const path2 = (req.url || "/").replace(/\/$/, "") || "/";
|
|
1628
|
+
const baseUrl = `${scheme}://${host}`;
|
|
1629
|
+
const url = new URL(path2, baseUrl);
|
|
1630
|
+
define(
|
|
1631
|
+
url,
|
|
1632
|
+
"query",
|
|
1633
|
+
(url2) => Object.fromEntries(url2.searchParams.entries())
|
|
1634
|
+
);
|
|
1635
|
+
const rawBody = await new Promise((resolve2, reject) => {
|
|
1636
|
+
const body2 = [];
|
|
1637
|
+
req.on("data", (chunk) => body2.push(chunk)).on("end", () => resolve2(Buffer.concat(body2))).on("error", reject);
|
|
1638
|
+
});
|
|
1639
|
+
const body = rawBody ? await parseBody(rawBody, headers2["content-type"], app.settings.uploads) : void 0;
|
|
1640
|
+
const events = createEvents();
|
|
1641
|
+
return {
|
|
1642
|
+
options: app.settings,
|
|
1643
|
+
platform: app.platform,
|
|
1644
|
+
url,
|
|
1645
|
+
method,
|
|
1646
|
+
body,
|
|
1647
|
+
headers: headers2,
|
|
1648
|
+
cookies: cookies2,
|
|
1649
|
+
init,
|
|
1650
|
+
events,
|
|
1651
|
+
app
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/context/winter.ts
|
|
1656
|
+
async function createWinter(req, app) {
|
|
1657
|
+
const init = performance.now();
|
|
1658
|
+
const method = req.method.toLowerCase();
|
|
1659
|
+
if (!isValidMethod(method)) {
|
|
1660
|
+
throw new Error(`Invalid HTTP method: ${method}`);
|
|
1661
|
+
}
|
|
1662
|
+
const headers2 = parseHeaders_default(req.headers);
|
|
1663
|
+
const cookies2 = parseCookies(headers2.cookie);
|
|
1664
|
+
const baseUrl = req.url.replace(/\/$/, "") || "/";
|
|
1665
|
+
const url = new URL(baseUrl);
|
|
1666
|
+
define(
|
|
1667
|
+
url,
|
|
1668
|
+
"query",
|
|
1669
|
+
(url2) => Object.fromEntries(url2.searchParams.entries())
|
|
1670
|
+
);
|
|
1671
|
+
const rawBody = Buffer.from(await req.arrayBuffer());
|
|
1672
|
+
const body = req.body ? await parseBody(rawBody, headers2["content-type"], app.settings.uploads) : void 0;
|
|
1673
|
+
const events = createEvents();
|
|
1674
|
+
return {
|
|
1675
|
+
options: app.settings,
|
|
1676
|
+
platform: app.platform,
|
|
1677
|
+
url,
|
|
1678
|
+
method,
|
|
1679
|
+
body,
|
|
1680
|
+
headers: headers2,
|
|
1681
|
+
cookies: cookies2,
|
|
1682
|
+
init,
|
|
1683
|
+
events,
|
|
1684
|
+
app
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// src/context/handlers.ts
|
|
1689
|
+
var Winter = async (app, request, env3) => {
|
|
1690
|
+
if (env3?.upgrade(request)) return;
|
|
1691
|
+
Object.assign(globalThis.env, env3);
|
|
1692
|
+
const ctx = await createWinter(request, app);
|
|
1693
|
+
const res = await handleRequest(app.handlers, ctx);
|
|
1694
|
+
ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
|
|
1695
|
+
return res;
|
|
1696
|
+
};
|
|
1697
|
+
var Node = async (app) => {
|
|
1698
|
+
const http = await import("http");
|
|
1699
|
+
http.createServer(async (request, response) => {
|
|
1700
|
+
const ctx = await createNode(request, app);
|
|
1701
|
+
if ("error" in ctx) throw ctx.error;
|
|
1702
|
+
const out = await handleRequest(app.handlers, ctx);
|
|
1703
|
+
response.writeHead(out.status || 200, parseHeaders_default(out.headers));
|
|
1704
|
+
if (out.body instanceof ReadableStream) {
|
|
1705
|
+
await iterate(out.body, (chunk) => response.write(chunk));
|
|
1706
|
+
} else {
|
|
1707
|
+
response.write(out.body || "");
|
|
1708
|
+
}
|
|
1709
|
+
response.end();
|
|
1710
|
+
}).listen(app.settings.port);
|
|
1711
|
+
};
|
|
1712
|
+
var Netlify = async (app, request, context) => {
|
|
1713
|
+
request.context = context;
|
|
1714
|
+
if (typeof Netlify === "undefined") {
|
|
1715
|
+
throw new Error("Netlify doesn't exist");
|
|
1716
|
+
}
|
|
1717
|
+
const ctx = await createWinter(request, app);
|
|
1718
|
+
const res = await handleRequest(app.handlers, ctx);
|
|
1719
|
+
ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
|
|
1720
|
+
return res;
|
|
1721
|
+
};
|
|
1525
1722
|
|
|
1526
1723
|
// src/router.ts
|
|
1724
|
+
function isMiddleware(x) {
|
|
1725
|
+
return typeof x === "function";
|
|
1726
|
+
}
|
|
1527
1727
|
var Router = class _Router {
|
|
1528
1728
|
handlers = {
|
|
1529
1729
|
socket: [],
|
|
@@ -1545,35 +1745,59 @@ var Router = class _Router {
|
|
|
1545
1745
|
middleware.unshift(path2);
|
|
1546
1746
|
path2 = "*";
|
|
1547
1747
|
}
|
|
1548
|
-
const
|
|
1549
|
-
for (const m of
|
|
1748
|
+
const methods2 = method === "*" ? Object.keys(this.handlers) : [method];
|
|
1749
|
+
for (const m of methods2) {
|
|
1550
1750
|
this.handlers[m].push([method, path2, ...middleware]);
|
|
1551
1751
|
}
|
|
1552
1752
|
return this.self();
|
|
1553
1753
|
}
|
|
1554
|
-
socket(
|
|
1555
|
-
|
|
1754
|
+
socket(pathOrMid, optionsOrMid, ...middleware) {
|
|
1755
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1756
|
+
return this.handle("socket", pathOrMid, optionsOrMid, ...middleware);
|
|
1757
|
+
}
|
|
1758
|
+
return this.handle("socket", pathOrMid, ...middleware);
|
|
1556
1759
|
}
|
|
1557
|
-
get(
|
|
1558
|
-
|
|
1760
|
+
get(pathOrMid, optionsOrMid, ...middleware) {
|
|
1761
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1762
|
+
return this.handle("get", pathOrMid, optionsOrMid, ...middleware);
|
|
1763
|
+
}
|
|
1764
|
+
return this.handle("get", pathOrMid, ...middleware);
|
|
1559
1765
|
}
|
|
1560
|
-
head(
|
|
1561
|
-
|
|
1766
|
+
head(pathOrMid, optionsOrMid, ...middleware) {
|
|
1767
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1768
|
+
return this.handle("head", pathOrMid, optionsOrMid, ...middleware);
|
|
1769
|
+
}
|
|
1770
|
+
return this.handle("head", pathOrMid, ...middleware);
|
|
1562
1771
|
}
|
|
1563
|
-
post(
|
|
1564
|
-
|
|
1772
|
+
post(pathOrMid, optionsOrMid, ...middleware) {
|
|
1773
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1774
|
+
return this.handle("post", pathOrMid, optionsOrMid, ...middleware);
|
|
1775
|
+
}
|
|
1776
|
+
return this.handle("post", pathOrMid, ...middleware);
|
|
1565
1777
|
}
|
|
1566
|
-
put(
|
|
1567
|
-
|
|
1778
|
+
put(pathOrMid, optionsOrMid, ...middleware) {
|
|
1779
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1780
|
+
return this.handle("put", pathOrMid, optionsOrMid, ...middleware);
|
|
1781
|
+
}
|
|
1782
|
+
return this.handle("put", pathOrMid, ...middleware);
|
|
1568
1783
|
}
|
|
1569
|
-
patch(
|
|
1570
|
-
|
|
1784
|
+
patch(pathOrMid, optionsOrMid, ...middleware) {
|
|
1785
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1786
|
+
return this.handle("patch", pathOrMid, optionsOrMid, ...middleware);
|
|
1787
|
+
}
|
|
1788
|
+
return this.handle("patch", pathOrMid, ...middleware);
|
|
1571
1789
|
}
|
|
1572
|
-
del(
|
|
1573
|
-
|
|
1790
|
+
del(pathOrMid, optionsOrMid, ...middleware) {
|
|
1791
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1792
|
+
return this.handle("delete", pathOrMid, optionsOrMid, ...middleware);
|
|
1793
|
+
}
|
|
1794
|
+
return this.handle("delete", pathOrMid, ...middleware);
|
|
1574
1795
|
}
|
|
1575
|
-
options(
|
|
1576
|
-
|
|
1796
|
+
options(pathOrMid, optionsOrMid, ...middleware) {
|
|
1797
|
+
if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
|
|
1798
|
+
return this.handle("options", pathOrMid, optionsOrMid, ...middleware);
|
|
1799
|
+
}
|
|
1800
|
+
return this.handle("options", pathOrMid, ...middleware);
|
|
1577
1801
|
}
|
|
1578
1802
|
use(...args) {
|
|
1579
1803
|
const path2 = typeof args[0] === "string" ? args.shift() : "*";
|
|
@@ -1601,6 +1825,10 @@ function isSerializable(body) {
|
|
|
1601
1825
|
if (typeof body === "string") return false;
|
|
1602
1826
|
if (body instanceof ReadableStream) return false;
|
|
1603
1827
|
if (body instanceof FormData) return false;
|
|
1828
|
+
if (body instanceof Blob) return false;
|
|
1829
|
+
if (body instanceof ArrayBuffer) return false;
|
|
1830
|
+
if (ArrayBuffer.isView(body)) return false;
|
|
1831
|
+
if (body instanceof URLSearchParams) return false;
|
|
1604
1832
|
return true;
|
|
1605
1833
|
}
|
|
1606
1834
|
function ServerTest(app) {
|
|
@@ -1641,297 +1869,14 @@ function ServerTest(app) {
|
|
|
1641
1869
|
};
|
|
1642
1870
|
}
|
|
1643
1871
|
|
|
1644
|
-
// src/context/parseBody.ts
|
|
1645
|
-
function getBoundary(header) {
|
|
1646
|
-
if (!header) return null;
|
|
1647
|
-
if (header.includes("multipart/form-data") && !header.includes("boundary=")) {
|
|
1648
|
-
console.error("Do not set the `Content-Type` manually for FormData");
|
|
1649
|
-
}
|
|
1650
|
-
const items = header.split(";");
|
|
1651
|
-
for (const item of items) {
|
|
1652
|
-
const trimmedItem = item.trim();
|
|
1653
|
-
if (trimmedItem.startsWith("boundary=")) {
|
|
1654
|
-
return trimmedItem.split("=")[1].trim();
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
return null;
|
|
1658
|
-
}
|
|
1659
|
-
function getMatching(string, regex) {
|
|
1660
|
-
const matches = string.match(regex);
|
|
1661
|
-
return matches?.[1] ?? "";
|
|
1662
|
-
}
|
|
1663
|
-
var saveFile = async (name, value, bucket) => {
|
|
1664
|
-
const ext = name.split(".").pop();
|
|
1665
|
-
const id = `${createId()}.${ext}`;
|
|
1666
|
-
await bucket.write(id, value);
|
|
1667
|
-
return id;
|
|
1668
|
-
};
|
|
1669
|
-
function splitBuffer(buffer, delimiter) {
|
|
1670
|
-
const result = [];
|
|
1671
|
-
let start = 0;
|
|
1672
|
-
let index = buffer.indexOf(delimiter);
|
|
1673
|
-
while (index !== -1) {
|
|
1674
|
-
result.push(buffer.slice(start, index));
|
|
1675
|
-
start = index + delimiter.length;
|
|
1676
|
-
index = buffer.indexOf(delimiter, start);
|
|
1677
|
-
}
|
|
1678
|
-
result.push(buffer.slice(start));
|
|
1679
|
-
return result;
|
|
1680
|
-
}
|
|
1681
|
-
var BREAK_BUFFER = Buffer.from("\r\n\r\n");
|
|
1682
|
-
function isProbablyText(buffer) {
|
|
1683
|
-
for (let i = 0; i < Math.min(buffer.length, 512); i++) {
|
|
1684
|
-
const byte = buffer[i];
|
|
1685
|
-
if (byte === 0) return false;
|
|
1686
|
-
if (byte < 7 || byte > 13 && byte < 32) return false;
|
|
1687
|
-
}
|
|
1688
|
-
return true;
|
|
1689
|
-
}
|
|
1690
|
-
async function parseBody(raw, contentType, bucket) {
|
|
1691
|
-
const contentTypeStr = Array.isArray(contentType) ? contentType[0] : contentType;
|
|
1692
|
-
let rawBuffer;
|
|
1693
|
-
if (raw instanceof Buffer) {
|
|
1694
|
-
rawBuffer = raw;
|
|
1695
|
-
} else if ("arrayBuffer" in raw && typeof raw.arrayBuffer === "function") {
|
|
1696
|
-
const arrayBuf = await raw.arrayBuffer();
|
|
1697
|
-
rawBuffer = Buffer.from(arrayBuf);
|
|
1698
|
-
} else {
|
|
1699
|
-
throw new Error("Unsupported raw type");
|
|
1700
|
-
}
|
|
1701
|
-
if (!rawBuffer) return {};
|
|
1702
|
-
if (!contentTypeStr || /text\/plain/.test(contentTypeStr)) {
|
|
1703
|
-
return rawBuffer.toString("utf-8");
|
|
1704
|
-
}
|
|
1705
|
-
if (/application\/json/.test(contentTypeStr)) {
|
|
1706
|
-
return JSON.parse(rawBuffer.toString("utf-8"));
|
|
1707
|
-
}
|
|
1708
|
-
const boundary = getBoundary(contentTypeStr);
|
|
1709
|
-
if (!boundary) return null;
|
|
1710
|
-
const body = {};
|
|
1711
|
-
const boundaryBuffer = Buffer.from(`--${boundary}`);
|
|
1712
|
-
const parts = splitBuffer(rawBuffer, boundaryBuffer);
|
|
1713
|
-
for (const part of parts) {
|
|
1714
|
-
if (part.length === 0 || part.equals(Buffer.from("--\r\n"))) continue;
|
|
1715
|
-
const idx = part.indexOf(BREAK_BUFFER);
|
|
1716
|
-
if (idx === -1) continue;
|
|
1717
|
-
const headerStr = part.slice(0, idx).toString("utf-8");
|
|
1718
|
-
const contentBuf = part.slice(idx + BREAK_BUFFER.length, part.length - 2);
|
|
1719
|
-
const name = getMatching(headerStr, /name="(.+?)"/).trim().replace(/\[\]$/, "");
|
|
1720
|
-
if (!name) continue;
|
|
1721
|
-
const filename = getMatching(headerStr, /filename="(.+?)"/).trim();
|
|
1722
|
-
if (filename) {
|
|
1723
|
-
if (!bucket) throw new Error("Bucket is required to save files");
|
|
1724
|
-
body[name] = await saveFile(filename, contentBuf, bucket);
|
|
1725
|
-
} else {
|
|
1726
|
-
const value = isProbablyText(contentBuf) ? contentBuf.toString("utf-8").trim() : contentBuf;
|
|
1727
|
-
if (body[name]) {
|
|
1728
|
-
if (!Array.isArray(body[name])) body[name] = [body[name]];
|
|
1729
|
-
body[name].push(value);
|
|
1730
|
-
} else {
|
|
1731
|
-
body[name] = value;
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
return body;
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// src/context/parseCookies.ts
|
|
1739
|
-
function parseCookies(cookies2) {
|
|
1740
|
-
if (!cookies2) return {};
|
|
1741
|
-
const cookieStr = Array.isArray(cookies2) ? cookies2[0] : cookies2;
|
|
1742
|
-
if (!cookieStr) return {};
|
|
1743
|
-
return Object.fromEntries(
|
|
1744
|
-
cookieStr.split(/;\s*/).map((part) => {
|
|
1745
|
-
const [key, ...rest] = part.split("=");
|
|
1746
|
-
return [key, decodeURIComponent(rest.join("="))];
|
|
1747
|
-
})
|
|
1748
|
-
);
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
// src/context/winter.ts
|
|
1752
|
-
function isValidMethod(method) {
|
|
1753
|
-
return [
|
|
1754
|
-
"get",
|
|
1755
|
-
"post",
|
|
1756
|
-
"put",
|
|
1757
|
-
"patch",
|
|
1758
|
-
"delete",
|
|
1759
|
-
"head",
|
|
1760
|
-
"options",
|
|
1761
|
-
"socket"
|
|
1762
|
-
].includes(method);
|
|
1763
|
-
}
|
|
1764
|
-
var winter_default = async (request, app) => {
|
|
1765
|
-
try {
|
|
1766
|
-
const ctx = {
|
|
1767
|
-
headers: {},
|
|
1768
|
-
cookies: {},
|
|
1769
|
-
url: void 0,
|
|
1770
|
-
options: app.settings || {},
|
|
1771
|
-
method: "get",
|
|
1772
|
-
init: performance.now(),
|
|
1773
|
-
req: request
|
|
1774
|
-
};
|
|
1775
|
-
const method = request.method.toLowerCase();
|
|
1776
|
-
if (!isValidMethod(method)) {
|
|
1777
|
-
throw new Error(`Invalid HTTP method: ${method}`);
|
|
1778
|
-
}
|
|
1779
|
-
ctx.method = method;
|
|
1780
|
-
const events = {};
|
|
1781
|
-
ctx.on = (name, callback2) => {
|
|
1782
|
-
events[name] = events[name] || [];
|
|
1783
|
-
events[name].push(callback2);
|
|
1784
|
-
};
|
|
1785
|
-
ctx.trigger = (name, data) => {
|
|
1786
|
-
if (!events[name]) return;
|
|
1787
|
-
for (const cb of events[name]) {
|
|
1788
|
-
cb(data);
|
|
1789
|
-
}
|
|
1790
|
-
};
|
|
1791
|
-
ctx.headers = parseHeaders_default(request.headers);
|
|
1792
|
-
ctx.cookies = parseCookies(ctx.headers.cookie);
|
|
1793
|
-
await auth_default.load(ctx);
|
|
1794
|
-
ctx.url = new URL(request.url.replace(/\/$/, ""));
|
|
1795
|
-
define(
|
|
1796
|
-
ctx.url,
|
|
1797
|
-
"query",
|
|
1798
|
-
(url) => Object.fromEntries(url.searchParams.entries())
|
|
1799
|
-
);
|
|
1800
|
-
if (request.body) {
|
|
1801
|
-
const type2 = ctx.headers["content-type"];
|
|
1802
|
-
ctx.body = await parseBody(request, type2, ctx.options.uploads);
|
|
1803
|
-
}
|
|
1804
|
-
ctx.app = app;
|
|
1805
|
-
ctx.platform = app.platform;
|
|
1806
|
-
ctx.machine = app.platform;
|
|
1807
|
-
return ctx;
|
|
1808
|
-
} catch (error) {
|
|
1809
|
-
return { error };
|
|
1810
|
-
}
|
|
1811
|
-
};
|
|
1812
|
-
|
|
1813
|
-
// src/context/node.ts
|
|
1814
|
-
function isValidMethod2(method) {
|
|
1815
|
-
return [
|
|
1816
|
-
"get",
|
|
1817
|
-
"post",
|
|
1818
|
-
"put",
|
|
1819
|
-
"patch",
|
|
1820
|
-
"delete",
|
|
1821
|
-
"head",
|
|
1822
|
-
"options",
|
|
1823
|
-
"socket"
|
|
1824
|
-
].includes(method);
|
|
1825
|
-
}
|
|
1826
|
-
var chunkArray = (arr, size) => arr.length > size ? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)] : [arr];
|
|
1827
|
-
var node_default = async (request, app) => {
|
|
1828
|
-
try {
|
|
1829
|
-
const ctx = {
|
|
1830
|
-
headers: {},
|
|
1831
|
-
cookies: {},
|
|
1832
|
-
url: void 0,
|
|
1833
|
-
options: app.settings || {},
|
|
1834
|
-
method: "get",
|
|
1835
|
-
req: request
|
|
1836
|
-
};
|
|
1837
|
-
const method = request.method?.toLowerCase() || "get";
|
|
1838
|
-
if (!isValidMethod2(method)) {
|
|
1839
|
-
throw new Error(`Invalid HTTP method: ${method}`);
|
|
1840
|
-
}
|
|
1841
|
-
ctx.method = method;
|
|
1842
|
-
const events = {};
|
|
1843
|
-
ctx.on = (name, callback2) => {
|
|
1844
|
-
events[name] = events[name] || [];
|
|
1845
|
-
events[name].push(callback2);
|
|
1846
|
-
};
|
|
1847
|
-
ctx.trigger = (name, data) => {
|
|
1848
|
-
if (!events[name]) return;
|
|
1849
|
-
for (const cb of events[name]) {
|
|
1850
|
-
cb(data);
|
|
1851
|
-
}
|
|
1852
|
-
};
|
|
1853
|
-
ctx.headers = parseHeaders_default(
|
|
1854
|
-
new Headers(chunkArray(request.rawHeaders, 2))
|
|
1855
|
-
);
|
|
1856
|
-
ctx.cookies = parseCookies(ctx.headers.cookie);
|
|
1857
|
-
await auth_default.load(ctx);
|
|
1858
|
-
const https = request.connection?.encrypted ? "https" : "http";
|
|
1859
|
-
const host = ctx.headers.host || `localhost:${ctx.options.port}`;
|
|
1860
|
-
const path2 = (request.url || "/").replace(/\/$/, "") || "/";
|
|
1861
|
-
ctx.url = new URL(path2, `${https}://${host}`);
|
|
1862
|
-
define(
|
|
1863
|
-
ctx.url,
|
|
1864
|
-
"query",
|
|
1865
|
-
(url) => Object.fromEntries(url.searchParams.entries())
|
|
1866
|
-
);
|
|
1867
|
-
await new Promise((resolve2, reject) => {
|
|
1868
|
-
const body = [];
|
|
1869
|
-
request.on("data", (chunk) => {
|
|
1870
|
-
body.push(chunk);
|
|
1871
|
-
}).on("end", async () => {
|
|
1872
|
-
const type2 = ctx.headers["content-type"];
|
|
1873
|
-
const concatenated = Buffer.concat(body);
|
|
1874
|
-
ctx.body = await parseBody(concatenated, type2, ctx.options.uploads);
|
|
1875
|
-
resolve2();
|
|
1876
|
-
}).on("error", reject);
|
|
1877
|
-
});
|
|
1878
|
-
ctx.app = app;
|
|
1879
|
-
ctx.platform = app.platform;
|
|
1880
|
-
ctx.machine = app.platform;
|
|
1881
|
-
return ctx;
|
|
1882
|
-
} catch (error) {
|
|
1883
|
-
return { error };
|
|
1884
|
-
}
|
|
1885
|
-
};
|
|
1886
|
-
|
|
1887
|
-
// src/context/handlers.ts
|
|
1888
|
-
var Winter = async (app, request, env3) => {
|
|
1889
|
-
if (env3?.upgrade(request)) return;
|
|
1890
|
-
Object.assign(globalThis.env, env3);
|
|
1891
|
-
const ctx = await winter_default(request, app);
|
|
1892
|
-
if ("error" in ctx) {
|
|
1893
|
-
throw ctx.error;
|
|
1894
|
-
}
|
|
1895
|
-
const res = await handleRequest(app.handlers, ctx);
|
|
1896
|
-
ctx.trigger("finish", { ...ctx, res, end: performance.now() });
|
|
1897
|
-
return res;
|
|
1898
|
-
};
|
|
1899
|
-
var Node = async (app) => {
|
|
1900
|
-
const http = await import("http");
|
|
1901
|
-
http.createServer(async (request, response) => {
|
|
1902
|
-
const ctx = await node_default(request, app);
|
|
1903
|
-
if ("error" in ctx) {
|
|
1904
|
-
throw ctx.error;
|
|
1905
|
-
}
|
|
1906
|
-
const out = await handleRequest(app.handlers, ctx);
|
|
1907
|
-
response.writeHead(out.status || 200, parseHeaders_default(out.headers));
|
|
1908
|
-
if (out.body instanceof ReadableStream) {
|
|
1909
|
-
await iterate(out.body, (chunk) => response.write(chunk));
|
|
1910
|
-
} else {
|
|
1911
|
-
response.write(out.body || "");
|
|
1912
|
-
}
|
|
1913
|
-
response.end();
|
|
1914
|
-
}).listen(app.settings.port);
|
|
1915
|
-
};
|
|
1916
|
-
var Netlify = async (app, request, context) => {
|
|
1917
|
-
request.context = context;
|
|
1918
|
-
if (typeof Netlify === "undefined") {
|
|
1919
|
-
throw new Error("Netlify doesn't exist");
|
|
1920
|
-
}
|
|
1921
|
-
const ctx = await winter_default(request, app);
|
|
1922
|
-
if ("error" in ctx) {
|
|
1923
|
-
throw ctx.error;
|
|
1924
|
-
}
|
|
1925
|
-
return await handleRequest(app.handlers, ctx);
|
|
1926
|
-
};
|
|
1927
|
-
|
|
1928
1872
|
// src/index.ts
|
|
1929
1873
|
var Server = class extends Router {
|
|
1930
1874
|
settings;
|
|
1931
1875
|
platform;
|
|
1932
|
-
port;
|
|
1933
1876
|
sockets;
|
|
1934
1877
|
websocket;
|
|
1878
|
+
// Needed to be explicit for Bun/WinterCG
|
|
1879
|
+
port;
|
|
1935
1880
|
constructor(options = {}) {
|
|
1936
1881
|
super();
|
|
1937
1882
|
this.settings = config(options);
|
|
@@ -1946,12 +1891,13 @@ var Server = class extends Router {
|
|
|
1946
1891
|
}
|
|
1947
1892
|
this.use(timer);
|
|
1948
1893
|
this.use(assets);
|
|
1894
|
+
this.use(session);
|
|
1895
|
+
if (this.settings.auth) {
|
|
1896
|
+
auth(this);
|
|
1897
|
+
}
|
|
1949
1898
|
if (this.settings.openapi) {
|
|
1950
1899
|
this.get(this.settings.openapi.path || "/docs", openapi_default);
|
|
1951
1900
|
}
|
|
1952
|
-
if (this.settings.auth) {
|
|
1953
|
-
this.use(auth2);
|
|
1954
|
-
}
|
|
1955
1901
|
}
|
|
1956
1902
|
// We need to return a function; some environment expect the default export
|
|
1957
1903
|
// to be a function that is called with the request, but we also want to
|
|
@@ -1989,6 +1935,7 @@ function server(options = {}) {
|
|
|
1989
1935
|
}
|
|
1990
1936
|
export {
|
|
1991
1937
|
Reply,
|
|
1938
|
+
Server,
|
|
1992
1939
|
ServerError_default as ServerError,
|
|
1993
1940
|
cookies,
|
|
1994
1941
|
server as default,
|