@access-dlsu/leapify 0.260605.1 → 0.260608.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/client/index.cjs +4 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +4 -2
- package/dist/client/index.js.map +1 -1
- package/dist/index.cjs +44 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +41 -66
- package/dist/index.js.map +1 -1
- package/dist/routes/internal/gforms-webhook.d.ts.map +1 -1
- package/dist/services/slots.d.ts +6 -22
- package/dist/services/slots.d.ts.map +1 -1
- package/dist/worker.js +41 -66
- package/dist/worker.js.map +1 -1
- package/package.json +10 -11
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,8 @@ var LeapifyError = class extends Error {
|
|
|
23
23
|
this.code = code;
|
|
24
24
|
this.name = "LeapifyError";
|
|
25
25
|
}
|
|
26
|
+
statusCode;
|
|
27
|
+
code;
|
|
26
28
|
};
|
|
27
29
|
var unauthorized = (message = "Unauthorized") => new LeapifyError(401, "UNAUTHORIZED", message);
|
|
28
30
|
var domainRestricted = () => new LeapifyError(
|
|
@@ -317,7 +319,7 @@ function createCorsMiddleware(allowedOrigins) {
|
|
|
317
319
|
);
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
|
-
if (c.req.path.startsWith("/api/uploads
|
|
322
|
+
if (c.req.path.startsWith("/api/uploads")) {
|
|
321
323
|
c.header("Access-Control-Allow-Origin", "*");
|
|
322
324
|
c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
323
325
|
if (c.req.method === "OPTIONS") {
|
|
@@ -769,6 +771,7 @@ var CacheService = class {
|
|
|
769
771
|
constructor(kv) {
|
|
770
772
|
this.kv = kv;
|
|
771
773
|
}
|
|
774
|
+
kv;
|
|
772
775
|
async get(key) {
|
|
773
776
|
return this.kv.get(key, "json");
|
|
774
777
|
}
|
|
@@ -808,71 +811,48 @@ var CacheService = class {
|
|
|
808
811
|
return `"${hashArray.map((b) => b.toString(16).padStart(2, "0")).join("")}"`;
|
|
809
812
|
}
|
|
810
813
|
};
|
|
811
|
-
var SLOT_KV_PREFIX = "slots:";
|
|
812
814
|
var SlotsService = class {
|
|
813
|
-
constructor(db
|
|
815
|
+
constructor(db) {
|
|
814
816
|
this.db = db;
|
|
815
|
-
this.cache = cache;
|
|
816
|
-
}
|
|
817
|
-
kvKey(slug) {
|
|
818
|
-
return `${SLOT_KV_PREFIX}${slug}`;
|
|
819
817
|
}
|
|
818
|
+
db;
|
|
820
819
|
/**
|
|
821
|
-
* Read current slot info
|
|
820
|
+
* Read current slot info from D1.
|
|
822
821
|
*/
|
|
823
822
|
async getSlots(slug) {
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
823
|
+
const event = await this.db.query.events.findFirst({
|
|
824
|
+
where: drizzleOrm.eq(events.slug, slug),
|
|
825
|
+
columns: { maxSlots: true, registeredSlots: true }
|
|
826
|
+
});
|
|
827
|
+
if (!event) return null;
|
|
828
|
+
return {
|
|
829
|
+
total: event.maxSlots,
|
|
830
|
+
registered: event.registeredSlots
|
|
831
|
+
};
|
|
827
832
|
}
|
|
828
833
|
/**
|
|
829
|
-
* Atomically increment registered_slots in D1
|
|
834
|
+
* Atomically increment registered_slots in D1.
|
|
830
835
|
* Called by the Google Forms Watch webhook handler.
|
|
831
836
|
*/
|
|
832
837
|
async increment(slug) {
|
|
833
838
|
await this.db.update(events).set({ registeredSlots: drizzleOrm.sql`${events.registeredSlots} + 1` }).where(drizzleOrm.eq(events.slug, slug));
|
|
834
|
-
return this.
|
|
839
|
+
return this.getSlots(slug);
|
|
835
840
|
}
|
|
836
841
|
/**
|
|
837
|
-
* Atomically decrement registered_slots in D1
|
|
842
|
+
* Atomically decrement registered_slots in D1.
|
|
838
843
|
* Used during reconciliation drift correction (not from user actions).
|
|
839
844
|
*/
|
|
840
845
|
async decrement(slug) {
|
|
841
846
|
await this.db.update(events).set({
|
|
842
847
|
registeredSlots: drizzleOrm.sql`MAX(0, ${events.registeredSlots} - 1)`
|
|
843
848
|
}).where(drizzleOrm.eq(events.slug, slug));
|
|
844
|
-
return this.
|
|
849
|
+
return this.getSlots(slug);
|
|
845
850
|
}
|
|
846
851
|
/**
|
|
847
852
|
* Set registered_slots to a specific value (used by reconciliation cron).
|
|
848
853
|
*/
|
|
849
854
|
async correctCount(slug, actualCount) {
|
|
850
855
|
await this.db.update(events).set({ registeredSlots: actualCount }).where(drizzleOrm.eq(events.slug, slug));
|
|
851
|
-
await this.invalidate(slug);
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Read from D1, write to KV, and return slot info.
|
|
855
|
-
*/
|
|
856
|
-
async refreshFromDb(slug) {
|
|
857
|
-
const event = await this.db.query.events.findFirst({
|
|
858
|
-
where: drizzleOrm.eq(events.slug, slug),
|
|
859
|
-
columns: { maxSlots: true, registeredSlots: true }
|
|
860
|
-
});
|
|
861
|
-
if (!event) return null;
|
|
862
|
-
const info = {
|
|
863
|
-
total: event.maxSlots,
|
|
864
|
-
registered: event.registeredSlots,
|
|
865
|
-
available: Math.max(0, event.maxSlots - event.registeredSlots),
|
|
866
|
-
isFull: event.registeredSlots >= event.maxSlots
|
|
867
|
-
};
|
|
868
|
-
await this.cache.set(this.kvKey(slug), info);
|
|
869
|
-
return info;
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Invalidate the KV cache for a specific event.
|
|
873
|
-
*/
|
|
874
|
-
async invalidate(slug) {
|
|
875
|
-
await this.cache.del(this.kvKey(slug));
|
|
876
856
|
}
|
|
877
857
|
};
|
|
878
858
|
|
|
@@ -1109,7 +1089,7 @@ var adminEventsRateLimit = createRateLimitMiddleware({
|
|
|
1109
1089
|
// src/routes/classes.ts
|
|
1110
1090
|
var EVENTS_LIST_KV_KEY = "events:list";
|
|
1111
1091
|
var EVENTS_ETAG_KV_KEY = "events:etag";
|
|
1112
|
-
var EVENTS_LIST_TTL =
|
|
1092
|
+
var EVENTS_LIST_TTL = 3600;
|
|
1113
1093
|
var createEventSchema = zod.z.object({
|
|
1114
1094
|
themeId: zod.z.string().min(1),
|
|
1115
1095
|
organizationId: zod.z.string().optional(),
|
|
@@ -1237,6 +1217,7 @@ classesRoute.get(
|
|
|
1237
1217
|
themeId: true,
|
|
1238
1218
|
organizationId: true,
|
|
1239
1219
|
title: true,
|
|
1220
|
+
description: true,
|
|
1240
1221
|
venue: true,
|
|
1241
1222
|
dateTime: true,
|
|
1242
1223
|
price: true,
|
|
@@ -1247,19 +1228,12 @@ classesRoute.get(
|
|
|
1247
1228
|
registrationClosesAt: true,
|
|
1248
1229
|
isSpotlight: true,
|
|
1249
1230
|
maxSlots: true,
|
|
1250
|
-
|
|
1251
|
-
gformsUrl: true,
|
|
1252
|
-
gformsEditorUrl: true,
|
|
1253
|
-
publishedAt: true
|
|
1231
|
+
gformsUrl: true
|
|
1254
1232
|
}
|
|
1255
1233
|
}),
|
|
1256
1234
|
EVENTS_LIST_TTL
|
|
1257
1235
|
);
|
|
1258
1236
|
c.header("ETag", etag);
|
|
1259
|
-
c.header(
|
|
1260
|
-
"Cache-Control",
|
|
1261
|
-
"public, max-age=604800, stale-while-revalidate=86400"
|
|
1262
|
-
);
|
|
1263
1237
|
return c.json({ data: serializeEvents(data) });
|
|
1264
1238
|
}
|
|
1265
1239
|
);
|
|
@@ -1283,7 +1257,8 @@ classesRoute.get(
|
|
|
1283
1257
|
}
|
|
1284
1258
|
});
|
|
1285
1259
|
if (!event) throw notFound("Event");
|
|
1286
|
-
|
|
1260
|
+
const { registeredSlots: _, ...rest } = event;
|
|
1261
|
+
return c.json({ data: serializeEvent(rest) });
|
|
1287
1262
|
}
|
|
1288
1263
|
);
|
|
1289
1264
|
classesRoute.get(
|
|
@@ -1300,11 +1275,10 @@ classesRoute.get(
|
|
|
1300
1275
|
async (c) => {
|
|
1301
1276
|
const { slug } = c.req.param();
|
|
1302
1277
|
const db = createDb(c.env.DB);
|
|
1303
|
-
const
|
|
1304
|
-
const slotsService = new SlotsService(db, cache);
|
|
1278
|
+
const slotsService = new SlotsService(db);
|
|
1305
1279
|
const info = await slotsService.getSlots(slug);
|
|
1306
1280
|
if (!info) throw notFound("Event");
|
|
1307
|
-
c.header("Cache-Control", "public, max-age=
|
|
1281
|
+
c.header("Cache-Control", "public, max-age=3, stale-while-revalidate=3");
|
|
1308
1282
|
return c.json({ data: info });
|
|
1309
1283
|
}
|
|
1310
1284
|
);
|
|
@@ -1325,9 +1299,8 @@ classesRoute.post(
|
|
|
1325
1299
|
async (c) => {
|
|
1326
1300
|
const { slug } = c.req.param();
|
|
1327
1301
|
const db = createDb(c.env.DB);
|
|
1328
|
-
const cache = new CacheService(c.env.KV);
|
|
1329
1302
|
const gforms = new GFormsService(c.env.GFORMS_SERVICE_ACCOUNT_JSON);
|
|
1330
|
-
const slots = new SlotsService(db
|
|
1303
|
+
const slots = new SlotsService(db);
|
|
1331
1304
|
const event = await db.query.events.findFirst({
|
|
1332
1305
|
where: drizzleOrm.eq(events.slug, slug),
|
|
1333
1306
|
columns: { gformsId: true }
|
|
@@ -1669,7 +1642,7 @@ siteConfigRoute.patch(
|
|
|
1669
1642
|
}
|
|
1670
1643
|
);
|
|
1671
1644
|
var FAQS_KV_KEY = "faqs:active";
|
|
1672
|
-
var FAQS_TTL =
|
|
1645
|
+
var FAQS_TTL = 86400;
|
|
1673
1646
|
var faqSchema = zod.z.object({
|
|
1674
1647
|
question: zod.z.string().min(1),
|
|
1675
1648
|
answer: zod.z.string().min(1),
|
|
@@ -1694,7 +1667,8 @@ faqsRoute.get(
|
|
|
1694
1667
|
}),
|
|
1695
1668
|
FAQS_TTL
|
|
1696
1669
|
);
|
|
1697
|
-
|
|
1670
|
+
const serialized = data.map(({ sortOrder, ...rest }) => rest);
|
|
1671
|
+
return c.json({ data: serialized });
|
|
1698
1672
|
}
|
|
1699
1673
|
);
|
|
1700
1674
|
faqsRoute.post(
|
|
@@ -1801,7 +1775,6 @@ gformsWebhookRoute.post(
|
|
|
1801
1775
|
const { formId } = payload;
|
|
1802
1776
|
if (!formId) return c.json({ error: "Missing formId" }, 400);
|
|
1803
1777
|
const db = createDb(c.env.DB);
|
|
1804
|
-
const cache = new CacheService(c.env.KV);
|
|
1805
1778
|
const event = await db.query.events.findFirst({
|
|
1806
1779
|
where: drizzleOrm.eq(events.gformsId, formId),
|
|
1807
1780
|
columns: { slug: true, maxSlots: true, registeredSlots: true }
|
|
@@ -1810,7 +1783,7 @@ gformsWebhookRoute.post(
|
|
|
1810
1783
|
console.warn(`[gforms-webhook] Unknown formId: ${formId}`);
|
|
1811
1784
|
return c.json({ ok: true });
|
|
1812
1785
|
}
|
|
1813
|
-
const slotsService = new SlotsService(db
|
|
1786
|
+
const slotsService = new SlotsService(db);
|
|
1814
1787
|
const updated = await slotsService.increment(event.slug);
|
|
1815
1788
|
console.log(
|
|
1816
1789
|
`[gforms-webhook] Incremented "${event.slug}": ${updated?.registered}/${updated?.total}`
|
|
@@ -1847,7 +1820,7 @@ async function reconcileSlots(env) {
|
|
|
1847
1820
|
const db = createDb(env.DB);
|
|
1848
1821
|
const cache = new CacheService(env.KV);
|
|
1849
1822
|
const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON);
|
|
1850
|
-
const slots = new SlotsService(db
|
|
1823
|
+
const slots = new SlotsService(db);
|
|
1851
1824
|
const lock = await cache.get(LOCK_KEY);
|
|
1852
1825
|
if (lock) {
|
|
1853
1826
|
console.log("[reconcile-slots] Lock held, skipping.");
|
|
@@ -2081,7 +2054,7 @@ var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
2081
2054
|
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
2082
2055
|
var uploadsRoute = new hono.Hono();
|
|
2083
2056
|
uploadsRoute.get(
|
|
2084
|
-
"
|
|
2057
|
+
"/*",
|
|
2085
2058
|
honoOpenapi.describeRoute({
|
|
2086
2059
|
tags: ["Uploads"],
|
|
2087
2060
|
summary: "Serve an image from R2 storage",
|
|
@@ -2095,7 +2068,7 @@ uploadsRoute.get(
|
|
|
2095
2068
|
if (!bucket) {
|
|
2096
2069
|
throw serviceUnavailable("File storage (R2) is not configured.");
|
|
2097
2070
|
}
|
|
2098
|
-
const path = c.req.path.split("/uploads/
|
|
2071
|
+
const path = c.req.path.split("/uploads/")[1];
|
|
2099
2072
|
if (!path) throw notFound("Image");
|
|
2100
2073
|
const object = await bucket.get(path);
|
|
2101
2074
|
if (!object) throw notFound("Image");
|
|
@@ -2114,7 +2087,7 @@ uploadsRoute.get(
|
|
|
2114
2087
|
}
|
|
2115
2088
|
);
|
|
2116
2089
|
uploadsRoute.post(
|
|
2117
|
-
"/
|
|
2090
|
+
"/",
|
|
2118
2091
|
honoOpenapi.describeRoute({
|
|
2119
2092
|
tags: ["Uploads"],
|
|
2120
2093
|
summary: "Upload an image to R2 storage (admin)",
|
|
@@ -2161,7 +2134,7 @@ uploadsRoute.post(
|
|
|
2161
2134
|
customMetadata: { uploadedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
2162
2135
|
});
|
|
2163
2136
|
const url = new URL(c.req.url);
|
|
2164
|
-
url.pathname =
|
|
2137
|
+
url.pathname = `/${key}`;
|
|
2165
2138
|
url.search = "";
|
|
2166
2139
|
return c.json(
|
|
2167
2140
|
{
|
|
@@ -2218,7 +2191,8 @@ themesRoute.get(
|
|
|
2218
2191
|
async (c) => {
|
|
2219
2192
|
const db = createDb(c.env.DB);
|
|
2220
2193
|
const data = await db.select().from(themes).orderBy(drizzleOrm.asc(themes.sortOrder), drizzleOrm.asc(themes.createdAt));
|
|
2221
|
-
|
|
2194
|
+
const serialized = data.map(({ sortOrder, ...rest }) => rest);
|
|
2195
|
+
return c.json({ data: serialized });
|
|
2222
2196
|
}
|
|
2223
2197
|
);
|
|
2224
2198
|
themesRoute.post(
|
|
@@ -2465,7 +2439,7 @@ function createApp(options = {}) {
|
|
|
2465
2439
|
documentation: {
|
|
2466
2440
|
info: {
|
|
2467
2441
|
title: "Leapify API",
|
|
2468
|
-
version: "0.
|
|
2442
|
+
version: "0.260608.1" ,
|
|
2469
2443
|
description: "DLSU CSO LEAP backend API"
|
|
2470
2444
|
},
|
|
2471
2445
|
openapi: "3.1.0"
|
|
@@ -2583,6 +2557,7 @@ var SesError = class extends Error {
|
|
|
2583
2557
|
this.status = status;
|
|
2584
2558
|
this.name = "SesError";
|
|
2585
2559
|
}
|
|
2560
|
+
status;
|
|
2586
2561
|
/**
|
|
2587
2562
|
* True for errors that are permanent (not worth retrying via SES again).
|
|
2588
2563
|
* 400 BadRequest, 403 Forbidden, 404 NotFound → non-retryable.
|
|
@@ -3115,7 +3090,7 @@ function defaultGetRuntimeConfig(env) {
|
|
|
3115
3090
|
};
|
|
3116
3091
|
}
|
|
3117
3092
|
function injectConfig(html, config) {
|
|
3118
|
-
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)}
|
|
3093
|
+
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)};</script>`;
|
|
3119
3094
|
return html.replace("</head>", `${configScript}</head>`);
|
|
3120
3095
|
}
|
|
3121
3096
|
function createWorkerHandler(options) {
|
|
@@ -3237,7 +3212,7 @@ function getRuntimeConfig(env) {
|
|
|
3237
3212
|
};
|
|
3238
3213
|
}
|
|
3239
3214
|
function injectConfig2(html, config) {
|
|
3240
|
-
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)}
|
|
3215
|
+
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)};</script>`;
|
|
3241
3216
|
return html.replace("</head>", `${configScript}</head>`);
|
|
3242
3217
|
}
|
|
3243
3218
|
|
|
@@ -3264,4 +3239,4 @@ exports.themes = themes;
|
|
|
3264
3239
|
exports.themesRelations = themesRelations;
|
|
3265
3240
|
exports.users = users;
|
|
3266
3241
|
//# sourceMappingURL=index.cjs.map
|
|
3267
|
-
//# sourceMappingURL=index.cjs.
|
|
3242
|
+
//# sourceMappingURL=index.cjs.mapap
|