@access-dlsu/leapify 0.260531.1 → 0.260601.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/dist/auth/auth.d.ts +2 -2
- package/dist/client/auth.d.ts +41 -41
- package/dist/client/index.cjs +2 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/index.cjs +126 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +124 -59
- package/dist/index.js.map +1 -1
- package/dist/lib/middleware/cors.d.ts.map +1 -1
- package/dist/lib/middleware/referer-guard.d.ts +1 -1
- package/dist/lib/middleware/referer-guard.d.ts.map +1 -1
- package/dist/routes/site-config.d.ts +2 -2
- package/dist/routes/site-config.d.ts.map +1 -1
- package/dist/worker.js +278 -213
- package/dist/worker.js.map +1 -1
- package/package.json +155 -155
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@ import { createTurnstileMiddleware, TURNSTILE_VERIFY_PATH, handleTurnstileVerify
|
|
|
2
2
|
import { __export } from './chunk-PZ5AY32C.js';
|
|
3
3
|
import { Hono } from 'hono';
|
|
4
4
|
import { cors } from 'hono/cors';
|
|
5
|
+
import { drizzle } from 'drizzle-orm/d1';
|
|
6
|
+
import { sqliteTable, integer, text, index, uniqueIndex } from 'drizzle-orm/sqlite-core';
|
|
7
|
+
import { sql, relations, eq, and, count, lte } from 'drizzle-orm';
|
|
5
8
|
import { createMiddleware } from 'hono/factory';
|
|
6
9
|
import { betterAuth } from 'better-auth';
|
|
7
10
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
|
|
8
11
|
import { bearer } from 'better-auth/plugins';
|
|
9
|
-
import { sql, relations, eq, and, count, lte } from 'drizzle-orm';
|
|
10
|
-
import { drizzle } from 'drizzle-orm/d1';
|
|
11
|
-
import { sqliteTable, integer, text, index, uniqueIndex } from 'drizzle-orm/sqlite-core';
|
|
12
12
|
import { zValidator } from '@hono/zod-validator';
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
|
|
@@ -20,6 +20,8 @@ var LeapifyError = class extends Error {
|
|
|
20
20
|
this.code = code;
|
|
21
21
|
this.name = "LeapifyError";
|
|
22
22
|
}
|
|
23
|
+
statusCode;
|
|
24
|
+
code;
|
|
23
25
|
};
|
|
24
26
|
var unauthorized = (message = "Unauthorized") => new LeapifyError(401, "UNAUTHORIZED", message);
|
|
25
27
|
var domainRestricted = () => new LeapifyError(
|
|
@@ -48,58 +50,6 @@ var errorHandler = (err, c) => {
|
|
|
48
50
|
500
|
|
49
51
|
);
|
|
50
52
|
};
|
|
51
|
-
function createCorsMiddleware(allowedOrigins) {
|
|
52
|
-
return async (c, next) => {
|
|
53
|
-
const origin = c.req.header("origin");
|
|
54
|
-
const dynamicOriginsJson = await c.env.KV.get("config:allowed_origins", "json");
|
|
55
|
-
const currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
56
|
-
if (c.req.path.startsWith("/api/uploads/images")) {
|
|
57
|
-
c.header("Access-Control-Allow-Origin", "*");
|
|
58
|
-
c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
59
|
-
if (c.req.method === "OPTIONS") {
|
|
60
|
-
return c.body(null, 204);
|
|
61
|
-
}
|
|
62
|
-
return next();
|
|
63
|
-
}
|
|
64
|
-
if (!c.req.path.startsWith("/health") && !c.req.path.startsWith("/api/auth") && !c.req.path.startsWith("/internal") && origin && !currentAllowedOrigins.includes("*") && !currentAllowedOrigins.includes(origin)) {
|
|
65
|
-
return c.json(
|
|
66
|
-
{
|
|
67
|
-
error: {
|
|
68
|
-
code: "DOMAIN_RESTRICTED",
|
|
69
|
-
message: `Origin ${origin} is not allowed`
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
403
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
const honoCors = cors({
|
|
76
|
-
origin: currentAllowedOrigins,
|
|
77
|
-
allowMethods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
|
|
78
|
-
allowHeaders: ["Content-Type", "Authorization"],
|
|
79
|
-
exposeHeaders: ["ETag", "Last-Modified", "Cache-Control"],
|
|
80
|
-
maxAge: 86400,
|
|
81
|
-
credentials: true
|
|
82
|
-
});
|
|
83
|
-
return honoCors(c, next);
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function createRefererGuard(allowedOrigins) {
|
|
87
|
-
const MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
88
|
-
const SKIP_PREFIXES = ["/health", "/internal", "/api/auth", "/.well-known"];
|
|
89
|
-
return createMiddleware(async (c, next) => {
|
|
90
|
-
if (!MUTATION_METHODS.has(c.req.method)) return next();
|
|
91
|
-
if (SKIP_PREFIXES.some((p) => c.req.path.startsWith(p))) return next();
|
|
92
|
-
const dynamicOriginsJson = await c.env.KV.get("config:allowed_origins", "json");
|
|
93
|
-
const currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
94
|
-
if (currentAllowedOrigins.includes("*")) return next();
|
|
95
|
-
const referer = c.req.header("referer") ?? "";
|
|
96
|
-
const isAllowed = currentAllowedOrigins.some((origin) => referer.startsWith(origin));
|
|
97
|
-
if (!isAllowed) {
|
|
98
|
-
throw forbidden("Request origin not permitted");
|
|
99
|
-
}
|
|
100
|
-
return next();
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
53
|
|
|
104
54
|
// src/db/schema/index.ts
|
|
105
55
|
var schema_exports = {};
|
|
@@ -332,8 +282,112 @@ var authVerification = sqliteTable(
|
|
|
332
282
|
function createDb(d1) {
|
|
333
283
|
return drizzle(d1, { schema: schema_exports });
|
|
334
284
|
}
|
|
335
|
-
|
|
336
|
-
|
|
285
|
+
async function getOriginsFromDb(env) {
|
|
286
|
+
try {
|
|
287
|
+
const db = createDb(env.DB);
|
|
288
|
+
const row = await db.query.siteConfig.findFirst({
|
|
289
|
+
where: eq(siteConfig.key, "allowed_origins")
|
|
290
|
+
});
|
|
291
|
+
if (row) return JSON.parse(row.value);
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
function createCorsMiddleware(allowedOrigins) {
|
|
297
|
+
return async (c, next) => {
|
|
298
|
+
const origin = c.req.header("origin");
|
|
299
|
+
const dynamicOriginsJson = await c.env.KV.get(
|
|
300
|
+
"config:allowed_origins",
|
|
301
|
+
"json"
|
|
302
|
+
);
|
|
303
|
+
let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
304
|
+
if (!dynamicOriginsJson) {
|
|
305
|
+
const dbOrigins = await getOriginsFromDb(c.env);
|
|
306
|
+
if (dbOrigins) {
|
|
307
|
+
currentAllowedOrigins = dbOrigins;
|
|
308
|
+
await c.env.KV.put(
|
|
309
|
+
"config:allowed_origins",
|
|
310
|
+
JSON.stringify(dbOrigins),
|
|
311
|
+
{ expirationTtl: 86400 }
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (c.req.path.startsWith("/api/uploads/images")) {
|
|
316
|
+
c.header("Access-Control-Allow-Origin", "*");
|
|
317
|
+
c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
318
|
+
if (c.req.method === "OPTIONS") {
|
|
319
|
+
return c.body(null, 204);
|
|
320
|
+
}
|
|
321
|
+
return next();
|
|
322
|
+
}
|
|
323
|
+
if (!c.req.path.startsWith("/health") && !c.req.path.startsWith("/api/auth") && !c.req.path.startsWith("/internal") && origin && !currentAllowedOrigins.includes("*") && !currentAllowedOrigins.includes(origin) && origin !== new URL(c.req.url).origin) {
|
|
324
|
+
return c.json(
|
|
325
|
+
{
|
|
326
|
+
error: {
|
|
327
|
+
code: "DOMAIN_RESTRICTED",
|
|
328
|
+
message: `Origin ${origin} is not allowed`
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
403
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
const honoCors = cors({
|
|
335
|
+
origin: currentAllowedOrigins,
|
|
336
|
+
allowMethods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
|
|
337
|
+
allowHeaders: ["Content-Type", "Authorization"],
|
|
338
|
+
exposeHeaders: ["ETag", "Last-Modified", "Cache-Control"],
|
|
339
|
+
maxAge: 86400,
|
|
340
|
+
credentials: true
|
|
341
|
+
});
|
|
342
|
+
return honoCors(c, next);
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async function getOriginsFromDb2(env) {
|
|
346
|
+
try {
|
|
347
|
+
const db = createDb(env.DB);
|
|
348
|
+
const row = await db.query.siteConfig.findFirst({
|
|
349
|
+
where: eq(siteConfig.key, "allowed_origins")
|
|
350
|
+
});
|
|
351
|
+
if (row) return JSON.parse(row.value);
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
function createRefererGuard(allowedOrigins) {
|
|
357
|
+
const MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
358
|
+
const SKIP_PREFIXES = ["/health", "/internal", "/api/auth", "/.well-known"];
|
|
359
|
+
return createMiddleware(async (c, next) => {
|
|
360
|
+
if (!MUTATION_METHODS.has(c.req.method)) return next();
|
|
361
|
+
if (SKIP_PREFIXES.some((p) => c.req.path.startsWith(p))) return next();
|
|
362
|
+
const dynamicOriginsJson = await c.env.KV.get(
|
|
363
|
+
"config:allowed_origins",
|
|
364
|
+
"json"
|
|
365
|
+
);
|
|
366
|
+
let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
367
|
+
if (!dynamicOriginsJson) {
|
|
368
|
+
const dbOrigins = await getOriginsFromDb2(c.env);
|
|
369
|
+
if (dbOrigins) {
|
|
370
|
+
currentAllowedOrigins = dbOrigins;
|
|
371
|
+
await c.env.KV.put(
|
|
372
|
+
"config:allowed_origins",
|
|
373
|
+
JSON.stringify(dbOrigins),
|
|
374
|
+
{ expirationTtl: 86400 }
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (currentAllowedOrigins.includes("*")) return next();
|
|
379
|
+
const referer = c.req.header("referer") ?? "";
|
|
380
|
+
const requestOrigin = new URL(c.req.url).origin;
|
|
381
|
+
if (referer.startsWith(requestOrigin)) return next();
|
|
382
|
+
const isAllowed = currentAllowedOrigins.some(
|
|
383
|
+
(origin) => referer.startsWith(origin)
|
|
384
|
+
);
|
|
385
|
+
if (!isAllowed) {
|
|
386
|
+
throw forbidden("Request origin not permitted");
|
|
387
|
+
}
|
|
388
|
+
return next();
|
|
389
|
+
});
|
|
390
|
+
}
|
|
337
391
|
var DLSU_DOMAIN = "@dlsu.edu.ph";
|
|
338
392
|
function createAuth(env) {
|
|
339
393
|
const db = createDb(env.DB);
|
|
@@ -629,7 +683,14 @@ healthRoute.get("/", async (c) => {
|
|
|
629
683
|
const env = c.env;
|
|
630
684
|
const hasSes = Boolean(env.SES_REGION) && Boolean(env.SES_ACCESS_KEY_ID) && Boolean(env.SES_SECRET_ACCESS_KEY);
|
|
631
685
|
const hasResend = Boolean(env.RESEND_API_KEY);
|
|
632
|
-
|
|
686
|
+
let hasGForms = false;
|
|
687
|
+
if (env.GFORMS_SERVICE_ACCOUNT_JSON) {
|
|
688
|
+
try {
|
|
689
|
+
const parsed = JSON.parse(env.GFORMS_SERVICE_ACCOUNT_JSON);
|
|
690
|
+
hasGForms = Boolean(parsed.client_email && parsed.private_key);
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
}
|
|
633
694
|
const probes = [];
|
|
634
695
|
if (hasSes) {
|
|
635
696
|
probes.push(
|
|
@@ -687,6 +748,7 @@ var CacheService = class {
|
|
|
687
748
|
constructor(kv) {
|
|
688
749
|
this.kv = kv;
|
|
689
750
|
}
|
|
751
|
+
kv;
|
|
690
752
|
async get(key) {
|
|
691
753
|
return this.kv.get(key, "json");
|
|
692
754
|
}
|
|
@@ -732,6 +794,8 @@ var SlotsService = class {
|
|
|
732
794
|
this.db = db;
|
|
733
795
|
this.cache = cache;
|
|
734
796
|
}
|
|
797
|
+
db;
|
|
798
|
+
cache;
|
|
735
799
|
kvKey(slug) {
|
|
736
800
|
return `${SLOT_KV_PREFIX}${slug}`;
|
|
737
801
|
}
|
|
@@ -1346,7 +1410,7 @@ siteConfigRoute.patch("/:key", authMiddleware, adminMiddleware, async (c) => {
|
|
|
1346
1410
|
set: { value: JSON.stringify(value), updatedAt: now }
|
|
1347
1411
|
});
|
|
1348
1412
|
await c.env.KV.put(`config:${key}`, JSON.stringify(value), {
|
|
1349
|
-
expirationTtl:
|
|
1413
|
+
expirationTtl: 86400
|
|
1350
1414
|
});
|
|
1351
1415
|
return c.json({ data: { key, value } });
|
|
1352
1416
|
});
|
|
@@ -1838,6 +1902,7 @@ var SesError = class extends Error {
|
|
|
1838
1902
|
this.status = status;
|
|
1839
1903
|
this.name = "SesError";
|
|
1840
1904
|
}
|
|
1905
|
+
status;
|
|
1841
1906
|
/**
|
|
1842
1907
|
* True for errors that are permanent (not worth retrying via SES again).
|
|
1843
1908
|
* 400 BadRequest, 403 Forbidden, 404 NotFound → non-retryable.
|