@access-dlsu/leapify 0.260601.1 → 0.260601.2
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/app.d.ts.map +1 -1
- package/dist/client/index.cjs +7 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -1
- package/dist/cron/reconcile-slots.d.ts.map +1 -1
- package/dist/index.cjs +221 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +223 -167
- package/dist/index.js.map +1 -1
- package/dist/routes/internal/batch-release.d.ts +4 -0
- package/dist/routes/internal/batch-release.d.ts.map +1 -0
- package/dist/routes/internal/reconcile-slots.d.ts +4 -0
- package/dist/routes/internal/reconcile-slots.d.ts.map +1 -0
- package/dist/routes/internal/reminder-emails.d.ts +4 -0
- package/dist/routes/internal/reminder-emails.d.ts.map +1 -0
- package/dist/routes/internal/renew-watches.d.ts +4 -0
- package/dist/routes/internal/renew-watches.d.ts.map +1 -0
- package/dist/services/gforms.d.ts.map +1 -1
- package/dist/worker.js +220 -164
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/errors.ts","../src/lib/middleware/error-handler.ts","../src/db/schema/index.ts","../src/db/schema/users.ts","../src/db/schema/themes.ts","../src/db/schema/organizations.ts","../src/db/schema/classes.ts","../src/db/schema/site-config.ts","../src/db/schema/faqs.ts","../src/db/schema/bookmarks.ts","../src/db/schema/auth.ts","../src/db/index.ts","../src/lib/middleware/cors.ts","../src/lib/middleware/referer-guard.ts","../src/lib/middleware/turnstile-challenge.ts","../src/auth/auth.ts","../src/auth/middleware.ts","../src/routes/health.ts","../src/services/cache.ts","../src/services/slots.ts","../src/services/gforms.ts","../src/lib/middleware/rate-limit.ts","../src/routes/classes.ts","../src/routes/users.ts","../src/routes/site-config.ts","../src/routes/faqs.ts","../src/routes/internal/gforms-webhook.ts","../src/routes/uploads.ts","../src/routes/themes.ts","../src/routes/organizations.ts","../src/app.ts","../src/services/ses.ts","../src/services/resend.ts","../src/lib/retry.ts","../src/services/email.ts","../src/queues/handlers.ts","../src/cron/batch-release.ts","../src/cron/reconcile-slots.ts","../src/cron/reminder-emails.ts","../src/cron/renew-watches.ts","../src/db/migrate.ts","../src/index.ts","../src/worker.ts"],"names":["sqliteTable","text","integer","sql","relations","index","getOriginsFromDb","eq","createMiddleware","count","Hono","and","z","zValidator","app","formatFrom","lte"],"mappings":"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","file":"worker.js","sourcesContent":["export class LeapifyError extends Error {\r\n constructor(\r\n public readonly statusCode: number,\r\n public readonly code: string,\r\n message: string,\r\n ) {\r\n super(message)\r\n this.name = 'LeapifyError'\r\n }\r\n}\r\n\r\nexport const unauthorized = (message = 'Unauthorized') =>\r\n new LeapifyError(401, 'UNAUTHORIZED', message)\r\n\r\nexport const domainRestricted = () =>\r\n new LeapifyError(\r\n 403,\r\n 'DOMAIN_RESTRICTED',\r\n 'Only @dlsu.edu.ph email addresses are allowed',\r\n )\r\n\r\nexport const forbidden = (message = 'Forbidden') =>\r\n new LeapifyError(403, 'FORBIDDEN', message)\r\n\r\nexport const notFound = (resource = 'Resource') =>\r\n new LeapifyError(404, 'NOT_FOUND', `${resource} not found`)\r\n\r\nexport const badRequest = (message = 'Bad request') =>\r\n new LeapifyError(400, 'BAD_REQUEST', message)\r\n\r\nexport const conflict = (message = 'Conflict') =>\r\n new LeapifyError(409, 'CONFLICT', message)\r\n\r\nexport const tooManyRequests = (message = 'Too many requests') =>\r\n new LeapifyError(429, 'TOO_MANY_REQUESTS', message)\r\n\r\nexport const serviceUnavailable = (message = 'Service temporarily unavailable') =>\r\n new LeapifyError(503, 'SERVICE_UNAVAILABLE', message)\r\n\r\nexport const internalError = (message = 'Internal server error') =>\r\n new LeapifyError(500, 'INTERNAL_ERROR', message)\r\n","import type { ErrorHandler } from 'hono'\r\nimport { LeapifyError } from '../errors'\r\n\r\nexport const errorHandler: ErrorHandler = (err, c) => {\r\n if (err instanceof LeapifyError) {\r\n return c.json(\r\n { error: { code: err.code, message: err.message } },\r\n err.statusCode as Parameters<typeof c.json>[1],\r\n )\r\n }\r\n\r\n console.error('[Leapify] Unhandled error:', err)\r\n\r\n return c.json(\r\n { error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },\r\n 500,\r\n )\r\n}\r\n","export * from './users'\r\nexport * from './classes'\r\nexport * from './organizations'\r\nexport * from './site-config'\r\nexport * from './faqs'\r\nexport * from './bookmarks'\r\nexport * from './auth'\r\nexport * from './themes'\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\nexport const users = sqliteTable('users', {\r\n id: text('id')\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n /**\r\n * References Better Auth's `user.id`.\r\n * Set on first login after the databaseHooks.user.create.after callback fires.\r\n */\r\n betterAuthId: text('better_auth_id').notNull().unique(),\r\n email: text('email').notNull().unique(),\r\n name: text('name').notNull(),\r\n role: text('role', { enum: ['student', 'admin', 'super_admin'] })\r\n .notNull()\r\n .default('student'),\r\n createdAt: integer('created_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n})\r\n\r\nexport type User = typeof users.$inferSelect\r\nexport type NewUser = typeof users.$inferInsert\r\nexport type UserRole = 'student' | 'admin' | 'super_admin'\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { relations } from 'drizzle-orm'\r\nimport { events } from './classes'\r\n\r\nexport const themes = sqliteTable('themes', {\r\n id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n name: text('name').notNull().unique(),\r\n path: text('path').notNull().unique(), // e.g. \"/pirates-cove\"\r\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\r\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\r\n})\r\n\r\nexport const themesRelations = relations(themes, ({ many }) => ({\r\n events: many(events),\r\n}))\r\n\r\nexport type Theme = typeof themes.$inferSelect\r\nexport type NewTheme = typeof themes.$inferInsert\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { relations, sql } from 'drizzle-orm'\r\nimport { events } from './classes'\r\n\r\nexport const organizations = sqliteTable('organizations', {\r\n id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n name: text('name').notNull().unique(),\r\n acronym: text('acronym').notNull().unique(),\r\n logoUrl: text('logo_url'),\r\n link: text('link'),\r\n createdAt: integer('created_at').notNull().default(sql`(unixepoch())`),\r\n})\r\n\r\nexport const organizationsRelations = relations(organizations, ({ many }) => ({\r\n events: many(events),\r\n}))\r\n\r\nexport type Organization = typeof organizations.$inferSelect\r\nexport type NewOrganization = typeof organizations.$inferInsert\r\n","import { sqliteTable, text, integer, index } from \"drizzle-orm/sqlite-core\";\r\nimport { sql, relations } from \"drizzle-orm\";\r\nimport { themes } from \"./themes\";\r\nimport { organizations } from \"./organizations\";\r\n\r\nexport const events = sqliteTable(\r\n \"events\",\r\n {\r\n id: text(\"id\")\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, \"\")),\r\n slug: text(\"slug\").notNull().unique(),\r\n // Theme reference\r\n themeId: text(\"theme_id\").references(() => themes.id),\r\n // Organization reference\r\n organizationId: text(\"organization_id\").references(() => organizations.id),\r\n\r\n // Core event fields (maps to LinkData)\r\n title: text(\"title\").notNull(),\r\n description: text(\"description\"),\r\n venue: text(\"venue\"),\r\n dateTime: text(\"date_time\"), // human-readable display string\r\n price: text(\"price\"), // e.g. \"Free\" or \"₱150\"\r\n backgroundImageUrl: text(\"background_image_url\"),\r\n classCode: text(\"class_code\"), // e.g. \"CSINTSY\"\r\n startTime: text(\"start_time\"), // start time string\r\n endTime: text(\"end_time\"), // end time string\r\n\r\n isSpotlight: integer(\"is_spotlight\", { mode: \"boolean\" }).notNull().default(false),\r\n\r\n // Slot tracking (local counter — NOT polled from Google Forms)\r\n maxSlots: integer(\"max_slots\").notNull().default(0),\r\n registeredSlots: integer(\"registered_slots\").notNull().default(0),\r\n gformsId: text(\"gforms_id\"), // Google Form ID for Watch + reconciliation\r\n gformsUrl: text(\"gforms_url\"), // informational link shown to students\r\n gformsEditorUrl: text(\"gforms_editor_url\"),\r\n registrationClosesAt: integer(\"registration_closes_at\"),\r\n watchId: text(\"watch_id\"), // stored after forms.watches.create\r\n watchExpiresAt: integer(\"watch_expires_at\"), // epoch — for renewal cron\r\n\r\n // Lifecycle / Release Queue\r\n status: text(\"status\", {\r\n enum: [\"draft\", \"queued\", \"published\", \"ended\", \"cancelled\"],\r\n })\r\n .notNull()\r\n .default(\"draft\"),\r\n releaseAt: integer(\"release_at\"), // scheduled publish epoch\r\n\r\n // Reminder tracking\r\n reminder24hSent: integer(\"reminder_24h_sent\", { mode: \"boolean\" })\r\n .notNull()\r\n .default(false),\r\n reminder1hSent: integer(\"reminder_1h_sent\", { mode: \"boolean\" })\r\n .notNull()\r\n .default(false),\r\n\r\n\r\n\r\n // Audit\r\n createdAt: integer(\"created_at\")\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n publishedAt: integer(\"published_at\"),\r\n },\r\n (table) => ({\r\n statusReleaseIdx: index(\"idx_events_status_release\").on(\r\n table.status,\r\n table.releaseAt,\r\n ),\r\n themeIdx: index(\"idx_events_theme_id\").on(table.themeId),\r\n organizationIdx: index(\"idx_events_organization_id\").on(table.organizationId),\r\n slugIdx: index(\"idx_events_slug\").on(table.slug),\r\n }),\r\n);\r\n\r\nexport const eventsRelations = relations(events, ({ one }) => ({\r\n theme: one(themes, {\r\n fields: [events.themeId],\r\n references: [themes.id],\r\n }),\r\n organization: one(organizations, {\r\n fields: [events.organizationId],\r\n references: [organizations.id],\r\n }),\r\n}));\r\n\r\nexport type Event = typeof events.$inferSelect;\r\nexport type NewEvent = typeof events.$inferInsert;\r\nexport type EventStatus =\r\n | \"draft\"\r\n | \"queued\"\r\n | \"published\"\r\n | \"ended\"\r\n | \"cancelled\";\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\n// Primary site config (from D1)\r\nexport const siteConfig = sqliteTable('site_config', {\r\n key: text('key').primaryKey(),\r\n value: text('value').notNull(), // JSON-serializable string\r\n updatedAt: integer('updated_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n})\r\n\r\nexport type SiteConfigRow = typeof siteConfig.$inferSelect\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\nexport const faqs = sqliteTable('faqs', {\r\n id: text('id')\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n question: text('question').notNull(),\r\n answer: text('answer').notNull(), // markdown supported\r\n category: text('category'), // optional grouping, e.g. \"Registration\"\r\n sortOrder: integer('sort_order').notNull().default(0),\r\n createdAt: integer('created_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n updatedAt: integer('updated_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n})\r\n\r\nexport type Faq = typeof faqs.$inferSelect\r\nexport type NewFaq = typeof faqs.$inferInsert\r\n","import {\r\n sqliteTable,\r\n text,\r\n integer,\r\n uniqueIndex,\r\n} from \"drizzle-orm/sqlite-core\";\r\nimport { sql, relations } from \"drizzle-orm\";\r\nimport { users } from \"./users\";\r\nimport { events } from \"./classes\";\r\n\r\nexport const bookmarks = sqliteTable(\r\n \"bookmarks\",\r\n {\r\n id: text(\"id\")\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, \"\")),\r\n userId: text(\"user_id\")\r\n .notNull()\r\n .references(() => users.id, { onDelete: \"cascade\" }),\r\n eventId: text(\"event_id\")\r\n .notNull()\r\n .references(() => events.id, { onDelete: \"cascade\" }),\r\n createdAt: integer(\"created_at\")\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n },\r\n (table) => ({\r\n userEventIdx: uniqueIndex(\"idx_bookmarks_user_event\").on(\r\n table.userId,\r\n table.eventId,\r\n ),\r\n }),\r\n);\r\n\r\n// Relational definitions\r\n// Required for Drizzle's `db.query.bookmarks.findMany({ with: { event } })`\r\n// relational API to resolve the join correctly.\r\nexport const bookmarksRelations = relations(bookmarks, ({ one }) => ({\r\n event: one(events, {\r\n fields: [bookmarks.eventId],\r\n references: [events.id],\r\n }),\r\n user: one(users, {\r\n fields: [bookmarks.userId],\r\n references: [users.id],\r\n }),\r\n}));\r\n\r\nexport type Bookmark = typeof bookmarks.$inferSelect;\r\nexport type NewBookmark = typeof bookmarks.$inferInsert;\r\n","/**\r\n * Better Auth database tables for SQLite / D1.\r\n *\r\n * These are the four tables Better Auth requires to manage sessions, accounts,\r\n * and email-verification tokens. They are defined here as Drizzle schema so\r\n * we own the migration (generated via `npm run db:generate`).\r\n *\r\n * Column names and types match what `npx @better-auth/cli generate` produces\r\n * for the sqlite dialect — do NOT rename them; Better Auth queries by these\r\n * exact column names.\r\n *\r\n * Relationship to our `users` table:\r\n * Better Auth `user.id` → leapify `users.better_auth_id`\r\n * (the FK lives on our side so we can join without touching BA internals)\r\n */\r\nimport { sqliteTable, text, integer, index } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\n// ─── user ───────────────────────────────────────────────────────────────────\r\n// Better Auth's own user record. Stores identity, email verification status,\r\n// and the profile image URL coming from the Google account.\r\n\r\nexport const authUser = sqliteTable('user', {\r\n id: text('id').primaryKey(),\r\n name: text('name').notNull(),\r\n email: text('email').notNull().unique(),\r\n emailVerified: integer('email_verified', { mode: 'boolean' })\r\n .notNull()\r\n .default(false),\r\n image: text('image'),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n})\r\n\r\n// ─── session ─────────────────────────────────────────────────────────────────\r\n// One row per active session. `token` is the opaque bearer token / cookie\r\n// value clients present on each request.\r\n\r\nexport const authSession = sqliteTable(\r\n 'session',\r\n {\r\n id: text('id').primaryKey(),\r\n expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),\r\n token: text('token').notNull().unique(),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n ipAddress: text('ip_address'),\r\n userAgent: text('user_agent'),\r\n userId: text('user_id')\r\n .notNull()\r\n .references(() => authUser.id, { onDelete: 'cascade' }),\r\n },\r\n (table) => [index('session_userId_idx').on(table.userId)],\r\n)\r\n\r\n// ─── account ─────────────────────────────────────────────────────────────────\r\n// OAuth provider account linked to a user. One row per provider per user\r\n// (e.g. a user who signed in with Google has one `account` with providerId=\"google\").\r\n\r\nexport const authAccount = sqliteTable(\r\n 'account',\r\n {\r\n id: text('id').primaryKey(),\r\n accountId: text('account_id').notNull(),\r\n providerId: text('provider_id').notNull(),\r\n userId: text('user_id')\r\n .notNull()\r\n .references(() => authUser.id, { onDelete: 'cascade' }),\r\n accessToken: text('access_token'),\r\n refreshToken: text('refresh_token'),\r\n idToken: text('id_token'),\r\n accessTokenExpiresAt: integer('access_token_expires_at', {\r\n mode: 'timestamp_ms',\r\n }),\r\n refreshTokenExpiresAt: integer('refresh_token_expires_at', {\r\n mode: 'timestamp_ms',\r\n }),\r\n scope: text('scope'),\r\n password: text('password'),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n },\r\n (table) => [index('account_userId_idx').on(table.userId)],\r\n)\r\n\r\n// ─── verification ─────────────────────────────────────────────────────────────\r\n// Short-lived tokens used for email verification / magic links.\r\n\r\nexport const authVerification = sqliteTable(\r\n 'verification',\r\n {\r\n id: text('id').primaryKey(),\r\n identifier: text('identifier').notNull(),\r\n value: text('value').notNull(),\r\n expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n },\r\n (table) => [index('verification_identifier_idx').on(table.identifier)],\r\n)\r\n","import { drizzle } from 'drizzle-orm/d1'\r\nimport * as schema from './schema'\r\n\r\nexport function createDb(d1: D1Database) {\r\n return drizzle(d1, { schema })\r\n}\r\n\r\nexport type LeapifyDb = ReturnType<typeof createDb>\r\n\r\nexport * from './schema'\r\n","import { cors } from 'hono/cors'\n\nimport type { MiddlewareHandler } from 'hono'\nimport { createDb } from '../../db'\nimport { siteConfig } from '../../db/schema/site-config'\nimport { eq } from 'drizzle-orm'\n\nasync function getOriginsFromDb(env: {\n DB: import('@cloudflare/workers-types').D1Database\n}): Promise<string[] | null> {\n try {\n const db = createDb(env.DB)\n const row = await db.query.siteConfig.findFirst({\n where: eq(siteConfig.key, 'allowed_origins')\n })\n if (row) return JSON.parse(row.value) as string[]\n } catch {\n /* D1 unavailable — fall through */\n }\n return null\n}\n\nexport function createCorsMiddleware(\n allowedOrigins: string[]\n): MiddlewareHandler {\n return async (c, next) => {\n const origin = c.req.header('origin')\n\n // Get dynamic allowed origins from KV if present, fallback to static list\n const dynamicOriginsJson = (await c.env.KV.get(\n 'config:allowed_origins',\n 'json'\n )) as string[] | null\n let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins\n if (!dynamicOriginsJson) {\n const dbOrigins = await getOriginsFromDb(c.env)\n if (dbOrigins) {\n currentAllowedOrigins = dbOrigins\n await c.env.KV.put(\n 'config:allowed_origins',\n JSON.stringify(dbOrigins),\n { expirationTtl: 86400 }\n )\n }\n }\n\n // Public Image Exemption: Allow any origin for images and skip strict checks.\n if (c.req.path.startsWith('/api/uploads/images')) {\n c.header('Access-Control-Allow-Origin', '*')\n c.header('Access-Control-Allow-Methods', 'GET, OPTIONS')\n if (c.req.method === 'OPTIONS') {\n return c.body(null, 204)\n }\n return next()\n }\n\n // Strict ADR-001 Check: If an Origin is present, it MUST be allowed.\n if (\n !c.req.path.startsWith('/health') &&\n !c.req.path.startsWith('/api/auth') &&\n !c.req.path.startsWith('/internal') &&\n origin &&\n !currentAllowedOrigins.includes('*') &&\n !currentAllowedOrigins.includes(origin) &&\n origin !== new URL(c.req.url).origin\n ) {\n return c.json(\n {\n error: {\n code: 'DOMAIN_RESTRICTED',\n message: `Origin ${origin} is not allowed`\n }\n },\n 403\n )\n }\n\n const honoCors = cors({\n origin: currentAllowedOrigins,\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization'],\n exposeHeaders: ['ETag', 'Last-Modified', 'Cache-Control'],\n maxAge: 86400,\n credentials: true\n })\n\n return honoCors(c, next)\n }\n}\n","import { createMiddleware } from 'hono/factory'\nimport type { LeapifyBindings } from '../../types'\nimport { forbidden } from '../errors'\nimport { createDb } from '../../db'\nimport { siteConfig } from '../../db/schema/site-config'\nimport { eq } from 'drizzle-orm'\n\nasync function getOriginsFromDb(env: {\n DB: import('@cloudflare/workers-types').D1Database\n}): Promise<string[] | null> {\n try {\n const db = createDb(env.DB)\n const row = await db.query.siteConfig.findFirst({\n where: eq(siteConfig.key, 'allowed_origins')\n })\n if (row) return JSON.parse(row.value) as string[]\n } catch {\n /* D1 unavailable — fall through */\n }\n return null\n}\n\n/**\n * Referer guard for mutation endpoints (ADR-006, Layer 6).\n *\n * Validates that the `Referer` header on state-mutating requests (POST, PATCH, PUT, DELETE)\n * matches one of the configured allowed origins. Safe methods (GET, HEAD, OPTIONS) are\n * always allowed through without a Referer check.\n *\n * This is a friction layer — it stops naive raw-HTTP clients that don't set Referer.\n * Sophisticated clients can spook it, so this must NOT be relied on as the sole control\n * for authenticated mutation endpoints (Firebase JWT is the primary control there).\n *\n * Skipped entirely for /health and /internal routes.\n */\nexport function createRefererGuard(allowedOrigins: string[]) {\n const MUTATION_METHODS = new Set(['POST', 'PATCH', 'PUT', 'DELETE'])\n const SKIP_PREFIXES = ['/health', '/internal', '/api/auth', '/.well-known']\n\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\n // Only checl mutation methods\n if (!MUTATION_METHODS.has(c.req.method)) return next()\n\n // Skip for operational routes\n if (SKIP_PREFIXES.some((p) => c.req.path.startsWith(p))) return next()\n\n // Get dynamic allowed origins: KV (fast cache) • D1 (source of truth) • static fallback\n const dynamicOriginsJson = (await c.env.KV.get(\n 'config:allowed_origins',\n 'json'\n )) as string[] | null\n let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins\n if (!dynamicOriginsJson) {\n const dbOrigins = await getOriginsFromDb(c.env)\n if (dbOrigins) {\n currentAllowedOrigins = dbOrigins\n await c.env.KV.put(\n 'config:allowed_origins',\n JSON.stringify(dbOrigins),\n { expirationTtl: 86400 }\n )\n }\n }\n\n // Wildcard currentAllowedOrigins = dev/library mode, skip enforcement\n if (currentAllowedOrigins.includes('*')) return next()\n\n const referer = c.req.header('referer') ?? ''\n\n // Same-origin requests (console calling its own worker) are always allowed\n const requestOrigin = new URL(c.req.url).origin\n if (referer.startsWith(requestOrigin)) return next()\n\n const isAllowed = currentAllowedOrigins.some((origin) =>\n referer.startsWith(origin)\n )\n if (!isAllowed) {\n throw forbidden('Request origin not permitted')\n }\n\n return next()\n })\n}\n","import { createMiddleware } from 'hono/factory'\r\nimport type { Context } from 'hono'\r\nimport type { LeapifyBindings } from '../../types'\r\n\r\nexport const TURNSTILE_PATH = '/.well-known/leapify/turnstile'\r\n\r\nexport const TURNSTILE_VERIFY_PATH = `${TURNSTILE_PATH}/verify`\r\n\r\nexport const TURNSTILE_COOKIE_NAME = 'leapify-turnstile'\r\n\r\nconst VERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'\r\n\r\nconst COOKIE_MAX_AGE_SEC = 86400\r\n\r\nconst EXEMPT_PATHS = [\r\n \"/health\",\r\n \"/internal\",\r\n \"/api/auth\",\r\n \"/api/uploads/images\",\r\n \"/api/classes\",\r\n \"/api/faqs\",\r\n \"/api/config\",\r\n TURNSTILE_VERIFY_PATH,\r\n];\r\n\r\nfunction base64urlEncode(bytes: Uint8Array): string {\r\n let binary = ''\r\n for (const byte of bytes) {\r\n binary += String.fromCharCode(byte)\r\n }\r\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\r\n}\r\n\r\nfunction base64urlDecode(str: string): Uint8Array<ArrayBuffer> {\r\n const padded = str.replace(/-/g, '+').replace(/_/g, '/')\r\n const binary = atob(padded)\r\n const bytes = new Uint8Array(new ArrayBuffer(binary.length))\r\n for (let i = 0; i < binary.length; i++) {\r\n bytes[i] = binary.charCodeAt(i)\r\n }\r\n return bytes\r\n}\r\n\r\nasync function importHmacKey(secret: string): Promise<CryptoKey> {\r\n return crypto.subtle.importKey(\r\n 'raw',\r\n new TextEncoder().encode(secret),\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign', 'verify']\r\n )\r\n}\r\n\r\nasync function signCookie(secret: string, ip: string): Promise<string> {\r\n const ts = Date.now()\r\n const nonce = base64urlEncode(crypto.getRandomValues(new Uint8Array(8)))\r\n const payload = `${ip}:${ts}:${nonce}`\r\n const key = await importHmacKey(secret)\r\n const sig = await crypto.subtle.sign(\r\n 'HMAC',\r\n key,\r\n new TextEncoder().encode(payload)\r\n )\r\n const sigB64 = base64urlEncode(new Uint8Array(sig))\r\n return `${base64urlEncode(new TextEncoder().encode(payload))}.${sigB64}`\r\n}\r\n\r\nasync function validateCookie(\r\n secret: string,\r\n cookie: string,\r\n ip: string\r\n): Promise<boolean> {\r\n try {\r\n const [payloadB64, sigB64] = cookie.split('.')\r\n if (!payloadB64 || !sigB64) return false\r\n\r\n const payloadBytes = base64urlDecode(payloadB64)\r\n const sigBytes = base64urlDecode(sigB64)\r\n\r\n const key = await importHmacKey(secret)\r\n const valid = await crypto.subtle.verify(\r\n 'HMAC',\r\n key,\r\n sigBytes,\r\n payloadBytes\r\n )\r\n if (!valid) return false\r\n\r\n const payload = new TextDecoder().decode(payloadBytes)\r\n const [cookieIp, tsStr] = payload.split(':')\r\n\r\n if (cookieIp !== ip) return false\r\n\r\n const ts = parseInt(tsStr, 10)\r\n if (isNaN(ts) || Date.now() - ts > COOKIE_MAX_AGE_SEC * 1000) return false\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nfunction getClientIp(c: Context<{ Bindings: LeapifyBindings }>): string {\r\n return (\r\n c.req.header('CF-Connecting-IP') ??\r\n c.req.header('X-Real-IP') ??\r\n c.req.header('X-Forwarded-For')?.split(',')[0]?.trim() ??\r\n 'unknown'\r\n )\r\n}\r\n\r\nfunction isExempt(path: string): boolean {\r\n const normalized = path.toLowerCase().replace(/\\/$/, '')\r\n return EXEMPT_PATHS.some((p) => {\r\n const ep = p.toLowerCase().replace(/\\/$/, '')\r\n return normalized === ep || normalized.startsWith(ep + '/')\r\n })\r\n}\r\n\r\nfunction setCookieHeader(c: Context<{ Bindings: LeapifyBindings }>, token: string): void {\r\n const isSecure = c.req.raw.url.startsWith(\"https\") || c.req.header(\"x-forwarded-proto\") === \"https\";\r\n c.header(\r\n \"Set-Cookie\",\r\n `${TURNSTILE_COOKIE_NAME}=${token}; Path=/; Max-Age=${COOKIE_MAX_AGE_SEC}; ${\r\n isSecure ? \"Secure; \" : \"\"\r\n }HttpOnly; SameSite=Lax`,\r\n );\r\n}\r\n\r\n/**\r\n * POST /.well-known/leapify/turnstile/verify\r\n *\r\n * Validates a Turnstile token and issues a signed cookie on success.\r\n */\r\nexport async function handleTurnstileVerify(\r\n c: Context<{ Bindings: LeapifyBindings }>\r\n) {\r\n const body = await c.req.json<{ token?: string }>()\r\n const { token } = body\r\n\r\n if (!token) {\r\n return c.json(\r\n { error: { code: 'VALIDATION_ERROR', message: 'Missing Turnstile token' } },\r\n 422\r\n )\r\n }\r\n\r\n const secret = c.env.TURNSTILE_SECRET_KEY\r\n if (!secret) {\r\n return c.json(\r\n { error: { code: 'CONFIG_ERROR', message: 'Turnstile not configured' } },\r\n 500\r\n )\r\n }\r\n\r\n const ip = getClientIp(c)\r\n const formData = new URLSearchParams()\r\n formData.append('secret', secret)\r\n formData.append('response', token)\r\n if (ip !== 'unknown') {\r\n formData.append('remoteip', ip)\r\n }\r\n\r\n const res = await fetch(VERIFY_URL, {\r\n method: 'POST',\r\n body: formData,\r\n })\r\n const outcome = await res.json() as { success: boolean; 'error-codes'?: string[] }\r\n\r\n if (!outcome.success) {\r\n return c.json(\r\n { error: { code: 'TURNSTILE_FAILED', message: 'Turnstile verification failed', details: outcome['error-codes'] } },\r\n 403\r\n )\r\n }\r\n\r\n const cookieToken = await signCookie(secret, ip)\r\n setCookieHeader(c, cookieToken)\r\n\r\n return c.json({ success: true })\r\n}\r\n\r\n/**\r\n * Turnstile challenge middleware.\r\n *\r\n * Requires a valid Turnstile-signed cookie on all non-exempt requests.\r\n * The client must first solve a Turnstile challenge and POST the token\r\n * to the verify endpoint to obtain the cookie.\r\n *\r\n * Exempt paths: /health, /internal, /api/auth, /api/uploads/images,\r\n * and the verify endpoint itself.\r\n */\r\nexport function createTurnstileMiddleware() {\r\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\r\n if (isExempt(c.req.path)) return next()\r\n\r\n if (c.req.method === 'OPTIONS') return next()\r\n\r\n // Skip challenge for authenticated requests (Bearer token present)\r\n // The auth middleware will handle session validation instead.\r\n if (c.req.header('Authorization')) return next()\r\n\r\n const secret = c.env.TURNSTILE_SECRET_KEY\r\n if (!secret) return next()\r\n\r\n const cookieHeader = c.req.header('Cookie') ?? ''\r\n const cookieMatch = cookieHeader.match(\r\n new RegExp(`${TURNSTILE_COOKIE_NAME}=([^;]+)`)\r\n )\r\n if (cookieMatch) {\r\n const ip = getClientIp(c)\r\n const valid = await validateCookie(secret, cookieMatch[1], ip)\r\n if (valid) return next()\r\n }\r\n\r\n return c.json(\r\n { error: { code: 'TURNSTILE_REQUIRED', message: 'Turnstile verification required' } },\r\n 401\r\n )\r\n })\r\n}\r\n","import { betterAuth } from 'better-auth'\r\nimport { drizzleAdapter } from 'better-auth/adapters/drizzle'\r\nimport { bearer } from 'better-auth/plugins'\r\nimport { count } from 'drizzle-orm'\r\nimport { createDb } from '../db'\r\nimport {\r\n authUser,\r\n authSession,\r\n authAccount,\r\n authVerification\r\n} from '../db/schema/auth'\r\nimport { users } from '../db/schema/users'\r\nimport type { LeapifyBindings } from '../types'\r\n\r\nconst DLSU_DOMAIN = '@dlsu.edu.ph'\r\n\r\n/**\r\n * Creates a request-scoped Better Auth instance.\r\n *\r\n * Must be a factory (not a module singleton) because CF Workers D1\r\n * bindings are only available at request time, not at module init.\r\n *\r\n * Features:\r\n * - Drizzle SQLite adapter backed by D1\r\n * - bearer() plugin: supports Authorization: Bearer <token> alongside cookies\r\n * - Google social provider (for GIS One Tap credential flow)\r\n * - databaseHooks enforces @dlsu.edu.ph domain server-side\r\n * - After successful user creation, upserts a row in our custom `users` table\r\n * to carry the application role\r\n */\r\nexport function createAuth(env: LeapifyBindings) {\r\n const db = createDb(env.DB)\r\n\r\n return betterAuth({\r\n baseURL: env.BETTER_AUTH_URL,\r\n secret: env.BETTER_AUTH_SECRET,\r\n\r\n advanced: {\r\n ipAddress: {\r\n ipAddressHeaders: ['cf-connecting-ip', 'x-forwarded-for', 'x-real-ip'],\r\n },\r\n },\r\n\r\n database: drizzleAdapter(db, {\r\n provider: 'sqlite',\r\n schema: {\r\n user: authUser,\r\n session: authSession,\r\n account: authAccount,\r\n verification: authVerification\r\n }\r\n }),\r\n\r\n plugins: [bearer()],\r\n\r\n socialProviders: {\r\n google: {\r\n clientId: env.GOOGLE_CLIENT_ID,\r\n clientSecret: env.GOOGLE_CLIENT_SECRET,\r\n hd: env.GOOGLE_HD || undefined\r\n }\r\n },\r\n\r\n databaseHooks: {\r\n user: {\r\n create: {\r\n /**\r\n * Runs before the Better Auth `user` row is inserted.\r\n * Reject non-DLSU accounts before they ever touch the DB.\r\n */\r\n before: async (user) => {\r\n if (!user.email.endsWith(DLSU_DOMAIN)) {\r\n // Throwing here causes Better Auth to return 403 to the client\r\n throw new Error(\r\n 'DOMAIN_RESTRICTED: only @dlsu.edu.ph accounts are allowed'\r\n )\r\n }\r\n return { data: user }\r\n },\r\n\r\n /**\r\n * Runs after the Better Auth `user` row is created.\r\n * Upsert a matching row in our application `users` table so that\r\n * the role column and D1 foreign keys are always consistent.\r\n *\r\n * The very first user to sign in is automatically promoted to\r\n * `super_admin` so the platform has an administrator from day one.\r\n */\r\n after: async (user) => {\r\n const [{ total }] = await db.select({ total: count() }).from(users)\r\n const isFirstUser = total === 0\r\n\r\n const base = {\r\n betterAuthId: user.id,\r\n email: user.email,\r\n name: user.name ?? user.email.split('@')[0],\r\n role: isFirstUser ? 'super_admin' as const : 'student' as const,\r\n }\r\n\r\n if (isFirstUser) {\r\n // Atomic: insert as super_admin, or update existing row (created\r\n // by the resolveUser race) to super_admin.\r\n await db\r\n .insert(users)\r\n .values(base)\r\n .onConflictDoUpdate({\r\n target: users.email,\r\n set: {\r\n betterAuthId: user.id,\r\n role: 'super_admin',\r\n name: user.name ?? user.email.split('@')[0],\r\n },\r\n })\r\n } else {\r\n await db\r\n .insert(users)\r\n .values(base)\r\n .onConflictDoUpdate({\r\n target: users.email,\r\n set: {\r\n betterAuthId: user.id,\r\n name: user.name ?? user.email.split('@')[0],\r\n },\r\n })\r\n }\r\n }\r\n }\r\n }\r\n }\r\n })\r\n}\r\n\r\nexport type Auth = ReturnType<typeof createAuth>\r\n","import { createMiddleware } from 'hono/factory'\r\nimport { eq } from 'drizzle-orm'\r\nimport { createAuth } from './auth'\r\nimport { domainRestricted, unauthorized, forbidden } from '../lib/errors'\r\nimport { createDb } from '../db'\r\nimport { users } from '../db/schema/users'\r\nimport type { LeapifyBindings } from '../types'\r\nimport type { LeapifyUser } from './types'\r\n\r\nconst SESSION_KV_PREFIX = 'auth:session:'\r\nconst SESSION_KV_TTL = 3600 // 1 hour max cache (capped by actual session expiry)\r\n\r\n// Context type augmentation — available in every route handler via c.get('user')\r\ndeclare module 'hono' {\r\n interface ContextVariableMap {\r\n user: LeapifyUser\r\n }\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Extract the raw bearer token from the Authorization header, or the\r\n * `better-auth.session_token` cookie, for use as a KV cache key.\r\n * Returns undefined when no credential is present.\r\n */\r\nfunction extractRawToken(c: { req: { header: (k: string) => string | undefined } }): string | undefined {\r\n const authHeader = c.req.header('Authorization')\r\n if (authHeader?.startsWith('Bearer ')) return authHeader.slice(7)\r\n // Cookie-based flow: Better Auth sets this cookie name by default\r\n const cookie = c.req.header('Cookie') ?? ''\r\n const match = cookie.match(/(?:^|;\\s*)better-auth\\.session_token=([^;]+)/)\r\n return match?.[1] ? decodeURIComponent(match[1]) : undefined\r\n}\r\n\r\nasync function resolveUser(\r\n env: LeapifyBindings,\r\n betterAuthUserId: string,\r\n betterAuthUserEmail: string,\r\n betterAuthUserName: string,\r\n betterAuthEmailVerified: boolean,\r\n): Promise<LeapifyUser> {\r\n const db = createDb(env.DB)\r\n\r\n let dbUser = await db.query.users.findFirst({\r\n where: eq(users.betterAuthId, betterAuthUserId),\r\n })\r\n\r\n if (!dbUser) {\r\n // First request after account creation — the databaseHooks.after callback\r\n // should have already inserted this row, but guard against races.\r\n const [created] = await db\r\n .insert(users)\r\n .values({\r\n betterAuthId: betterAuthUserId,\r\n email: betterAuthUserEmail,\r\n name: betterAuthUserName ?? betterAuthUserEmail.split('@')[0],\r\n })\r\n .onConflictDoUpdate({\r\n target: users.email,\r\n set: {\r\n betterAuthId: betterAuthUserId,\r\n name: betterAuthUserName ?? betterAuthUserEmail.split('@')[0],\r\n },\r\n })\r\n .returning()\r\n dbUser = created\r\n }\r\n\r\n if (!dbUser) throw unauthorized('Failed to resolve user record')\r\n\r\n return {\r\n uid: betterAuthUserId,\r\n dbId: dbUser.id,\r\n role: dbUser.role,\r\n email: betterAuthUserEmail,\r\n name: betterAuthUserName ?? betterAuthUserEmail.split('@')[0],\r\n emailVerified: betterAuthEmailVerified,\r\n }\r\n}\r\n\r\n// ─── Auth middleware (required) ───────────────────────────────────────────────\r\n\r\nexport const authMiddleware = createMiddleware<{ Bindings: LeapifyBindings }>(\r\n async (c, next) => {\r\n const rawToken = extractRawToken(c)\r\n\r\n // Fast path: KV cache hit — skip DB round-trip entirely\r\n if (rawToken) {\r\n const cached = await c.env.KV.get<LeapifyUser>(\r\n `${SESSION_KV_PREFIX}${rawToken}`,\r\n 'json',\r\n )\r\n if (cached) {\r\n c.set('user', cached)\r\n return next()\r\n }\r\n }\r\n\r\n // Slow path: validate session via Better Auth\r\n const auth = createAuth(c.env)\r\n const session = await auth.api.getSession({ headers: c.req.raw.headers })\r\n\r\n if (!session?.user) {\r\n throw unauthorized('No valid session found')\r\n }\r\n\r\n // Domain guard — belt-and-suspenders (databaseHooks already rejects at\r\n // account creation time, but protects in case an existing account somehow\r\n // has a non-DLSU email)\r\n if (!session.user.email.endsWith('@dlsu.edu.ph')) {\r\n throw domainRestricted()\r\n }\r\n\r\n const leapifyUser = await resolveUser(\r\n c.env,\r\n session.user.id,\r\n session.user.email,\r\n session.user.name,\r\n session.user.emailVerified,\r\n )\r\n\r\n // Cache in KV, TTL = min(time until session expires, 1 hour)\r\n if (rawToken) {\r\n const sessionExpiresAt = new Date(session.session.expiresAt).getTime()\r\n const secondsRemaining = Math.floor((sessionExpiresAt - Date.now()) / 1000)\r\n const kvTtl = Math.max(1, Math.min(secondsRemaining, SESSION_KV_TTL))\r\n await c.env.KV.put(\r\n `${SESSION_KV_PREFIX}${rawToken}`,\r\n JSON.stringify(leapifyUser),\r\n { expirationTtl: kvTtl },\r\n )\r\n }\r\n\r\n c.set('user', leapifyUser)\r\n return next()\r\n },\r\n)\r\n\r\n// ─── Optional auth middleware ─────────────────────────────────────────────────\r\n\r\nexport const optionalAuthMiddleware = createMiddleware<{\r\n Bindings: LeapifyBindings\r\n}>(async (c, next) => {\r\n const rawToken = extractRawToken(c)\r\n // No credential present → treat as guest\r\n if (!rawToken) {\r\n c.set('user', null as unknown as LeapifyUser)\r\n return next()\r\n }\r\n // Credential present → enforce full verification\r\n return authMiddleware(c, next)\r\n})\r\n\r\n// ─── Admin guard (use after authMiddleware) ───────────────────────────────────\r\n\r\nexport const adminMiddleware = createMiddleware<{ Bindings: LeapifyBindings }>(\r\n async (c, next) => {\r\n const user = c.get('user')\r\n if (!user || !['admin', 'super_admin'].includes(user.role)) {\r\n throw forbidden('Admin access required')\r\n }\r\n return next()\r\n },\r\n)\r\n\r\n// ─── Internal route guard ─────────────────────────────────────────────────────\r\n\r\nexport const internalMiddleware = createMiddleware<{\r\n Bindings: LeapifyBindings\r\n}>(async (c, next) => {\r\n const secret = c.req.header('X-Internal-Secret')\r\n if (!secret || secret !== c.env.INTERNAL_API_SECRET) {\r\n throw forbidden('Invalid internal secret')\r\n }\r\n return next()\r\n})\r\n","import { Hono } from 'hono'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\n\r\nexport const healthRoute = new Hono<LeapifyEnv>()\r\n\r\n// ─── Individual service probes ──────────────────────────────────────────────\r\n\r\ninterface ServiceHealth {\r\n configured: boolean\r\n ok: boolean\r\n latencyMs: number\r\n error?: string\r\n}\r\n\r\nasync function probeResend(apiKey: string): Promise<ServiceHealth> {\r\n const start = Date.now()\r\n try {\r\n const res = await fetch('https://api.resend.com/domains', {\r\n headers: { Authorization: `Bearer ${apiKey}` },\r\n })\r\n return {\r\n configured: true,\r\n ok: res.ok,\r\n latencyMs: Date.now() - start,\r\n ...(res.ok ? {} : { error: `HTTP ${res.status}` }),\r\n }\r\n } catch (e) {\r\n return {\r\n configured: true,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: String(e),\r\n }\r\n }\r\n}\r\n\r\nasync function probeSes(\r\n region: string,\r\n accessKeyId: string,\r\n secretAccessKey: string,\r\n): Promise<ServiceHealth> {\r\n // SES v2 has no lightweight \"ping\" endpoint. Verify credentials are\r\n // configured and the region is valid by checking the env vars.\r\n const start = Date.now()\r\n try {\r\n if (!region || !accessKeyId || !secretAccessKey) {\r\n return {\r\n configured: false,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: 'Missing SES credentials',\r\n }\r\n }\r\n return {\r\n configured: true,\r\n ok: true,\r\n latencyMs: Date.now() - start,\r\n }\r\n } catch (e) {\r\n return {\r\n configured: true,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: String(e),\r\n }\r\n }\r\n}\r\n\r\nasync function probeGForms(\r\n serviceAccountJson: string,\r\n): Promise<ServiceHealth> {\r\n const start = Date.now()\r\n try {\r\n const creds = JSON.parse(serviceAccountJson) as {\r\n client_email: string\r\n private_key: string\r\n }\r\n\r\n // Try to get an OAuth2 token — verifies the SA key is valid\r\n const now = Math.floor(Date.now() / 1000)\r\n const claims = {\r\n iss: creds.client_email,\r\n scope: 'https://www.googleapis.com/auth/forms.responses.readonly',\r\n aud: 'https://oauth2.googleapis.com/token',\r\n iat: now,\r\n exp: now + 3600,\r\n }\r\n\r\n const header = { alg: 'RS256', typ: 'JWT' }\r\n const encode = (obj: unknown) =>\r\n btoa(JSON.stringify(obj))\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=/g, '')\r\n\r\n const signingInput = `${encode(header)}.${encode(claims)}`\r\n\r\n const pemBody = creds.private_key\r\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\r\n .replace(/-----END PRIVATE KEY-----/, '')\r\n .replace(/\\s/g, '')\r\n\r\n const keyBytes = Uint8Array.from(atob(pemBody), (c) => c.charCodeAt(0))\r\n const privateKey = await crypto.subtle.importKey(\r\n 'pkcs8',\r\n keyBytes,\r\n { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },\r\n false,\r\n ['sign'],\r\n )\r\n\r\n const signature = await crypto.subtle.sign(\r\n 'RSASSA-PKCS1-v1_5',\r\n privateKey,\r\n new TextEncoder().encode(signingInput),\r\n )\r\n\r\n const sigB64 = btoa(String.fromCharCode(...new Uint8Array(signature)))\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=/g, '')\r\n\r\n const jwt = `${signingInput}.${sigB64}`\r\n\r\n const res = await fetch('https://oauth2.googleapis.com/token', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\r\n assertion: jwt,\r\n }),\r\n })\r\n\r\n return {\r\n configured: true,\r\n ok: res.ok,\r\n latencyMs: Date.now() - start,\r\n ...(res.ok ? {} : { error: `HTTP ${res.status}` }),\r\n }\r\n } catch (e) {\r\n return {\r\n configured: true,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: String(e),\r\n }\r\n }\r\n}\r\n\r\n// ─── Route ──────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * GET /health\r\n *\r\n * Publicly accessible — no CORS, no auth, no PoW.\r\n * Runs lightweight probes against each configured external service.\r\n *\r\n * Response:\r\n * {\r\n * status: 'ok' | 'degraded',\r\n * timestamp: string,\r\n * services: {\r\n * ses: { configured, ok, latencyMs, error? },\r\n * resend: { configured, ok, latencyMs, error? },\r\n * gforms: { configured, ok, latencyMs, error? },\r\n * }\r\n * }\r\n */\r\nhealthRoute.get('/', async (c) => {\r\n const env = c.env\r\n\r\n const hasSes = Boolean(env.SES_REGION) && Boolean(env.SES_ACCESS_KEY_ID) && Boolean(env.SES_SECRET_ACCESS_KEY)\r\n const hasResend = Boolean(env.RESEND_API_KEY)\r\n let hasGForms = false\r\n if (env.GFORMS_SERVICE_ACCOUNT_JSON) {\r\n try {\r\n const parsed = JSON.parse(env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n hasGForms = Boolean(parsed.client_email && parsed.private_key)\r\n } catch {\r\n /* invalid JSON — not configured */\r\n }\r\n }\r\n\r\n const probes: Promise<[string, ServiceHealth]>[] = []\r\n\r\n if (hasSes) {\r\n probes.push(\r\n probeSes(env.SES_REGION!, env.SES_ACCESS_KEY_ID!, env.SES_SECRET_ACCESS_KEY!).then(\r\n (h) => ['ses', h] as const,\r\n ),\r\n )\r\n }\r\n if (hasResend) {\r\n probes.push(\r\n probeResend(env.RESEND_API_KEY!).then((h) => ['resend', h] as const),\r\n )\r\n }\r\n if (hasGForms) {\r\n probes.push(\r\n probeGForms(env.GFORMS_SERVICE_ACCOUNT_JSON!).then(\r\n (h) => ['gforms', h] as const,\r\n ),\r\n )\r\n }\r\n\r\n const results = await Promise.all(probes)\r\n\r\n const services: Record<string, ServiceHealth> = {}\r\n for (const [name, health] of results) {\r\n services[name] = health\r\n }\r\n\r\n // If no services are configured, still report ok\r\n const allOk = results.length === 0 || results.every(([, h]) => h.ok)\r\n\r\n return c.json({\r\n data: {\r\n status: allOk ? 'OK' : 'DEGRADED',\r\n timestamp: new Date().toISOString(),\r\n services,\r\n },\r\n })\r\n})\r\n\r\n// ─── Queue burst (internal) ─────────────────────────────────────────────────\r\n\r\n/**\r\n * POST /health/queue-burst\r\n * Internal load testing endpoint that blasts 100 mock items into the queue.\r\n */\r\nhealthRoute.post('/queue-burst', authMiddleware, adminMiddleware, async (c) => {\r\n if (!c.env.EMAIL_QUEUE) {\r\n return c.json({ error: 'Queue binding missing' }, 400)\r\n }\r\n\r\n const batch = Array.from({ length: 100 }, (_, i) => ({\r\n body: {\r\n type: 'audit_log',\r\n payload: {\r\n action: 'queue_load_test',\r\n userId: 'system',\r\n meta: { index: i, time: Date.now() },\r\n },\r\n },\r\n }))\r\n\r\n await (c.env.EMAIL_QUEUE as any).sendBatch(batch)\r\n\r\n return c.json({ status: 'queued', count: 100 })\r\n})\r\n","import type { KVNamespace } from '@cloudflare/workers-types'\r\n\r\n/**\r\n * Typed KV cache wrapper with get/set/del and stale-while-revalidate helpers.\r\n */\r\nexport class CacheService {\r\n constructor(private readonly kv: KVNamespace) {}\r\n\r\n async get<T>(key: string): Promise<T | null> {\r\n return this.kv.get<T>(key, 'json')\r\n }\r\n\r\n async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {\r\n await this.kv.put(key, JSON.stringify(value), {\r\n ...(ttlSeconds ? { expirationTtl: ttlSeconds } : {}),\r\n })\r\n }\r\n\r\n async del(key: string): Promise<void> {\r\n await this.kv.delete(key)\r\n }\r\n\r\n /**\r\n * Returns cached value immediately. If stale or missing, refreshes in\r\n * the background using the provided fetcher. Pass `ctx` to use\r\n * ctx.waitUntil() so the refresh doesn't block the response.\r\n */\r\n async getOrSet<T>(\r\n key: string,\r\n fetcher: () => Promise<T>,\r\n ttlSeconds: number,\r\n ctx?: ExecutionContext,\r\n ): Promise<T> {\r\n const cached = await this.get<T>(key)\r\n if (cached !== null) return cached\r\n\r\n const fresh = await fetcher()\r\n const writePromise = this.set(key, fresh, ttlSeconds)\r\n\r\n if (ctx) {\r\n ctx.waitUntil(writePromise)\r\n } else {\r\n await writePromise\r\n }\r\n\r\n return fresh\r\n }\r\n\r\n /**\r\n * Generate a simple ETag from a version string by hashing with SHA-256.\r\n */\r\n async generateETag(versionString: string): Promise<string> {\r\n const encoder = new TextEncoder()\r\n const data = encoder.encode(versionString)\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data)\r\n const hashArray = Array.from(new Uint8Array(hashBuffer))\r\n return `\"${hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')}\"`\r\n }\r\n}\r\n","import { eq, sql } from 'drizzle-orm'\r\nimport type { LeapifyDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport type { CacheService } from './cache'\r\n\r\nconst SLOT_KV_PREFIX = 'slots:'\r\n\r\nexport interface SlotInfo {\r\n available: number\r\n total: number\r\n registered: number\r\n isFull: boolean\r\n}\r\n\r\n/**\r\n * Manages real-time slot counts using a local D1 counter + KV cache.\r\n * Google Forms Watch webhook increments the counter; reads go through KV.\r\n *\r\n * CF Cache (Cache-Control: public, max-age=5) sits in front of the /slots\r\n * endpoint, so KV is only read once per 5-second window per edge location.\r\n */\r\nexport class SlotsService {\r\n constructor(\r\n private readonly db: LeapifyDb,\r\n private readonly cache: CacheService,\r\n ) {}\r\n\r\n kvKey(slug: string) {\r\n return `${SLOT_KV_PREFIX}${slug}`\r\n }\r\n\r\n /**\r\n * Read current slot info — KV first, D1 on miss.\r\n */\r\n async getSlots(slug: string): Promise<SlotInfo | null> {\r\n // Try KV first\r\n const cached = await this.cache.get<SlotInfo>(this.kvKey(slug))\r\n if (cached) return cached\r\n\r\n // Fall back to D1\r\n return this.refreshFromDb(slug)\r\n }\r\n\r\n /**\r\n * Atomically increment registered_slots in D1 and update KV.\r\n * Called by the Google Forms Watch webhook handler.\r\n */\r\n async increment(slug: string): Promise<SlotInfo | null> {\r\n await this.db\r\n .update(events)\r\n .set({ registeredSlots: sql`${events.registeredSlots} + 1` })\r\n .where(eq(events.slug, slug))\r\n\r\n return this.refreshFromDb(slug)\r\n }\r\n\r\n /**\r\n * Atomically decrement registered_slots in D1 and update KV.\r\n * Used during reconciliation drift correction (not from user actions).\r\n */\r\n async decrement(slug: string): Promise<SlotInfo | null> {\r\n await this.db\r\n .update(events)\r\n .set({\r\n registeredSlots: sql`MAX(0, ${events.registeredSlots} - 1)`,\r\n })\r\n .where(eq(events.slug, slug))\r\n\r\n return this.refreshFromDb(slug)\r\n }\r\n\r\n /**\r\n * Set registered_slots to a specific value (used by reconciliation cron).\r\n */\r\n async correctCount(slug: string, actualCount: number): Promise<void> {\r\n await this.db\r\n .update(events)\r\n .set({ registeredSlots: actualCount })\r\n .where(eq(events.slug, slug))\r\n\r\n await this.invalidate(slug)\r\n }\r\n\r\n /**\r\n * Read from D1, write to KV, and return slot info.\r\n */\r\n async refreshFromDb(slug: string): Promise<SlotInfo | null> {\r\n const event = await this.db.query.events.findFirst({\r\n where: eq(events.slug, slug),\r\n columns: { maxSlots: true, registeredSlots: true },\r\n })\r\n\r\n if (!event) return null\r\n\r\n const info: SlotInfo = {\r\n total: event.maxSlots,\r\n registered: event.registeredSlots,\r\n available: Math.max(0, event.maxSlots - event.registeredSlots),\r\n isFull: event.registeredSlots >= event.maxSlots,\r\n }\r\n\r\n // Cache with no TTL — explicitly invalidated on every write\r\n await this.cache.set(this.kvKey(slug), info)\r\n\r\n return info\r\n }\r\n\r\n /**\r\n * Invalidate the KV cache for a specific event.\r\n */\r\n async invalidate(slug: string): Promise<void> {\r\n await this.cache.del(this.kvKey(slug))\r\n }\r\n}\r\n","/**\r\n * Google Forms API client using service account OAuth2.\r\n * Fully fetch-native — no googleapis SDK (not edge compatible).\r\n */\r\n\r\ninterface ServiceAccountCredentials {\r\n type: string;\r\n project_id: string;\r\n private_key_id: string;\r\n private_key: string;\r\n client_email: string;\r\n client_id: string;\r\n}\r\n\r\ninterface TokenResponse {\r\n access_token: string;\r\n expires_in: number;\r\n token_type: string;\r\n}\r\n\r\ninterface FormResponse {\r\n responseId: string;\r\n createTime: string;\r\n lastSubmittedTime: string;\r\n respondentEmail?: string;\r\n answers?: Record<string, unknown>;\r\n}\r\n\r\ninterface ListResponsesResult {\r\n responses: FormResponse[];\r\n nextPageToken?: string;\r\n}\r\n\r\nexport class GFormsService {\r\n private accessToken: string | null = null;\r\n private tokenExpiresAt = 0;\r\n private readonly credentials: ServiceAccountCredentials;\r\n\r\n constructor(serviceAccountJson: string) {\r\n this.credentials = JSON.parse(\r\n serviceAccountJson,\r\n ) as ServiceAccountCredentials;\r\n }\r\n\r\n // OAuth2 token management\r\n\r\n private async getAccessToken(): Promise<string> {\r\n if (this.accessToken && Date.now() < this.tokenExpiresAt) {\r\n return this.accessToken;\r\n }\r\n\r\n const token = await this.fetchServiceAccountToken();\r\n this.accessToken = token.access_token;\r\n this.tokenExpiresAt = Date.now() + (token.expires_in - 60) * 1000; // 1min buffer\r\n return this.accessToken;\r\n }\r\n\r\n private async fetchServiceAccountToken(): Promise<TokenResponse> {\r\n const now = Math.floor(Date.now() / 1000);\r\n const claims = {\r\n iss: this.credentials.client_email,\r\n scope: \"https://www.googleapis.com/auth/forms.responses.readonly\",\r\n aud: \"https://oauth2.googleapis.com/token\",\r\n iat: now,\r\n exp: now + 3600,\r\n };\r\n\r\n const jwt = await this.createJwt(claims);\r\n\r\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\r\n body: new URLSearchParams({\r\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\r\n assertion: jwt,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Failed to get service account token: ${err}`);\r\n }\r\n\r\n return response.json() as Promise<TokenResponse>;\r\n }\r\n\r\n private async createJwt(claims: Record<string, unknown>): Promise<string> {\r\n const header = { alg: \"RS256\", typ: \"JWT\" };\r\n\r\n const encode = (obj: unknown) =>\r\n btoa(JSON.stringify(obj))\r\n .replace(/\\+/g, \"-\")\r\n .replace(/\\//g, \"_\")\r\n .replace(/=/g, \"\");\r\n\r\n const headerB64 = encode(header);\r\n const payloadB64 = encode(claims);\r\n const signingInput = `${headerB64}.${payloadB64}`;\r\n\r\n // Import private key from PEM\r\n const pemBody = this.credentials.private_key\r\n .replace(/-----BEGIN PRIVATE KEY-----/, \"\")\r\n .replace(/-----END PRIVATE KEY-----/, \"\")\r\n .replace(/\\s/g, \"\");\r\n\r\n const keyBytes = Uint8Array.from(atob(pemBody), (c) => c.charCodeAt(0));\r\n\r\n const privateKey = await crypto.subtle.importKey(\r\n \"pkcs8\",\r\n keyBytes,\r\n { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" },\r\n false,\r\n [\"sign\"],\r\n );\r\n\r\n const signature = await crypto.subtle.sign(\r\n \"RSASSA-PKCS1-v1_5\",\r\n privateKey,\r\n new TextEncoder().encode(signingInput),\r\n );\r\n\r\n const sigB64 = btoa(String.fromCharCode(...new Uint8Array(signature)))\r\n .replace(/\\+/g, \"-\")\r\n .replace(/\\//g, \"_\")\r\n .replace(/=/g, \"\");\r\n\r\n return `${signingInput}.${sigB64}`;\r\n }\r\n\r\n // Forms API\r\n\r\n /**\r\n * Get the total response count for a form.\r\n * Used by reconciliation cron — not called at runtime per-user.\r\n */\r\n async getResponseCount(formId: string): Promise<number> {\r\n return this.getExactResponseCount(formId);\r\n }\r\n\r\n /**\r\n * List all form responses including respondent emails.\r\n * Used by reminder email cron — 1 call per event per reminder cycle.\r\n */\r\n async getAllResponses(formId: string): Promise<FormResponse[]> {\r\n const token = await this.getAccessToken();\r\n const allResponses: FormResponse[] = [];\r\n let pageToken: string | undefined;\r\n\r\n do {\r\n const url = new URL(\r\n `https://forms.googleapis.com/v1/forms/${formId}/responses`,\r\n );\r\n url.searchParams.set(\"pageSize\", \"500\");\r\n if (pageToken) url.searchParams.set(\"pageToken\", pageToken);\r\n\r\n const response = await fetch(url.toString(), {\r\n headers: { Authorization: `Bearer ${token}` },\r\n });\r\n\r\n if (!response.ok) throw new Error(`Forms API error: ${response.status}`);\r\n\r\n const data = (await response.json()) as ListResponsesResult;\r\n allResponses.push(...(data.responses ?? []));\r\n pageToken = data.nextPageToken;\r\n } while (pageToken);\r\n\r\n return allResponses;\r\n }\r\n\r\n /**\r\n * Get respondent email addresses for a form.\r\n * Google Forms automatically collects emails when \"Collect email addresses\" is enabled.\r\n */\r\n async getRespondentEmails(formId: string): Promise<string[]> {\r\n const responses = await this.getAllResponses(formId);\r\n return responses\r\n .map((r) => r.respondentEmail)\r\n .filter((email): email is string => Boolean(email));\r\n }\r\n\r\n /**\r\n * Get the total response count more accurately by fetching all responses.\r\n * Used for precise reconciliation.\r\n */\r\n async getExactResponseCount(formId: string): Promise<number> {\r\n const responses = await this.getAllResponses(formId);\r\n return responses.length;\r\n }\r\n\r\n // Watch management\r\n\r\n /**\r\n * Create a Watch on a Google Form.\r\n * Fires a push notification to webhookUrl on every new response.\r\n *\r\n * @param formId - Google Form ID\r\n * @param webhookUrl - Full HTTPS URL Google will POST to on new submissions\r\n * (e.g. https://leap.yourdomain.com/internal/gforms-webhook)\r\n */\r\n async createWatch(\r\n formId: string,\r\n webhookUrl: string,\r\n ): Promise<{ watchId: string; expireTime: string }> {\r\n const token = await this.getAccessToken();\r\n\r\n const response = await fetch(\r\n `https://forms.googleapis.com/v1/forms/${formId}/watches`,\r\n {\r\n method: \"POST\",\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n \"Content-Type\": \"application/json\",\r\n },\r\n body: JSON.stringify({\r\n watch: {\r\n target: {\r\n // HTTP push: Google POSTs the notification directly to our worker.\r\n // The URL must be HTTPS and publicly reachable.\r\n httpTarget: { uri: webhookUrl },\r\n },\r\n eventType: \"RESPONSES\",\r\n },\r\n }),\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Failed to create Watch: ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as { id: string; expireTime: string };\r\n return { watchId: data.id, expireTime: data.expireTime };\r\n }\r\n\r\n /**\r\n * Renew an existing Watch (reset its 7-day TTL).\r\n */\r\n async renewWatch(\r\n formId: string,\r\n watchId: string,\r\n ): Promise<{ expireTime: string }> {\r\n const token = await this.getAccessToken();\r\n\r\n const response = await fetch(\r\n `https://forms.googleapis.com/v1/forms/${formId}/watches/${watchId}:renew`,\r\n {\r\n method: \"POST\",\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n \"Content-Type\": \"application/json\",\r\n },\r\n body: JSON.stringify({}),\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Failed to renew Watch ${watchId}: ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as { expireTime: string };\r\n return { expireTime: data.expireTime };\r\n }\r\n}\r\n","import { createMiddleware } from 'hono/factory'\r\nimport type { LeapifyBindings } from '../../types'\r\nimport { tooManyRequests } from '../errors'\r\n\r\nexport interface RateLimitConfig {\r\n /** KV key namespace segment — e.g. 'events' or 'bookmarks' */\r\n endpoint: string\r\n /** Max requests allowed within the window */\r\n limit: number\r\n /** Window size in seconds */\r\n windowSec: number\r\n /**\r\n * How to identify the requester.\r\n * - 'ip' → CF-Connecting-IP (guests, public endpoints)\r\n * - 'uid' → authenticated user ID from c.get('user') (must run after authMiddleware)\r\n */\r\n identifier: 'ip' | 'uid'\r\n}\r\n\r\n/**\r\n * KV token-bucket rate limiter (ADR-006, Layer 3 & 5).\r\n *\r\n * Key schema: `rl:<endpoint>:<identifier>`\r\n *\r\n * Uses a simple counter with KV TTL as the window reset mechanism.\r\n * On first request in a window, the key is created with expirationTtl = windowSec.\r\n * Subsequent requests within the window increment the counter.\r\n * When the key expires, the window resets automatically.\r\n *\r\n * Trade-off: this is eventually consistent under concurrent requests at window\r\n * boundaries — acceptable given the ~5% over-limit tolerance for edge caches.\r\n */\r\nexport function createRateLimitMiddleware(config: RateLimitConfig) {\r\n const { endpoint, limit, windowSec, identifier } = config\r\n\r\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\r\n // Skip rate limiting for challenge verification (clients solving challenges should not be blocked)\r\n if (c.req.path === '/.well-known/leapify/turnstile/verify') return next()\r\n\r\n // Also skip old PoW path for backward compatibility\r\n if (c.req.path === '/.well-known/leapify/pow/verify') return next()\r\n\r\n const id =\r\n identifier === 'uid'\r\n ? (c.get('user')?.uid ?? c.req.header('CF-Connecting-IP') ?? 'unknown')\r\n : (c.req.header('CF-Connecting-IP') ?? 'unknown')\r\n\r\n const key = `rl:${endpoint}:${id}`\r\n\r\n const raw = await c.env.KV.get(key)\r\n const count = raw !== null ? parseInt(raw, 10) : 0\r\n\r\n if (count >= limit) {\r\n c.header('Retry-After', String(windowSec))\r\n c.header('X-RateLimit-Limit', String(limit))\r\n c.header('X-RateLimit-Remaining', '0')\r\n throw tooManyRequests(`Rate limit exceeded. Try again in ${windowSec}s.`)\r\n }\r\n\r\n // Increment. On first request (count === 0), set TTL to open the window.\r\n // On subsequent requests, preserve the existing TTL by not resetting it.\r\n if (count === 0) {\r\n await c.env.KV.put(key, '1', { expirationTtl: windowSec })\r\n } else {\r\n // KV doesn't support atomic increment — we read then write.\r\n // Slight over-counting is acceptable; it errs on the side of caution.\r\n await c.env.KV.put(key, String(count + 1), { expirationTtl: windowSec })\r\n }\r\n\r\n c.header('X-RateLimit-Limit', String(limit))\r\n c.header('X-RateLimit-Remaining', String(limit - count - 1))\r\n\r\n return next()\r\n })\r\n}\r\n\r\n// Pre-configured middlewares per ADR-006 recommended limits\r\n\r\n/** GET /events — 60 req/60s per IP */\r\nexport const eventsListRateLimit = createRateLimitMiddleware({\r\n endpoint: 'events-list',\r\n limit: 60,\r\n windowSec: 60,\r\n identifier: 'ip',\r\n})\r\n\r\n/** GET /events/:slug/slots — 120 req/60s per IP */\r\nexport const eventsSlotsRateLimit = createRateLimitMiddleware({\r\n endpoint: 'events-slots',\r\n limit: 120,\r\n windowSec: 60,\r\n identifier: 'ip',\r\n})\r\n\r\n/** POST /users/me/bookmarks — 10 req/60s per UID (must run after authMiddleware) */\r\nexport const bookmarksRateLimit = createRateLimitMiddleware({\r\n endpoint: 'bookmarks',\r\n limit: 10,\r\n windowSec: 60,\r\n identifier: 'uid',\r\n})\r\n\r\n/** POST /events (admin) — 20 req/60s per UID (must run after authMiddleware) */\r\nexport const adminEventsRateLimit = createRateLimitMiddleware({\r\n endpoint: 'admin-events',\r\n limit: 20,\r\n windowSec: 60,\r\n identifier: 'uid',\r\n})\r\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq, and, sql } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { CacheService } from '../services/cache'\r\nimport { SlotsService } from '../services/slots'\r\nimport { GFormsService } from '../services/gforms'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound } from '../lib/errors'\r\nimport {\r\n eventsListRateLimit,\r\n eventsSlotsRateLimit,\r\n adminEventsRateLimit,\r\n} from '../lib/middleware/rate-limit'\r\n\r\nconst EVENTS_LIST_KV_KEY = 'events:list'\r\nconst EVENTS_ETAG_KV_KEY = 'events:etag'\r\nconst EVENTS_LIST_TTL = 300 // 5 min KV cache for list\r\n\r\nconst createEventSchema = z.object({\r\n themeId: z.string().min(1),\r\n organizationId: z.string().optional(),\r\n title: z.string().min(1),\r\n description: z.string().optional(),\r\n venue: z.string().optional(),\r\n dateTime: z.string().optional(),\r\n price: z.string().optional(),\r\n backgroundImageUrl: z.string().url().optional(),\r\n classCode: z.string().optional(),\r\n startTime: z.string().optional(),\r\n endTime: z.string().optional(),\r\n registrationClosesAt: z.number().optional(),\r\n isSpotlight: z.boolean().default(false),\r\n maxSlots: z.number().int().min(0).default(0),\r\n gformsId: z.string().optional(),\r\n gformsUrl: z.string().url().optional(),\r\n gformsEditorUrl: z.string().url().optional(),\r\n releaseAt: z.number().optional(),\r\n status: z.enum(['draft', 'queued', 'published']).default('draft'),\r\n})\r\n\r\nexport const classesRoute = new Hono<LeapifyEnv>()\r\n\r\nfunction generateSlug(title: string): string {\r\n return title\r\n .toLowerCase()\r\n .trim()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/[\\s_-]+/g, '-')\r\n .replace(/^-+|-+$/g, '')\r\n}\r\n\r\n// GET /events/admin — admin only, returns all events regardless of status\r\nclassesRoute.get('/admin', authMiddleware, adminMiddleware, async (c) => {\r\n const db = createDb(c.env.DB)\r\n const data = await db.query.events.findMany({\r\n with: { theme: true, organization: true },\r\n orderBy: (e, { desc }) => [desc(e.createdAt)],\r\n })\r\n return c.json({ data })\r\n})\r\n\r\n// POST /events/admin/publish — admin only, batch publish queued events\r\nclassesRoute.post('/admin/publish', authMiddleware, adminMiddleware, async (c) => {\r\n const body = await c.req.json<{ ids: string[]; releaseAt?: number }>()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n if (!body.ids?.length) {\r\n return c.json({ error: 'ids are required' }, 400)\r\n }\r\n\r\n if (body.releaseAt) {\r\n // Schedule for later\r\n await db\r\n .update(events)\r\n .set({ releaseAt: body.releaseAt, status: 'queued' })\r\n .where(\r\n sql`${events.id} IN (${sql.join(\r\n body.ids.map((id) => sql`${id}`),\r\n sql`, `,\r\n )})`,\r\n )\r\n } else {\r\n // Publish now\r\n await db\r\n .update(events)\r\n .set({ status: 'published', publishedAt: sql`(unixepoch())` })\r\n .where(\r\n sql`${events.id} IN (${sql.join(\r\n body.ids.map((id) => sql`${id}`),\r\n sql`, `,\r\n )})`,\r\n )\r\n }\r\n\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.json({ data: { updated: body.ids.length } })\r\n})\r\n\r\n// GET /events — public, ETag + 7-day browser cache\r\nclassesRoute.get('/', eventsListRateLimit, async (c) => {\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n // Generate ETag from latest publishedAt timestamp\r\n const [latest] = await db\r\n .select({ max: events.publishedAt })\r\n .from(events)\r\n .where(eq(events.status, 'published'))\r\n .limit(1)\r\n\r\n const etag = await cache.getOrSet(\r\n EVENTS_ETAG_KV_KEY,\r\n () => cache.generateETag(String(latest?.max ?? '0')),\r\n 300,\r\n )\r\n\r\n // Handle conditional GET\r\n const ifNoneMatch = c.req.header('If-None-Match')\r\n if (ifNoneMatch === etag) {\r\n return c.body(null, 304)\r\n }\r\n\r\n const data = await cache.getOrSet(\r\n EVENTS_LIST_KV_KEY,\r\n () =>\r\n db.query.events.findMany({\r\n where: eq(events.status, 'published'),\r\n with: {\r\n theme: true,\r\n organization: true,\r\n },\r\n columns: {\r\n id: true,\r\n slug: true,\r\n themeId: true,\r\n organizationId: true,\r\n title: true,\r\n venue: true,\r\n dateTime: true,\r\n price: true,\r\n backgroundImageUrl: true,\r\n classCode: true,\r\n startTime: true,\r\n endTime: true,\r\n registrationClosesAt: true,\r\n isSpotlight: true,\r\n maxSlots: true,\r\n registeredSlots: true,\r\n gformsUrl: true,\r\n gformsEditorUrl: true,\r\n publishedAt: true,\r\n },\r\n }),\r\n EVENTS_LIST_TTL,\r\n )\r\n\r\n c.header('ETag', etag)\r\n c.header(\r\n 'Cache-Control',\r\n 'public, max-age=604800, stale-while-revalidate=86400',\r\n ) // 7 days\r\n return c.json({ data })\r\n})\r\n\r\n// GET /events/:slug\r\nclassesRoute.get('/:slug', async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n\r\n const event = await db.query.events.findFirst({\r\n where: and(eq(events.slug, slug), eq(events.status, 'published')),\r\n with: {\r\n theme: true,\r\n },\r\n })\r\n\r\n if (!event) throw notFound('Event')\r\n\r\n return c.json({ data: event })\r\n})\r\n\r\n// GET /events/:slug/slots — real-time, CF Cache 5s\r\nclassesRoute.get('/:slug/slots', eventsSlotsRateLimit, async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n const slotsService = new SlotsService(db, cache)\r\n\r\n const info = await slotsService.getSlots(slug)\r\n if (!info) throw notFound('Event')\r\n\r\n // CF edge cache: all 30k users share this cached response for 5s\r\n c.header('Cache-Control', 'public, max-age=5, stale-while-revalidate=5')\r\n\r\n return c.json({ data: info })\r\n})\r\n\r\n// POST /events — admin only\r\nclassesRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n adminEventsRateLimit,\r\n zValidator('json', createEventSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const slug = generateSlug(body.title)\r\n\r\n const [created] = await db.insert(events).values({ ...body, slug }).returning()\r\n\r\n // If publishing immediately, create a Google Forms Watch\r\n if (\r\n body.status === 'published' &&\r\n body.gformsId &&\r\n c.env.GFORMS_SERVICE_ACCOUNT_JSON\r\n ) {\r\n const webhookUrl = c.get('gformsWebhookUrl')\r\n if (webhookUrl) {\r\n const gforms = new GFormsService(c.env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n try {\r\n const watch = await gforms.createWatch(body.gformsId, webhookUrl)\r\n const expiry = Math.floor(\r\n new Date(watch.expireTime ?? '').getTime() / 1000,\r\n )\r\n await db\r\n .update(events)\r\n .set({ watchId: watch.watchId, watchExpiresAt: expiry })\r\n .where(eq(events.id, created!.id))\r\n } catch (err) {\r\n console.error('[events] Failed to create Watch:', err)\r\n }\r\n } else {\r\n console.warn(\r\n '[events] gformsWebhookUrl not configured \\u2014 Watch not created. Pass gformsWebhookUrl to createLeapify().',\r\n )\r\n }\r\n }\r\n\r\n // Invalidate list cache\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.json({ data: created }, 201)\r\n },\r\n)\r\n\r\n// PATCH /events/:slug — admin only\r\nclassesRoute.patch('/:slug', authMiddleware, adminMiddleware, async (c) => {\r\n const { slug } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof createEventSchema>>>()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n let newSlug: string | undefined\r\n if (body.title) {\r\n newSlug = generateSlug(body.title)\r\n }\r\n\r\n const [updated] = await db\r\n .update(events)\r\n .set(newSlug ? { ...body, slug: newSlug } : body)\r\n .where(eq(events.slug, slug))\r\n .returning()\r\n\r\n if (!updated) throw notFound('Event')\r\n\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.json({ data: updated })\r\n})\r\n\r\n// DELETE /events/:slug — admin only\r\nclassesRoute.delete('/:slug', authMiddleware, adminMiddleware, async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const [deleted] = await db.delete(events).where(eq(events.slug, slug)).returning()\r\n\r\n if (!deleted) throw notFound('Event')\r\n\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.body(null, 204)\r\n})\r\n","import { Hono } from \"hono\";\r\nimport { eq, and } from \"drizzle-orm\";\r\nimport type { LeapifyEnv } from \"../types\";\r\nimport { createDb } from \"../db\";\r\nimport { users, type UserRole } from \"../db/schema/users\";\r\nimport { authUser } from \"../db/schema/auth\";\r\nimport { bookmarks } from \"../db/schema/bookmarks\";\r\nimport { events } from \"../db/schema/classes\";\r\nimport { authMiddleware, adminMiddleware, optionalAuthMiddleware } from \"../auth/middleware\";\r\nimport { notFound, badRequest } from \"../lib/errors\";\r\nimport { bookmarksRateLimit } from \"../lib/middleware/rate-limit\";\r\n\r\nconst VALID_ROLES: UserRole[] = [\"student\", \"admin\", \"super_admin\"];\r\n\r\nexport const usersRoute = new Hono<LeapifyEnv>();\r\n\r\n// ─── Admin: User Management ─────────────────────────────────────────────────\r\n\r\n// GET /users — admin only, list all users\r\nusersRoute.get(\"/\", authMiddleware, adminMiddleware, async (c) => {\r\n const db = createDb(c.env.DB);\r\n const data = await db.select().from(users);\r\n return c.json({ data });\r\n});\r\n\r\n// PATCH /users/:id/role — admin only, change user role\r\nusersRoute.patch(\"/:id/role\", authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param();\r\n const { role } = await c.req.json<{ role: string }>();\r\n\r\n if (!role || !VALID_ROLES.includes(role as UserRole)) {\r\n throw badRequest(\"Role must be 'student', 'admin', or 'super_admin'.\");\r\n }\r\n\r\n const db = createDb(c.env.DB);\r\n const [updated] = await db\r\n .update(users)\r\n .set({ role: role as UserRole })\r\n .where(eq(users.id, id))\r\n .returning();\r\n\r\n if (!updated) throw notFound(\"User\");\r\n\r\n return c.json({ data: updated });\r\n});\r\n\r\n// POST /users/by-email — admin only, find or create user by email and set role\r\nusersRoute.post(\"/by-email\", authMiddleware, adminMiddleware, async (c) => {\r\n const { email, role } = await c.req.json<{ email: string; role: string }>();\r\n\r\n if (!email || !role || !VALID_ROLES.includes(role as UserRole)) {\r\n throw badRequest(\"Email and valid role ('student', 'admin', 'super_admin') are required.\");\r\n }\r\n\r\n const db = createDb(c.env.DB);\r\n\r\n // Find existing user by email\r\n const existing = await db.query.users.findFirst({\r\n where: eq(users.email, email),\r\n });\r\n\r\n if (existing) {\r\n // Update role\r\n const [updated] = await db\r\n .update(users)\r\n .set({ role: role as UserRole })\r\n .where(eq(users.email, email))\r\n .returning();\r\n return c.json({ data: updated });\r\n }\r\n\r\n // Create a placeholder user (they'll get a real betterAuthId on first login)\r\n const [created] = await db\r\n .insert(users)\r\n .values({ betterAuthId: `pending:${email}`, email, name: email.split(\"@\")[0], role: role as UserRole })\r\n .returning();\r\n\r\n return c.json({ data: created }, 201);\r\n});\r\n\r\n// ─── Public / Auth: User Profile ─────────────────────────────────────────────\r\n\r\n// GET /users/me\r\nusersRoute.get(\"/me\", optionalAuthMiddleware, async (c) => {\r\n const user = c.get(\"user\");\r\n if (!user) return c.json({ data: null });\r\n\r\n const db = createDb(c.env.DB);\r\n const profile = await db.query.users.findFirst({\r\n where: eq(users.id, user.dbId),\r\n });\r\n\r\n if (!profile) return c.json({ data: null });\r\n\r\n // Join with authUser to get the Google profile image\r\n const auth = await db.query.authUser.findFirst({\r\n where: eq(authUser.id, profile.betterAuthId),\r\n columns: { image: true },\r\n });\r\n\r\n return c.json({ data: { ...profile, image: auth?.image ?? null } });\r\n});\r\n\r\n// GET /users/me/bookmarks\r\nusersRoute.get(\"/me/bookmarks\", optionalAuthMiddleware, async (c) => {\r\n const user = c.get(\"user\");\r\n if (!user) return c.json({ data: [] });\r\n\r\n const db = createDb(c.env.DB);\r\n const rows = await db.query.bookmarks.findMany({\r\n where: eq(bookmarks.userId, user.dbId),\r\n with: { event: true },\r\n });\r\n\r\n const data = rows.map((r) => ({ bookmarkedAt: r.createdAt, event: r.event }));\r\n return c.json({ data });\r\n});\r\n\r\n// POST /users/me/bookmarks/:eventId — toggle\r\nusersRoute.post(\"/me/bookmarks/:eventId\", authMiddleware, bookmarksRateLimit, async (c) => {\r\n const { eventId } = c.req.param();\r\n const user = c.get(\"user\");\r\n const db = createDb(c.env.DB);\r\n\r\n // Verify event exists\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.id, eventId),\r\n columns: { id: true },\r\n });\r\n if (!event) throw notFound(\"Event\");\r\n\r\n // Try an atomic insert that silently skips if the unique constraint is hit.\r\n const inserted = await db\r\n .insert(bookmarks)\r\n .values({ userId: user.dbId, eventId })\r\n .onConflictDoNothing({ target: [bookmarks.userId, bookmarks.eventId] })\r\n .returning();\r\n\r\n if (inserted.length > 0) {\r\n return c.json({ data: { bookmarked: true } }, 201);\r\n }\r\n\r\n await db\r\n .delete(bookmarks)\r\n .where(and(eq(bookmarks.userId, user.dbId), eq(bookmarks.eventId, eventId)));\r\n\r\n return c.json({ data: { bookmarked: false } }, 200);\r\n});\r\n\r\n// DELETE /users/me/bookmarks/:eventId\r\nusersRoute.delete(\"/me/bookmarks/:eventId\", authMiddleware, async (c) => {\r\n const { eventId } = c.req.param();\r\n const user = c.get(\"user\");\r\n const db = createDb(c.env.DB);\r\n\r\n await db\r\n .delete(bookmarks)\r\n .where(\r\n and(eq(bookmarks.userId, user.dbId), eq(bookmarks.eventId, eventId)),\r\n );\r\n\r\n return c.json({ data: { bookmarked: false } });\r\n});\r\n","import { Hono } from 'hono'\nimport type { LeapifyEnv, SiteConfigKey, SiteConfigMap } from '../types'\nimport { createDb } from '../db'\nimport { siteConfig } from '../db/schema/site-config'\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\nimport { forbidden } from '../lib/errors'\n\nexport const siteConfigRoute = new Hono<LeapifyEnv>()\n\n// GET /config — public\nsiteConfigRoute.get('/', async (c) => {\n const db = createDb(c.env.DB)\n\n const rows = await db.query.siteConfig.findMany()\n const config = Object.fromEntries(\n rows.map((r) => [r.key, JSON.parse(r.value)])\n ) as Partial<SiteConfigMap>\n\n return c.json({\n data: {\n comingSoonUntil: config.coming_soon_until ?? null,\n siteEndsAt: config.site_ends_at ?? null,\n siteName: config.site_name ?? null,\n registrationGloballyOpen: config.registration_globally_open ?? true,\n maintenanceMode: config.maintenance_mode ?? false,\n allowedOrigins: config.allowed_origins ?? null,\n now: Math.floor(Date.now() / 1000)\n }\n })\n})\n\n// PATCH /config/:key — admin only (allowed_origins is super_admin only)\nsiteConfigRoute.patch('/:key', authMiddleware, adminMiddleware, async (c) => {\n const key = c.req.param('key') as SiteConfigKey\n const { value } = await c.req.json<{ value: SiteConfigMap[typeof key] }>()\n\n if (key === 'allowed_origins') {\n const user = c.get('user')\n if (!user || user.role !== 'super_admin') {\n throw forbidden('Super Admin access required to change allowed origins')\n }\n }\n\n const db = createDb(c.env.DB)\n const now = Math.floor(Date.now() / 1000)\n\n await db\n .insert(siteConfig)\n .values({ key, value: JSON.stringify(value), updatedAt: now })\n .onConflictDoUpdate({\n target: siteConfig.key,\n set: { value: JSON.stringify(value), updatedAt: now }\n })\n\n // Write-through to KV so the maintenance-mode / CORS middlewares can read it\n // without a D1 round-trip on every request. TTL 1 day — KV is the fast cache;\n // D1 is the durable source of truth used as fallback when KV expires.\n await c.env.KV.put(`config:${key}`, JSON.stringify(value), {\n expirationTtl: 86400\n })\n\n return c.json({ data: { key, value } })\n})\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { faqs } from '../db/schema/faqs'\r\nimport { CacheService } from '../services/cache'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound } from '../lib/errors'\r\n\r\nconst FAQS_KV_KEY = 'faqs:active'\r\nconst FAQS_TTL = 600 // 10 min\r\n\r\nconst faqSchema = z.object({\r\n question: z.string().min(1),\r\n answer: z.string().min(1),\r\n category: z.string().optional(),\r\n sortOrder: z.number().int().default(0),\r\n})\r\n\r\nexport const faqsRoute = new Hono<LeapifyEnv>()\r\n\r\n// GET /faqs — public, KV cached 10min\r\nfaqsRoute.get('/', async (c) => {\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const data = await cache.getOrSet(\r\n FAQS_KV_KEY,\r\n () =>\r\n db.query.faqs.findMany({\r\n orderBy: (t, { asc }) => [asc(t.sortOrder), asc(t.createdAt)],\r\n }),\r\n FAQS_TTL,\r\n )\r\n\r\n return c.json({ data })\r\n})\r\n\r\n// POST /faqs — admin\r\nfaqsRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n zValidator('json', faqSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const [created] = await db.insert(faqs).values(body).returning()\r\n await cache.del(FAQS_KV_KEY)\r\n\r\n return c.json({ data: created }, 201)\r\n },\r\n)\r\n\r\n// PATCH /faqs/:id — admin\r\nfaqsRoute.patch('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof faqSchema>>>()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n const now = Math.floor(Date.now() / 1000)\r\n\r\n const [updated] = await db\r\n .update(faqs)\r\n .set({ ...body, updatedAt: now })\r\n .where(eq(faqs.id, id))\r\n .returning()\r\n\r\n if (!updated) throw notFound('FAQ')\r\n await cache.del(FAQS_KV_KEY)\r\n\r\n return c.json({ data: updated })\r\n})\r\n\r\n// DELETE /faqs/:id — admin, hard delete\r\nfaqsRoute.delete('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const [deleted] = await db.delete(faqs).where(eq(faqs.id, id)).returning()\r\n\r\n if (!deleted) throw notFound('FAQ')\r\n await cache.del(FAQS_KV_KEY)\r\n\r\n return c.json({ data: { deleted: true } })\r\n})\r\n","import { Hono } from \"hono\";\r\nimport { eq } from \"drizzle-orm\";\r\nimport type { LeapifyEnv } from \"../../types\";\r\nimport { createDb } from \"../../db\";\r\nimport { events } from \"../../db/schema/classes\";\r\nimport { SlotsService } from \"../../services/slots\";\r\nimport { CacheService } from \"../../services/cache\";\r\nimport { internalMiddleware } from \"../../auth/middleware\";\r\n\r\nexport const gformsWebhookRoute = new Hono<LeapifyEnv>();\r\n\r\n/**\r\n * POST /internal/gforms-webhook\r\n *\r\n * Receives Google Forms Watch push notifications.\r\n * Each notification means one student submitted the form.\r\n * We increment the local D1 counter and update KV — no Google API call made.\r\n *\r\n * Security:\r\n * 1. X-Internal-Secret header (internalMiddleware) — prevents external access\r\n * 2. X-Goog-Signature HMAC — verifies payload is genuinely from Google\r\n */\r\ngformsWebhookRoute.post(\"/\", internalMiddleware, async (c) => {\r\n const rawBody = await c.req.text();\r\n\r\n // Verify Google HMAC signature\r\n const signature = c.req.header(\"X-Goog-Signature\");\r\n if (signature) {\r\n const isValid = await verifyGoogSignature(\r\n rawBody,\r\n signature,\r\n c.env.GFORMS_WEBHOOK_SECRET,\r\n );\r\n if (!isValid) {\r\n return c.json({ error: \"Invalid signature\" }, 403);\r\n }\r\n }\r\n\r\n let payload: { formId?: string; watchId?: string };\r\n try {\r\n payload = JSON.parse(rawBody);\r\n } catch {\r\n return c.json({ error: \"Invalid payload\" }, 400);\r\n }\r\n\r\n const { formId } = payload;\r\n if (!formId) return c.json({ error: \"Missing formId\" }, 400);\r\n\r\n const db = createDb(c.env.DB);\r\n const cache = new CacheService(c.env.KV);\r\n\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.gformsId, formId),\r\n columns: { slug: true, maxSlots: true, registeredSlots: true },\r\n });\r\n\r\n if (!event) {\r\n console.warn(`[gforms-webhook] Unknown formId: ${formId}`);\r\n return c.json({ ok: true });\r\n }\r\n\r\n const slotsService = new SlotsService(db, cache);\r\n const updated = await slotsService.increment(event.slug);\r\n\r\n console.log(\r\n `[gforms-webhook] Incremented \"${event.slug}\": ${updated?.registered}/${updated?.total}`,\r\n );\r\n\r\n return c.json({ ok: true });\r\n});\r\n\r\n// HMAC verification\r\n\r\nasync function verifyGoogSignature(\r\n body: string,\r\n signature: string,\r\n secret: string,\r\n): Promise<boolean> {\r\n try {\r\n const key = await crypto.subtle.importKey(\r\n \"raw\",\r\n new TextEncoder().encode(secret),\r\n { name: \"HMAC\", hash: \"SHA-256\" },\r\n false,\r\n [\"verify\"],\r\n );\r\n\r\n const sigHex = signature.replace(/^hmac-sha256=/, \"\");\r\n const sigBytes = Uint8Array.from(\r\n sigHex.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [],\r\n );\r\n\r\n return crypto.subtle.verify(\r\n \"HMAC\",\r\n key,\r\n sigBytes,\r\n new TextEncoder().encode(body),\r\n );\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","import { Hono } from 'hono'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { badRequest, serviceUnavailable, notFound } from '../lib/errors'\r\n\r\n// ─── Constants ────────────────────────────────────────────────────────────────\r\n\r\nconst ALLOWED_MIME_TYPES = new Set([\r\n 'image/jpeg',\r\n 'image/png',\r\n 'image/webp',\r\n 'image/gif',\r\n 'image/svg+xml',\r\n])\r\n\r\n/** 10 MB */\r\nconst MAX_FILE_SIZE = 10 * 1024 * 1024\r\n\r\nexport const uploadsRoute = new Hono<LeapifyEnv>()\r\n\r\n/**\r\n * GET /uploads/images/* — public\r\n *\r\n * Serves an image from the R2 `FILES` bucket.\r\n */\r\nuploadsRoute.get('/images/*', async (c) => {\r\n const bucket = c.env.FILES\r\n if (!bucket) {\r\n throw serviceUnavailable('File storage (R2) is not configured.')\r\n }\r\n\r\n // Get the path after /images/\r\n const path = c.req.path.split('/uploads/images/')[1]\r\n if (!path) throw notFound('Image')\r\n\r\n const object = await bucket.get(path)\r\n if (!object) throw notFound('Image')\r\n\r\n const headers: Record<string, string> = {\r\n etag: object.httpEtag,\r\n // Cache at the edge/browser for 1 month\r\n \"Cache-Control\": \"public, max-age=2592000, immutable\",\r\n };\r\n\r\n if (object.httpMetadata?.contentType) {\r\n headers['Content-Type'] = object.httpMetadata.contentType\r\n }\r\n if (object.httpMetadata?.cacheControl) {\r\n headers['Cache-Control'] = object.httpMetadata.cacheControl\r\n }\r\n\r\n return c.body(object.body as unknown as ReadableStream, 200, headers)\r\n})\r\n\r\n/**\r\n * POST /uploads/images — admin only\r\n *\r\n * Accepts multipart/form-data with a single `file` field.\r\n * Stores the file in the R2 `FILES` bucket under a timestamped path and\r\n * returns the public URL.\r\n */\r\nuploadsRoute.post(\r\n '/images',\r\n authMiddleware,\r\n adminMiddleware,\r\n async (c) => {\r\n const bucket = c.env.FILES\r\n if (!bucket) {\r\n throw serviceUnavailable('File storage (R2) is not configured.')\r\n }\r\n\r\n // Parse multipart body\r\n let formData: FormData\r\n try {\r\n formData = await c.req.formData()\r\n } catch {\r\n throw badRequest('Request body must be multipart/form-data.')\r\n }\r\n\r\n const file = formData.get('file')\r\n if (!(file instanceof File)) {\r\n throw badRequest('A \"file\" field is required.')\r\n }\r\n\r\n // Validate MIME type\r\n const contentType = file.type || 'application/octet-stream'\r\n if (!ALLOWED_MIME_TYPES.has(contentType)) {\r\n throw badRequest(\r\n `Unsupported file type \"${contentType}\".`,\r\n )\r\n }\r\n\r\n // Validate size\r\n if (file.size > MAX_FILE_SIZE) {\r\n throw badRequest('File exceeds 10MB limit.')\r\n }\r\n\r\n // Build a storage key\r\n const folder = sanitizeFolder(formData.get('folder'))\r\n const ext = extensionFromMime(contentType)\r\n const ts = Date.now()\r\n const rand = Math.random().toString(36).slice(2, 8)\r\n const key = `${folder}/${ts}-${rand}.${ext}`\r\n\r\n // Upload to R2\r\n const arrayBuffer = await file.arrayBuffer()\r\n await bucket.put(key, arrayBuffer, {\r\n httpMetadata: { contentType },\r\n customMetadata: { uploadedAt: new Date().toISOString() },\r\n })\r\n\r\n // Construct URL based on the current request\r\n const url = new URL(c.req.url)\r\n url.pathname = `${url.pathname.replace(/\\/$/, '')}/${key}`\r\n url.search = ''\r\n\r\n return c.json(\r\n {\r\n data: {\r\n url: url.toString(),\r\n key,\r\n size: file.size,\r\n contentType,\r\n },\r\n },\r\n 201,\r\n )\r\n },\r\n)\r\n\r\n// ─── Helpers ──────────────────────────────────────────────────────────────────\r\n\r\nfunction sanitizeFolder(raw: FormDataEntryValue | null): string {\r\n if (typeof raw !== 'string' || !raw.trim()) return 'images'\r\n return raw\r\n .trim()\r\n .replace(/[^a-zA-Z0-9\\-_/]/g, '')\r\n .replace(/\\/+/g, '/')\r\n .replace(/^\\/|\\/$/g, '')\r\n || 'images'\r\n}\r\n\r\nfunction extensionFromMime(mime: string): string {\r\n const map: Record<string, string> = {\r\n 'image/jpeg': 'jpg',\r\n 'image/png': 'png',\r\n 'image/webp': 'webp',\r\n 'image/gif': 'gif',\r\n 'image/svg+xml': 'svg',\r\n }\r\n return map[mime] ?? 'bin'\r\n}\r\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { themes } from '../db/schema/themes'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound, conflict } from '../lib/errors'\r\n\r\nconst createThemeSchema = z.object({\r\n name: z.string().min(1),\r\n path: z.string().min(1),\r\n})\r\n\r\nexport const themesRoute = new Hono<LeapifyEnv>()\r\n\r\n// GET /themes — public\r\nthemesRoute.get('/', async (c) => {\r\n const db = createDb(c.env.DB)\r\n const data = await db.select().from(themes)\r\n return c.json({ data })\r\n})\r\n\r\n// POST /themes — admin only\r\nthemesRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n zValidator('json', createThemeSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [created] = await db.insert(themes).values(body).returning()\r\n return c.json({ data: created }, 201)\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('A theme with this name or path already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// PATCH /themes/:id — admin only\r\nthemesRoute.patch(\r\n '/:id',\r\n authMiddleware,\r\n adminMiddleware,\r\n async (c) => {\r\n const { id } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof createThemeSchema>>>()\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [updated] = await db\r\n .update(themes)\r\n .set(body)\r\n .where(eq(themes.id, id))\r\n .returning()\r\n\r\n if (!updated) throw notFound('Theme')\r\n\r\n return c.json({ data: updated })\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('A theme with this name or path already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// DELETE /themes/:id — admin only\r\nthemesRoute.delete('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n\r\n const [deleted] = await db.delete(themes).where(eq(themes.id, id)).returning()\r\n\r\n if (!deleted) throw notFound('Theme')\r\n\r\n return c.body(null, 204)\r\n})\r\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { organizations } from '../db/schema/organizations'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound, conflict } from '../lib/errors'\r\n\r\nconst createOrganizationSchema = z.object({\r\n name: z.string().min(1),\r\n acronym: z.string().min(1),\r\n logoUrl: z.string().url().nullable().optional(),\r\n link: z.string().url().nullable().optional(),\r\n})\r\n\r\nexport const organizationsRoute = new Hono<LeapifyEnv>()\r\n\r\n// GET /organizations — public\r\norganizationsRoute.get('/', async (c) => {\r\n const db = createDb(c.env.DB)\r\n const data = await db.select().from(organizations)\r\n return c.json({ data })\r\n})\r\n\r\n// POST /organizations — admin only\r\norganizationsRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n zValidator('json', createOrganizationSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [created] = await db.insert(organizations).values(body).returning()\r\n return c.json({ data: created }, 201)\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('An organization with this name or acronym already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// PATCH /organizations/:id — admin only\r\norganizationsRoute.patch(\r\n '/:id',\r\n authMiddleware,\r\n adminMiddleware,\r\n async (c) => {\r\n const { id } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof createOrganizationSchema>>>()\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [updated] = await db\r\n .update(organizations)\r\n .set(body)\r\n .where(eq(organizations.id, id))\r\n .returning()\r\n\r\n if (!updated) throw notFound('Organization')\r\n\r\n return c.json({ data: updated })\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('An organization with this name or acronym already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// DELETE /organizations/:id — admin only\r\norganizationsRoute.delete('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n\r\n const [deleted] = await db.delete(organizations).where(eq(organizations.id, id)).returning()\r\n\r\n if (!deleted) throw notFound('Organization')\r\n\r\n return c.body(null, 204)\r\n})\r\n","import { Hono } from 'hono'\r\nimport type { LeapifyEnv } from './types'\r\nimport { errorHandler } from './lib/middleware/error-handler'\r\nimport { createCorsMiddleware } from './lib/middleware/cors'\r\nimport { createRefererGuard } from './lib/middleware/referer-guard'\r\nimport {\r\n createTurnstileMiddleware,\r\n handleTurnstileVerify,\r\n TURNSTILE_VERIFY_PATH,\r\n} from './lib/middleware/turnstile-challenge'\r\nimport { serviceUnavailable } from './lib/errors'\r\nimport { createAuth } from './auth/auth'\r\nimport { healthRoute } from './routes/health'\r\nimport { classesRoute } from './routes/classes'\r\nimport { usersRoute } from './routes/users'\r\nimport { siteConfigRoute } from './routes/site-config'\r\nimport { faqsRoute } from './routes/faqs'\r\nimport { gformsWebhookRoute } from './routes/internal/gforms-webhook'\r\nimport { uploadsRoute } from './routes/uploads'\r\nimport { themesRoute } from './routes/themes'\r\nimport { organizationsRoute } from './routes/organizations'\r\n\r\nexport interface LeapifyAppOptions {\r\n allowedOrigins?: string[]\r\n /**\r\n * Public HTTPS URL of your Cloudflare Worker.\r\n * Required for Google Forms Watch push notifications to work.\r\n * Google will POST to {gformsWebhookUrl}/internal/gforms-webhook on each new submission.\r\n *\r\n * @example 'https://leap.yourdomain.com'\r\n */\r\n gformsWebhookUrl?: string\r\n}\r\n\r\nexport function createApp(options: LeapifyAppOptions = {}): Hono<LeapifyEnv> {\r\n const app = new Hono<LeapifyEnv>()\r\n\r\n // Expose gformsWebhookUrl to routes via app-level middleware\r\n if (options.gformsWebhookUrl) {\r\n const webhookUrl = `${options.gformsWebhookUrl.replace(/\\/$/, '')}/internal/gforms-webhook`\r\n app.use('*', async (c, next) => {\r\n c.set('gformsWebhookUrl', webhookUrl)\r\n return next()\r\n })\r\n }\r\n\r\n // Global middleware\r\n app.use('*', createCorsMiddleware(options.allowedOrigins ?? ['*']))\r\n app.use('*', createTurnstileMiddleware())\r\n app.use('*', createRefererGuard(options.allowedOrigins ?? ['*']))\r\n\r\n // Better Auth HTTP handler — OAuth redirects, callbacks, session, token endpoints.\r\n // Mounted BEFORE the maintenance check so auth is always reachable.\r\n app.on(['POST', 'GET'], '/api/auth/*', (c) => {\r\n const auth = createAuth(c.env)\r\n\r\n // Ensure cf-connecting-ip is present for Better Auth rate limiting\r\n const req = c.req.raw\r\n if (!req.headers.get('cf-connecting-ip')) {\r\n const forwarded = req.headers.get('x-forwarded-for')\r\n const ip = forwarded?.split(',')[0]?.trim() || '127.0.0.1'\r\n const newHeaders = new Headers(req.headers)\r\n newHeaders.set('cf-connecting-ip', ip)\r\n return auth.handler(new Request(req.url, {\r\n method: req.method,\r\n headers: newHeaders,\r\n body: (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') ? null : req.body,\r\n redirect: req.redirect,\r\n }))\r\n }\r\n\r\n return auth.handler(req)\r\n })\r\n\r\n // Maintenance mode check\r\n app.use('*', async (c, next) => {\r\n // Skip for health, auth, and internal routes so operators can still access them\r\n if (\r\n c.req.path === '/health' ||\r\n c.req.path.startsWith('/api/auth') ||\r\n c.req.path.startsWith('/internal')\r\n ) {\r\n return next()\r\n }\r\n // Read maintenance_mode flag from KV (set via PATCH /config/maintenance_mode).\r\n // KV is faster than D1 for this hot-path check — O(1) per request.\r\n const flag = await c.env.KV.get<boolean>('config:maintenance_mode', 'json')\r\n if (flag === true) {\r\n throw serviceUnavailable(\r\n 'The site is currently under maintenance. Please check back soon.',\r\n )\r\n }\r\n return next()\r\n })\r\n\r\n // Routes\r\n app.post(TURNSTILE_VERIFY_PATH, handleTurnstileVerify)\r\n app.route('/health', healthRoute)\r\n app.route('/api/config', siteConfigRoute)\r\n app.route('/api/classes', classesRoute)\r\n app.route('/api/themes', themesRoute)\r\n app.route('/api/users', usersRoute)\r\n app.route('/api/organizations', organizationsRoute)\r\n app.route('/api/faqs', faqsRoute)\r\n app.route('/api/uploads', uploadsRoute)\r\n app.route('/internal/gforms-webhook', gformsWebhookRoute)\r\n\r\n // Error handler\r\n app.onError(errorHandler)\r\n\r\n // 404\r\n app.notFound((c) =>\r\n c.json({ error: { code: 'NOT_FOUND', message: 'Route not found' } }, 404),\r\n )\r\n\r\n return app\r\n}\r\n","/**\r\n * Amazon SES (v2) transactional email client.\r\n *\r\n * Uses the SES v2 SendEmail REST endpoint with AWS SigV4 signing.\r\n * Fully fetch-native — no SDK, fully edge-compatible (Cloudflare Workers).\r\n *\r\n * Docs: https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html\r\n */\r\n\r\nexport interface SesEmailOptions {\r\n to: string | string[]\r\n subject: string\r\n html: string\r\n from?: string\r\n replyTo?: string\r\n}\r\n\r\nexport interface SesBatchEmailOptions {\r\n emails: SesEmailOptions[]\r\n}\r\n\r\nexport class SesService {\r\n private readonly region: string\r\n private readonly accessKeyId: string\r\n private readonly secretAccessKey: string\r\n private readonly defaultFrom: string\r\n\r\n constructor(opts: {\r\n region: string\r\n accessKeyId: string\r\n secretAccessKey: string\r\n fromAddress: string\r\n fromName?: string | undefined\r\n }) {\r\n this.region = opts.region\r\n this.accessKeyId = opts.accessKeyId\r\n this.secretAccessKey = opts.secretAccessKey\r\n this.defaultFrom = formatFrom(opts.fromName, opts.fromAddress)\r\n }\r\n\r\n /**\r\n * Send a single email via SES v2 SendEmail.\r\n * Throws on any non-2xx response.\r\n */\r\n async sendEmail(options: SesEmailOptions): Promise<{ messageId: string }> {\r\n const toAddresses = Array.isArray(options.to) ? options.to : [options.to]\r\n const from = options.from ?? this.defaultFrom\r\n\r\n const body = JSON.stringify({\r\n FromEmailAddress: from,\r\n Destination: { ToAddresses: toAddresses },\r\n Content: {\r\n Simple: {\r\n Subject: { Data: options.subject, Charset: 'UTF-8' },\r\n Body: { Html: { Data: options.html, Charset: 'UTF-8' } },\r\n },\r\n },\r\n ...(options.replyTo\r\n ? { ReplyToAddresses: [options.replyTo] }\r\n : {}),\r\n })\r\n\r\n const response = await this._signedFetch('POST', '/v2/email/outbound-emails', body)\r\n\r\n if (!response.ok) {\r\n const err = await response.text()\r\n throw new SesError(response.status, err)\r\n }\r\n\r\n const data = (await response.json()) as { MessageId: string }\r\n return { messageId: data.MessageId }\r\n }\r\n\r\n /**\r\n * Send a batch of emails sequentially (SES v2 has no native batch endpoint).\r\n * Each message is sent individually; partial failure does NOT abort remaining sends.\r\n * Returns settled results so the caller can decide what to do with failures.\r\n */\r\n async sendBatch(emails: SesEmailOptions[]): Promise<PromiseSettledResult<{ messageId: string }>[]> {\r\n return Promise.allSettled(emails.map((e) => this.sendEmail(e)))\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // SigV4 signing\r\n // ---------------------------------------------------------------------------\r\n\r\n private async _signedFetch(method: string, path: string, body: string): Promise<Response> {\r\n const url = `https://email.${this.region}.amazonaws.com${path}`\r\n const now = new Date()\r\n const amzDate = formatAmzDate(now)\r\n const dateStamp = amzDate.slice(0, 8)\r\n const host = `email.${this.region}.amazonaws.com`\r\n const service = 'ses'\r\n\r\n const payloadHash = await sha256Hex(body)\r\n\r\n const canonicalHeaders = `content-type:application/json\\nhost:${host}\\nx-amz-date:${amzDate}\\n`\r\n const signedHeaders = 'content-type;host;x-amz-date'\r\n\r\n const canonicalRequest = [\r\n method,\r\n path,\r\n '', // query string\r\n canonicalHeaders,\r\n signedHeaders,\r\n payloadHash,\r\n ].join('\\n')\r\n\r\n const credentialScope = `${dateStamp}/${this.region}/${service}/aws4_request`\r\n const stringToSign = [\r\n 'AWS4-HMAC-SHA256',\r\n amzDate,\r\n credentialScope,\r\n await sha256Hex(canonicalRequest),\r\n ].join('\\n')\r\n\r\n const signingKey = await getSigningKey(this.secretAccessKey, dateStamp, this.region, service)\r\n const signature = await hmacHex(signingKey, stringToSign)\r\n\r\n const authHeader =\r\n `AWS4-HMAC-SHA256 Credential=${this.accessKeyId}/${credentialScope}, ` +\r\n `SignedHeaders=${signedHeaders}, Signature=${signature}`\r\n\r\n return fetch(url, {\r\n method,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Amz-Date': amzDate,\r\n Authorization: authHeader,\r\n },\r\n body,\r\n })\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// SES-specific error — carries the HTTP status so callers can classify it\r\n// ---------------------------------------------------------------------------\r\n\r\nexport class SesError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(`SES error ${status}: ${message}`)\r\n this.name = 'SesError'\r\n }\r\n\r\n /**\r\n * True for errors that are permanent (not worth retrying via SES again).\r\n * 400 BadRequest, 403 Forbidden, 404 NotFound → non-retryable.\r\n * 429 ThrottlingException, 5xx → retryable.\r\n */\r\n get isNonRetryable(): boolean {\r\n return this.status >= 400 && this.status < 500 && this.status !== 429\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// SigV4 crypto helpers (Web Crypto API — available in CF Workers)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatAmzDate(d: Date): string {\r\n return d.toISOString().replace(/[:-]|\\.\\d{3}/g, '').slice(0, 15) + 'Z'\r\n}\r\n\r\nasync function sha256Hex(data: string): Promise<string> {\r\n const encoded = new TextEncoder().encode(data)\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', encoded)\r\n return bufToHex(hashBuffer)\r\n}\r\n\r\nasync function hmacHex(key: ArrayBuffer, data: string): Promise<string> {\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw',\r\n key,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign'],\r\n )\r\n const sig = await crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(data))\r\n return bufToHex(sig)\r\n}\r\n\r\nasync function hmacBuffer(key: ArrayBuffer, data: string): Promise<ArrayBuffer> {\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw',\r\n key,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign'],\r\n )\r\n return crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(data))\r\n}\r\n\r\nasync function getSigningKey(\r\n secret: string,\r\n dateStamp: string,\r\n region: string,\r\n service: string,\r\n): Promise<ArrayBuffer> {\r\n const secretBytes = new TextEncoder().encode(`AWS4${secret}`)\r\n // Slice to get a plain ArrayBuffer (TextEncoder returns Uint8Array<ArrayBufferLike>)\r\n const kDate = await hmacBuffer(secretBytes.buffer.slice(0) as ArrayBuffer, dateStamp)\r\n const kRegion = await hmacBuffer(kDate, region)\r\n const kService = await hmacBuffer(kRegion, service)\r\n return hmacBuffer(kService, 'aws4_request')\r\n}\r\n\r\nfunction bufToHex(buf: ArrayBuffer): string {\r\n return Array.from(new Uint8Array(buf))\r\n .map((b) => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n}\r\n\r\n/**\r\n * Format email 'From' field as \"Name <email@domain.com>\" if name provided.\r\n */\r\nfunction formatFrom(name: string | undefined, email: string): string {\r\n if (!name) return email\r\n return `${name} <${email}>`\r\n}\r\n","/**\r\n * Resend transactional email client.\r\n * Fully fetch-native — no SDK, fully edge-compatible.\r\n */\r\n\r\nexport interface SendEmailOptions {\r\n to: string | string[];\r\n subject: string;\r\n html: string;\r\n from?: string; // defaults to LeapifyConfig.resend.fromAddress\r\n replyTo?: string;\r\n}\r\n\r\nexport interface BatchEmailOptions {\r\n emails: SendEmailOptions[];\r\n}\r\n\r\nexport class ResendService {\r\n private readonly apiKey: string;\r\n private readonly defaultFrom: string;\r\n\r\n constructor(apiKey: string, fromAddress: string) {\r\n this.apiKey = apiKey;\r\n this.defaultFrom = fromAddress;\r\n }\r\n\r\n private get headers() {\r\n return {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n \"Content-Type\": \"application/json\",\r\n };\r\n }\r\n\r\n async sendEmail(options: SendEmailOptions): Promise<{ id: string }> {\r\n const response = await fetch(\"https://api.resend.com/emails\", {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify({\r\n from: options.from ?? this.defaultFrom,\r\n to: Array.isArray(options.to) ? options.to : [options.to],\r\n subject: options.subject,\r\n html: options.html,\r\n ...(options.replyTo ? { reply_to: options.replyTo } : {}),\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Resend API error ${response.status}: ${err}`);\r\n }\r\n\r\n return response.json() as Promise<{ id: string }>;\r\n }\r\n\r\n async sendBatch(emails: SendEmailOptions[]): Promise<{ id: string }[]> {\r\n const response = await fetch(\"https://api.resend.com/emails/batch\", {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify(\r\n emails.map((e) => ({\r\n from: e.from ?? this.defaultFrom,\r\n to: Array.isArray(e.to) ? e.to : [e.to],\r\n subject: e.subject,\r\n html: e.html,\r\n ...(e.replyTo ? { reply_to: e.replyTo } : {}),\r\n })),\r\n ),\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Resend batch API error ${response.status}: ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as { data: { id: string }[] };\r\n return data.data;\r\n }\r\n}\r\n\r\n// Email templates\r\n\r\nexport function buildReminderEmail(event: {\r\n title: string;\r\n organization?: string | null;\r\n dateTime?: string | null;\r\n startTime?: string | null;\r\n venue?: string | null;\r\n gformsUrl?: string | null;\r\n}): string {\r\n const timeDisplay = [event.dateTime, event.startTime].filter(Boolean).join(\" at \");\r\n return `\r\n <div style=\"font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 24px;\">\r\n <h2 style=\"color: #1a1a2e;\">📅 Reminder: ${event.title}</h2>\r\n ${event.organization ? `<p style=\"color: #666;\">Organized by: <strong>${event.organization}</strong></p>` : \"\"}\r\n ${timeDisplay ? `<p>🕐 <strong>${timeDisplay}</strong></p>` : \"\"}\r\n ${event.venue ? `<p>📍 <strong>${event.venue}</strong></p>` : \"\"}\r\n <hr style=\"margin: 24px 0; border: none; border-top: 1px solid #eee;\" />\r\n ${\r\n event.gformsUrl\r\n ? `<p>You registered for this event. See you there!</p>\r\n <a href=\"${event.gformsUrl}\" style=\"display:inline-block;padding:12px 24px;background:#4f46e5;color:#fff;border-radius:6px;text-decoration:none;\">View Registration</a>`\r\n : \"<p>You registered for this event. See you there!</p>\"\r\n }\r\n <p style=\"margin-top: 32px; font-size: 12px; color: #999;\">\r\n DLSU CSO LEAP — This is an automated reminder.\r\n </p>\r\n </div>\r\n `;\r\n}\r\n","export interface RetryOptions {\r\n maxAttempts?: number\r\n baseDelayMs?: number\r\n maxDelayMs?: number\r\n shouldRetry?: (error: unknown) => boolean\r\n}\r\n\r\n/**\r\n * Retry an async function with exponential backoff + jitter.\r\n * Default: 3 attempts, 100ms base delay, 5s max delay.\r\n */\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n options: RetryOptions = {},\r\n): Promise<T> {\r\n const {\r\n maxAttempts = 3,\r\n baseDelayMs = 100,\r\n maxDelayMs = 5000,\r\n shouldRetry = defaultShouldRetry,\r\n } = options\r\n\r\n let lastError: unknown\r\n\r\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\r\n try {\r\n return await fn()\r\n } catch (error) {\r\n lastError = error\r\n if (attempt === maxAttempts || !shouldRetry(error)) throw error\r\n\r\n const exponential = baseDelayMs * 2 ** (attempt - 1)\r\n const jitter = Math.random() * 50\r\n const delay = Math.min(exponential + jitter, maxDelayMs)\r\n\r\n await sleep(delay)\r\n }\r\n }\r\n\r\n throw lastError\r\n}\r\n\r\nfunction defaultShouldRetry(error: unknown): boolean {\r\n // Retry on 429 and 5xx HTTP errors\r\n if (error instanceof Response) {\r\n return error.status === 429 || error.status >= 500\r\n }\r\n return true\r\n}\r\n\r\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))\r\n","/**\r\n * EmailRouter — provider-agnostic email facade.\r\n *\r\n * Strategy:\r\n * 1. Try Amazon SES (primary) with up to SES_MAX_ATTEMPTS retries.\r\n * 2. If SES raises a non-retryable error AND Resend is configured\r\n * (RESEND_API_KEY is set), fall back to Resend.\r\n * 3. If Resend is not configured, re-throw the SES error immediately.\r\n *\r\n * The caller (queue handler) is responsible for final DLQ behaviour via\r\n * message.retry() / message.ack().\r\n */\r\n\r\nimport { SesService, SesError } from './ses'\r\nimport { ResendService, type SendEmailOptions } from './resend'\r\nimport { withRetry } from '../lib/retry'\r\n\r\nconst SES_MAX_ATTEMPTS = 3\r\n\r\nexport interface EmailPayload {\r\n to: string | string[]\r\n subject: string\r\n html: string\r\n from?: string\r\n replyTo?: string\r\n}\r\n\r\nexport interface EmailRouterConfig {\r\n /** SES credentials — optional */\r\n ses?: {\r\n region: string\r\n accessKeyId: string\r\n secretAccessKey: string\r\n fromAddress: string\r\n } | undefined\r\n /** Resend credentials — optional */\r\n resend?: {\r\n apiKey: string\r\n fromAddress: string\r\n } | undefined\r\n /** Optional display name for 'From' header (e.g. \"Leapify\") */\r\n fromName?: string | undefined\r\n}\r\n\r\nexport class EmailRouter {\r\n private readonly ses: SesService | null\r\n private readonly resend: ResendService | null\r\n\r\n constructor(config: EmailRouterConfig) {\r\n this.ses = config.ses\r\n ? new SesService({ ...config.ses, fromName: config.fromName })\r\n : null\r\n this.resend = config.resend\r\n ? new ResendService(config.resend.apiKey, formatFrom(config.fromName, config.resend.fromAddress))\r\n : null\r\n }\r\n\r\n /**\r\n * Send a single email.\r\n * Tries SES first (with retries), then Resend if configured and SES fails permanently.\r\n */\r\n async sendEmail(payload: EmailPayload): Promise<{ provider: 'ses' | 'resend'; id: string }> {\r\n // 1. Try SES if configured\r\n if (this.ses) {\r\n try {\r\n const result = await withRetry(() => this.ses!.sendEmail(payload), {\r\n maxAttempts: SES_MAX_ATTEMPTS,\r\n shouldRetry: (err) => !(err instanceof SesError && err.isNonRetryable),\r\n })\r\n return { provider: 'ses', id: result.messageId }\r\n } catch (sesErr) {\r\n const isPermanent = sesErr instanceof SesError && sesErr.isNonRetryable\r\n\r\n // Fallback to Resend if SES has permanent failure\r\n if (isPermanent && this.resend) {\r\n console.warn('[EmailRouter] SES failed permanently — falling back to Resend', sesErr)\r\n const result = await this.resend.sendEmail(toResendOptions(payload))\r\n return { provider: 'resend', id: result.id }\r\n }\r\n\r\n throw sesErr\r\n }\r\n }\r\n\r\n // 2. Try Resend if SES is not configured but Resend is\r\n if (this.resend) {\r\n const result = await this.resend.sendEmail(toResendOptions(payload))\r\n return { provider: 'resend', id: result.id }\r\n }\r\n\r\n // 3. No providers configured\r\n throw new Error('No email providers (SES or Resend) are configured.')\r\n }\r\n\r\n /**\r\n * Send a batch of emails.\r\n * Each email is routed individually through sendEmail() so per-message\r\n * fallback logic applies consistently.\r\n *\r\n * Returns settled results — partial failures do NOT abort the batch.\r\n */\r\n async sendBatch(\r\n payloads: EmailPayload[],\r\n ): Promise<PromiseSettledResult<{ provider: 'ses' | 'resend'; id: string }>[]> {\r\n return Promise.allSettled(payloads.map((p) => this.sendEmail(p)))\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Factory — build an EmailRouter from raw Worker bindings\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type EmailEnv = {\r\n SES_REGION?: string\r\n SES_ACCESS_KEY_ID?: string\r\n SES_SECRET_ACCESS_KEY?: string\r\n SES_FROM_ADDRESS?: string\r\n EMAIL_FROM_NAME?: string\r\n ALLOWED_ORIGINS?: string\r\n RESEND_API_KEY?: string\r\n RESEND_FROM_ADDRESS?: string\r\n}\r\n\r\n/**\r\n * Build an EmailRouter from Cloudflare Worker bindings.\r\n * Returns null if no email providers are configured.\r\n */\r\nexport function createEmailRouter(env: EmailEnv): EmailRouter | null {\r\n const hasSes = env.SES_REGION && env.SES_ACCESS_KEY_ID && env.SES_SECRET_ACCESS_KEY\r\n const hasResend = !!env.RESEND_API_KEY\r\n\r\n if (!hasSes && !hasResend) {\r\n return null\r\n }\r\n\r\n return new EmailRouter({\r\n ses: hasSes\r\n ? {\r\n region: env.SES_REGION!,\r\n accessKeyId: env.SES_ACCESS_KEY_ID!,\r\n secretAccessKey: env.SES_SECRET_ACCESS_KEY!,\r\n fromAddress: env.SES_FROM_ADDRESS ?? 'noreply@leap.dlsu.edu.ph',\r\n }\r\n : undefined,\r\n fromName: env.EMAIL_FROM_NAME ?? undefined,\r\n resend: hasResend\r\n ? {\r\n apiKey: env.RESEND_API_KEY!,\r\n fromAddress: env.RESEND_FROM_ADDRESS ?? 'noreply@leap.dlsu.edu.ph',\r\n }\r\n : undefined,\r\n })\r\n}\r\n\r\n/**\r\n * Format email 'From' field as \"Name <email@domain.com>\" if name provided.\r\n */\r\nfunction formatFrom(name: string | undefined, email: string): string {\r\n if (!name) return email\r\n return `${name} <${email}>`\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Internal helpers\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction toResendOptions(payload: EmailPayload): SendEmailOptions {\r\n return {\r\n to: payload.to,\r\n subject: payload.subject,\r\n html: payload.html,\r\n ...(payload.from !== undefined ? { from: payload.from } : {}),\r\n ...(payload.replyTo !== undefined ? { replyTo: payload.replyTo } : {}),\r\n }\r\n}\r\n","import { eq } from 'drizzle-orm'\r\nimport type { LeapifyBindings } from '../types'\r\nimport type { LeapifyJob } from './jobs'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { createEmailRouter, type EmailRouter } from '../services/email'\r\nimport { buildReminderEmail } from '../services/resend'\r\nimport { GFormsService } from '../services/gforms'\r\n\r\n/**\r\n * CF Queue consumer handler.\r\n * Export from the consumer repo's worker entry like:\r\n *\r\n * ```ts\r\n * import { createQueueHandler } from 'leapify'\r\n * export const queue = createQueueHandler\r\n * ```\r\n */\r\nexport function createQueueHandler(env: LeapifyBindings) {\r\n return async (batch: MessageBatch<LeapifyJob>): Promise<void> => {\r\n const db = createDb(env.DB)\r\n const email = createEmailRouter(env)\r\n const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n\r\n for (const message of batch.messages) {\r\n try {\r\n await processJob(message.body, { db, email, gforms, env })\r\n message.ack()\r\n } catch (err) {\r\n console.error(`[Queue] Failed to process job ${message.body.type}:`, err)\r\n message.retry()\r\n }\r\n }\r\n }\r\n}\r\n\r\nasync function processJob(\r\n job: LeapifyJob,\r\n services: {\r\n db: ReturnType<typeof createDb>\r\n email: EmailRouter | null\r\n gforms: GFormsService\r\n env: LeapifyBindings\r\n },\r\n): Promise<void> {\r\n const { db, email, gforms } = services\r\n\r\n switch (job.type) {\r\n case 'send_email': {\r\n if (!email) throw new Error('Email provider not configured')\r\n const result = await email.sendEmail(job.payload)\r\n console.log(`[Queue] send_email dispatched via ${result.provider} (id=${result.id})`)\r\n break\r\n }\r\n\r\n case 'send_reminder_email': {\r\n if (!email) throw new Error('Email provider not configured')\r\n\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.id, job.payload.eventId),\r\n with: { organization: true },\r\n })\r\n if (!event?.gformsId) break\r\n\r\n const emails = await gforms.getRespondentEmails(event.gformsId)\r\n if (emails.length === 0) break\r\n\r\n const isDay = job.payload.hoursBeforeEvent === 24\r\n const subject = isDay\r\n ? `Reminder: \"${event.title}\" is tomorrow!`\r\n : `Reminder: \"${event.title}\" is in 1 hour!`\r\n const html = buildReminderEmail({\r\n title: event.title,\r\n organization: event.organization?.name ?? null,\r\n dateTime: event.dateTime,\r\n startTime: event.startTime,\r\n venue: event.venue,\r\n gformsUrl: event.gformsUrl,\r\n })\r\n\r\n const BATCH_SIZE = 50\r\n const chunks: string[][] = []\r\n for (let i = 0; i < emails.length; i += BATCH_SIZE) {\r\n chunks.push(emails.slice(i, i + BATCH_SIZE))\r\n }\r\n const failures: string[] = []\r\n for (let i = 0; i < chunks.length; i++) {\r\n const chunk = chunks[i]\r\n try {\r\n await email.sendEmail({ to: chunk, subject, html })\r\n } catch (err) {\r\n failures.push(...chunk)\r\n console.error(\r\n `[Queue] send_reminder_email: ${failures.length}/${chunk.length} messages failed in batch ${i / BATCH_SIZE + 1}`,\r\n err,\r\n )\r\n }\r\n }\r\n\r\n const field = isDay ? 'reminder24hSent' : 'reminder1hSent'\r\n await db.update(events).set({ [field]: true }).where(eq(events.id, event.id))\r\n break\r\n }\r\n\r\n case 'audit_log': {\r\n console.log(`[Queue] audit_log: ${job.payload.action} by ${job.payload.userId}`)\r\n break\r\n }\r\n\r\n\r\n\r\n case 'notify_batch_release': {\r\n console.log(`[Queue] notify_batch_release: ${job.payload.eventIds.length} events released`)\r\n break\r\n }\r\n\r\n case 'renew_forms_watch': {\r\n try {\r\n await gforms.renewWatch(job.payload.formId, job.payload.watchId)\r\n console.log(`[Queue] renew_forms_watch: renewed watch for form ${job.payload.formId}`)\r\n } catch (err) {\r\n console.error(`[Queue] renew_forms_watch failed for form ${job.payload.formId}:`, err)\r\n }\r\n break\r\n }\r\n }\r\n}\r\n","import { eq, and, lte, sql } from 'drizzle-orm'\r\nimport type { LeapifyBindings } from '../types'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { CacheService } from '../services/cache'\r\n\r\n/**\r\n * Cron: every 1 minute (`* * * * *`)\r\n *\r\n * Finds all events with status='queued' whose release_at has passed,\r\n * publishes them atomically, and invalidates the events list KV cache.\r\n */\r\nexport async function batchRelease(env: LeapifyBindings): Promise<void> {\r\n const db = createDb(env.DB)\r\n const cache = new CacheService(env.KV)\r\n\r\n const now = Math.floor(Date.now() / 1000)\r\n\r\n // Fetch queued events ready to publish\r\n const toPublish = await db.query.events.findMany({\r\n where: and(eq(events.status, 'queued'), lte(events.releaseAt, now)),\r\n columns: { id: true, slug: true },\r\n })\r\n\r\n if (toPublish.length === 0) return\r\n\r\n const ids = toPublish.map((e) => e.id)\r\n\r\n // Batch update to 'published'\r\n await db\r\n .update(events)\r\n .set({ status: 'published', publishedAt: sql`(unixepoch())` })\r\n .where(\r\n // Drizzle doesn't have inArray for D1; use raw SQL for batch\r\n sql`${events.id} IN (${sql.join(\r\n ids.map((id) => sql`${id}`),\r\n sql`, `,\r\n )})`,\r\n )\r\n\r\n // Invalidate events list cache\r\n await cache.del('events:list')\r\n await cache.del('events:etag')\r\n\r\n console.log(\r\n `[batch-release] Published ${toPublish.length} events:`,\r\n toPublish.map((e) => e.slug).join(', '),\r\n )\r\n}\r\n","import { eq } from \"drizzle-orm\";\r\nimport type { LeapifyBindings } from \"../types\";\r\nimport { createDb } from \"../db\";\r\nimport { events } from \"../db/schema/classes\";\r\nimport { CacheService } from \"../services/cache\";\r\nimport { GFormsService } from \"../services/gforms\";\r\nimport { SlotsService } from \"../services/slots\";\r\n\r\nconst LOCK_KEY = \"cron:reconcile-slots:lock\";\r\nconst LOCK_TTL = 300; // 5 minutes\r\n\r\n/**\r\n * Cron: every 5 minutes (`*\\/5 * * * *`)\r\n *\r\n * Compares D1 registered_slots against actual Google Forms response counts.\r\n * Corrects any drift caused by missed webhook notifications.\r\n * Uses a distributed lock (KV) to ensure only one instance runs.\r\n */\r\nexport async function reconcileSlots(env: LeapifyBindings): Promise<void> {\r\n const db = createDb(env.DB);\r\n const cache = new CacheService(env.KV);\r\n const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON);\r\n const slots = new SlotsService(db, cache);\r\n\r\n // Distributed lock\r\n const lock = await cache.get<string>(LOCK_KEY);\r\n if (lock) {\r\n console.log(\"[reconcile-slots] Lock held, skipping.\");\r\n return;\r\n }\r\n await cache.set(LOCK_KEY, \"1\", LOCK_TTL);\r\n\r\n try {\r\n // Fetch all published events with a Google Form\r\n const publishedEvents = await db.query.events.findMany({\r\n where: eq(events.status, \"published\"),\r\n columns: { id: true, slug: true, gformsId: true, registeredSlots: true },\r\n });\r\n\r\n const eventsWithForms = publishedEvents.filter((e) => e.gformsId);\r\n let corrected = 0;\r\n\r\n for (const event of eventsWithForms) {\r\n try {\r\n const googleCount = await gforms.getExactResponseCount(event.gformsId!);\r\n const localCount = event.registeredSlots;\r\n\r\n if (googleCount !== localCount) {\r\n console.warn(\r\n `[reconcile-slots] Drift on \"${event.slug}\": local=${localCount}, google=${googleCount}`,\r\n );\r\n await slots.correctCount(event.slug, googleCount);\r\n corrected++;\r\n }\r\n } catch (err) {\r\n // Don't let one form failure abort the whole reconciliation\r\n console.error(`[reconcile-slots] Error checking \"${event.slug}\":`, err);\r\n }\r\n }\r\n\r\n console.log(\r\n `[reconcile-slots] Checked ${eventsWithForms.length} events, corrected ${corrected}.`,\r\n );\r\n } finally {\r\n await cache.del(LOCK_KEY);\r\n }\r\n}\r\n","import { and, eq } from \"drizzle-orm\";\r\nimport type { LeapifyBindings } from \"../types\";\r\nimport { createDb } from \"../db\";\r\nimport { events } from \"../db/schema/classes\";\r\n\r\n/**\r\n * Parse a human-readable dateTime (e.g. \"May 7, 2026\") and optional startTime\r\n * (e.g. \"14:30\") into a Unix timestamp (seconds). Returns null if unparseable.\r\n */\r\nfunction parseStartTimestamp(\r\n dateTime: string | null,\r\n startTime: string | null,\r\n): number | null {\r\n if (!dateTime) return null;\r\n const combined = startTime ? `${dateTime} ${startTime}` : dateTime;\r\n const ms = Date.parse(combined);\r\n return Number.isNaN(ms) ? null : Math.floor(ms / 1000);\r\n}\r\n\r\n/**\r\n * Cron: every hour (`0 * * * *`)\r\n *\r\n * Scans published events for those approaching their start time.\r\n * Queues send_reminder_email jobs for events within the 24h and 1h windows.\r\n */\r\nexport async function reminderEmails(env: LeapifyBindings): Promise<void> {\r\n if (!env.EMAIL_QUEUE) {\r\n console.warn(\r\n \"[reminder-emails] EMAIL_QUEUE binding not configured, skipping.\",\r\n );\r\n return;\r\n }\r\n\r\n const hasSes = !!(\r\n env.SES_REGION &&\r\n env.SES_ACCESS_KEY_ID &&\r\n env.SES_SECRET_ACCESS_KEY\r\n );\r\n const hasResend = !!env.RESEND_API_KEY;\r\n if (!hasSes && !hasResend) {\r\n console.warn(\r\n \"[reminder-emails] No email providers configured. Skipping reminders.\",\r\n );\r\n return;\r\n }\r\n\r\n const db = createDb(env.DB);\r\n const now = Math.floor(Date.now() / 1000);\r\n\r\n // Fetch published events that haven't had 24h reminders sent\r\n // We filter in-memory since startsAt is derived from dateTime + startTime\r\n const candidates24h = await db.query.events.findMany({\r\n where: and(\r\n eq(events.status, \"published\"),\r\n eq(events.reminder24hSent, false),\r\n ),\r\n columns: {\r\n id: true,\r\n dateTime: true,\r\n startTime: true,\r\n },\r\n });\r\n\r\n for (const event of candidates24h) {\r\n const startsAt = parseStartTimestamp(event.dateTime, event.startTime);\r\n if (!startsAt) continue;\r\n\r\n const hoursUntil = (startsAt - now) / 3600;\r\n if (hoursUntil <= 25 && hoursUntil >= 23) {\r\n await env.EMAIL_QUEUE.send({\r\n type: \"send_reminder_email\",\r\n payload: { eventId: event.id, hoursBeforeEvent: 24 },\r\n });\r\n }\r\n }\r\n\r\n // Fetch published events that haven't had 1h reminders sent\r\n const candidates1h = await db.query.events.findMany({\r\n where: and(\r\n eq(events.status, \"published\"),\r\n eq(events.reminder1hSent, false),\r\n ),\r\n columns: {\r\n id: true,\r\n dateTime: true,\r\n startTime: true,\r\n },\r\n });\r\n\r\n for (const event of candidates1h) {\r\n const startsAt = parseStartTimestamp(event.dateTime, event.startTime);\r\n if (!startsAt) continue;\r\n\r\n const hoursUntil = (startsAt - now) / 3600;\r\n if (hoursUntil <= 1.5 && hoursUntil >= 0) {\r\n await env.EMAIL_QUEUE.send({\r\n type: \"send_reminder_email\",\r\n payload: { eventId: event.id, hoursBeforeEvent: 1 },\r\n });\r\n }\r\n }\r\n}\r\n","import { and, eq, lte } from 'drizzle-orm'\r\nimport type { LeapifyBindings } from '../types'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { GFormsService } from '../services/gforms'\r\n\r\nconst RENEWAL_WINDOW = 86400 // renew watches expiring within 24 hours\r\n\r\n/**\r\n * Cron: daily at midnight (`0 0 * * *`)\r\n *\r\n * Finds Google Forms Watches expiring within 24 hours and renews them.\r\n * Watches have a hard 7-day TTL; this cron keeps them alive indefinitely.\r\n */\r\nexport async function renewWatches(env: LeapifyBindings): Promise<void> {\r\n const db = createDb(env.DB)\r\n const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n\r\n const now = Math.floor(Date.now() / 1000)\r\n const threshold = now + RENEWAL_WINDOW\r\n\r\n const expiring = await db.query.events.findMany({\r\n where: and(\r\n eq(events.status, 'published'),\r\n lte(events.watchExpiresAt, threshold),\r\n ),\r\n columns: { id: true, slug: true, gformsId: true, watchId: true, watchExpiresAt: true },\r\n })\r\n\r\n const watchEvents = expiring.filter((e) => e.gformsId && e.watchId)\r\n let renewed = 0\r\n\r\n for (const event of watchEvents) {\r\n try {\r\n const result = await gforms.renewWatch(event.gformsId!, event.watchId!)\r\n const newExpiry = Math.floor(new Date(result.expireTime).getTime() / 1000)\r\n\r\n await db\r\n .update(events)\r\n .set({ watchExpiresAt: newExpiry })\r\n .where(eq(events.id, event.id))\r\n\r\n renewed++\r\n console.log(`[renew-watches] Renewed Watch for \"${event.slug}\", expires ${result.expireTime}`)\r\n } catch (err) {\r\n console.error(`[renew-watches] Failed to renew Watch for \"${event.slug}\":`, err)\r\n }\r\n }\r\n\r\n console.log(`[renew-watches] Renewed ${renewed}/${watchEvents.length} watches.`)\r\n}\r\n","/**\r\n * Auto-migration helper for D1 databases.\r\n *\r\n * When enabled, ensures all required tables exist on the first request.\r\n * Safe to call on every boot — all statements use IF NOT EXISTS.\r\n *\r\n * Import from 'leapify' — server-only.\r\n *\r\n * @example\r\n * // worker.ts\r\n * import { createLeapify } from 'leapify'\r\n *\r\n * export default createLeapify({ autoMigrate: true })\r\n */\r\n\r\n// ─── Patches for existing databases ──────────────────────────────────────────\r\n// ALTER TABLE statements that add columns added after initial deploy.\r\n// Safe to run on every boot — duplicate column errors are caught and ignored.\r\n\r\nconst PATCH_STATEMENTS = [\r\n `ALTER TABLE \"themes\" ADD COLUMN \"updated_at\" integer NOT NULL DEFAULT (unixepoch())`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"organization_id\" text`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"class_code\" text`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"start_time\" text`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"end_time\" text`,\r\n `ALTER TABLE \"events\" RENAME COLUMN \"is_major\" TO \"is_spotlight\"`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_organization_id\" ON \"events\" (\"organization_id\")`,\r\n];\r\n\r\n// ─── Full schema for fresh databases ────────────────────────────────────────\r\n\r\nconst CREATE_STATEMENTS = [\r\n // Better Auth: user\r\n `CREATE TABLE IF NOT EXISTS \"user\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"email\" text NOT NULL,\r\n \"email_verified\" integer DEFAULT false NOT NULL,\r\n \"image\" text,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"user_email_unique\" ON \"user\" (\"email\")`,\r\n\r\n // Better Auth: session\r\n `CREATE TABLE IF NOT EXISTS \"session\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"expires_at\" integer NOT NULL,\r\n \"token\" text NOT NULL,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"ip_address\" text,\r\n \"user_agent\" text,\r\n \"user_id\" text NOT NULL,\r\n FOREIGN KEY (\"user_id\") REFERENCES \"user\"(\"id\") ON UPDATE no action ON DELETE cascade\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"session_token_unique\" ON \"session\" (\"token\")`,\r\n `CREATE INDEX IF NOT EXISTS \"session_userId_idx\" ON \"session\" (\"user_id\")`,\r\n\r\n // Better Auth: account\r\n `CREATE TABLE IF NOT EXISTS \"account\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"account_id\" text NOT NULL,\r\n \"provider_id\" text NOT NULL,\r\n \"user_id\" text NOT NULL,\r\n \"access_token\" text,\r\n \"refresh_token\" text,\r\n \"id_token\" text,\r\n \"access_token_expires_at\" integer,\r\n \"refresh_token_expires_at\" integer,\r\n \"scope\" text,\r\n \"password\" text,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n FOREIGN KEY (\"user_id\") REFERENCES \"user\"(\"id\") ON UPDATE no action ON DELETE cascade\r\n )`,\r\n `CREATE INDEX IF NOT EXISTS \"account_userId_idx\" ON \"account\" (\"user_id\")`,\r\n\r\n // Better Auth: verification\r\n `CREATE TABLE IF NOT EXISTS \"verification\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"identifier\" text NOT NULL,\r\n \"value\" text NOT NULL,\r\n \"expires_at\" integer NOT NULL,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)),\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer))\r\n )`,\r\n `CREATE INDEX IF NOT EXISTS \"verification_identifier_idx\" ON \"verification\" (\"identifier\")`,\r\n\r\n // App: users\r\n `CREATE TABLE IF NOT EXISTS \"users\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"better_auth_id\" text NOT NULL,\r\n \"email\" text NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"role\" text DEFAULT 'student' NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"users_better_auth_id_unique\" ON \"users\" (\"better_auth_id\")`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"users_email_unique\" ON \"users\" (\"email\")`,\r\n\r\n // App: organizations (before events, due to FK)\r\n `CREATE TABLE IF NOT EXISTS \"organizations\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"acronym\" text NOT NULL,\r\n \"logo_url\" text,\r\n \"link\" text,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"organizations_name_unique\" ON \"organizations\" (\"name\")`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"organizations_acronym_unique\" ON \"organizations\" (\"acronym\")`,\r\n\r\n // App: themes (before events, due to FK)\r\n `CREATE TABLE IF NOT EXISTS \"themes\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"path\" text NOT NULL,\r\n \"created_at\" integer NOT NULL,\r\n \"updated_at\" integer NOT NULL DEFAULT (unixepoch())\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"themes_name_unique\" ON \"themes\" (\"name\")`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"themes_path_unique\" ON \"themes\" (\"path\")`,\r\n\r\n // App: events\r\n `CREATE TABLE IF NOT EXISTS \"events\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"slug\" text NOT NULL,\r\n \"theme_id\" text,\r\n \"organization_id\" text,\r\n \"title\" text NOT NULL,\r\n \"description\" text,\r\n \"venue\" text,\r\n \"date_time\" text,\r\n \"price\" text,\r\n \"background_image_url\" text,\r\n \"class_code\" text,\r\n \"start_time\" text,\r\n \"end_time\" text,\r\n \"is_spotlight\" integer DEFAULT false NOT NULL,\r\n \"max_slots\" integer DEFAULT 0 NOT NULL,\r\n \"registered_slots\" integer DEFAULT 0 NOT NULL,\r\n \"gforms_id\" text,\r\n \"gforms_url\" text,\r\n \"gforms_editor_url\" text,\r\n \"registration_closes_at\" integer,\r\n \"watch_id\" text,\r\n \"watch_expires_at\" integer,\r\n \"status\" text DEFAULT 'draft' NOT NULL,\r\n \"release_at\" integer,\r\n \"reminder_24h_sent\" integer DEFAULT false NOT NULL,\r\n \"reminder_1h_sent\" integer DEFAULT false NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL,\r\n \"published_at\" integer,\r\n FOREIGN KEY (\"theme_id\") REFERENCES \"themes\"(\"id\") ON UPDATE no action ON DELETE set null,\r\n FOREIGN KEY (\"organization_id\") REFERENCES \"organizations\"(\"id\") ON UPDATE no action ON DELETE set null\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"events_slug_unique\" ON \"events\" (\"slug\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_status_release\" ON \"events\" (\"status\", \"release_at\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_theme_id\" ON \"events\" (\"theme_id\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_organization_id\" ON \"events\" (\"organization_id\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_slug\" ON \"events\" (\"slug\")`,\r\n\r\n // App: faqs\r\n `CREATE TABLE IF NOT EXISTS \"faqs\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"question\" text NOT NULL,\r\n \"answer\" text NOT NULL,\r\n \"category\" text,\r\n \"sort_order\" integer DEFAULT 0 NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL,\r\n \"updated_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n\r\n // App: site_config\r\n `CREATE TABLE IF NOT EXISTS \"site_config\" (\r\n \"key\" text PRIMARY KEY NOT NULL,\r\n \"value\" text NOT NULL,\r\n \"updated_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n\r\n // App: bookmarks\r\n `CREATE TABLE IF NOT EXISTS \"bookmarks\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"user_id\" text NOT NULL,\r\n \"event_id\" text NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL,\r\n FOREIGN KEY (\"user_id\") REFERENCES \"users\"(\"id\") ON UPDATE no action ON DELETE cascade,\r\n FOREIGN KEY (\"event_id\") REFERENCES \"events\"(\"id\") ON UPDATE no action ON DELETE cascade\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"idx_bookmarks_user_event\" ON \"bookmarks\" (\"user_id\", \"event_id\")`,\r\n];\r\n\r\n/**\r\n * Ensure all required tables exist in the D1 database.\r\n *\r\n * Uses CREATE TABLE / INDEX IF NOT EXISTS so it's safe to call on every\r\n * boot. Only executes the full schema if the `user` table is missing\r\n * (i.e. fresh database).\r\n */\r\nexport async function ensureDatabase(d1: D1Database): Promise<void> {\r\n // Check if database is fresh (no tables yet)\r\n const { results } = await d1\r\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='user'\")\r\n .all<{ name: string }>();\r\n\r\n if (results.length === 0) {\r\n // Fresh database — create all tables\r\n for (const sql of CREATE_STATEMENTS) {\r\n await d1.prepare(sql).run();\r\n }\r\n }\r\n\r\n // Always run patches (safe for both fresh and existing databases)\r\n for (const sql of PATCH_STATEMENTS) {\r\n try {\r\n await d1.prepare(sql).run();\r\n } catch (err: any) {\r\n // Ignore \"duplicate column\" or \"no such column\" errors from ALTER TABLE\r\n if (\r\n err?.message?.includes(\"duplicate column\") ||\r\n (err?.message?.includes(\"no such column\") &&\r\n err?.message?.includes(\"is_major\"))\r\n ) {\r\n continue;\r\n }\r\n throw err;\r\n }\r\n }\r\n}\r\n","/**\r\n * Leapify — Fullstack npm module for DLSU CSO LEAP event websites.\r\n *\r\n * This file is the **server-side entry point**. It exports createLeapify(),\r\n * a Cloudflare Workers-compatible handler. Mount it in your server layer:\r\n *\r\n * // Next.js API route / SvelteKit endpoint / Cloudflare Pages Function / worker.ts\r\n * import { createLeapify } from 'leapify'\r\n *\r\n * export default createLeapify({\r\n * allowedOrigins: ['https://yourleapsite.com'],\r\n * })\r\n *\r\n * createLeapify() returns { fetch, scheduled, queue } — shaped for CF Workers.\r\n * See wrangler.toml.example for required bindings (D1, KV, Queues, Crons).\r\n *\r\n * For browser / client-component usage, import from 'leapify/client' instead:\r\n *\r\n * import { createLeapifyClient, getLeapifyToken } from 'leapify/client'\r\n * import type { LeapEvent } from 'leapify/types'\r\n */\r\n\r\nif (typeof document !== \"undefined\") {\r\n throw new Error(\r\n \"[leapify] This module is server-only (Cloudflare Workers / server runtimes). \" +\r\n \"Do not import it in browser or client-component code. \" +\r\n \"Use 'leapify/client' for browser-safe typed API utilities.\",\r\n );\r\n}\r\n\r\nimport { createApp, type LeapifyAppOptions } from \"./app\";\r\nimport { createQueueHandler } from \"./queues/handlers\";\r\nimport { batchRelease } from \"./cron/batch-release\";\r\nimport { reconcileSlots } from \"./cron/reconcile-slots\";\r\nimport { reminderEmails } from \"./cron/reminder-emails\";\r\nimport { renewWatches } from \"./cron/renew-watches\";\r\nimport { ensureDatabase } from \"./db/migrate\";\r\nimport type { LeapifyBindings } from \"./types\";\r\nimport type { LeapifyJob } from \"./queues/jobs\";\r\n\r\nexport interface LeapifyOptions extends LeapifyAppOptions {\r\n /**\r\n * Automatically ensure all required tables exist on first request.\r\n * Safe for production — idempotent (only runs on fresh databases).\r\n * @default false\r\n */\r\n autoMigrate?: boolean;\r\n}\r\n\r\n/**\r\n * Primary factory function. Returns a Cloudflare Workers-compatible export object.\r\n *\r\n * @example\r\n * // worker.ts — the entire consumer worker implementation\r\n * import { createLeapify } from 'leapify'\r\n * export default createLeapify({ allowedOrigins: ['https://yourdomain.com'] })\r\n */\r\nexport function createLeapify(options: LeapifyOptions = {}) {\r\n const app = createApp(options);\r\n let loggedEmailConfig = false;\r\n let migrated = false;\r\n\r\n return {\r\n /**\r\n * Cloudflare Workers fetch handler.\r\n * Handles all HTTP requests routed through Leapify.\r\n */\r\n async fetch(\r\n request: Request,\r\n env: LeapifyBindings,\r\n ctx: ExecutionContext,\r\n ): Promise<Response> {\r\n if (!loggedEmailConfig) {\r\n loggedEmailConfig = true;\r\n const hasSes = !!(\r\n env.SES_REGION &&\r\n env.SES_ACCESS_KEY_ID &&\r\n env.SES_SECRET_ACCESS_KEY\r\n );\r\n const hasResend = !!env.RESEND_API_KEY;\r\n\r\n if (!hasSes && !hasResend) {\r\n console.warn(\r\n \"[leapify] Email functionality is DISABLED (no SES or Resend credentials).\",\r\n );\r\n }\r\n }\r\n\r\n if (options.autoMigrate && !migrated) {\r\n migrated = true;\r\n try {\r\n await ensureDatabase(env.DB);\r\n } catch (err) {\r\n console.error(\"[leapify] Auto-migration failed:\", err);\r\n }\r\n }\r\n\r\n return app.fetch(request, env, ctx);\r\n },\r\n\r\n // Cloudflare Workers scheduled handler. Routes cron triggers by schedule string.\r\n // Cron schedule (configured in wrangler.toml):\r\n // \"* * * * *\" → batch-release\r\n // \"*/5 * * * *\" → reconcile-slots\r\n // \"0 * * * *\" → reminder-emails + lifecycle-check\r\n // \"0 0 * * *\" → renew-watches\r\n async scheduled(\r\n event: ScheduledEvent,\r\n env: LeapifyBindings,\r\n ctx: ExecutionContext,\r\n ): Promise<void> {\r\n const { cron } = event;\r\n\r\n if (cron === \"* * * * *\") await batchRelease(env);\r\n if (cron === \"*/5 * * * *\") await reconcileSlots(env);\r\n if (cron === \"0 * * * *\") {\r\n ctx.waitUntil(reminderEmails(env));\r\n }\r\n if (cron === \"0 0 * * *\") await renewWatches(env);\r\n },\r\n\r\n /**\r\n * Cloudflare Queue consumer.\r\n * Processes async jobs (emails, audit logs, snapshots, watch renewals).\r\n */\r\n async queue(\r\n batch: MessageBatch<LeapifyJob>,\r\n env: LeapifyBindings,\r\n ): Promise<void> {\r\n const handler = createQueueHandler(env);\r\n return handler(batch);\r\n },\r\n };\r\n}\r\n\r\n// Re-exports\r\n\r\nexport { createQueueHandler } from \"./queues/handlers\";\r\nexport { createDb } from \"./db\";\r\nexport { ensureDatabase } from \"./db/migrate\";\r\nexport { createWorkerHandler, type CreateWorkerHandlerOptions } from \"./worker-handler\";\r\n\r\nexport type {\r\n LeapifyBindings,\r\n LeapifyEnv,\r\n SiteConfigKey,\r\n SiteConfigMap,\r\n} from \"./types\";\r\nexport type { LeapifyUser } from \"./auth/types\";\r\nexport type { LeapifyDb } from \"./db\";\r\nexport type { LeapifyJob } from \"./queues/jobs\";\r\nexport type { SlotInfo } from \"./services/slots\";\r\n\r\n/**\r\n * Runtime config shape injected into HTML pages by the worker.\r\n * Use on the server side to build the config object.\r\n */\r\nexport interface RuntimeConfig {\r\n production: boolean;\r\n leapifyApiUrl: string;\r\n turnstileSiteKey?: string;\r\n}\r\n\r\n/**\r\n * Build the runtime config from Worker bindings.\r\n * Used by createWorkerHandler() and standalone workers.\r\n */\r\nexport function getRuntimeConfig(env: LeapifyBindings): RuntimeConfig {\r\n return {\r\n production: true,\r\n leapifyApiUrl: \"\",\r\n ...(env.TURNSTILE_SITE_KEY ? { turnstileSiteKey: env.TURNSTILE_SITE_KEY } : {}),\r\n };\r\n}\r\n\r\n/**\r\n * Inject runtime config into an HTML string as a window.__CONFIG__ script tag.\r\n * Used by createWorkerHandler() and standalone workers.\r\n */\r\nexport function injectConfig(html: string, config: RuntimeConfig): string {\r\n const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)};</script>`;\r\n return html.replace(\"</head>\", `${configScript}</head>`);\r\n}\r\n\r\n// Schema re-exports for consumers running drizzle-kit migrations\r\nexport * from \"./db/schema\";\r\n","/**\r\n * Leapify — Standalone Cloudflare Worker entry point.\r\n *\r\n * ─────────────────────────────────────────────────────────────────────\r\n * MODE 1: Standalone Server (this file)\r\n * ─────────────────────────────────────────────────────────────────────\r\n * Deploy Leapify directly as a Cloudflare Worker — no frontend code\r\n * required. Configure wrangler.toml with your bindings + secrets and run:\r\n *\r\n * wrangler deploy\r\n *\r\n * CORS is controlled via the ALLOWED_ORIGINS Worker secret:\r\n * wrangler secret put ALLOWED_ORIGINS\r\n * # value: \"https://yoursite.com,https://www.yoursite.com\"\r\n *\r\n * ─────────────────────────────────────────────────────────────────────\r\n * MODE 2: npm module (src/index.ts)\r\n * ─────────────────────────────────────────────────────────────────────\r\n * Install leapify into your own project and mount it:\r\n *\r\n * npm install leapify\r\n *\r\n * import { createLeapify } from 'leapify'\r\n * export default createLeapify({ allowedOrigins: ['https://yoursite.com'] })\r\n *\r\n * See README.md for full mode comparison.\r\n */\r\n\r\nimport { createLeapify } from './index'\r\nimport type { LeapifyBindings } from './types'\r\nimport type { LeapifyJob } from './queues/jobs'\r\n\r\n/**\r\n * Parse ALLOWED_ORIGINS env var.\r\n * Falls back to [] which blocks all cross-origin requests (safest default).\r\n * Set to \"*\" to allow all origins (only appropriate for public-read APIs\r\n * without sensitive user data).\r\n */\r\nfunction parseAllowedOrigins(raw: string | undefined): string[] {\r\n if (!raw) return []\r\n return raw\r\n .split(',')\r\n .map((s) => s.trim())\r\n .filter(Boolean)\r\n}\r\n\r\nlet app: ReturnType<typeof createLeapify> | null = null;\r\n\r\n/**\r\n * Singleton Leapify app instance.\r\n * Ensures that startup logic (like email warnings) only runs once per Worker lifecycle.\r\n */\r\nfunction getApp(env: LeapifyBindings): NonNullable<typeof app> {\r\n if (!app) {\r\n const allowedOrigins = parseAllowedOrigins(env.ALLOWED_ORIGINS);\r\n app = createLeapify({ allowedOrigins });\r\n }\r\n return app;\r\n}\r\n\r\nconst leapify = {\r\n fetch(request: Request, env: LeapifyBindings, ctx: ExecutionContext): Promise<Response> {\r\n return getApp(env).fetch(request, env, ctx);\r\n },\r\n\r\n async scheduled(event: ScheduledEvent, env: LeapifyBindings, ctx: ExecutionContext): Promise<void> {\r\n return getApp(env).scheduled(event, env, ctx);\r\n },\r\n\r\n async queue(batch: MessageBatch<LeapifyJob>, env: LeapifyBindings): Promise<void> {\r\n return getApp(env).queue(batch, env);\r\n },\r\n};\r\n\r\nexport default leapify;\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/errors.ts","../src/lib/middleware/error-handler.ts","../src/db/schema/index.ts","../src/db/schema/users.ts","../src/db/schema/themes.ts","../src/db/schema/organizations.ts","../src/db/schema/classes.ts","../src/db/schema/site-config.ts","../src/db/schema/faqs.ts","../src/db/schema/bookmarks.ts","../src/db/schema/auth.ts","../src/db/index.ts","../src/lib/middleware/cors.ts","../src/lib/middleware/referer-guard.ts","../src/lib/middleware/turnstile-challenge.ts","../src/auth/auth.ts","../src/auth/middleware.ts","../src/routes/health.ts","../src/services/cache.ts","../src/services/slots.ts","../src/services/gforms.ts","../src/lib/middleware/rate-limit.ts","../src/routes/classes.ts","../src/routes/users.ts","../src/routes/site-config.ts","../src/routes/faqs.ts","../src/routes/internal/gforms-webhook.ts","../src/cron/reconcile-slots.ts","../src/routes/internal/reconcile-slots.ts","../src/cron/batch-release.ts","../src/routes/internal/batch-release.ts","../src/cron/reminder-emails.ts","../src/routes/internal/reminder-emails.ts","../src/cron/renew-watches.ts","../src/routes/internal/renew-watches.ts","../src/routes/uploads.ts","../src/routes/themes.ts","../src/routes/organizations.ts","../src/app.ts","../src/services/ses.ts","../src/services/resend.ts","../src/lib/retry.ts","../src/services/email.ts","../src/queues/handlers.ts","../src/db/migrate.ts","../src/index.ts","../src/worker.ts"],"names":["sqliteTable","text","integer","sql","relations","index","getOriginsFromDb","eq","createMiddleware","count","Hono","and","z","zValidator","lte","app","formatFrom"],"mappings":"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","file":"worker.js","sourcesContent":["export class LeapifyError extends Error {\r\n constructor(\r\n public readonly statusCode: number,\r\n public readonly code: string,\r\n message: string,\r\n ) {\r\n super(message)\r\n this.name = 'LeapifyError'\r\n }\r\n}\r\n\r\nexport const unauthorized = (message = 'Unauthorized') =>\r\n new LeapifyError(401, 'UNAUTHORIZED', message)\r\n\r\nexport const domainRestricted = () =>\r\n new LeapifyError(\r\n 403,\r\n 'DOMAIN_RESTRICTED',\r\n 'Only @dlsu.edu.ph email addresses are allowed',\r\n )\r\n\r\nexport const forbidden = (message = 'Forbidden') =>\r\n new LeapifyError(403, 'FORBIDDEN', message)\r\n\r\nexport const notFound = (resource = 'Resource') =>\r\n new LeapifyError(404, 'NOT_FOUND', `${resource} not found`)\r\n\r\nexport const badRequest = (message = 'Bad request') =>\r\n new LeapifyError(400, 'BAD_REQUEST', message)\r\n\r\nexport const conflict = (message = 'Conflict') =>\r\n new LeapifyError(409, 'CONFLICT', message)\r\n\r\nexport const tooManyRequests = (message = 'Too many requests') =>\r\n new LeapifyError(429, 'TOO_MANY_REQUESTS', message)\r\n\r\nexport const serviceUnavailable = (message = 'Service temporarily unavailable') =>\r\n new LeapifyError(503, 'SERVICE_UNAVAILABLE', message)\r\n\r\nexport const internalError = (message = 'Internal server error') =>\r\n new LeapifyError(500, 'INTERNAL_ERROR', message)\r\n","import type { ErrorHandler } from 'hono'\r\nimport { LeapifyError } from '../errors'\r\n\r\nexport const errorHandler: ErrorHandler = (err, c) => {\r\n if (err instanceof LeapifyError) {\r\n return c.json(\r\n { error: { code: err.code, message: err.message } },\r\n err.statusCode as Parameters<typeof c.json>[1],\r\n )\r\n }\r\n\r\n console.error('[Leapify] Unhandled error:', err)\r\n\r\n return c.json(\r\n { error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },\r\n 500,\r\n )\r\n}\r\n","export * from './users'\r\nexport * from './classes'\r\nexport * from './organizations'\r\nexport * from './site-config'\r\nexport * from './faqs'\r\nexport * from './bookmarks'\r\nexport * from './auth'\r\nexport * from './themes'\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\nexport const users = sqliteTable('users', {\r\n id: text('id')\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n /**\r\n * References Better Auth's `user.id`.\r\n * Set on first login after the databaseHooks.user.create.after callback fires.\r\n */\r\n betterAuthId: text('better_auth_id').notNull().unique(),\r\n email: text('email').notNull().unique(),\r\n name: text('name').notNull(),\r\n role: text('role', { enum: ['student', 'admin', 'super_admin'] })\r\n .notNull()\r\n .default('student'),\r\n createdAt: integer('created_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n})\r\n\r\nexport type User = typeof users.$inferSelect\r\nexport type NewUser = typeof users.$inferInsert\r\nexport type UserRole = 'student' | 'admin' | 'super_admin'\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { relations } from 'drizzle-orm'\r\nimport { events } from './classes'\r\n\r\nexport const themes = sqliteTable('themes', {\r\n id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n name: text('name').notNull().unique(),\r\n path: text('path').notNull().unique(), // e.g. \"/pirates-cove\"\r\n createdAt: integer('created_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\r\n updatedAt: integer('updated_at').notNull().$defaultFn(() => Math.floor(Date.now() / 1000)),\r\n})\r\n\r\nexport const themesRelations = relations(themes, ({ many }) => ({\r\n events: many(events),\r\n}))\r\n\r\nexport type Theme = typeof themes.$inferSelect\r\nexport type NewTheme = typeof themes.$inferInsert\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { relations, sql } from 'drizzle-orm'\r\nimport { events } from './classes'\r\n\r\nexport const organizations = sqliteTable('organizations', {\r\n id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n name: text('name').notNull().unique(),\r\n acronym: text('acronym').notNull().unique(),\r\n logoUrl: text('logo_url'),\r\n link: text('link'),\r\n createdAt: integer('created_at').notNull().default(sql`(unixepoch())`),\r\n})\r\n\r\nexport const organizationsRelations = relations(organizations, ({ many }) => ({\r\n events: many(events),\r\n}))\r\n\r\nexport type Organization = typeof organizations.$inferSelect\r\nexport type NewOrganization = typeof organizations.$inferInsert\r\n","import { sqliteTable, text, integer, index } from \"drizzle-orm/sqlite-core\";\r\nimport { sql, relations } from \"drizzle-orm\";\r\nimport { themes } from \"./themes\";\r\nimport { organizations } from \"./organizations\";\r\n\r\nexport const events = sqliteTable(\r\n \"events\",\r\n {\r\n id: text(\"id\")\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, \"\")),\r\n slug: text(\"slug\").notNull().unique(),\r\n // Theme reference\r\n themeId: text(\"theme_id\").references(() => themes.id),\r\n // Organization reference\r\n organizationId: text(\"organization_id\").references(() => organizations.id),\r\n\r\n // Core event fields (maps to LinkData)\r\n title: text(\"title\").notNull(),\r\n description: text(\"description\"),\r\n venue: text(\"venue\"),\r\n dateTime: text(\"date_time\"), // human-readable display string\r\n price: text(\"price\"), // e.g. \"Free\" or \"₱150\"\r\n backgroundImageUrl: text(\"background_image_url\"),\r\n classCode: text(\"class_code\"), // e.g. \"CSINTSY\"\r\n startTime: text(\"start_time\"), // start time string\r\n endTime: text(\"end_time\"), // end time string\r\n\r\n isSpotlight: integer(\"is_spotlight\", { mode: \"boolean\" }).notNull().default(false),\r\n\r\n // Slot tracking (local counter — NOT polled from Google Forms)\r\n maxSlots: integer(\"max_slots\").notNull().default(0),\r\n registeredSlots: integer(\"registered_slots\").notNull().default(0),\r\n gformsId: text(\"gforms_id\"), // Google Form ID for Watch + reconciliation\r\n gformsUrl: text(\"gforms_url\"), // informational link shown to students\r\n gformsEditorUrl: text(\"gforms_editor_url\"),\r\n registrationClosesAt: integer(\"registration_closes_at\"),\r\n watchId: text(\"watch_id\"), // stored after forms.watches.create\r\n watchExpiresAt: integer(\"watch_expires_at\"), // epoch — for renewal cron\r\n\r\n // Lifecycle / Release Queue\r\n status: text(\"status\", {\r\n enum: [\"draft\", \"queued\", \"published\", \"ended\", \"cancelled\"],\r\n })\r\n .notNull()\r\n .default(\"draft\"),\r\n releaseAt: integer(\"release_at\"), // scheduled publish epoch\r\n\r\n // Reminder tracking\r\n reminder24hSent: integer(\"reminder_24h_sent\", { mode: \"boolean\" })\r\n .notNull()\r\n .default(false),\r\n reminder1hSent: integer(\"reminder_1h_sent\", { mode: \"boolean\" })\r\n .notNull()\r\n .default(false),\r\n\r\n\r\n\r\n // Audit\r\n createdAt: integer(\"created_at\")\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n publishedAt: integer(\"published_at\"),\r\n },\r\n (table) => ({\r\n statusReleaseIdx: index(\"idx_events_status_release\").on(\r\n table.status,\r\n table.releaseAt,\r\n ),\r\n themeIdx: index(\"idx_events_theme_id\").on(table.themeId),\r\n organizationIdx: index(\"idx_events_organization_id\").on(table.organizationId),\r\n slugIdx: index(\"idx_events_slug\").on(table.slug),\r\n }),\r\n);\r\n\r\nexport const eventsRelations = relations(events, ({ one }) => ({\r\n theme: one(themes, {\r\n fields: [events.themeId],\r\n references: [themes.id],\r\n }),\r\n organization: one(organizations, {\r\n fields: [events.organizationId],\r\n references: [organizations.id],\r\n }),\r\n}));\r\n\r\nexport type Event = typeof events.$inferSelect;\r\nexport type NewEvent = typeof events.$inferInsert;\r\nexport type EventStatus =\r\n | \"draft\"\r\n | \"queued\"\r\n | \"published\"\r\n | \"ended\"\r\n | \"cancelled\";\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\n// Primary site config (from D1)\r\nexport const siteConfig = sqliteTable('site_config', {\r\n key: text('key').primaryKey(),\r\n value: text('value').notNull(), // JSON-serializable string\r\n updatedAt: integer('updated_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n})\r\n\r\nexport type SiteConfigRow = typeof siteConfig.$inferSelect\r\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\nexport const faqs = sqliteTable('faqs', {\r\n id: text('id')\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, '')),\r\n question: text('question').notNull(),\r\n answer: text('answer').notNull(), // markdown supported\r\n category: text('category'), // optional grouping, e.g. \"Registration\"\r\n sortOrder: integer('sort_order').notNull().default(0),\r\n createdAt: integer('created_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n updatedAt: integer('updated_at')\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n})\r\n\r\nexport type Faq = typeof faqs.$inferSelect\r\nexport type NewFaq = typeof faqs.$inferInsert\r\n","import {\r\n sqliteTable,\r\n text,\r\n integer,\r\n uniqueIndex,\r\n} from \"drizzle-orm/sqlite-core\";\r\nimport { sql, relations } from \"drizzle-orm\";\r\nimport { users } from \"./users\";\r\nimport { events } from \"./classes\";\r\n\r\nexport const bookmarks = sqliteTable(\r\n \"bookmarks\",\r\n {\r\n id: text(\"id\")\r\n .primaryKey()\r\n .$defaultFn(() => crypto.randomUUID().replace(/-/g, \"\")),\r\n userId: text(\"user_id\")\r\n .notNull()\r\n .references(() => users.id, { onDelete: \"cascade\" }),\r\n eventId: text(\"event_id\")\r\n .notNull()\r\n .references(() => events.id, { onDelete: \"cascade\" }),\r\n createdAt: integer(\"created_at\")\r\n .notNull()\r\n .default(sql`(unixepoch())`),\r\n },\r\n (table) => ({\r\n userEventIdx: uniqueIndex(\"idx_bookmarks_user_event\").on(\r\n table.userId,\r\n table.eventId,\r\n ),\r\n }),\r\n);\r\n\r\n// Relational definitions\r\n// Required for Drizzle's `db.query.bookmarks.findMany({ with: { event } })`\r\n// relational API to resolve the join correctly.\r\nexport const bookmarksRelations = relations(bookmarks, ({ one }) => ({\r\n event: one(events, {\r\n fields: [bookmarks.eventId],\r\n references: [events.id],\r\n }),\r\n user: one(users, {\r\n fields: [bookmarks.userId],\r\n references: [users.id],\r\n }),\r\n}));\r\n\r\nexport type Bookmark = typeof bookmarks.$inferSelect;\r\nexport type NewBookmark = typeof bookmarks.$inferInsert;\r\n","/**\r\n * Better Auth database tables for SQLite / D1.\r\n *\r\n * These are the four tables Better Auth requires to manage sessions, accounts,\r\n * and email-verification tokens. They are defined here as Drizzle schema so\r\n * we own the migration (generated via `npm run db:generate`).\r\n *\r\n * Column names and types match what `npx @better-auth/cli generate` produces\r\n * for the sqlite dialect — do NOT rename them; Better Auth queries by these\r\n * exact column names.\r\n *\r\n * Relationship to our `users` table:\r\n * Better Auth `user.id` → leapify `users.better_auth_id`\r\n * (the FK lives on our side so we can join without touching BA internals)\r\n */\r\nimport { sqliteTable, text, integer, index } from 'drizzle-orm/sqlite-core'\r\nimport { sql } from 'drizzle-orm'\r\n\r\n// ─── user ───────────────────────────────────────────────────────────────────\r\n// Better Auth's own user record. Stores identity, email verification status,\r\n// and the profile image URL coming from the Google account.\r\n\r\nexport const authUser = sqliteTable('user', {\r\n id: text('id').primaryKey(),\r\n name: text('name').notNull(),\r\n email: text('email').notNull().unique(),\r\n emailVerified: integer('email_verified', { mode: 'boolean' })\r\n .notNull()\r\n .default(false),\r\n image: text('image'),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n})\r\n\r\n// ─── session ─────────────────────────────────────────────────────────────────\r\n// One row per active session. `token` is the opaque bearer token / cookie\r\n// value clients present on each request.\r\n\r\nexport const authSession = sqliteTable(\r\n 'session',\r\n {\r\n id: text('id').primaryKey(),\r\n expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),\r\n token: text('token').notNull().unique(),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n ipAddress: text('ip_address'),\r\n userAgent: text('user_agent'),\r\n userId: text('user_id')\r\n .notNull()\r\n .references(() => authUser.id, { onDelete: 'cascade' }),\r\n },\r\n (table) => [index('session_userId_idx').on(table.userId)],\r\n)\r\n\r\n// ─── account ─────────────────────────────────────────────────────────────────\r\n// OAuth provider account linked to a user. One row per provider per user\r\n// (e.g. a user who signed in with Google has one `account` with providerId=\"google\").\r\n\r\nexport const authAccount = sqliteTable(\r\n 'account',\r\n {\r\n id: text('id').primaryKey(),\r\n accountId: text('account_id').notNull(),\r\n providerId: text('provider_id').notNull(),\r\n userId: text('user_id')\r\n .notNull()\r\n .references(() => authUser.id, { onDelete: 'cascade' }),\r\n accessToken: text('access_token'),\r\n refreshToken: text('refresh_token'),\r\n idToken: text('id_token'),\r\n accessTokenExpiresAt: integer('access_token_expires_at', {\r\n mode: 'timestamp_ms',\r\n }),\r\n refreshTokenExpiresAt: integer('refresh_token_expires_at', {\r\n mode: 'timestamp_ms',\r\n }),\r\n scope: text('scope'),\r\n password: text('password'),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .notNull()\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n },\r\n (table) => [index('account_userId_idx').on(table.userId)],\r\n)\r\n\r\n// ─── verification ─────────────────────────────────────────────────────────────\r\n// Short-lived tokens used for email verification / magic links.\r\n\r\nexport const authVerification = sqliteTable(\r\n 'verification',\r\n {\r\n id: text('id').primaryKey(),\r\n identifier: text('identifier').notNull(),\r\n value: text('value').notNull(),\r\n expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),\r\n createdAt: integer('created_at', { mode: 'timestamp_ms' })\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n updatedAt: integer('updated_at', { mode: 'timestamp_ms' })\r\n .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`),\r\n },\r\n (table) => [index('verification_identifier_idx').on(table.identifier)],\r\n)\r\n","import { drizzle } from 'drizzle-orm/d1'\r\nimport * as schema from './schema'\r\n\r\nexport function createDb(d1: D1Database) {\r\n return drizzle(d1, { schema })\r\n}\r\n\r\nexport type LeapifyDb = ReturnType<typeof createDb>\r\n\r\nexport * from './schema'\r\n","import { cors } from 'hono/cors'\n\nimport type { MiddlewareHandler } from 'hono'\nimport { createDb } from '../../db'\nimport { siteConfig } from '../../db/schema/site-config'\nimport { eq } from 'drizzle-orm'\n\nasync function getOriginsFromDb(env: {\n DB: import('@cloudflare/workers-types').D1Database\n}): Promise<string[] | null> {\n try {\n const db = createDb(env.DB)\n const row = await db.query.siteConfig.findFirst({\n where: eq(siteConfig.key, 'allowed_origins')\n })\n if (row) return JSON.parse(row.value) as string[]\n } catch {\n /* D1 unavailable — fall through */\n }\n return null\n}\n\nexport function createCorsMiddleware(\n allowedOrigins: string[]\n): MiddlewareHandler {\n return async (c, next) => {\n const origin = c.req.header('origin')\n\n // Get dynamic allowed origins from KV if present, fallback to static list\n const dynamicOriginsJson = (await c.env.KV.get(\n 'config:allowed_origins',\n 'json'\n )) as string[] | null\n let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins\n if (!dynamicOriginsJson) {\n const dbOrigins = await getOriginsFromDb(c.env)\n if (dbOrigins) {\n currentAllowedOrigins = dbOrigins\n await c.env.KV.put(\n 'config:allowed_origins',\n JSON.stringify(dbOrigins),\n { expirationTtl: 86400 }\n )\n }\n }\n\n // Public Image Exemption: Allow any origin for images and skip strict checks.\n if (c.req.path.startsWith('/api/uploads/images')) {\n c.header('Access-Control-Allow-Origin', '*')\n c.header('Access-Control-Allow-Methods', 'GET, OPTIONS')\n if (c.req.method === 'OPTIONS') {\n return c.body(null, 204)\n }\n return next()\n }\n\n // Strict ADR-001 Check: If an Origin is present, it MUST be allowed.\n if (\n !c.req.path.startsWith('/health') &&\n !c.req.path.startsWith('/api/auth') &&\n !c.req.path.startsWith('/internal') &&\n origin &&\n !currentAllowedOrigins.includes('*') &&\n !currentAllowedOrigins.includes(origin) &&\n origin !== new URL(c.req.url).origin\n ) {\n return c.json(\n {\n error: {\n code: 'DOMAIN_RESTRICTED',\n message: `Origin ${origin} is not allowed`\n }\n },\n 403\n )\n }\n\n const honoCors = cors({\n origin: currentAllowedOrigins,\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization'],\n exposeHeaders: ['ETag', 'Last-Modified', 'Cache-Control'],\n maxAge: 86400,\n credentials: true\n })\n\n return honoCors(c, next)\n }\n}\n","import { createMiddleware } from 'hono/factory'\nimport type { LeapifyBindings } from '../../types'\nimport { forbidden } from '../errors'\nimport { createDb } from '../../db'\nimport { siteConfig } from '../../db/schema/site-config'\nimport { eq } from 'drizzle-orm'\n\nasync function getOriginsFromDb(env: {\n DB: import('@cloudflare/workers-types').D1Database\n}): Promise<string[] | null> {\n try {\n const db = createDb(env.DB)\n const row = await db.query.siteConfig.findFirst({\n where: eq(siteConfig.key, 'allowed_origins')\n })\n if (row) return JSON.parse(row.value) as string[]\n } catch {\n /* D1 unavailable — fall through */\n }\n return null\n}\n\n/**\n * Referer guard for mutation endpoints (ADR-006, Layer 6).\n *\n * Validates that the `Referer` header on state-mutating requests (POST, PATCH, PUT, DELETE)\n * matches one of the configured allowed origins. Safe methods (GET, HEAD, OPTIONS) are\n * always allowed through without a Referer check.\n *\n * This is a friction layer — it stops naive raw-HTTP clients that don't set Referer.\n * Sophisticated clients can spook it, so this must NOT be relied on as the sole control\n * for authenticated mutation endpoints (Firebase JWT is the primary control there).\n *\n * Skipped entirely for /health and /internal routes.\n */\nexport function createRefererGuard(allowedOrigins: string[]) {\n const MUTATION_METHODS = new Set(['POST', 'PATCH', 'PUT', 'DELETE'])\n const SKIP_PREFIXES = ['/health', '/internal', '/api/auth', '/.well-known']\n\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\n // Only checl mutation methods\n if (!MUTATION_METHODS.has(c.req.method)) return next()\n\n // Skip for operational routes\n if (SKIP_PREFIXES.some((p) => c.req.path.startsWith(p))) return next()\n\n // Get dynamic allowed origins: KV (fast cache) • D1 (source of truth) • static fallback\n const dynamicOriginsJson = (await c.env.KV.get(\n 'config:allowed_origins',\n 'json'\n )) as string[] | null\n let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins\n if (!dynamicOriginsJson) {\n const dbOrigins = await getOriginsFromDb(c.env)\n if (dbOrigins) {\n currentAllowedOrigins = dbOrigins\n await c.env.KV.put(\n 'config:allowed_origins',\n JSON.stringify(dbOrigins),\n { expirationTtl: 86400 }\n )\n }\n }\n\n // Wildcard currentAllowedOrigins = dev/library mode, skip enforcement\n if (currentAllowedOrigins.includes('*')) return next()\n\n const referer = c.req.header('referer') ?? ''\n\n // Same-origin requests (console calling its own worker) are always allowed\n const requestOrigin = new URL(c.req.url).origin\n if (referer.startsWith(requestOrigin)) return next()\n\n const isAllowed = currentAllowedOrigins.some((origin) =>\n referer.startsWith(origin)\n )\n if (!isAllowed) {\n throw forbidden('Request origin not permitted')\n }\n\n return next()\n })\n}\n","import { createMiddleware } from 'hono/factory'\r\nimport type { Context } from 'hono'\r\nimport type { LeapifyBindings } from '../../types'\r\n\r\nexport const TURNSTILE_PATH = '/.well-known/leapify/turnstile'\r\n\r\nexport const TURNSTILE_VERIFY_PATH = `${TURNSTILE_PATH}/verify`\r\n\r\nexport const TURNSTILE_COOKIE_NAME = 'leapify-turnstile'\r\n\r\nconst VERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'\r\n\r\nconst COOKIE_MAX_AGE_SEC = 86400\r\n\r\nconst EXEMPT_PATHS = [\r\n \"/health\",\r\n \"/internal\",\r\n \"/api/auth\",\r\n \"/api/uploads/images\",\r\n \"/api/classes\",\r\n \"/api/faqs\",\r\n \"/api/config\",\r\n TURNSTILE_VERIFY_PATH,\r\n];\r\n\r\nfunction base64urlEncode(bytes: Uint8Array): string {\r\n let binary = ''\r\n for (const byte of bytes) {\r\n binary += String.fromCharCode(byte)\r\n }\r\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\r\n}\r\n\r\nfunction base64urlDecode(str: string): Uint8Array<ArrayBuffer> {\r\n const padded = str.replace(/-/g, '+').replace(/_/g, '/')\r\n const binary = atob(padded)\r\n const bytes = new Uint8Array(new ArrayBuffer(binary.length))\r\n for (let i = 0; i < binary.length; i++) {\r\n bytes[i] = binary.charCodeAt(i)\r\n }\r\n return bytes\r\n}\r\n\r\nasync function importHmacKey(secret: string): Promise<CryptoKey> {\r\n return crypto.subtle.importKey(\r\n 'raw',\r\n new TextEncoder().encode(secret),\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign', 'verify']\r\n )\r\n}\r\n\r\nasync function signCookie(secret: string, ip: string): Promise<string> {\r\n const ts = Date.now()\r\n const nonce = base64urlEncode(crypto.getRandomValues(new Uint8Array(8)))\r\n const payload = `${ip}:${ts}:${nonce}`\r\n const key = await importHmacKey(secret)\r\n const sig = await crypto.subtle.sign(\r\n 'HMAC',\r\n key,\r\n new TextEncoder().encode(payload)\r\n )\r\n const sigB64 = base64urlEncode(new Uint8Array(sig))\r\n return `${base64urlEncode(new TextEncoder().encode(payload))}.${sigB64}`\r\n}\r\n\r\nasync function validateCookie(\r\n secret: string,\r\n cookie: string,\r\n ip: string\r\n): Promise<boolean> {\r\n try {\r\n const [payloadB64, sigB64] = cookie.split('.')\r\n if (!payloadB64 || !sigB64) return false\r\n\r\n const payloadBytes = base64urlDecode(payloadB64)\r\n const sigBytes = base64urlDecode(sigB64)\r\n\r\n const key = await importHmacKey(secret)\r\n const valid = await crypto.subtle.verify(\r\n 'HMAC',\r\n key,\r\n sigBytes,\r\n payloadBytes\r\n )\r\n if (!valid) return false\r\n\r\n const payload = new TextDecoder().decode(payloadBytes)\r\n const [cookieIp, tsStr] = payload.split(':')\r\n\r\n if (cookieIp !== ip) return false\r\n\r\n const ts = parseInt(tsStr, 10)\r\n if (isNaN(ts) || Date.now() - ts > COOKIE_MAX_AGE_SEC * 1000) return false\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nfunction getClientIp(c: Context<{ Bindings: LeapifyBindings }>): string {\r\n return (\r\n c.req.header('CF-Connecting-IP') ??\r\n c.req.header('X-Real-IP') ??\r\n c.req.header('X-Forwarded-For')?.split(',')[0]?.trim() ??\r\n 'unknown'\r\n )\r\n}\r\n\r\nfunction isExempt(path: string): boolean {\r\n const normalized = path.toLowerCase().replace(/\\/$/, '')\r\n return EXEMPT_PATHS.some((p) => {\r\n const ep = p.toLowerCase().replace(/\\/$/, '')\r\n return normalized === ep || normalized.startsWith(ep + '/')\r\n })\r\n}\r\n\r\nfunction setCookieHeader(c: Context<{ Bindings: LeapifyBindings }>, token: string): void {\r\n const isSecure = c.req.raw.url.startsWith(\"https\") || c.req.header(\"x-forwarded-proto\") === \"https\";\r\n c.header(\r\n \"Set-Cookie\",\r\n `${TURNSTILE_COOKIE_NAME}=${token}; Path=/; Max-Age=${COOKIE_MAX_AGE_SEC}; ${\r\n isSecure ? \"Secure; \" : \"\"\r\n }HttpOnly; SameSite=Lax`,\r\n );\r\n}\r\n\r\n/**\r\n * POST /.well-known/leapify/turnstile/verify\r\n *\r\n * Validates a Turnstile token and issues a signed cookie on success.\r\n */\r\nexport async function handleTurnstileVerify(\r\n c: Context<{ Bindings: LeapifyBindings }>\r\n) {\r\n const body = await c.req.json<{ token?: string }>()\r\n const { token } = body\r\n\r\n if (!token) {\r\n return c.json(\r\n { error: { code: 'VALIDATION_ERROR', message: 'Missing Turnstile token' } },\r\n 422\r\n )\r\n }\r\n\r\n const secret = c.env.TURNSTILE_SECRET_KEY\r\n if (!secret) {\r\n return c.json(\r\n { error: { code: 'CONFIG_ERROR', message: 'Turnstile not configured' } },\r\n 500\r\n )\r\n }\r\n\r\n const ip = getClientIp(c)\r\n const formData = new URLSearchParams()\r\n formData.append('secret', secret)\r\n formData.append('response', token)\r\n if (ip !== 'unknown') {\r\n formData.append('remoteip', ip)\r\n }\r\n\r\n const res = await fetch(VERIFY_URL, {\r\n method: 'POST',\r\n body: formData,\r\n })\r\n const outcome = await res.json() as { success: boolean; 'error-codes'?: string[] }\r\n\r\n if (!outcome.success) {\r\n return c.json(\r\n { error: { code: 'TURNSTILE_FAILED', message: 'Turnstile verification failed', details: outcome['error-codes'] } },\r\n 403\r\n )\r\n }\r\n\r\n const cookieToken = await signCookie(secret, ip)\r\n setCookieHeader(c, cookieToken)\r\n\r\n return c.json({ success: true })\r\n}\r\n\r\n/**\r\n * Turnstile challenge middleware.\r\n *\r\n * Requires a valid Turnstile-signed cookie on all non-exempt requests.\r\n * The client must first solve a Turnstile challenge and POST the token\r\n * to the verify endpoint to obtain the cookie.\r\n *\r\n * Exempt paths: /health, /internal, /api/auth, /api/uploads/images,\r\n * and the verify endpoint itself.\r\n */\r\nexport function createTurnstileMiddleware() {\r\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\r\n if (isExempt(c.req.path)) return next()\r\n\r\n if (c.req.method === 'OPTIONS') return next()\r\n\r\n // Skip challenge for authenticated requests (Bearer token present)\r\n // The auth middleware will handle session validation instead.\r\n if (c.req.header('Authorization')) return next()\r\n\r\n const secret = c.env.TURNSTILE_SECRET_KEY\r\n if (!secret) return next()\r\n\r\n const cookieHeader = c.req.header('Cookie') ?? ''\r\n const cookieMatch = cookieHeader.match(\r\n new RegExp(`${TURNSTILE_COOKIE_NAME}=([^;]+)`)\r\n )\r\n if (cookieMatch) {\r\n const ip = getClientIp(c)\r\n const valid = await validateCookie(secret, cookieMatch[1], ip)\r\n if (valid) return next()\r\n }\r\n\r\n return c.json(\r\n { error: { code: 'TURNSTILE_REQUIRED', message: 'Turnstile verification required' } },\r\n 401\r\n )\r\n })\r\n}\r\n","import { betterAuth } from 'better-auth'\r\nimport { drizzleAdapter } from 'better-auth/adapters/drizzle'\r\nimport { bearer } from 'better-auth/plugins'\r\nimport { count } from 'drizzle-orm'\r\nimport { createDb } from '../db'\r\nimport {\r\n authUser,\r\n authSession,\r\n authAccount,\r\n authVerification\r\n} from '../db/schema/auth'\r\nimport { users } from '../db/schema/users'\r\nimport type { LeapifyBindings } from '../types'\r\n\r\nconst DLSU_DOMAIN = '@dlsu.edu.ph'\r\n\r\n/**\r\n * Creates a request-scoped Better Auth instance.\r\n *\r\n * Must be a factory (not a module singleton) because CF Workers D1\r\n * bindings are only available at request time, not at module init.\r\n *\r\n * Features:\r\n * - Drizzle SQLite adapter backed by D1\r\n * - bearer() plugin: supports Authorization: Bearer <token> alongside cookies\r\n * - Google social provider (for GIS One Tap credential flow)\r\n * - databaseHooks enforces @dlsu.edu.ph domain server-side\r\n * - After successful user creation, upserts a row in our custom `users` table\r\n * to carry the application role\r\n */\r\nexport function createAuth(env: LeapifyBindings) {\r\n const db = createDb(env.DB)\r\n\r\n return betterAuth({\r\n baseURL: env.BETTER_AUTH_URL,\r\n secret: env.BETTER_AUTH_SECRET,\r\n\r\n advanced: {\r\n ipAddress: {\r\n ipAddressHeaders: ['cf-connecting-ip', 'x-forwarded-for', 'x-real-ip'],\r\n },\r\n },\r\n\r\n database: drizzleAdapter(db, {\r\n provider: 'sqlite',\r\n schema: {\r\n user: authUser,\r\n session: authSession,\r\n account: authAccount,\r\n verification: authVerification\r\n }\r\n }),\r\n\r\n plugins: [bearer()],\r\n\r\n socialProviders: {\r\n google: {\r\n clientId: env.GOOGLE_CLIENT_ID,\r\n clientSecret: env.GOOGLE_CLIENT_SECRET,\r\n hd: env.GOOGLE_HD || undefined\r\n }\r\n },\r\n\r\n databaseHooks: {\r\n user: {\r\n create: {\r\n /**\r\n * Runs before the Better Auth `user` row is inserted.\r\n * Reject non-DLSU accounts before they ever touch the DB.\r\n */\r\n before: async (user) => {\r\n if (!user.email.endsWith(DLSU_DOMAIN)) {\r\n // Throwing here causes Better Auth to return 403 to the client\r\n throw new Error(\r\n 'DOMAIN_RESTRICTED: only @dlsu.edu.ph accounts are allowed'\r\n )\r\n }\r\n return { data: user }\r\n },\r\n\r\n /**\r\n * Runs after the Better Auth `user` row is created.\r\n * Upsert a matching row in our application `users` table so that\r\n * the role column and D1 foreign keys are always consistent.\r\n *\r\n * The very first user to sign in is automatically promoted to\r\n * `super_admin` so the platform has an administrator from day one.\r\n */\r\n after: async (user) => {\r\n const [{ total }] = await db.select({ total: count() }).from(users)\r\n const isFirstUser = total === 0\r\n\r\n const base = {\r\n betterAuthId: user.id,\r\n email: user.email,\r\n name: user.name ?? user.email.split('@')[0],\r\n role: isFirstUser ? 'super_admin' as const : 'student' as const,\r\n }\r\n\r\n if (isFirstUser) {\r\n // Atomic: insert as super_admin, or update existing row (created\r\n // by the resolveUser race) to super_admin.\r\n await db\r\n .insert(users)\r\n .values(base)\r\n .onConflictDoUpdate({\r\n target: users.email,\r\n set: {\r\n betterAuthId: user.id,\r\n role: 'super_admin',\r\n name: user.name ?? user.email.split('@')[0],\r\n },\r\n })\r\n } else {\r\n await db\r\n .insert(users)\r\n .values(base)\r\n .onConflictDoUpdate({\r\n target: users.email,\r\n set: {\r\n betterAuthId: user.id,\r\n name: user.name ?? user.email.split('@')[0],\r\n },\r\n })\r\n }\r\n }\r\n }\r\n }\r\n }\r\n })\r\n}\r\n\r\nexport type Auth = ReturnType<typeof createAuth>\r\n","import { createMiddleware } from 'hono/factory'\r\nimport { eq } from 'drizzle-orm'\r\nimport { createAuth } from './auth'\r\nimport { domainRestricted, unauthorized, forbidden } from '../lib/errors'\r\nimport { createDb } from '../db'\r\nimport { users } from '../db/schema/users'\r\nimport type { LeapifyBindings } from '../types'\r\nimport type { LeapifyUser } from './types'\r\n\r\nconst SESSION_KV_PREFIX = 'auth:session:'\r\nconst SESSION_KV_TTL = 3600 // 1 hour max cache (capped by actual session expiry)\r\n\r\n// Context type augmentation — available in every route handler via c.get('user')\r\ndeclare module 'hono' {\r\n interface ContextVariableMap {\r\n user: LeapifyUser\r\n }\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Extract the raw bearer token from the Authorization header, or the\r\n * `better-auth.session_token` cookie, for use as a KV cache key.\r\n * Returns undefined when no credential is present.\r\n */\r\nfunction extractRawToken(c: { req: { header: (k: string) => string | undefined } }): string | undefined {\r\n const authHeader = c.req.header('Authorization')\r\n if (authHeader?.startsWith('Bearer ')) return authHeader.slice(7)\r\n // Cookie-based flow: Better Auth sets this cookie name by default\r\n const cookie = c.req.header('Cookie') ?? ''\r\n const match = cookie.match(/(?:^|;\\s*)better-auth\\.session_token=([^;]+)/)\r\n return match?.[1] ? decodeURIComponent(match[1]) : undefined\r\n}\r\n\r\nasync function resolveUser(\r\n env: LeapifyBindings,\r\n betterAuthUserId: string,\r\n betterAuthUserEmail: string,\r\n betterAuthUserName: string,\r\n betterAuthEmailVerified: boolean,\r\n): Promise<LeapifyUser> {\r\n const db = createDb(env.DB)\r\n\r\n let dbUser = await db.query.users.findFirst({\r\n where: eq(users.betterAuthId, betterAuthUserId),\r\n })\r\n\r\n if (!dbUser) {\r\n // First request after account creation — the databaseHooks.after callback\r\n // should have already inserted this row, but guard against races.\r\n const [created] = await db\r\n .insert(users)\r\n .values({\r\n betterAuthId: betterAuthUserId,\r\n email: betterAuthUserEmail,\r\n name: betterAuthUserName ?? betterAuthUserEmail.split('@')[0],\r\n })\r\n .onConflictDoUpdate({\r\n target: users.email,\r\n set: {\r\n betterAuthId: betterAuthUserId,\r\n name: betterAuthUserName ?? betterAuthUserEmail.split('@')[0],\r\n },\r\n })\r\n .returning()\r\n dbUser = created\r\n }\r\n\r\n if (!dbUser) throw unauthorized('Failed to resolve user record')\r\n\r\n return {\r\n uid: betterAuthUserId,\r\n dbId: dbUser.id,\r\n role: dbUser.role,\r\n email: betterAuthUserEmail,\r\n name: betterAuthUserName ?? betterAuthUserEmail.split('@')[0],\r\n emailVerified: betterAuthEmailVerified,\r\n }\r\n}\r\n\r\n// ─── Auth middleware (required) ───────────────────────────────────────────────\r\n\r\nexport const authMiddleware = createMiddleware<{ Bindings: LeapifyBindings }>(\r\n async (c, next) => {\r\n const rawToken = extractRawToken(c)\r\n\r\n // Fast path: KV cache hit — skip DB round-trip entirely\r\n if (rawToken) {\r\n const cached = await c.env.KV.get<LeapifyUser>(\r\n `${SESSION_KV_PREFIX}${rawToken}`,\r\n 'json',\r\n )\r\n if (cached) {\r\n c.set('user', cached)\r\n return next()\r\n }\r\n }\r\n\r\n // Slow path: validate session via Better Auth\r\n const auth = createAuth(c.env)\r\n const session = await auth.api.getSession({ headers: c.req.raw.headers })\r\n\r\n if (!session?.user) {\r\n throw unauthorized('No valid session found')\r\n }\r\n\r\n // Domain guard — belt-and-suspenders (databaseHooks already rejects at\r\n // account creation time, but protects in case an existing account somehow\r\n // has a non-DLSU email)\r\n if (!session.user.email.endsWith('@dlsu.edu.ph')) {\r\n throw domainRestricted()\r\n }\r\n\r\n const leapifyUser = await resolveUser(\r\n c.env,\r\n session.user.id,\r\n session.user.email,\r\n session.user.name,\r\n session.user.emailVerified,\r\n )\r\n\r\n // Cache in KV, TTL = min(time until session expires, 1 hour)\r\n if (rawToken) {\r\n const sessionExpiresAt = new Date(session.session.expiresAt).getTime()\r\n const secondsRemaining = Math.floor((sessionExpiresAt - Date.now()) / 1000)\r\n const kvTtl = Math.max(1, Math.min(secondsRemaining, SESSION_KV_TTL))\r\n await c.env.KV.put(\r\n `${SESSION_KV_PREFIX}${rawToken}`,\r\n JSON.stringify(leapifyUser),\r\n { expirationTtl: kvTtl },\r\n )\r\n }\r\n\r\n c.set('user', leapifyUser)\r\n return next()\r\n },\r\n)\r\n\r\n// ─── Optional auth middleware ─────────────────────────────────────────────────\r\n\r\nexport const optionalAuthMiddleware = createMiddleware<{\r\n Bindings: LeapifyBindings\r\n}>(async (c, next) => {\r\n const rawToken = extractRawToken(c)\r\n // No credential present → treat as guest\r\n if (!rawToken) {\r\n c.set('user', null as unknown as LeapifyUser)\r\n return next()\r\n }\r\n // Credential present → enforce full verification\r\n return authMiddleware(c, next)\r\n})\r\n\r\n// ─── Admin guard (use after authMiddleware) ───────────────────────────────────\r\n\r\nexport const adminMiddleware = createMiddleware<{ Bindings: LeapifyBindings }>(\r\n async (c, next) => {\r\n const user = c.get('user')\r\n if (!user || !['admin', 'super_admin'].includes(user.role)) {\r\n throw forbidden('Admin access required')\r\n }\r\n return next()\r\n },\r\n)\r\n\r\n// ─── Internal route guard ─────────────────────────────────────────────────────\r\n\r\nexport const internalMiddleware = createMiddleware<{\r\n Bindings: LeapifyBindings\r\n}>(async (c, next) => {\r\n const secret = c.req.header('X-Internal-Secret')\r\n if (!secret || secret !== c.env.INTERNAL_API_SECRET) {\r\n throw forbidden('Invalid internal secret')\r\n }\r\n return next()\r\n})\r\n","import { Hono } from 'hono'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\n\r\nexport const healthRoute = new Hono<LeapifyEnv>()\r\n\r\n// ─── Individual service probes ──────────────────────────────────────────────\r\n\r\ninterface ServiceHealth {\r\n configured: boolean\r\n ok: boolean\r\n latencyMs: number\r\n error?: string\r\n}\r\n\r\nasync function probeResend(apiKey: string): Promise<ServiceHealth> {\r\n const start = Date.now()\r\n try {\r\n const res = await fetch('https://api.resend.com/domains', {\r\n headers: { Authorization: `Bearer ${apiKey}` },\r\n })\r\n return {\r\n configured: true,\r\n ok: res.ok,\r\n latencyMs: Date.now() - start,\r\n ...(res.ok ? {} : { error: `HTTP ${res.status}` }),\r\n }\r\n } catch (e) {\r\n return {\r\n configured: true,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: String(e),\r\n }\r\n }\r\n}\r\n\r\nasync function probeSes(\r\n region: string,\r\n accessKeyId: string,\r\n secretAccessKey: string,\r\n): Promise<ServiceHealth> {\r\n // SES v2 has no lightweight \"ping\" endpoint. Verify credentials are\r\n // configured and the region is valid by checking the env vars.\r\n const start = Date.now()\r\n try {\r\n if (!region || !accessKeyId || !secretAccessKey) {\r\n return {\r\n configured: false,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: 'Missing SES credentials',\r\n }\r\n }\r\n return {\r\n configured: true,\r\n ok: true,\r\n latencyMs: Date.now() - start,\r\n }\r\n } catch (e) {\r\n return {\r\n configured: true,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: String(e),\r\n }\r\n }\r\n}\r\n\r\nasync function probeGForms(\r\n serviceAccountJson: string,\r\n): Promise<ServiceHealth> {\r\n const start = Date.now()\r\n try {\r\n const creds = JSON.parse(serviceAccountJson) as {\r\n client_email: string\r\n private_key: string\r\n }\r\n\r\n // Try to get an OAuth2 token — verifies the SA key is valid\r\n const now = Math.floor(Date.now() / 1000)\r\n const claims = {\r\n iss: creds.client_email,\r\n scope: 'https://www.googleapis.com/auth/forms.responses.readonly',\r\n aud: 'https://oauth2.googleapis.com/token',\r\n iat: now,\r\n exp: now + 3600,\r\n }\r\n\r\n const header = { alg: 'RS256', typ: 'JWT' }\r\n const encode = (obj: unknown) =>\r\n btoa(JSON.stringify(obj))\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=/g, '')\r\n\r\n const signingInput = `${encode(header)}.${encode(claims)}`\r\n\r\n const pemBody = creds.private_key\r\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\r\n .replace(/-----END PRIVATE KEY-----/, '')\r\n .replace(/\\s/g, '')\r\n\r\n const keyBytes = Uint8Array.from(atob(pemBody), (c) => c.charCodeAt(0))\r\n const privateKey = await crypto.subtle.importKey(\r\n 'pkcs8',\r\n keyBytes,\r\n { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },\r\n false,\r\n ['sign'],\r\n )\r\n\r\n const signature = await crypto.subtle.sign(\r\n 'RSASSA-PKCS1-v1_5',\r\n privateKey,\r\n new TextEncoder().encode(signingInput),\r\n )\r\n\r\n const sigB64 = btoa(String.fromCharCode(...new Uint8Array(signature)))\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=/g, '')\r\n\r\n const jwt = `${signingInput}.${sigB64}`\r\n\r\n const res = await fetch('https://oauth2.googleapis.com/token', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\r\n assertion: jwt,\r\n }),\r\n })\r\n\r\n return {\r\n configured: true,\r\n ok: res.ok,\r\n latencyMs: Date.now() - start,\r\n ...(res.ok ? {} : { error: `HTTP ${res.status}` }),\r\n }\r\n } catch (e) {\r\n return {\r\n configured: true,\r\n ok: false,\r\n latencyMs: Date.now() - start,\r\n error: String(e),\r\n }\r\n }\r\n}\r\n\r\n// ─── Route ──────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * GET /health\r\n *\r\n * Publicly accessible — no CORS, no auth, no PoW.\r\n * Runs lightweight probes against each configured external service.\r\n *\r\n * Response:\r\n * {\r\n * status: 'ok' | 'degraded',\r\n * timestamp: string,\r\n * services: {\r\n * ses: { configured, ok, latencyMs, error? },\r\n * resend: { configured, ok, latencyMs, error? },\r\n * gforms: { configured, ok, latencyMs, error? },\r\n * }\r\n * }\r\n */\r\nhealthRoute.get('/', async (c) => {\r\n const env = c.env\r\n\r\n const hasSes = Boolean(env.SES_REGION) && Boolean(env.SES_ACCESS_KEY_ID) && Boolean(env.SES_SECRET_ACCESS_KEY)\r\n const hasResend = Boolean(env.RESEND_API_KEY)\r\n let hasGForms = false\r\n if (env.GFORMS_SERVICE_ACCOUNT_JSON) {\r\n try {\r\n const parsed = JSON.parse(env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n hasGForms = Boolean(parsed.client_email && parsed.private_key)\r\n } catch {\r\n /* invalid JSON — not configured */\r\n }\r\n }\r\n\r\n const probes: Promise<[string, ServiceHealth]>[] = []\r\n\r\n if (hasSes) {\r\n probes.push(\r\n probeSes(env.SES_REGION!, env.SES_ACCESS_KEY_ID!, env.SES_SECRET_ACCESS_KEY!).then(\r\n (h) => ['ses', h] as const,\r\n ),\r\n )\r\n }\r\n if (hasResend) {\r\n probes.push(\r\n probeResend(env.RESEND_API_KEY!).then((h) => ['resend', h] as const),\r\n )\r\n }\r\n if (hasGForms) {\r\n probes.push(\r\n probeGForms(env.GFORMS_SERVICE_ACCOUNT_JSON!).then(\r\n (h) => ['gforms', h] as const,\r\n ),\r\n )\r\n }\r\n\r\n const results = await Promise.all(probes)\r\n\r\n const services: Record<string, ServiceHealth> = {}\r\n for (const [name, health] of results) {\r\n services[name] = health\r\n }\r\n\r\n // If no services are configured, still report ok\r\n const allOk = results.length === 0 || results.every(([, h]) => h.ok)\r\n\r\n return c.json({\r\n data: {\r\n status: allOk ? 'OK' : 'DEGRADED',\r\n timestamp: new Date().toISOString(),\r\n services,\r\n },\r\n })\r\n})\r\n\r\n// ─── Queue burst (internal) ─────────────────────────────────────────────────\r\n\r\n/**\r\n * POST /health/queue-burst\r\n * Internal load testing endpoint that blasts 100 mock items into the queue.\r\n */\r\nhealthRoute.post('/queue-burst', authMiddleware, adminMiddleware, async (c) => {\r\n if (!c.env.EMAIL_QUEUE) {\r\n return c.json({ error: 'Queue binding missing' }, 400)\r\n }\r\n\r\n const batch = Array.from({ length: 100 }, (_, i) => ({\r\n body: {\r\n type: 'audit_log',\r\n payload: {\r\n action: 'queue_load_test',\r\n userId: 'system',\r\n meta: { index: i, time: Date.now() },\r\n },\r\n },\r\n }))\r\n\r\n await (c.env.EMAIL_QUEUE as any).sendBatch(batch)\r\n\r\n return c.json({ status: 'queued', count: 100 })\r\n})\r\n","import type { KVNamespace } from '@cloudflare/workers-types'\r\n\r\n/**\r\n * Typed KV cache wrapper with get/set/del and stale-while-revalidate helpers.\r\n */\r\nexport class CacheService {\r\n constructor(private readonly kv: KVNamespace) {}\r\n\r\n async get<T>(key: string): Promise<T | null> {\r\n return this.kv.get<T>(key, 'json')\r\n }\r\n\r\n async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {\r\n await this.kv.put(key, JSON.stringify(value), {\r\n ...(ttlSeconds ? { expirationTtl: ttlSeconds } : {}),\r\n })\r\n }\r\n\r\n async del(key: string): Promise<void> {\r\n await this.kv.delete(key)\r\n }\r\n\r\n /**\r\n * Returns cached value immediately. If stale or missing, refreshes in\r\n * the background using the provided fetcher. Pass `ctx` to use\r\n * ctx.waitUntil() so the refresh doesn't block the response.\r\n */\r\n async getOrSet<T>(\r\n key: string,\r\n fetcher: () => Promise<T>,\r\n ttlSeconds: number,\r\n ctx?: ExecutionContext,\r\n ): Promise<T> {\r\n const cached = await this.get<T>(key)\r\n if (cached !== null) return cached\r\n\r\n const fresh = await fetcher()\r\n const writePromise = this.set(key, fresh, ttlSeconds)\r\n\r\n if (ctx) {\r\n ctx.waitUntil(writePromise)\r\n } else {\r\n await writePromise\r\n }\r\n\r\n return fresh\r\n }\r\n\r\n /**\r\n * Generate a simple ETag from a version string by hashing with SHA-256.\r\n */\r\n async generateETag(versionString: string): Promise<string> {\r\n const encoder = new TextEncoder()\r\n const data = encoder.encode(versionString)\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', data)\r\n const hashArray = Array.from(new Uint8Array(hashBuffer))\r\n return `\"${hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')}\"`\r\n }\r\n}\r\n","import { eq, sql } from 'drizzle-orm'\r\nimport type { LeapifyDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport type { CacheService } from './cache'\r\n\r\nconst SLOT_KV_PREFIX = 'slots:'\r\n\r\nexport interface SlotInfo {\r\n available: number\r\n total: number\r\n registered: number\r\n isFull: boolean\r\n}\r\n\r\n/**\r\n * Manages real-time slot counts using a local D1 counter + KV cache.\r\n * Google Forms Watch webhook increments the counter; reads go through KV.\r\n *\r\n * CF Cache (Cache-Control: public, max-age=5) sits in front of the /slots\r\n * endpoint, so KV is only read once per 5-second window per edge location.\r\n */\r\nexport class SlotsService {\r\n constructor(\r\n private readonly db: LeapifyDb,\r\n private readonly cache: CacheService,\r\n ) {}\r\n\r\n kvKey(slug: string) {\r\n return `${SLOT_KV_PREFIX}${slug}`\r\n }\r\n\r\n /**\r\n * Read current slot info — KV first, D1 on miss.\r\n */\r\n async getSlots(slug: string): Promise<SlotInfo | null> {\r\n // Try KV first\r\n const cached = await this.cache.get<SlotInfo>(this.kvKey(slug))\r\n if (cached) return cached\r\n\r\n // Fall back to D1\r\n return this.refreshFromDb(slug)\r\n }\r\n\r\n /**\r\n * Atomically increment registered_slots in D1 and update KV.\r\n * Called by the Google Forms Watch webhook handler.\r\n */\r\n async increment(slug: string): Promise<SlotInfo | null> {\r\n await this.db\r\n .update(events)\r\n .set({ registeredSlots: sql`${events.registeredSlots} + 1` })\r\n .where(eq(events.slug, slug))\r\n\r\n return this.refreshFromDb(slug)\r\n }\r\n\r\n /**\r\n * Atomically decrement registered_slots in D1 and update KV.\r\n * Used during reconciliation drift correction (not from user actions).\r\n */\r\n async decrement(slug: string): Promise<SlotInfo | null> {\r\n await this.db\r\n .update(events)\r\n .set({\r\n registeredSlots: sql`MAX(0, ${events.registeredSlots} - 1)`,\r\n })\r\n .where(eq(events.slug, slug))\r\n\r\n return this.refreshFromDb(slug)\r\n }\r\n\r\n /**\r\n * Set registered_slots to a specific value (used by reconciliation cron).\r\n */\r\n async correctCount(slug: string, actualCount: number): Promise<void> {\r\n await this.db\r\n .update(events)\r\n .set({ registeredSlots: actualCount })\r\n .where(eq(events.slug, slug))\r\n\r\n await this.invalidate(slug)\r\n }\r\n\r\n /**\r\n * Read from D1, write to KV, and return slot info.\r\n */\r\n async refreshFromDb(slug: string): Promise<SlotInfo | null> {\r\n const event = await this.db.query.events.findFirst({\r\n where: eq(events.slug, slug),\r\n columns: { maxSlots: true, registeredSlots: true },\r\n })\r\n\r\n if (!event) return null\r\n\r\n const info: SlotInfo = {\r\n total: event.maxSlots,\r\n registered: event.registeredSlots,\r\n available: Math.max(0, event.maxSlots - event.registeredSlots),\r\n isFull: event.registeredSlots >= event.maxSlots,\r\n }\r\n\r\n // Cache with no TTL — explicitly invalidated on every write\r\n await this.cache.set(this.kvKey(slug), info)\r\n\r\n return info\r\n }\r\n\r\n /**\r\n * Invalidate the KV cache for a specific event.\r\n */\r\n async invalidate(slug: string): Promise<void> {\r\n await this.cache.del(this.kvKey(slug))\r\n }\r\n}\r\n","/**\r\n * Google Forms API client using service account OAuth2.\r\n * Fully fetch-native — no googleapis SDK (not edge compatible).\r\n */\r\n\r\ninterface ServiceAccountCredentials {\r\n type: string;\r\n project_id: string;\r\n private_key_id: string;\r\n private_key: string;\r\n client_email: string;\r\n client_id: string;\r\n}\r\n\r\ninterface TokenResponse {\r\n access_token: string;\r\n expires_in: number;\r\n token_type: string;\r\n}\r\n\r\ninterface FormResponse {\r\n responseId: string;\r\n createTime: string;\r\n lastSubmittedTime: string;\r\n respondentEmail?: string;\r\n answers?: Record<string, unknown>;\r\n}\r\n\r\ninterface ListResponsesResult {\r\n responses: FormResponse[];\r\n nextPageToken?: string;\r\n}\r\n\r\nexport class GFormsService {\r\n private accessToken: string | null = null;\r\n private tokenExpiresAt = 0;\r\n private readonly credentials: ServiceAccountCredentials;\r\n\r\n constructor(serviceAccountJson: string) {\r\n this.credentials = JSON.parse(\r\n serviceAccountJson,\r\n ) as ServiceAccountCredentials;\r\n }\r\n\r\n // OAuth2 token management\r\n\r\n private async getAccessToken(): Promise<string> {\r\n if (this.accessToken && Date.now() < this.tokenExpiresAt) {\r\n return this.accessToken;\r\n }\r\n\r\n const token = await this.fetchServiceAccountToken();\r\n this.accessToken = token.access_token;\r\n this.tokenExpiresAt = Date.now() + (token.expires_in - 60) * 1000; // 1min buffer\r\n return this.accessToken;\r\n }\r\n\r\n private async fetchServiceAccountToken(): Promise<TokenResponse> {\r\n const now = Math.floor(Date.now() / 1000);\r\n const claims = {\r\n iss: this.credentials.client_email,\r\n scope: \"https://www.googleapis.com/auth/forms.responses.readonly\",\r\n aud: \"https://oauth2.googleapis.com/token\",\r\n iat: now,\r\n exp: now + 3600,\r\n };\r\n\r\n const jwt = await this.createJwt(claims);\r\n\r\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\r\n body: new URLSearchParams({\r\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\r\n assertion: jwt,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Failed to get service account token: ${err}`);\r\n }\r\n\r\n return response.json() as Promise<TokenResponse>;\r\n }\r\n\r\n private async createJwt(claims: Record<string, unknown>): Promise<string> {\r\n const header = { alg: \"RS256\", typ: \"JWT\" };\r\n\r\n const encode = (obj: unknown) =>\r\n btoa(JSON.stringify(obj))\r\n .replace(/\\+/g, \"-\")\r\n .replace(/\\//g, \"_\")\r\n .replace(/=/g, \"\");\r\n\r\n const headerB64 = encode(header);\r\n const payloadB64 = encode(claims);\r\n const signingInput = `${headerB64}.${payloadB64}`;\r\n\r\n // Import private key from PEM\r\n const pemBody = this.credentials.private_key\r\n .replace(/-----BEGIN PRIVATE KEY-----/, \"\")\r\n .replace(/-----END PRIVATE KEY-----/, \"\")\r\n .replace(/\\s/g, \"\");\r\n\r\n const keyBytes = Uint8Array.from(atob(pemBody), (c) => c.charCodeAt(0));\r\n\r\n const privateKey = await crypto.subtle.importKey(\r\n \"pkcs8\",\r\n keyBytes,\r\n { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" },\r\n false,\r\n [\"sign\"],\r\n );\r\n\r\n const signature = await crypto.subtle.sign(\r\n \"RSASSA-PKCS1-v1_5\",\r\n privateKey,\r\n new TextEncoder().encode(signingInput),\r\n );\r\n\r\n const sigB64 = btoa(String.fromCharCode(...new Uint8Array(signature)))\r\n .replace(/\\+/g, \"-\")\r\n .replace(/\\//g, \"_\")\r\n .replace(/=/g, \"\");\r\n\r\n return `${signingInput}.${sigB64}`;\r\n }\r\n\r\n // Forms API\r\n\r\n /**\r\n * Get the total response count for a form.\r\n * Used by reconciliation cron — not called at runtime per-user.\r\n */\r\n async getResponseCount(formId: string): Promise<number> {\r\n return this.getExactResponseCount(formId);\r\n }\r\n\r\n /**\r\n * List all form responses including respondent emails.\r\n * Used by reminder email cron — 1 call per event per reminder cycle.\r\n */\r\n async getAllResponses(formId: string): Promise<FormResponse[]> {\r\n const token = await this.getAccessToken();\r\n const allResponses: FormResponse[] = [];\r\n let pageToken: string | undefined;\r\n\r\n do {\r\n const url = new URL(\r\n `https://forms.googleapis.com/v1/forms/${formId}/responses`,\r\n );\r\n url.searchParams.set(\"pageSize\", \"500\");\r\n if (pageToken) url.searchParams.set(\"pageToken\", pageToken);\r\n\r\n const response = await fetch(url.toString(), {\r\n headers: { Authorization: `Bearer ${token}` },\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Forms API error: ${response.status} ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as ListResponsesResult;\r\n allResponses.push(...(data.responses ?? []));\r\n pageToken = data.nextPageToken;\r\n } while (pageToken);\r\n\r\n return allResponses;\r\n }\r\n\r\n /**\r\n * Get respondent email addresses for a form.\r\n * Google Forms automatically collects emails when \"Collect email addresses\" is enabled.\r\n */\r\n async getRespondentEmails(formId: string): Promise<string[]> {\r\n const responses = await this.getAllResponses(formId);\r\n return responses\r\n .map((r) => r.respondentEmail)\r\n .filter((email): email is string => Boolean(email));\r\n }\r\n\r\n /**\r\n * Get the total response count more accurately by fetching all responses.\r\n * Used for precise reconciliation.\r\n */\r\n async getExactResponseCount(formId: string): Promise<number> {\r\n const responses = await this.getAllResponses(formId);\r\n return responses.length;\r\n }\r\n\r\n // Watch management\r\n\r\n /**\r\n * Create a Watch on a Google Form.\r\n * Fires a push notification to webhookUrl on every new response.\r\n *\r\n * @param formId - Google Form ID\r\n * @param webhookUrl - Full HTTPS URL Google will POST to on new submissions\r\n * (e.g. https://leap.yourdomain.com/internal/gforms-webhook)\r\n */\r\n async createWatch(\r\n formId: string,\r\n webhookUrl: string,\r\n ): Promise<{ watchId: string; expireTime: string }> {\r\n const token = await this.getAccessToken();\r\n\r\n const response = await fetch(\r\n `https://forms.googleapis.com/v1/forms/${formId}/watches`,\r\n {\r\n method: \"POST\",\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n \"Content-Type\": \"application/json\",\r\n },\r\n body: JSON.stringify({\r\n watch: {\r\n target: {\r\n // HTTP push: Google POSTs the notification directly to our worker.\r\n // The URL must be HTTPS and publicly reachable.\r\n httpTarget: { uri: webhookUrl },\r\n },\r\n eventType: \"RESPONSES\",\r\n },\r\n }),\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Failed to create Watch: ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as { id: string; expireTime: string };\r\n return { watchId: data.id, expireTime: data.expireTime };\r\n }\r\n\r\n /**\r\n * Renew an existing Watch (reset its 7-day TTL).\r\n */\r\n async renewWatch(\r\n formId: string,\r\n watchId: string,\r\n ): Promise<{ expireTime: string }> {\r\n const token = await this.getAccessToken();\r\n\r\n const response = await fetch(\r\n `https://forms.googleapis.com/v1/forms/${formId}/watches/${watchId}:renew`,\r\n {\r\n method: \"POST\",\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n \"Content-Type\": \"application/json\",\r\n },\r\n body: JSON.stringify({}),\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Failed to renew Watch ${watchId}: ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as { expireTime: string };\r\n return { expireTime: data.expireTime };\r\n }\r\n}\r\n","import { createMiddleware } from 'hono/factory'\r\nimport type { LeapifyBindings } from '../../types'\r\nimport { tooManyRequests } from '../errors'\r\n\r\nexport interface RateLimitConfig {\r\n /** KV key namespace segment — e.g. 'events' or 'bookmarks' */\r\n endpoint: string\r\n /** Max requests allowed within the window */\r\n limit: number\r\n /** Window size in seconds */\r\n windowSec: number\r\n /**\r\n * How to identify the requester.\r\n * - 'ip' → CF-Connecting-IP (guests, public endpoints)\r\n * - 'uid' → authenticated user ID from c.get('user') (must run after authMiddleware)\r\n */\r\n identifier: 'ip' | 'uid'\r\n}\r\n\r\n/**\r\n * KV token-bucket rate limiter (ADR-006, Layer 3 & 5).\r\n *\r\n * Key schema: `rl:<endpoint>:<identifier>`\r\n *\r\n * Uses a simple counter with KV TTL as the window reset mechanism.\r\n * On first request in a window, the key is created with expirationTtl = windowSec.\r\n * Subsequent requests within the window increment the counter.\r\n * When the key expires, the window resets automatically.\r\n *\r\n * Trade-off: this is eventually consistent under concurrent requests at window\r\n * boundaries — acceptable given the ~5% over-limit tolerance for edge caches.\r\n */\r\nexport function createRateLimitMiddleware(config: RateLimitConfig) {\r\n const { endpoint, limit, windowSec, identifier } = config\r\n\r\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\r\n // Skip rate limiting for challenge verification (clients solving challenges should not be blocked)\r\n if (c.req.path === '/.well-known/leapify/turnstile/verify') return next()\r\n\r\n // Also skip old PoW path for backward compatibility\r\n if (c.req.path === '/.well-known/leapify/pow/verify') return next()\r\n\r\n const id =\r\n identifier === 'uid'\r\n ? (c.get('user')?.uid ?? c.req.header('CF-Connecting-IP') ?? 'unknown')\r\n : (c.req.header('CF-Connecting-IP') ?? 'unknown')\r\n\r\n const key = `rl:${endpoint}:${id}`\r\n\r\n const raw = await c.env.KV.get(key)\r\n const count = raw !== null ? parseInt(raw, 10) : 0\r\n\r\n if (count >= limit) {\r\n c.header('Retry-After', String(windowSec))\r\n c.header('X-RateLimit-Limit', String(limit))\r\n c.header('X-RateLimit-Remaining', '0')\r\n throw tooManyRequests(`Rate limit exceeded. Try again in ${windowSec}s.`)\r\n }\r\n\r\n // Increment. On first request (count === 0), set TTL to open the window.\r\n // On subsequent requests, preserve the existing TTL by not resetting it.\r\n if (count === 0) {\r\n await c.env.KV.put(key, '1', { expirationTtl: windowSec })\r\n } else {\r\n // KV doesn't support atomic increment — we read then write.\r\n // Slight over-counting is acceptable; it errs on the side of caution.\r\n await c.env.KV.put(key, String(count + 1), { expirationTtl: windowSec })\r\n }\r\n\r\n c.header('X-RateLimit-Limit', String(limit))\r\n c.header('X-RateLimit-Remaining', String(limit - count - 1))\r\n\r\n return next()\r\n })\r\n}\r\n\r\n// Pre-configured middlewares per ADR-006 recommended limits\r\n\r\n/** GET /events — 60 req/60s per IP */\r\nexport const eventsListRateLimit = createRateLimitMiddleware({\r\n endpoint: 'events-list',\r\n limit: 60,\r\n windowSec: 60,\r\n identifier: 'ip',\r\n})\r\n\r\n/** GET /events/:slug/slots — 120 req/60s per IP */\r\nexport const eventsSlotsRateLimit = createRateLimitMiddleware({\r\n endpoint: 'events-slots',\r\n limit: 120,\r\n windowSec: 60,\r\n identifier: 'ip',\r\n})\r\n\r\n/** POST /users/me/bookmarks — 10 req/60s per UID (must run after authMiddleware) */\r\nexport const bookmarksRateLimit = createRateLimitMiddleware({\r\n endpoint: 'bookmarks',\r\n limit: 10,\r\n windowSec: 60,\r\n identifier: 'uid',\r\n})\r\n\r\n/** POST /events (admin) — 20 req/60s per UID (must run after authMiddleware) */\r\nexport const adminEventsRateLimit = createRateLimitMiddleware({\r\n endpoint: 'admin-events',\r\n limit: 20,\r\n windowSec: 60,\r\n identifier: 'uid',\r\n})\r\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq, and, sql } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { CacheService } from '../services/cache'\r\nimport { SlotsService } from '../services/slots'\r\nimport { GFormsService } from '../services/gforms'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound } from '../lib/errors'\r\nimport {\r\n eventsListRateLimit,\r\n eventsSlotsRateLimit,\r\n adminEventsRateLimit,\r\n} from '../lib/middleware/rate-limit'\r\n\r\nconst EVENTS_LIST_KV_KEY = 'events:list'\r\nconst EVENTS_ETAG_KV_KEY = 'events:etag'\r\nconst EVENTS_LIST_TTL = 300 // 5 min KV cache for list\r\n\r\nconst createEventSchema = z.object({\r\n themeId: z.string().min(1),\r\n organizationId: z.string().optional(),\r\n title: z.string().min(1),\r\n description: z.string().optional(),\r\n venue: z.string().optional(),\r\n dateTime: z.string().optional(),\r\n price: z.string().optional(),\r\n backgroundImageUrl: z.string().url().optional(),\r\n classCode: z.string().optional(),\r\n startTime: z.string().optional(),\r\n endTime: z.string().optional(),\r\n registrationClosesAt: z.number().optional(),\r\n isSpotlight: z.boolean().default(false),\r\n maxSlots: z.number().int().min(0).default(0),\r\n gformsId: z.string().optional(),\r\n gformsUrl: z.string().url().optional(),\r\n gformsEditorUrl: z.string().url().optional(),\r\n releaseAt: z.number().optional(),\r\n status: z.enum(['draft', 'queued', 'published']).default('draft'),\r\n})\r\n\r\nexport const classesRoute = new Hono<LeapifyEnv>()\r\n\r\nfunction generateSlug(title: string): string {\r\n return title\r\n .toLowerCase()\r\n .trim()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/[\\s_-]+/g, '-')\r\n .replace(/^-+|-+$/g, '')\r\n}\r\n\r\n// GET /events/admin — admin only, returns all events regardless of status\r\nclassesRoute.get('/admin', authMiddleware, adminMiddleware, async (c) => {\r\n const db = createDb(c.env.DB)\r\n const data = await db.query.events.findMany({\r\n with: { theme: true, organization: true },\r\n orderBy: (e, { desc }) => [desc(e.createdAt)],\r\n })\r\n return c.json({ data })\r\n})\r\n\r\n// POST /events/admin/publish — admin only, batch publish queued events\r\nclassesRoute.post('/admin/publish', authMiddleware, adminMiddleware, async (c) => {\r\n const body = await c.req.json<{ ids: string[]; releaseAt?: number }>()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n if (!body.ids?.length) {\r\n return c.json({ error: 'ids are required' }, 400)\r\n }\r\n\r\n if (body.releaseAt) {\r\n // Schedule for later\r\n await db\r\n .update(events)\r\n .set({ releaseAt: body.releaseAt, status: 'queued' })\r\n .where(\r\n sql`${events.id} IN (${sql.join(\r\n body.ids.map((id) => sql`${id}`),\r\n sql`, `,\r\n )})`,\r\n )\r\n } else {\r\n // Publish now\r\n await db\r\n .update(events)\r\n .set({ status: 'published', publishedAt: sql`(unixepoch())` })\r\n .where(\r\n sql`${events.id} IN (${sql.join(\r\n body.ids.map((id) => sql`${id}`),\r\n sql`, `,\r\n )})`,\r\n )\r\n }\r\n\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.json({ data: { updated: body.ids.length } })\r\n})\r\n\r\n// GET /events — public, ETag + 7-day browser cache\r\nclassesRoute.get('/', eventsListRateLimit, async (c) => {\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n // Generate ETag from latest publishedAt timestamp\r\n const [latest] = await db\r\n .select({ max: events.publishedAt })\r\n .from(events)\r\n .where(eq(events.status, 'published'))\r\n .limit(1)\r\n\r\n const etag = await cache.getOrSet(\r\n EVENTS_ETAG_KV_KEY,\r\n () => cache.generateETag(String(latest?.max ?? '0')),\r\n 300,\r\n )\r\n\r\n // Handle conditional GET\r\n const ifNoneMatch = c.req.header('If-None-Match')\r\n if (ifNoneMatch === etag) {\r\n return c.body(null, 304)\r\n }\r\n\r\n const data = await cache.getOrSet(\r\n EVENTS_LIST_KV_KEY,\r\n () =>\r\n db.query.events.findMany({\r\n where: eq(events.status, 'published'),\r\n with: {\r\n theme: true,\r\n organization: true,\r\n },\r\n columns: {\r\n id: true,\r\n slug: true,\r\n themeId: true,\r\n organizationId: true,\r\n title: true,\r\n venue: true,\r\n dateTime: true,\r\n price: true,\r\n backgroundImageUrl: true,\r\n classCode: true,\r\n startTime: true,\r\n endTime: true,\r\n registrationClosesAt: true,\r\n isSpotlight: true,\r\n maxSlots: true,\r\n registeredSlots: true,\r\n gformsUrl: true,\r\n gformsEditorUrl: true,\r\n publishedAt: true,\r\n },\r\n }),\r\n EVENTS_LIST_TTL,\r\n )\r\n\r\n c.header('ETag', etag)\r\n c.header(\r\n 'Cache-Control',\r\n 'public, max-age=604800, stale-while-revalidate=86400',\r\n ) // 7 days\r\n return c.json({ data })\r\n})\r\n\r\n// GET /events/:slug\r\nclassesRoute.get('/:slug', async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n\r\n const event = await db.query.events.findFirst({\r\n where: and(eq(events.slug, slug), eq(events.status, 'published')),\r\n with: {\r\n theme: true,\r\n },\r\n })\r\n\r\n if (!event) throw notFound('Event')\r\n\r\n return c.json({ data: event })\r\n})\r\n\r\n// GET /events/:slug/slots — real-time, CF Cache 5s\r\nclassesRoute.get('/:slug/slots', eventsSlotsRateLimit, async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n const slotsService = new SlotsService(db, cache)\r\n\r\n const info = await slotsService.getSlots(slug)\r\n if (!info) throw notFound('Event')\r\n\r\n // CF edge cache: all 30k users share this cached response for 5s\r\n c.header('Cache-Control', 'public, max-age=5, stale-while-revalidate=5')\r\n\r\n return c.json({ data: info })\r\n})\r\n\r\n// POST /events/:slug/reconcile — admin only, corrects slot count for one event\r\nclassesRoute.post('/:slug/reconcile', authMiddleware, adminMiddleware, async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n const gforms = new GFormsService(c.env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n const slots = new SlotsService(db, cache)\r\n\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.slug, slug),\r\n columns: { gformsId: true },\r\n })\r\n if (!event) throw notFound('Event')\r\n if (!event.gformsId) return c.json({ error: 'No gformsId set for this event' }, 400)\r\n\r\n try {\r\n const googleCount = await gforms.getExactResponseCount(event.gformsId)\r\n await slots.correctCount(slug, googleCount)\r\n return c.json({ data: { registeredSlots: googleCount } })\r\n } catch (err: any) {\r\n const message = err?.message ?? 'Failed to fetch from Google Forms API'\r\n return c.json({ error: { code: 'GFORMS_API_ERROR', message } }, 502)\r\n }\r\n})\r\n\r\n// POST /events — admin only\r\nclassesRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n adminEventsRateLimit,\r\n zValidator('json', createEventSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const slug = generateSlug(body.title)\r\n\r\n const [created] = await db.insert(events).values({ ...body, slug }).returning()\r\n\r\n // If publishing immediately, create a Google Forms Watch\r\n if (\r\n body.status === 'published' &&\r\n body.gformsId &&\r\n c.env.GFORMS_SERVICE_ACCOUNT_JSON\r\n ) {\r\n const webhookUrl = c.get('gformsWebhookUrl')\r\n if (webhookUrl) {\r\n const gforms = new GFormsService(c.env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n try {\r\n const watch = await gforms.createWatch(body.gformsId, webhookUrl)\r\n const expiry = Math.floor(\r\n new Date(watch.expireTime ?? '').getTime() / 1000,\r\n )\r\n await db\r\n .update(events)\r\n .set({ watchId: watch.watchId, watchExpiresAt: expiry })\r\n .where(eq(events.id, created!.id))\r\n } catch (err) {\r\n console.error('[events] Failed to create Watch:', err)\r\n }\r\n } else {\r\n console.warn(\r\n '[events] gformsWebhookUrl not configured \\u2014 Watch not created. Pass gformsWebhookUrl to createLeapify().',\r\n )\r\n }\r\n }\r\n\r\n // Invalidate list cache\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.json({ data: created }, 201)\r\n },\r\n)\r\n\r\n// PATCH /events/:slug — admin only\r\nclassesRoute.patch('/:slug', authMiddleware, adminMiddleware, async (c) => {\r\n const { slug } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof createEventSchema>>>()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n let newSlug: string | undefined\r\n if (body.title) {\r\n newSlug = generateSlug(body.title)\r\n }\r\n\r\n const [updated] = await db\r\n .update(events)\r\n .set(newSlug ? { ...body, slug: newSlug } : body)\r\n .where(eq(events.slug, slug))\r\n .returning()\r\n\r\n if (!updated) throw notFound('Event')\r\n\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.json({ data: updated })\r\n})\r\n\r\n// DELETE /events/:slug — admin only\r\nclassesRoute.delete('/:slug', authMiddleware, adminMiddleware, async (c) => {\r\n const { slug } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const [deleted] = await db.delete(events).where(eq(events.slug, slug)).returning()\r\n\r\n if (!deleted) throw notFound('Event')\r\n\r\n await Promise.all([\r\n cache.del(EVENTS_LIST_KV_KEY),\r\n cache.del(EVENTS_ETAG_KV_KEY),\r\n ])\r\n\r\n return c.body(null, 204)\r\n})\r\n","import { Hono } from \"hono\";\r\nimport { eq, and } from \"drizzle-orm\";\r\nimport type { LeapifyEnv } from \"../types\";\r\nimport { createDb } from \"../db\";\r\nimport { users, type UserRole } from \"../db/schema/users\";\r\nimport { authUser } from \"../db/schema/auth\";\r\nimport { bookmarks } from \"../db/schema/bookmarks\";\r\nimport { events } from \"../db/schema/classes\";\r\nimport { authMiddleware, adminMiddleware, optionalAuthMiddleware } from \"../auth/middleware\";\r\nimport { notFound, badRequest } from \"../lib/errors\";\r\nimport { bookmarksRateLimit } from \"../lib/middleware/rate-limit\";\r\n\r\nconst VALID_ROLES: UserRole[] = [\"student\", \"admin\", \"super_admin\"];\r\n\r\nexport const usersRoute = new Hono<LeapifyEnv>();\r\n\r\n// ─── Admin: User Management ─────────────────────────────────────────────────\r\n\r\n// GET /users — admin only, list all users\r\nusersRoute.get(\"/\", authMiddleware, adminMiddleware, async (c) => {\r\n const db = createDb(c.env.DB);\r\n const data = await db.select().from(users);\r\n return c.json({ data });\r\n});\r\n\r\n// PATCH /users/:id/role — admin only, change user role\r\nusersRoute.patch(\"/:id/role\", authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param();\r\n const { role } = await c.req.json<{ role: string }>();\r\n\r\n if (!role || !VALID_ROLES.includes(role as UserRole)) {\r\n throw badRequest(\"Role must be 'student', 'admin', or 'super_admin'.\");\r\n }\r\n\r\n const db = createDb(c.env.DB);\r\n const [updated] = await db\r\n .update(users)\r\n .set({ role: role as UserRole })\r\n .where(eq(users.id, id))\r\n .returning();\r\n\r\n if (!updated) throw notFound(\"User\");\r\n\r\n return c.json({ data: updated });\r\n});\r\n\r\n// POST /users/by-email — admin only, find or create user by email and set role\r\nusersRoute.post(\"/by-email\", authMiddleware, adminMiddleware, async (c) => {\r\n const { email, role } = await c.req.json<{ email: string; role: string }>();\r\n\r\n if (!email || !role || !VALID_ROLES.includes(role as UserRole)) {\r\n throw badRequest(\"Email and valid role ('student', 'admin', 'super_admin') are required.\");\r\n }\r\n\r\n const db = createDb(c.env.DB);\r\n\r\n // Find existing user by email\r\n const existing = await db.query.users.findFirst({\r\n where: eq(users.email, email),\r\n });\r\n\r\n if (existing) {\r\n // Update role\r\n const [updated] = await db\r\n .update(users)\r\n .set({ role: role as UserRole })\r\n .where(eq(users.email, email))\r\n .returning();\r\n return c.json({ data: updated });\r\n }\r\n\r\n // Create a placeholder user (they'll get a real betterAuthId on first login)\r\n const [created] = await db\r\n .insert(users)\r\n .values({ betterAuthId: `pending:${email}`, email, name: email.split(\"@\")[0], role: role as UserRole })\r\n .returning();\r\n\r\n return c.json({ data: created }, 201);\r\n});\r\n\r\n// ─── Public / Auth: User Profile ─────────────────────────────────────────────\r\n\r\n// GET /users/me\r\nusersRoute.get(\"/me\", optionalAuthMiddleware, async (c) => {\r\n const user = c.get(\"user\");\r\n if (!user) return c.json({ data: null });\r\n\r\n const db = createDb(c.env.DB);\r\n const profile = await db.query.users.findFirst({\r\n where: eq(users.id, user.dbId),\r\n });\r\n\r\n if (!profile) return c.json({ data: null });\r\n\r\n // Join with authUser to get the Google profile image\r\n const auth = await db.query.authUser.findFirst({\r\n where: eq(authUser.id, profile.betterAuthId),\r\n columns: { image: true },\r\n });\r\n\r\n return c.json({ data: { ...profile, image: auth?.image ?? null } });\r\n});\r\n\r\n// GET /users/me/bookmarks\r\nusersRoute.get(\"/me/bookmarks\", optionalAuthMiddleware, async (c) => {\r\n const user = c.get(\"user\");\r\n if (!user) return c.json({ data: [] });\r\n\r\n const db = createDb(c.env.DB);\r\n const rows = await db.query.bookmarks.findMany({\r\n where: eq(bookmarks.userId, user.dbId),\r\n with: { event: true },\r\n });\r\n\r\n const data = rows.map((r) => ({ bookmarkedAt: r.createdAt, event: r.event }));\r\n return c.json({ data });\r\n});\r\n\r\n// POST /users/me/bookmarks/:eventId — toggle\r\nusersRoute.post(\"/me/bookmarks/:eventId\", authMiddleware, bookmarksRateLimit, async (c) => {\r\n const { eventId } = c.req.param();\r\n const user = c.get(\"user\");\r\n const db = createDb(c.env.DB);\r\n\r\n // Verify event exists\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.id, eventId),\r\n columns: { id: true },\r\n });\r\n if (!event) throw notFound(\"Event\");\r\n\r\n // Try an atomic insert that silently skips if the unique constraint is hit.\r\n const inserted = await db\r\n .insert(bookmarks)\r\n .values({ userId: user.dbId, eventId })\r\n .onConflictDoNothing({ target: [bookmarks.userId, bookmarks.eventId] })\r\n .returning();\r\n\r\n if (inserted.length > 0) {\r\n return c.json({ data: { bookmarked: true } }, 201);\r\n }\r\n\r\n await db\r\n .delete(bookmarks)\r\n .where(and(eq(bookmarks.userId, user.dbId), eq(bookmarks.eventId, eventId)));\r\n\r\n return c.json({ data: { bookmarked: false } }, 200);\r\n});\r\n\r\n// DELETE /users/me/bookmarks/:eventId\r\nusersRoute.delete(\"/me/bookmarks/:eventId\", authMiddleware, async (c) => {\r\n const { eventId } = c.req.param();\r\n const user = c.get(\"user\");\r\n const db = createDb(c.env.DB);\r\n\r\n await db\r\n .delete(bookmarks)\r\n .where(\r\n and(eq(bookmarks.userId, user.dbId), eq(bookmarks.eventId, eventId)),\r\n );\r\n\r\n return c.json({ data: { bookmarked: false } });\r\n});\r\n","import { Hono } from 'hono'\nimport type { LeapifyEnv, SiteConfigKey, SiteConfigMap } from '../types'\nimport { createDb } from '../db'\nimport { siteConfig } from '../db/schema/site-config'\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\nimport { forbidden } from '../lib/errors'\n\nexport const siteConfigRoute = new Hono<LeapifyEnv>()\n\n// GET /config — public\nsiteConfigRoute.get('/', async (c) => {\n const db = createDb(c.env.DB)\n\n const rows = await db.query.siteConfig.findMany()\n const config = Object.fromEntries(\n rows.map((r) => [r.key, JSON.parse(r.value)])\n ) as Partial<SiteConfigMap>\n\n return c.json({\n data: {\n comingSoonUntil: config.coming_soon_until ?? null,\n siteEndsAt: config.site_ends_at ?? null,\n siteName: config.site_name ?? null,\n registrationGloballyOpen: config.registration_globally_open ?? true,\n maintenanceMode: config.maintenance_mode ?? false,\n allowedOrigins: config.allowed_origins ?? null,\n now: Math.floor(Date.now() / 1000)\n }\n })\n})\n\n// PATCH /config/:key — admin only (allowed_origins is super_admin only)\nsiteConfigRoute.patch('/:key', authMiddleware, adminMiddleware, async (c) => {\n const key = c.req.param('key') as SiteConfigKey\n const { value } = await c.req.json<{ value: SiteConfigMap[typeof key] }>()\n\n if (key === 'allowed_origins') {\n const user = c.get('user')\n if (!user || user.role !== 'super_admin') {\n throw forbidden('Super Admin access required to change allowed origins')\n }\n }\n\n const db = createDb(c.env.DB)\n const now = Math.floor(Date.now() / 1000)\n\n await db\n .insert(siteConfig)\n .values({ key, value: JSON.stringify(value), updatedAt: now })\n .onConflictDoUpdate({\n target: siteConfig.key,\n set: { value: JSON.stringify(value), updatedAt: now }\n })\n\n // Write-through to KV so the maintenance-mode / CORS middlewares can read it\n // without a D1 round-trip on every request. TTL 1 day — KV is the fast cache;\n // D1 is the durable source of truth used as fallback when KV expires.\n await c.env.KV.put(`config:${key}`, JSON.stringify(value), {\n expirationTtl: 86400\n })\n\n return c.json({ data: { key, value } })\n})\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { faqs } from '../db/schema/faqs'\r\nimport { CacheService } from '../services/cache'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound } from '../lib/errors'\r\n\r\nconst FAQS_KV_KEY = 'faqs:active'\r\nconst FAQS_TTL = 600 // 10 min\r\n\r\nconst faqSchema = z.object({\r\n question: z.string().min(1),\r\n answer: z.string().min(1),\r\n category: z.string().optional(),\r\n sortOrder: z.number().int().default(0),\r\n})\r\n\r\nexport const faqsRoute = new Hono<LeapifyEnv>()\r\n\r\n// GET /faqs — public, KV cached 10min\r\nfaqsRoute.get('/', async (c) => {\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const data = await cache.getOrSet(\r\n FAQS_KV_KEY,\r\n () =>\r\n db.query.faqs.findMany({\r\n orderBy: (t, { asc }) => [asc(t.sortOrder), asc(t.createdAt)],\r\n }),\r\n FAQS_TTL,\r\n )\r\n\r\n return c.json({ data })\r\n})\r\n\r\n// POST /faqs — admin\r\nfaqsRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n zValidator('json', faqSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const [created] = await db.insert(faqs).values(body).returning()\r\n await cache.del(FAQS_KV_KEY)\r\n\r\n return c.json({ data: created }, 201)\r\n },\r\n)\r\n\r\n// PATCH /faqs/:id — admin\r\nfaqsRoute.patch('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof faqSchema>>>()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n const now = Math.floor(Date.now() / 1000)\r\n\r\n const [updated] = await db\r\n .update(faqs)\r\n .set({ ...body, updatedAt: now })\r\n .where(eq(faqs.id, id))\r\n .returning()\r\n\r\n if (!updated) throw notFound('FAQ')\r\n await cache.del(FAQS_KV_KEY)\r\n\r\n return c.json({ data: updated })\r\n})\r\n\r\n// DELETE /faqs/:id — admin, hard delete\r\nfaqsRoute.delete('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n const cache = new CacheService(c.env.KV)\r\n\r\n const [deleted] = await db.delete(faqs).where(eq(faqs.id, id)).returning()\r\n\r\n if (!deleted) throw notFound('FAQ')\r\n await cache.del(FAQS_KV_KEY)\r\n\r\n return c.json({ data: { deleted: true } })\r\n})\r\n","import { Hono } from \"hono\";\r\nimport { eq } from \"drizzle-orm\";\r\nimport type { LeapifyEnv } from \"../../types\";\r\nimport { createDb } from \"../../db\";\r\nimport { events } from \"../../db/schema/classes\";\r\nimport { SlotsService } from \"../../services/slots\";\r\nimport { CacheService } from \"../../services/cache\";\r\nimport { internalMiddleware } from \"../../auth/middleware\";\r\n\r\nexport const gformsWebhookRoute = new Hono<LeapifyEnv>();\r\n\r\n/**\r\n * POST /internal/gforms-webhook\r\n *\r\n * Receives Google Forms Watch push notifications.\r\n * Each notification means one student submitted the form.\r\n * We increment the local D1 counter and update KV — no Google API call made.\r\n *\r\n * Security:\r\n * 1. X-Internal-Secret header (internalMiddleware) — prevents external access\r\n * 2. X-Goog-Signature HMAC — verifies payload is genuinely from Google\r\n */\r\ngformsWebhookRoute.post(\"/\", internalMiddleware, async (c) => {\r\n const rawBody = await c.req.text();\r\n\r\n // Verify Google HMAC signature\r\n const signature = c.req.header(\"X-Goog-Signature\");\r\n if (signature) {\r\n const isValid = await verifyGoogSignature(\r\n rawBody,\r\n signature,\r\n c.env.GFORMS_WEBHOOK_SECRET,\r\n );\r\n if (!isValid) {\r\n return c.json({ error: \"Invalid signature\" }, 403);\r\n }\r\n }\r\n\r\n let payload: { formId?: string; watchId?: string };\r\n try {\r\n payload = JSON.parse(rawBody);\r\n } catch {\r\n return c.json({ error: \"Invalid payload\" }, 400);\r\n }\r\n\r\n const { formId } = payload;\r\n if (!formId) return c.json({ error: \"Missing formId\" }, 400);\r\n\r\n const db = createDb(c.env.DB);\r\n const cache = new CacheService(c.env.KV);\r\n\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.gformsId, formId),\r\n columns: { slug: true, maxSlots: true, registeredSlots: true },\r\n });\r\n\r\n if (!event) {\r\n console.warn(`[gforms-webhook] Unknown formId: ${formId}`);\r\n return c.json({ ok: true });\r\n }\r\n\r\n const slotsService = new SlotsService(db, cache);\r\n const updated = await slotsService.increment(event.slug);\r\n\r\n console.log(\r\n `[gforms-webhook] Incremented \"${event.slug}\": ${updated?.registered}/${updated?.total}`,\r\n );\r\n\r\n return c.json({ ok: true });\r\n});\r\n\r\n// HMAC verification\r\n\r\nasync function verifyGoogSignature(\r\n body: string,\r\n signature: string,\r\n secret: string,\r\n): Promise<boolean> {\r\n try {\r\n const key = await crypto.subtle.importKey(\r\n \"raw\",\r\n new TextEncoder().encode(secret),\r\n { name: \"HMAC\", hash: \"SHA-256\" },\r\n false,\r\n [\"verify\"],\r\n );\r\n\r\n const sigHex = signature.replace(/^hmac-sha256=/, \"\");\r\n const sigBytes = Uint8Array.from(\r\n sigHex.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [],\r\n );\r\n\r\n return crypto.subtle.verify(\r\n \"HMAC\",\r\n key,\r\n sigBytes,\r\n new TextEncoder().encode(body),\r\n );\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","import { isNotNull } from \"drizzle-orm\";\r\nimport type { LeapifyBindings } from \"../types\";\r\nimport { createDb } from \"../db\";\r\nimport { events } from \"../db/schema/classes\";\r\nimport { CacheService } from \"../services/cache\";\r\nimport { GFormsService } from \"../services/gforms\";\r\nimport { SlotsService } from \"../services/slots\";\r\n\r\nconst LOCK_KEY = \"cron:reconcile-slots:lock\";\r\nconst LOCK_TTL = 300; // 5 minutes\r\n\r\n/**\r\n * Cron: every 5 minutes (`*\\/5 * * * *`)\r\n *\r\n * Compares D1 registered_slots against actual Google Forms response counts.\r\n * Corrects any drift caused by missed webhook notifications.\r\n * Uses a distributed lock (KV) to ensure only one instance runs.\r\n */\r\nexport async function reconcileSlots(env: LeapifyBindings): Promise<void> {\r\n const db = createDb(env.DB);\r\n const cache = new CacheService(env.KV);\r\n const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON);\r\n const slots = new SlotsService(db, cache);\r\n\r\n // Distributed lock\r\n const lock = await cache.get<string>(LOCK_KEY);\r\n if (lock) {\r\n console.log(\"[reconcile-slots] Lock held, skipping.\");\r\n return;\r\n }\r\n await cache.set(LOCK_KEY, \"1\", LOCK_TTL);\r\n\r\n try {\r\n // Fetch all published events with a Google Form\r\n const publishedEvents = await db.query.events.findMany({\r\n where: isNotNull(events.gformsId),\r\n columns: { id: true, slug: true, gformsId: true, registeredSlots: true },\r\n });\r\n const eventsWithForms = publishedEvents.filter((e) => e.gformsId);\n let corrected = 0;\r\n\r\n for (const event of eventsWithForms) {\r\n try {\r\n const googleCount = await gforms.getExactResponseCount(event.gformsId!);\r\n const localCount = event.registeredSlots;\r\n\r\n if (googleCount !== localCount) {\r\n console.warn(\r\n `[reconcile-slots] Drift on \"${event.slug}\": local=${localCount}, google=${googleCount}`,\r\n );\r\n await slots.correctCount(event.slug, googleCount);\r\n corrected++;\r\n }\r\n } catch (err) {\r\n // Don't let one form failure abort the whole reconciliation\r\n console.error(`[reconcile-slots] Error checking \"${event.slug}\":`, err);\r\n }\r\n }\r\n\r\n console.log(\r\n `[reconcile-slots] Checked ${eventsWithForms.length} events, corrected ${corrected}.`,\r\n );\r\n } finally {\r\n await cache.del(LOCK_KEY);\r\n }\r\n}\r\n","import { Hono } from \"hono\";\nimport type { LeapifyEnv } from \"../../types\";\nimport { reconcileSlots } from \"../../cron/reconcile-slots\";\nimport { internalMiddleware } from \"../../auth/middleware\";\n\nexport const reconcileSlotsRoute = new Hono<LeapifyEnv>();\n\nreconcileSlotsRoute.post(\"/\", internalMiddleware, async (c) => {\n await reconcileSlots(c.env);\n return c.json({ ok: true });\n});\n","import { eq, and, lte, sql } from 'drizzle-orm'\r\nimport type { LeapifyBindings } from '../types'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { CacheService } from '../services/cache'\r\n\r\n/**\r\n * Cron: every 1 minute (`* * * * *`)\r\n *\r\n * Finds all events with status='queued' whose release_at has passed,\r\n * publishes them atomically, and invalidates the events list KV cache.\r\n */\r\nexport async function batchRelease(env: LeapifyBindings): Promise<void> {\r\n const db = createDb(env.DB)\r\n const cache = new CacheService(env.KV)\r\n\r\n const now = Math.floor(Date.now() / 1000)\r\n\r\n // Fetch queued events ready to publish\r\n const toPublish = await db.query.events.findMany({\r\n where: and(eq(events.status, 'queued'), lte(events.releaseAt, now)),\r\n columns: { id: true, slug: true },\r\n })\r\n\r\n if (toPublish.length === 0) return\r\n\r\n const ids = toPublish.map((e) => e.id)\r\n\r\n // Batch update to 'published'\r\n await db\r\n .update(events)\r\n .set({ status: 'published', publishedAt: sql`(unixepoch())` })\r\n .where(\r\n // Drizzle doesn't have inArray for D1; use raw SQL for batch\r\n sql`${events.id} IN (${sql.join(\r\n ids.map((id) => sql`${id}`),\r\n sql`, `,\r\n )})`,\r\n )\r\n\r\n // Invalidate events list cache\r\n await cache.del('events:list')\r\n await cache.del('events:etag')\r\n\r\n console.log(\r\n `[batch-release] Published ${toPublish.length} events:`,\r\n toPublish.map((e) => e.slug).join(', '),\r\n )\r\n}\r\n","import { Hono } from \"hono\";\nimport type { LeapifyEnv } from \"../../types\";\nimport { batchRelease } from \"../../cron/batch-release\";\nimport { internalMiddleware } from \"../../auth/middleware\";\n\nexport const batchReleaseRoute = new Hono<LeapifyEnv>();\n\nbatchReleaseRoute.post(\"/\", internalMiddleware, async (c) => {\n await batchRelease(c.env);\n return c.json({ ok: true });\n});\n","import { and, eq } from \"drizzle-orm\";\r\nimport type { LeapifyBindings } from \"../types\";\r\nimport { createDb } from \"../db\";\r\nimport { events } from \"../db/schema/classes\";\r\n\r\n/**\r\n * Parse a human-readable dateTime (e.g. \"May 7, 2026\") and optional startTime\r\n * (e.g. \"14:30\") into a Unix timestamp (seconds). Returns null if unparseable.\r\n */\r\nfunction parseStartTimestamp(\r\n dateTime: string | null,\r\n startTime: string | null,\r\n): number | null {\r\n if (!dateTime) return null;\r\n const combined = startTime ? `${dateTime} ${startTime}` : dateTime;\r\n const ms = Date.parse(combined);\r\n return Number.isNaN(ms) ? null : Math.floor(ms / 1000);\r\n}\r\n\r\n/**\r\n * Cron: every hour (`0 * * * *`)\r\n *\r\n * Scans published events for those approaching their start time.\r\n * Queues send_reminder_email jobs for events within the 24h and 1h windows.\r\n */\r\nexport async function reminderEmails(env: LeapifyBindings): Promise<void> {\r\n if (!env.EMAIL_QUEUE) {\r\n console.warn(\r\n \"[reminder-emails] EMAIL_QUEUE binding not configured, skipping.\",\r\n );\r\n return;\r\n }\r\n\r\n const hasSes = !!(\r\n env.SES_REGION &&\r\n env.SES_ACCESS_KEY_ID &&\r\n env.SES_SECRET_ACCESS_KEY\r\n );\r\n const hasResend = !!env.RESEND_API_KEY;\r\n if (!hasSes && !hasResend) {\r\n console.warn(\r\n \"[reminder-emails] No email providers configured. Skipping reminders.\",\r\n );\r\n return;\r\n }\r\n\r\n const db = createDb(env.DB);\r\n const now = Math.floor(Date.now() / 1000);\r\n\r\n // Fetch published events that haven't had 24h reminders sent\r\n // We filter in-memory since startsAt is derived from dateTime + startTime\r\n const candidates24h = await db.query.events.findMany({\r\n where: and(\r\n eq(events.status, \"published\"),\r\n eq(events.reminder24hSent, false),\r\n ),\r\n columns: {\r\n id: true,\r\n dateTime: true,\r\n startTime: true,\r\n },\r\n });\r\n\r\n for (const event of candidates24h) {\r\n const startsAt = parseStartTimestamp(event.dateTime, event.startTime);\r\n if (!startsAt) continue;\r\n\r\n const hoursUntil = (startsAt - now) / 3600;\r\n if (hoursUntil <= 25 && hoursUntil >= 23) {\r\n await env.EMAIL_QUEUE.send({\r\n type: \"send_reminder_email\",\r\n payload: { eventId: event.id, hoursBeforeEvent: 24 },\r\n });\r\n }\r\n }\r\n\r\n // Fetch published events that haven't had 1h reminders sent\r\n const candidates1h = await db.query.events.findMany({\r\n where: and(\r\n eq(events.status, \"published\"),\r\n eq(events.reminder1hSent, false),\r\n ),\r\n columns: {\r\n id: true,\r\n dateTime: true,\r\n startTime: true,\r\n },\r\n });\r\n\r\n for (const event of candidates1h) {\r\n const startsAt = parseStartTimestamp(event.dateTime, event.startTime);\r\n if (!startsAt) continue;\r\n\r\n const hoursUntil = (startsAt - now) / 3600;\r\n if (hoursUntil <= 1.5 && hoursUntil >= 0) {\r\n await env.EMAIL_QUEUE.send({\r\n type: \"send_reminder_email\",\r\n payload: { eventId: event.id, hoursBeforeEvent: 1 },\r\n });\r\n }\r\n }\r\n}\r\n","import { Hono } from \"hono\";\nimport type { LeapifyEnv } from \"../../types\";\nimport { reminderEmails } from \"../../cron/reminder-emails\";\nimport { internalMiddleware } from \"../../auth/middleware\";\n\nexport const reminderEmailsRoute = new Hono<LeapifyEnv>();\n\nreminderEmailsRoute.post(\"/\", internalMiddleware, async (c) => {\n await reminderEmails(c.env);\n return c.json({ ok: true });\n});\n","import { and, eq, lte } from 'drizzle-orm'\r\nimport type { LeapifyBindings } from '../types'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { GFormsService } from '../services/gforms'\r\n\r\nconst RENEWAL_WINDOW = 86400 // renew watches expiring within 24 hours\r\n\r\n/**\r\n * Cron: daily at midnight (`0 0 * * *`)\r\n *\r\n * Finds Google Forms Watches expiring within 24 hours and renews them.\r\n * Watches have a hard 7-day TTL; this cron keeps them alive indefinitely.\r\n */\r\nexport async function renewWatches(env: LeapifyBindings): Promise<void> {\r\n const db = createDb(env.DB)\r\n const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n\r\n const now = Math.floor(Date.now() / 1000)\r\n const threshold = now + RENEWAL_WINDOW\r\n\r\n const expiring = await db.query.events.findMany({\r\n where: and(\r\n eq(events.status, 'published'),\r\n lte(events.watchExpiresAt, threshold),\r\n ),\r\n columns: { id: true, slug: true, gformsId: true, watchId: true, watchExpiresAt: true },\r\n })\r\n\r\n const watchEvents = expiring.filter((e) => e.gformsId && e.watchId)\r\n let renewed = 0\r\n\r\n for (const event of watchEvents) {\r\n try {\r\n const result = await gforms.renewWatch(event.gformsId!, event.watchId!)\r\n const newExpiry = Math.floor(new Date(result.expireTime).getTime() / 1000)\r\n\r\n await db\r\n .update(events)\r\n .set({ watchExpiresAt: newExpiry })\r\n .where(eq(events.id, event.id))\r\n\r\n renewed++\r\n console.log(`[renew-watches] Renewed Watch for \"${event.slug}\", expires ${result.expireTime}`)\r\n } catch (err) {\r\n console.error(`[renew-watches] Failed to renew Watch for \"${event.slug}\":`, err)\r\n }\r\n }\r\n\r\n console.log(`[renew-watches] Renewed ${renewed}/${watchEvents.length} watches.`)\r\n}\r\n","import { Hono } from \"hono\";\nimport type { LeapifyEnv } from \"../../types\";\nimport { renewWatches } from \"../../cron/renew-watches\";\nimport { internalMiddleware } from \"../../auth/middleware\";\n\nexport const renewWatchesRoute = new Hono<LeapifyEnv>();\n\nrenewWatchesRoute.post(\"/\", internalMiddleware, async (c) => {\n await renewWatches(c.env);\n return c.json({ ok: true });\n});\n","import { Hono } from 'hono'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { badRequest, serviceUnavailable, notFound } from '../lib/errors'\r\n\r\n// ─── Constants ────────────────────────────────────────────────────────────────\r\n\r\nconst ALLOWED_MIME_TYPES = new Set([\r\n 'image/jpeg',\r\n 'image/png',\r\n 'image/webp',\r\n 'image/gif',\r\n 'image/svg+xml',\r\n])\r\n\r\n/** 10 MB */\r\nconst MAX_FILE_SIZE = 10 * 1024 * 1024\r\n\r\nexport const uploadsRoute = new Hono<LeapifyEnv>()\r\n\r\n/**\r\n * GET /uploads/images/* — public\r\n *\r\n * Serves an image from the R2 `FILES` bucket.\r\n */\r\nuploadsRoute.get('/images/*', async (c) => {\r\n const bucket = c.env.FILES\r\n if (!bucket) {\r\n throw serviceUnavailable('File storage (R2) is not configured.')\r\n }\r\n\r\n // Get the path after /images/\r\n const path = c.req.path.split('/uploads/images/')[1]\r\n if (!path) throw notFound('Image')\r\n\r\n const object = await bucket.get(path)\r\n if (!object) throw notFound('Image')\r\n\r\n const headers: Record<string, string> = {\r\n etag: object.httpEtag,\r\n // Cache at the edge/browser for 1 month\r\n \"Cache-Control\": \"public, max-age=2592000, immutable\",\r\n };\r\n\r\n if (object.httpMetadata?.contentType) {\r\n headers['Content-Type'] = object.httpMetadata.contentType\r\n }\r\n if (object.httpMetadata?.cacheControl) {\r\n headers['Cache-Control'] = object.httpMetadata.cacheControl\r\n }\r\n\r\n return c.body(object.body as unknown as ReadableStream, 200, headers)\r\n})\r\n\r\n/**\r\n * POST /uploads/images — admin only\r\n *\r\n * Accepts multipart/form-data with a single `file` field.\r\n * Stores the file in the R2 `FILES` bucket under a timestamped path and\r\n * returns the public URL.\r\n */\r\nuploadsRoute.post(\r\n '/images',\r\n authMiddleware,\r\n adminMiddleware,\r\n async (c) => {\r\n const bucket = c.env.FILES\r\n if (!bucket) {\r\n throw serviceUnavailable('File storage (R2) is not configured.')\r\n }\r\n\r\n // Parse multipart body\r\n let formData: FormData\r\n try {\r\n formData = await c.req.formData()\r\n } catch {\r\n throw badRequest('Request body must be multipart/form-data.')\r\n }\r\n\r\n const file = formData.get('file')\r\n if (!(file instanceof File)) {\r\n throw badRequest('A \"file\" field is required.')\r\n }\r\n\r\n // Validate MIME type\r\n const contentType = file.type || 'application/octet-stream'\r\n if (!ALLOWED_MIME_TYPES.has(contentType)) {\r\n throw badRequest(\r\n `Unsupported file type \"${contentType}\".`,\r\n )\r\n }\r\n\r\n // Validate size\r\n if (file.size > MAX_FILE_SIZE) {\r\n throw badRequest('File exceeds 10MB limit.')\r\n }\r\n\r\n // Build a storage key\r\n const folder = sanitizeFolder(formData.get('folder'))\r\n const ext = extensionFromMime(contentType)\r\n const ts = Date.now()\r\n const rand = Math.random().toString(36).slice(2, 8)\r\n const key = `${folder}/${ts}-${rand}.${ext}`\r\n\r\n // Upload to R2\r\n const arrayBuffer = await file.arrayBuffer()\r\n await bucket.put(key, arrayBuffer, {\r\n httpMetadata: { contentType },\r\n customMetadata: { uploadedAt: new Date().toISOString() },\r\n })\r\n\r\n // Construct URL based on the current request\r\n const url = new URL(c.req.url)\r\n url.pathname = `${url.pathname.replace(/\\/$/, '')}/${key}`\r\n url.search = ''\r\n\r\n return c.json(\r\n {\r\n data: {\r\n url: url.toString(),\r\n key,\r\n size: file.size,\r\n contentType,\r\n },\r\n },\r\n 201,\r\n )\r\n },\r\n)\r\n\r\n// ─── Helpers ──────────────────────────────────────────────────────────────────\r\n\r\nfunction sanitizeFolder(raw: FormDataEntryValue | null): string {\r\n if (typeof raw !== 'string' || !raw.trim()) return 'images'\r\n return raw\r\n .trim()\r\n .replace(/[^a-zA-Z0-9\\-_/]/g, '')\r\n .replace(/\\/+/g, '/')\r\n .replace(/^\\/|\\/$/g, '')\r\n || 'images'\r\n}\r\n\r\nfunction extensionFromMime(mime: string): string {\r\n const map: Record<string, string> = {\r\n 'image/jpeg': 'jpg',\r\n 'image/png': 'png',\r\n 'image/webp': 'webp',\r\n 'image/gif': 'gif',\r\n 'image/svg+xml': 'svg',\r\n }\r\n return map[mime] ?? 'bin'\r\n}\r\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { themes } from '../db/schema/themes'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound, conflict } from '../lib/errors'\r\n\r\nconst createThemeSchema = z.object({\r\n name: z.string().min(1),\r\n path: z.string().min(1),\r\n})\r\n\r\nexport const themesRoute = new Hono<LeapifyEnv>()\r\n\r\n// GET /themes — public\r\nthemesRoute.get('/', async (c) => {\r\n const db = createDb(c.env.DB)\r\n const data = await db.select().from(themes)\r\n return c.json({ data })\r\n})\r\n\r\n// POST /themes — admin only\r\nthemesRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n zValidator('json', createThemeSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [created] = await db.insert(themes).values(body).returning()\r\n return c.json({ data: created }, 201)\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('A theme with this name or path already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// PATCH /themes/:id — admin only\r\nthemesRoute.patch(\r\n '/:id',\r\n authMiddleware,\r\n adminMiddleware,\r\n async (c) => {\r\n const { id } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof createThemeSchema>>>()\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [updated] = await db\r\n .update(themes)\r\n .set(body)\r\n .where(eq(themes.id, id))\r\n .returning()\r\n\r\n if (!updated) throw notFound('Theme')\r\n\r\n return c.json({ data: updated })\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('A theme with this name or path already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// DELETE /themes/:id — admin only\r\nthemesRoute.delete('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n\r\n const [deleted] = await db.delete(themes).where(eq(themes.id, id)).returning()\r\n\r\n if (!deleted) throw notFound('Theme')\r\n\r\n return c.body(null, 204)\r\n})\r\n","import { Hono } from 'hono'\r\nimport { zValidator } from '@hono/zod-validator'\r\nimport { z } from 'zod'\r\nimport { eq } from 'drizzle-orm'\r\nimport type { LeapifyEnv } from '../types'\r\nimport { createDb } from '../db'\r\nimport { organizations } from '../db/schema/organizations'\r\nimport { authMiddleware, adminMiddleware } from '../auth/middleware'\r\nimport { notFound, conflict } from '../lib/errors'\r\n\r\nconst createOrganizationSchema = z.object({\r\n name: z.string().min(1),\r\n acronym: z.string().min(1),\r\n logoUrl: z.string().url().nullable().optional(),\r\n link: z.string().url().nullable().optional(),\r\n})\r\n\r\nexport const organizationsRoute = new Hono<LeapifyEnv>()\r\n\r\n// GET /organizations — public\r\norganizationsRoute.get('/', async (c) => {\r\n const db = createDb(c.env.DB)\r\n const data = await db.select().from(organizations)\r\n return c.json({ data })\r\n})\r\n\r\n// POST /organizations — admin only\r\norganizationsRoute.post(\r\n '/',\r\n authMiddleware,\r\n adminMiddleware,\r\n zValidator('json', createOrganizationSchema),\r\n async (c) => {\r\n const body = c.req.valid('json')\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [created] = await db.insert(organizations).values(body).returning()\r\n return c.json({ data: created }, 201)\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('An organization with this name or acronym already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// PATCH /organizations/:id — admin only\r\norganizationsRoute.patch(\r\n '/:id',\r\n authMiddleware,\r\n adminMiddleware,\r\n async (c) => {\r\n const { id } = c.req.param()\r\n const body = await c.req.json<Partial<z.infer<typeof createOrganizationSchema>>>()\r\n const db = createDb(c.env.DB)\r\n\r\n try {\r\n const [updated] = await db\r\n .update(organizations)\r\n .set(body)\r\n .where(eq(organizations.id, id))\r\n .returning()\r\n\r\n if (!updated) throw notFound('Organization')\r\n\r\n return c.json({ data: updated })\r\n } catch (err: any) {\r\n if (err.message && err.message.includes('UNIQUE constraint failed')) {\r\n throw conflict('An organization with this name or acronym already exists.')\r\n }\r\n throw err\r\n }\r\n },\r\n)\r\n\r\n// DELETE /organizations/:id — admin only\r\norganizationsRoute.delete('/:id', authMiddleware, adminMiddleware, async (c) => {\r\n const { id } = c.req.param()\r\n const db = createDb(c.env.DB)\r\n\r\n const [deleted] = await db.delete(organizations).where(eq(organizations.id, id)).returning()\r\n\r\n if (!deleted) throw notFound('Organization')\r\n\r\n return c.body(null, 204)\r\n})\r\n","import { Hono } from 'hono'\r\nimport type { LeapifyEnv } from './types'\r\nimport { errorHandler } from './lib/middleware/error-handler'\r\nimport { createCorsMiddleware } from './lib/middleware/cors'\r\nimport { createRefererGuard } from './lib/middleware/referer-guard'\r\nimport {\r\n createTurnstileMiddleware,\r\n handleTurnstileVerify,\r\n TURNSTILE_VERIFY_PATH,\r\n} from './lib/middleware/turnstile-challenge'\r\nimport { serviceUnavailable } from './lib/errors'\r\nimport { createAuth } from './auth/auth'\r\nimport { healthRoute } from './routes/health'\r\nimport { classesRoute } from './routes/classes'\r\nimport { usersRoute } from './routes/users'\r\nimport { siteConfigRoute } from './routes/site-config'\r\nimport { faqsRoute } from './routes/faqs'\r\nimport { gformsWebhookRoute } from './routes/internal/gforms-webhook'\r\nimport { reconcileSlotsRoute } from './routes/internal/reconcile-slots'\r\nimport { batchReleaseRoute } from './routes/internal/batch-release'\r\nimport { reminderEmailsRoute } from './routes/internal/reminder-emails'\r\nimport { renewWatchesRoute } from './routes/internal/renew-watches'\r\nimport { uploadsRoute } from './routes/uploads'\r\nimport { themesRoute } from './routes/themes'\r\nimport { organizationsRoute } from './routes/organizations'\r\n\r\nexport interface LeapifyAppOptions {\r\n allowedOrigins?: string[]\r\n /**\r\n * Public HTTPS URL of your Cloudflare Worker.\r\n * Required for Google Forms Watch push notifications to work.\r\n * Google will POST to {gformsWebhookUrl}/internal/gforms-webhook on each new submission.\r\n *\r\n * @example 'https://leap.yourdomain.com'\r\n */\r\n gformsWebhookUrl?: string\r\n}\r\n\r\nexport function createApp(options: LeapifyAppOptions = {}): Hono<LeapifyEnv> {\r\n const app = new Hono<LeapifyEnv>()\r\n\r\n // Expose gformsWebhookUrl to routes via app-level middleware\r\n if (options.gformsWebhookUrl) {\r\n const webhookUrl = `${options.gformsWebhookUrl.replace(/\\/$/, '')}/internal/gforms-webhook`\r\n app.use('*', async (c, next) => {\r\n c.set('gformsWebhookUrl', webhookUrl)\r\n return next()\r\n })\r\n }\r\n\r\n // Global middleware\r\n app.use('*', createCorsMiddleware(options.allowedOrigins ?? ['*']))\r\n app.use('*', createTurnstileMiddleware())\r\n app.use('*', createRefererGuard(options.allowedOrigins ?? ['*']))\r\n\r\n // Better Auth HTTP handler — OAuth redirects, callbacks, session, token endpoints.\r\n // Mounted BEFORE the maintenance check so auth is always reachable.\r\n app.on(['POST', 'GET'], '/api/auth/*', (c) => {\r\n const auth = createAuth(c.env)\r\n\r\n // Ensure cf-connecting-ip is present for Better Auth rate limiting\r\n const req = c.req.raw\r\n if (!req.headers.get('cf-connecting-ip')) {\r\n const forwarded = req.headers.get('x-forwarded-for')\r\n const ip = forwarded?.split(',')[0]?.trim() || '127.0.0.1'\r\n const newHeaders = new Headers(req.headers)\r\n newHeaders.set('cf-connecting-ip', ip)\r\n return auth.handler(new Request(req.url, {\r\n method: req.method,\r\n headers: newHeaders,\r\n body: (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') ? null : req.body,\r\n redirect: req.redirect,\r\n }))\r\n }\r\n\r\n return auth.handler(req)\r\n })\r\n\r\n // Maintenance mode check\r\n app.use('*', async (c, next) => {\r\n // Skip for health, auth, and internal routes so operators can still access them\r\n if (\r\n c.req.path === '/health' ||\r\n c.req.path.startsWith('/api/auth') ||\r\n c.req.path.startsWith('/internal')\r\n ) {\r\n return next()\r\n }\r\n // Read maintenance_mode flag from KV (set via PATCH /config/maintenance_mode).\r\n // KV is faster than D1 for this hot-path check — O(1) per request.\r\n const flag = await c.env.KV.get<boolean>('config:maintenance_mode', 'json')\r\n if (flag === true) {\r\n throw serviceUnavailable(\r\n 'The site is currently under maintenance. Please check back soon.',\r\n )\r\n }\r\n return next()\r\n })\r\n\r\n // Routes\r\n app.post(TURNSTILE_VERIFY_PATH, handleTurnstileVerify)\r\n app.route('/health', healthRoute)\r\n app.route('/api/config', siteConfigRoute)\r\n app.route('/api/classes', classesRoute)\r\n app.route('/api/themes', themesRoute)\r\n app.route('/api/users', usersRoute)\r\n app.route('/api/organizations', organizationsRoute)\r\n app.route('/api/faqs', faqsRoute)\r\n app.route('/api/uploads', uploadsRoute)\r\n app.route('/internal/gforms-webhook', gformsWebhookRoute)\r\n app.route('/internal/reconcile-slots', reconcileSlotsRoute)\r\n app.route('/internal/batch-release', batchReleaseRoute)\r\n app.route('/internal/reminder-emails', reminderEmailsRoute)\r\n app.route('/internal/renew-watches', renewWatchesRoute)\r\n\r\n // Error handler\r\n app.onError(errorHandler)\r\n\r\n // 404\r\n app.notFound((c) =>\r\n c.json({ error: { code: 'NOT_FOUND', message: 'Route not found' } }, 404),\r\n )\r\n\r\n return app\r\n}\r\n","/**\r\n * Amazon SES (v2) transactional email client.\r\n *\r\n * Uses the SES v2 SendEmail REST endpoint with AWS SigV4 signing.\r\n * Fully fetch-native — no SDK, fully edge-compatible (Cloudflare Workers).\r\n *\r\n * Docs: https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html\r\n */\r\n\r\nexport interface SesEmailOptions {\r\n to: string | string[]\r\n subject: string\r\n html: string\r\n from?: string\r\n replyTo?: string\r\n}\r\n\r\nexport interface SesBatchEmailOptions {\r\n emails: SesEmailOptions[]\r\n}\r\n\r\nexport class SesService {\r\n private readonly region: string\r\n private readonly accessKeyId: string\r\n private readonly secretAccessKey: string\r\n private readonly defaultFrom: string\r\n\r\n constructor(opts: {\r\n region: string\r\n accessKeyId: string\r\n secretAccessKey: string\r\n fromAddress: string\r\n fromName?: string | undefined\r\n }) {\r\n this.region = opts.region\r\n this.accessKeyId = opts.accessKeyId\r\n this.secretAccessKey = opts.secretAccessKey\r\n this.defaultFrom = formatFrom(opts.fromName, opts.fromAddress)\r\n }\r\n\r\n /**\r\n * Send a single email via SES v2 SendEmail.\r\n * Throws on any non-2xx response.\r\n */\r\n async sendEmail(options: SesEmailOptions): Promise<{ messageId: string }> {\r\n const toAddresses = Array.isArray(options.to) ? options.to : [options.to]\r\n const from = options.from ?? this.defaultFrom\r\n\r\n const body = JSON.stringify({\r\n FromEmailAddress: from,\r\n Destination: { ToAddresses: toAddresses },\r\n Content: {\r\n Simple: {\r\n Subject: { Data: options.subject, Charset: 'UTF-8' },\r\n Body: { Html: { Data: options.html, Charset: 'UTF-8' } },\r\n },\r\n },\r\n ...(options.replyTo\r\n ? { ReplyToAddresses: [options.replyTo] }\r\n : {}),\r\n })\r\n\r\n const response = await this._signedFetch('POST', '/v2/email/outbound-emails', body)\r\n\r\n if (!response.ok) {\r\n const err = await response.text()\r\n throw new SesError(response.status, err)\r\n }\r\n\r\n const data = (await response.json()) as { MessageId: string }\r\n return { messageId: data.MessageId }\r\n }\r\n\r\n /**\r\n * Send a batch of emails sequentially (SES v2 has no native batch endpoint).\r\n * Each message is sent individually; partial failure does NOT abort remaining sends.\r\n * Returns settled results so the caller can decide what to do with failures.\r\n */\r\n async sendBatch(emails: SesEmailOptions[]): Promise<PromiseSettledResult<{ messageId: string }>[]> {\r\n return Promise.allSettled(emails.map((e) => this.sendEmail(e)))\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // SigV4 signing\r\n // ---------------------------------------------------------------------------\r\n\r\n private async _signedFetch(method: string, path: string, body: string): Promise<Response> {\r\n const url = `https://email.${this.region}.amazonaws.com${path}`\r\n const now = new Date()\r\n const amzDate = formatAmzDate(now)\r\n const dateStamp = amzDate.slice(0, 8)\r\n const host = `email.${this.region}.amazonaws.com`\r\n const service = 'ses'\r\n\r\n const payloadHash = await sha256Hex(body)\r\n\r\n const canonicalHeaders = `content-type:application/json\\nhost:${host}\\nx-amz-date:${amzDate}\\n`\r\n const signedHeaders = 'content-type;host;x-amz-date'\r\n\r\n const canonicalRequest = [\r\n method,\r\n path,\r\n '', // query string\r\n canonicalHeaders,\r\n signedHeaders,\r\n payloadHash,\r\n ].join('\\n')\r\n\r\n const credentialScope = `${dateStamp}/${this.region}/${service}/aws4_request`\r\n const stringToSign = [\r\n 'AWS4-HMAC-SHA256',\r\n amzDate,\r\n credentialScope,\r\n await sha256Hex(canonicalRequest),\r\n ].join('\\n')\r\n\r\n const signingKey = await getSigningKey(this.secretAccessKey, dateStamp, this.region, service)\r\n const signature = await hmacHex(signingKey, stringToSign)\r\n\r\n const authHeader =\r\n `AWS4-HMAC-SHA256 Credential=${this.accessKeyId}/${credentialScope}, ` +\r\n `SignedHeaders=${signedHeaders}, Signature=${signature}`\r\n\r\n return fetch(url, {\r\n method,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Amz-Date': amzDate,\r\n Authorization: authHeader,\r\n },\r\n body,\r\n })\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// SES-specific error — carries the HTTP status so callers can classify it\r\n// ---------------------------------------------------------------------------\r\n\r\nexport class SesError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n message: string,\r\n ) {\r\n super(`SES error ${status}: ${message}`)\r\n this.name = 'SesError'\r\n }\r\n\r\n /**\r\n * True for errors that are permanent (not worth retrying via SES again).\r\n * 400 BadRequest, 403 Forbidden, 404 NotFound → non-retryable.\r\n * 429 ThrottlingException, 5xx → retryable.\r\n */\r\n get isNonRetryable(): boolean {\r\n return this.status >= 400 && this.status < 500 && this.status !== 429\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// SigV4 crypto helpers (Web Crypto API — available in CF Workers)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatAmzDate(d: Date): string {\r\n return d.toISOString().replace(/[:-]|\\.\\d{3}/g, '').slice(0, 15) + 'Z'\r\n}\r\n\r\nasync function sha256Hex(data: string): Promise<string> {\r\n const encoded = new TextEncoder().encode(data)\r\n const hashBuffer = await crypto.subtle.digest('SHA-256', encoded)\r\n return bufToHex(hashBuffer)\r\n}\r\n\r\nasync function hmacHex(key: ArrayBuffer, data: string): Promise<string> {\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw',\r\n key,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign'],\r\n )\r\n const sig = await crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(data))\r\n return bufToHex(sig)\r\n}\r\n\r\nasync function hmacBuffer(key: ArrayBuffer, data: string): Promise<ArrayBuffer> {\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw',\r\n key,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign'],\r\n )\r\n return crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(data))\r\n}\r\n\r\nasync function getSigningKey(\r\n secret: string,\r\n dateStamp: string,\r\n region: string,\r\n service: string,\r\n): Promise<ArrayBuffer> {\r\n const secretBytes = new TextEncoder().encode(`AWS4${secret}`)\r\n // Slice to get a plain ArrayBuffer (TextEncoder returns Uint8Array<ArrayBufferLike>)\r\n const kDate = await hmacBuffer(secretBytes.buffer.slice(0) as ArrayBuffer, dateStamp)\r\n const kRegion = await hmacBuffer(kDate, region)\r\n const kService = await hmacBuffer(kRegion, service)\r\n return hmacBuffer(kService, 'aws4_request')\r\n}\r\n\r\nfunction bufToHex(buf: ArrayBuffer): string {\r\n return Array.from(new Uint8Array(buf))\r\n .map((b) => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n}\r\n\r\n/**\r\n * Format email 'From' field as \"Name <email@domain.com>\" if name provided.\r\n */\r\nfunction formatFrom(name: string | undefined, email: string): string {\r\n if (!name) return email\r\n return `${name} <${email}>`\r\n}\r\n","/**\r\n * Resend transactional email client.\r\n * Fully fetch-native — no SDK, fully edge-compatible.\r\n */\r\n\r\nexport interface SendEmailOptions {\r\n to: string | string[];\r\n subject: string;\r\n html: string;\r\n from?: string; // defaults to LeapifyConfig.resend.fromAddress\r\n replyTo?: string;\r\n}\r\n\r\nexport interface BatchEmailOptions {\r\n emails: SendEmailOptions[];\r\n}\r\n\r\nexport class ResendService {\r\n private readonly apiKey: string;\r\n private readonly defaultFrom: string;\r\n\r\n constructor(apiKey: string, fromAddress: string) {\r\n this.apiKey = apiKey;\r\n this.defaultFrom = fromAddress;\r\n }\r\n\r\n private get headers() {\r\n return {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n \"Content-Type\": \"application/json\",\r\n };\r\n }\r\n\r\n async sendEmail(options: SendEmailOptions): Promise<{ id: string }> {\r\n const response = await fetch(\"https://api.resend.com/emails\", {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify({\r\n from: options.from ?? this.defaultFrom,\r\n to: Array.isArray(options.to) ? options.to : [options.to],\r\n subject: options.subject,\r\n html: options.html,\r\n ...(options.replyTo ? { reply_to: options.replyTo } : {}),\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Resend API error ${response.status}: ${err}`);\r\n }\r\n\r\n return response.json() as Promise<{ id: string }>;\r\n }\r\n\r\n async sendBatch(emails: SendEmailOptions[]): Promise<{ id: string }[]> {\r\n const response = await fetch(\"https://api.resend.com/emails/batch\", {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify(\r\n emails.map((e) => ({\r\n from: e.from ?? this.defaultFrom,\r\n to: Array.isArray(e.to) ? e.to : [e.to],\r\n subject: e.subject,\r\n html: e.html,\r\n ...(e.replyTo ? { reply_to: e.replyTo } : {}),\r\n })),\r\n ),\r\n });\r\n\r\n if (!response.ok) {\r\n const err = await response.text();\r\n throw new Error(`Resend batch API error ${response.status}: ${err}`);\r\n }\r\n\r\n const data = (await response.json()) as { data: { id: string }[] };\r\n return data.data;\r\n }\r\n}\r\n\r\n// Email templates\r\n\r\nexport function buildReminderEmail(event: {\r\n title: string;\r\n organization?: string | null;\r\n dateTime?: string | null;\r\n startTime?: string | null;\r\n venue?: string | null;\r\n gformsUrl?: string | null;\r\n}): string {\r\n const timeDisplay = [event.dateTime, event.startTime].filter(Boolean).join(\" at \");\r\n return `\r\n <div style=\"font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 24px;\">\r\n <h2 style=\"color: #1a1a2e;\">📅 Reminder: ${event.title}</h2>\r\n ${event.organization ? `<p style=\"color: #666;\">Organized by: <strong>${event.organization}</strong></p>` : \"\"}\r\n ${timeDisplay ? `<p>🕐 <strong>${timeDisplay}</strong></p>` : \"\"}\r\n ${event.venue ? `<p>📍 <strong>${event.venue}</strong></p>` : \"\"}\r\n <hr style=\"margin: 24px 0; border: none; border-top: 1px solid #eee;\" />\r\n ${\r\n event.gformsUrl\r\n ? `<p>You registered for this event. See you there!</p>\r\n <a href=\"${event.gformsUrl}\" style=\"display:inline-block;padding:12px 24px;background:#4f46e5;color:#fff;border-radius:6px;text-decoration:none;\">View Registration</a>`\r\n : \"<p>You registered for this event. See you there!</p>\"\r\n }\r\n <p style=\"margin-top: 32px; font-size: 12px; color: #999;\">\r\n DLSU CSO LEAP — This is an automated reminder.\r\n </p>\r\n </div>\r\n `;\r\n}\r\n","export interface RetryOptions {\r\n maxAttempts?: number\r\n baseDelayMs?: number\r\n maxDelayMs?: number\r\n shouldRetry?: (error: unknown) => boolean\r\n}\r\n\r\n/**\r\n * Retry an async function with exponential backoff + jitter.\r\n * Default: 3 attempts, 100ms base delay, 5s max delay.\r\n */\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n options: RetryOptions = {},\r\n): Promise<T> {\r\n const {\r\n maxAttempts = 3,\r\n baseDelayMs = 100,\r\n maxDelayMs = 5000,\r\n shouldRetry = defaultShouldRetry,\r\n } = options\r\n\r\n let lastError: unknown\r\n\r\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\r\n try {\r\n return await fn()\r\n } catch (error) {\r\n lastError = error\r\n if (attempt === maxAttempts || !shouldRetry(error)) throw error\r\n\r\n const exponential = baseDelayMs * 2 ** (attempt - 1)\r\n const jitter = Math.random() * 50\r\n const delay = Math.min(exponential + jitter, maxDelayMs)\r\n\r\n await sleep(delay)\r\n }\r\n }\r\n\r\n throw lastError\r\n}\r\n\r\nfunction defaultShouldRetry(error: unknown): boolean {\r\n // Retry on 429 and 5xx HTTP errors\r\n if (error instanceof Response) {\r\n return error.status === 429 || error.status >= 500\r\n }\r\n return true\r\n}\r\n\r\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))\r\n","/**\r\n * EmailRouter — provider-agnostic email facade.\r\n *\r\n * Strategy:\r\n * 1. Try Amazon SES (primary) with up to SES_MAX_ATTEMPTS retries.\r\n * 2. If SES raises a non-retryable error AND Resend is configured\r\n * (RESEND_API_KEY is set), fall back to Resend.\r\n * 3. If Resend is not configured, re-throw the SES error immediately.\r\n *\r\n * The caller (queue handler) is responsible for final DLQ behaviour via\r\n * message.retry() / message.ack().\r\n */\r\n\r\nimport { SesService, SesError } from './ses'\r\nimport { ResendService, type SendEmailOptions } from './resend'\r\nimport { withRetry } from '../lib/retry'\r\n\r\nconst SES_MAX_ATTEMPTS = 3\r\n\r\nexport interface EmailPayload {\r\n to: string | string[]\r\n subject: string\r\n html: string\r\n from?: string\r\n replyTo?: string\r\n}\r\n\r\nexport interface EmailRouterConfig {\r\n /** SES credentials — optional */\r\n ses?: {\r\n region: string\r\n accessKeyId: string\r\n secretAccessKey: string\r\n fromAddress: string\r\n } | undefined\r\n /** Resend credentials — optional */\r\n resend?: {\r\n apiKey: string\r\n fromAddress: string\r\n } | undefined\r\n /** Optional display name for 'From' header (e.g. \"Leapify\") */\r\n fromName?: string | undefined\r\n}\r\n\r\nexport class EmailRouter {\r\n private readonly ses: SesService | null\r\n private readonly resend: ResendService | null\r\n\r\n constructor(config: EmailRouterConfig) {\r\n this.ses = config.ses\r\n ? new SesService({ ...config.ses, fromName: config.fromName })\r\n : null\r\n this.resend = config.resend\r\n ? new ResendService(config.resend.apiKey, formatFrom(config.fromName, config.resend.fromAddress))\r\n : null\r\n }\r\n\r\n /**\r\n * Send a single email.\r\n * Tries SES first (with retries), then Resend if configured and SES fails permanently.\r\n */\r\n async sendEmail(payload: EmailPayload): Promise<{ provider: 'ses' | 'resend'; id: string }> {\r\n // 1. Try SES if configured\r\n if (this.ses) {\r\n try {\r\n const result = await withRetry(() => this.ses!.sendEmail(payload), {\r\n maxAttempts: SES_MAX_ATTEMPTS,\r\n shouldRetry: (err) => !(err instanceof SesError && err.isNonRetryable),\r\n })\r\n return { provider: 'ses', id: result.messageId }\r\n } catch (sesErr) {\r\n const isPermanent = sesErr instanceof SesError && sesErr.isNonRetryable\r\n\r\n // Fallback to Resend if SES has permanent failure\r\n if (isPermanent && this.resend) {\r\n console.warn('[EmailRouter] SES failed permanently — falling back to Resend', sesErr)\r\n const result = await this.resend.sendEmail(toResendOptions(payload))\r\n return { provider: 'resend', id: result.id }\r\n }\r\n\r\n throw sesErr\r\n }\r\n }\r\n\r\n // 2. Try Resend if SES is not configured but Resend is\r\n if (this.resend) {\r\n const result = await this.resend.sendEmail(toResendOptions(payload))\r\n return { provider: 'resend', id: result.id }\r\n }\r\n\r\n // 3. No providers configured\r\n throw new Error('No email providers (SES or Resend) are configured.')\r\n }\r\n\r\n /**\r\n * Send a batch of emails.\r\n * Each email is routed individually through sendEmail() so per-message\r\n * fallback logic applies consistently.\r\n *\r\n * Returns settled results — partial failures do NOT abort the batch.\r\n */\r\n async sendBatch(\r\n payloads: EmailPayload[],\r\n ): Promise<PromiseSettledResult<{ provider: 'ses' | 'resend'; id: string }>[]> {\r\n return Promise.allSettled(payloads.map((p) => this.sendEmail(p)))\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Factory — build an EmailRouter from raw Worker bindings\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type EmailEnv = {\r\n SES_REGION?: string\r\n SES_ACCESS_KEY_ID?: string\r\n SES_SECRET_ACCESS_KEY?: string\r\n SES_FROM_ADDRESS?: string\r\n EMAIL_FROM_NAME?: string\r\n ALLOWED_ORIGINS?: string\r\n RESEND_API_KEY?: string\r\n RESEND_FROM_ADDRESS?: string\r\n}\r\n\r\n/**\r\n * Build an EmailRouter from Cloudflare Worker bindings.\r\n * Returns null if no email providers are configured.\r\n */\r\nexport function createEmailRouter(env: EmailEnv): EmailRouter | null {\r\n const hasSes = env.SES_REGION && env.SES_ACCESS_KEY_ID && env.SES_SECRET_ACCESS_KEY\r\n const hasResend = !!env.RESEND_API_KEY\r\n\r\n if (!hasSes && !hasResend) {\r\n return null\r\n }\r\n\r\n return new EmailRouter({\r\n ses: hasSes\r\n ? {\r\n region: env.SES_REGION!,\r\n accessKeyId: env.SES_ACCESS_KEY_ID!,\r\n secretAccessKey: env.SES_SECRET_ACCESS_KEY!,\r\n fromAddress: env.SES_FROM_ADDRESS ?? 'noreply@leap.dlsu.edu.ph',\r\n }\r\n : undefined,\r\n fromName: env.EMAIL_FROM_NAME ?? undefined,\r\n resend: hasResend\r\n ? {\r\n apiKey: env.RESEND_API_KEY!,\r\n fromAddress: env.RESEND_FROM_ADDRESS ?? 'noreply@leap.dlsu.edu.ph',\r\n }\r\n : undefined,\r\n })\r\n}\r\n\r\n/**\r\n * Format email 'From' field as \"Name <email@domain.com>\" if name provided.\r\n */\r\nfunction formatFrom(name: string | undefined, email: string): string {\r\n if (!name) return email\r\n return `${name} <${email}>`\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Internal helpers\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction toResendOptions(payload: EmailPayload): SendEmailOptions {\r\n return {\r\n to: payload.to,\r\n subject: payload.subject,\r\n html: payload.html,\r\n ...(payload.from !== undefined ? { from: payload.from } : {}),\r\n ...(payload.replyTo !== undefined ? { replyTo: payload.replyTo } : {}),\r\n }\r\n}\r\n","import { eq } from 'drizzle-orm'\r\nimport type { LeapifyBindings } from '../types'\r\nimport type { LeapifyJob } from './jobs'\r\nimport { createDb } from '../db'\r\nimport { events } from '../db/schema/classes'\r\nimport { createEmailRouter, type EmailRouter } from '../services/email'\r\nimport { buildReminderEmail } from '../services/resend'\r\nimport { GFormsService } from '../services/gforms'\r\n\r\n/**\r\n * CF Queue consumer handler.\r\n * Export from the consumer repo's worker entry like:\r\n *\r\n * ```ts\r\n * import { createQueueHandler } from 'leapify'\r\n * export const queue = createQueueHandler\r\n * ```\r\n */\r\nexport function createQueueHandler(env: LeapifyBindings) {\r\n return async (batch: MessageBatch<LeapifyJob>): Promise<void> => {\r\n const db = createDb(env.DB)\r\n const email = createEmailRouter(env)\r\n const gforms = new GFormsService(env.GFORMS_SERVICE_ACCOUNT_JSON)\r\n\r\n for (const message of batch.messages) {\r\n try {\r\n await processJob(message.body, { db, email, gforms, env })\r\n message.ack()\r\n } catch (err) {\r\n console.error(`[Queue] Failed to process job ${message.body.type}:`, err)\r\n message.retry()\r\n }\r\n }\r\n }\r\n}\r\n\r\nasync function processJob(\r\n job: LeapifyJob,\r\n services: {\r\n db: ReturnType<typeof createDb>\r\n email: EmailRouter | null\r\n gforms: GFormsService\r\n env: LeapifyBindings\r\n },\r\n): Promise<void> {\r\n const { db, email, gforms } = services\r\n\r\n switch (job.type) {\r\n case 'send_email': {\r\n if (!email) throw new Error('Email provider not configured')\r\n const result = await email.sendEmail(job.payload)\r\n console.log(`[Queue] send_email dispatched via ${result.provider} (id=${result.id})`)\r\n break\r\n }\r\n\r\n case 'send_reminder_email': {\r\n if (!email) throw new Error('Email provider not configured')\r\n\r\n const event = await db.query.events.findFirst({\r\n where: eq(events.id, job.payload.eventId),\r\n with: { organization: true },\r\n })\r\n if (!event?.gformsId) break\r\n\r\n const emails = await gforms.getRespondentEmails(event.gformsId)\r\n if (emails.length === 0) break\r\n\r\n const isDay = job.payload.hoursBeforeEvent === 24\r\n const subject = isDay\r\n ? `Reminder: \"${event.title}\" is tomorrow!`\r\n : `Reminder: \"${event.title}\" is in 1 hour!`\r\n const html = buildReminderEmail({\r\n title: event.title,\r\n organization: event.organization?.name ?? null,\r\n dateTime: event.dateTime,\r\n startTime: event.startTime,\r\n venue: event.venue,\r\n gformsUrl: event.gformsUrl,\r\n })\r\n\r\n const BATCH_SIZE = 50\r\n const chunks: string[][] = []\r\n for (let i = 0; i < emails.length; i += BATCH_SIZE) {\r\n chunks.push(emails.slice(i, i + BATCH_SIZE))\r\n }\r\n const failures: string[] = []\r\n for (let i = 0; i < chunks.length; i++) {\r\n const chunk = chunks[i]\r\n try {\r\n await email.sendEmail({ to: chunk, subject, html })\r\n } catch (err) {\r\n failures.push(...chunk)\r\n console.error(\r\n `[Queue] send_reminder_email: ${failures.length}/${chunk.length} messages failed in batch ${i / BATCH_SIZE + 1}`,\r\n err,\r\n )\r\n }\r\n }\r\n\r\n const field = isDay ? 'reminder24hSent' : 'reminder1hSent'\r\n await db.update(events).set({ [field]: true }).where(eq(events.id, event.id))\r\n break\r\n }\r\n\r\n case 'audit_log': {\r\n console.log(`[Queue] audit_log: ${job.payload.action} by ${job.payload.userId}`)\r\n break\r\n }\r\n\r\n\r\n\r\n case 'notify_batch_release': {\r\n console.log(`[Queue] notify_batch_release: ${job.payload.eventIds.length} events released`)\r\n break\r\n }\r\n\r\n case 'renew_forms_watch': {\r\n try {\r\n await gforms.renewWatch(job.payload.formId, job.payload.watchId)\r\n console.log(`[Queue] renew_forms_watch: renewed watch for form ${job.payload.formId}`)\r\n } catch (err) {\r\n console.error(`[Queue] renew_forms_watch failed for form ${job.payload.formId}:`, err)\r\n }\r\n break\r\n }\r\n }\r\n}\r\n","/**\r\n * Auto-migration helper for D1 databases.\r\n *\r\n * When enabled, ensures all required tables exist on the first request.\r\n * Safe to call on every boot — all statements use IF NOT EXISTS.\r\n *\r\n * Import from 'leapify' — server-only.\r\n *\r\n * @example\r\n * // worker.ts\r\n * import { createLeapify } from 'leapify'\r\n *\r\n * export default createLeapify({ autoMigrate: true })\r\n */\r\n\r\n// ─── Patches for existing databases ──────────────────────────────────────────\r\n// ALTER TABLE statements that add columns added after initial deploy.\r\n// Safe to run on every boot — duplicate column errors are caught and ignored.\r\n\r\nconst PATCH_STATEMENTS = [\r\n `ALTER TABLE \"themes\" ADD COLUMN \"updated_at\" integer NOT NULL DEFAULT (unixepoch())`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"organization_id\" text`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"class_code\" text`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"start_time\" text`,\r\n `ALTER TABLE \"events\" ADD COLUMN \"end_time\" text`,\r\n `ALTER TABLE \"events\" RENAME COLUMN \"is_major\" TO \"is_spotlight\"`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_organization_id\" ON \"events\" (\"organization_id\")`,\r\n];\r\n\r\n// ─── Full schema for fresh databases ────────────────────────────────────────\r\n\r\nconst CREATE_STATEMENTS = [\r\n // Better Auth: user\r\n `CREATE TABLE IF NOT EXISTS \"user\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"email\" text NOT NULL,\r\n \"email_verified\" integer DEFAULT false NOT NULL,\r\n \"image\" text,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"user_email_unique\" ON \"user\" (\"email\")`,\r\n\r\n // Better Auth: session\r\n `CREATE TABLE IF NOT EXISTS \"session\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"expires_at\" integer NOT NULL,\r\n \"token\" text NOT NULL,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"ip_address\" text,\r\n \"user_agent\" text,\r\n \"user_id\" text NOT NULL,\r\n FOREIGN KEY (\"user_id\") REFERENCES \"user\"(\"id\") ON UPDATE no action ON DELETE cascade\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"session_token_unique\" ON \"session\" (\"token\")`,\r\n `CREATE INDEX IF NOT EXISTS \"session_userId_idx\" ON \"session\" (\"user_id\")`,\r\n\r\n // Better Auth: account\r\n `CREATE TABLE IF NOT EXISTS \"account\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"account_id\" text NOT NULL,\r\n \"provider_id\" text NOT NULL,\r\n \"user_id\" text NOT NULL,\r\n \"access_token\" text,\r\n \"refresh_token\" text,\r\n \"id_token\" text,\r\n \"access_token_expires_at\" integer,\r\n \"refresh_token_expires_at\" integer,\r\n \"scope\" text,\r\n \"password\" text,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,\r\n FOREIGN KEY (\"user_id\") REFERENCES \"user\"(\"id\") ON UPDATE no action ON DELETE cascade\r\n )`,\r\n `CREATE INDEX IF NOT EXISTS \"account_userId_idx\" ON \"account\" (\"user_id\")`,\r\n\r\n // Better Auth: verification\r\n `CREATE TABLE IF NOT EXISTS \"verification\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"identifier\" text NOT NULL,\r\n \"value\" text NOT NULL,\r\n \"expires_at\" integer NOT NULL,\r\n \"created_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)),\r\n \"updated_at\" integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer))\r\n )`,\r\n `CREATE INDEX IF NOT EXISTS \"verification_identifier_idx\" ON \"verification\" (\"identifier\")`,\r\n\r\n // App: users\r\n `CREATE TABLE IF NOT EXISTS \"users\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"better_auth_id\" text NOT NULL,\r\n \"email\" text NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"role\" text DEFAULT 'student' NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"users_better_auth_id_unique\" ON \"users\" (\"better_auth_id\")`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"users_email_unique\" ON \"users\" (\"email\")`,\r\n\r\n // App: organizations (before events, due to FK)\r\n `CREATE TABLE IF NOT EXISTS \"organizations\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"acronym\" text NOT NULL,\r\n \"logo_url\" text,\r\n \"link\" text,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"organizations_name_unique\" ON \"organizations\" (\"name\")`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"organizations_acronym_unique\" ON \"organizations\" (\"acronym\")`,\r\n\r\n // App: themes (before events, due to FK)\r\n `CREATE TABLE IF NOT EXISTS \"themes\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"name\" text NOT NULL,\r\n \"path\" text NOT NULL,\r\n \"created_at\" integer NOT NULL,\r\n \"updated_at\" integer NOT NULL DEFAULT (unixepoch())\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"themes_name_unique\" ON \"themes\" (\"name\")`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"themes_path_unique\" ON \"themes\" (\"path\")`,\r\n\r\n // App: events\r\n `CREATE TABLE IF NOT EXISTS \"events\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"slug\" text NOT NULL,\r\n \"theme_id\" text,\r\n \"organization_id\" text,\r\n \"title\" text NOT NULL,\r\n \"description\" text,\r\n \"venue\" text,\r\n \"date_time\" text,\r\n \"price\" text,\r\n \"background_image_url\" text,\r\n \"class_code\" text,\r\n \"start_time\" text,\r\n \"end_time\" text,\r\n \"is_spotlight\" integer DEFAULT false NOT NULL,\r\n \"max_slots\" integer DEFAULT 0 NOT NULL,\r\n \"registered_slots\" integer DEFAULT 0 NOT NULL,\r\n \"gforms_id\" text,\r\n \"gforms_url\" text,\r\n \"gforms_editor_url\" text,\r\n \"registration_closes_at\" integer,\r\n \"watch_id\" text,\r\n \"watch_expires_at\" integer,\r\n \"status\" text DEFAULT 'draft' NOT NULL,\r\n \"release_at\" integer,\r\n \"reminder_24h_sent\" integer DEFAULT false NOT NULL,\r\n \"reminder_1h_sent\" integer DEFAULT false NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL,\r\n \"published_at\" integer,\r\n FOREIGN KEY (\"theme_id\") REFERENCES \"themes\"(\"id\") ON UPDATE no action ON DELETE set null,\r\n FOREIGN KEY (\"organization_id\") REFERENCES \"organizations\"(\"id\") ON UPDATE no action ON DELETE set null\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"events_slug_unique\" ON \"events\" (\"slug\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_status_release\" ON \"events\" (\"status\", \"release_at\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_theme_id\" ON \"events\" (\"theme_id\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_organization_id\" ON \"events\" (\"organization_id\")`,\r\n `CREATE INDEX IF NOT EXISTS \"idx_events_slug\" ON \"events\" (\"slug\")`,\r\n\r\n // App: faqs\r\n `CREATE TABLE IF NOT EXISTS \"faqs\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"question\" text NOT NULL,\r\n \"answer\" text NOT NULL,\r\n \"category\" text,\r\n \"sort_order\" integer DEFAULT 0 NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL,\r\n \"updated_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n\r\n // App: site_config\r\n `CREATE TABLE IF NOT EXISTS \"site_config\" (\r\n \"key\" text PRIMARY KEY NOT NULL,\r\n \"value\" text NOT NULL,\r\n \"updated_at\" integer DEFAULT (unixepoch()) NOT NULL\r\n )`,\r\n\r\n // App: bookmarks\r\n `CREATE TABLE IF NOT EXISTS \"bookmarks\" (\r\n \"id\" text PRIMARY KEY NOT NULL,\r\n \"user_id\" text NOT NULL,\r\n \"event_id\" text NOT NULL,\r\n \"created_at\" integer DEFAULT (unixepoch()) NOT NULL,\r\n FOREIGN KEY (\"user_id\") REFERENCES \"users\"(\"id\") ON UPDATE no action ON DELETE cascade,\r\n FOREIGN KEY (\"event_id\") REFERENCES \"events\"(\"id\") ON UPDATE no action ON DELETE cascade\r\n )`,\r\n `CREATE UNIQUE INDEX IF NOT EXISTS \"idx_bookmarks_user_event\" ON \"bookmarks\" (\"user_id\", \"event_id\")`,\r\n];\r\n\r\n/**\r\n * Ensure all required tables exist in the D1 database.\r\n *\r\n * Uses CREATE TABLE / INDEX IF NOT EXISTS so it's safe to call on every\r\n * boot. Only executes the full schema if the `user` table is missing\r\n * (i.e. fresh database).\r\n */\r\nexport async function ensureDatabase(d1: D1Database): Promise<void> {\r\n // Check if database is fresh (no tables yet)\r\n const { results } = await d1\r\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='user'\")\r\n .all<{ name: string }>();\r\n\r\n if (results.length === 0) {\r\n // Fresh database — create all tables\r\n for (const sql of CREATE_STATEMENTS) {\r\n await d1.prepare(sql).run();\r\n }\r\n }\r\n\r\n // Always run patches (safe for both fresh and existing databases)\r\n for (const sql of PATCH_STATEMENTS) {\r\n try {\r\n await d1.prepare(sql).run();\r\n } catch (err: any) {\r\n // Ignore \"duplicate column\" or \"no such column\" errors from ALTER TABLE\r\n if (\r\n err?.message?.includes(\"duplicate column\") ||\r\n (err?.message?.includes(\"no such column\") &&\r\n err?.message?.includes(\"is_major\"))\r\n ) {\r\n continue;\r\n }\r\n throw err;\r\n }\r\n }\r\n}\r\n","/**\r\n * Leapify — Fullstack npm module for DLSU CSO LEAP event websites.\r\n *\r\n * This file is the **server-side entry point**. It exports createLeapify(),\r\n * a Cloudflare Workers-compatible handler. Mount it in your server layer:\r\n *\r\n * // Next.js API route / SvelteKit endpoint / Cloudflare Pages Function / worker.ts\r\n * import { createLeapify } from 'leapify'\r\n *\r\n * export default createLeapify({\r\n * allowedOrigins: ['https://yourleapsite.com'],\r\n * })\r\n *\r\n * createLeapify() returns { fetch, scheduled, queue } — shaped for CF Workers.\r\n * See wrangler.toml.example for required bindings (D1, KV, Queues, Crons).\r\n *\r\n * For browser / client-component usage, import from 'leapify/client' instead:\r\n *\r\n * import { createLeapifyClient, getLeapifyToken } from 'leapify/client'\r\n * import type { LeapEvent } from 'leapify/types'\r\n */\r\n\r\nif (typeof document !== \"undefined\") {\r\n throw new Error(\r\n \"[leapify] This module is server-only (Cloudflare Workers / server runtimes). \" +\r\n \"Do not import it in browser or client-component code. \" +\r\n \"Use 'leapify/client' for browser-safe typed API utilities.\",\r\n );\r\n}\r\n\r\nimport { createApp, type LeapifyAppOptions } from \"./app\";\r\nimport { createQueueHandler } from \"./queues/handlers\";\r\nimport { batchRelease } from \"./cron/batch-release\";\r\nimport { reconcileSlots } from \"./cron/reconcile-slots\";\r\nimport { reminderEmails } from \"./cron/reminder-emails\";\r\nimport { renewWatches } from \"./cron/renew-watches\";\r\nimport { ensureDatabase } from \"./db/migrate\";\r\nimport type { LeapifyBindings } from \"./types\";\r\nimport type { LeapifyJob } from \"./queues/jobs\";\r\n\r\nexport interface LeapifyOptions extends LeapifyAppOptions {\r\n /**\r\n * Automatically ensure all required tables exist on first request.\r\n * Safe for production — idempotent (only runs on fresh databases).\r\n * @default false\r\n */\r\n autoMigrate?: boolean;\r\n}\r\n\r\n/**\r\n * Primary factory function. Returns a Cloudflare Workers-compatible export object.\r\n *\r\n * @example\r\n * // worker.ts — the entire consumer worker implementation\r\n * import { createLeapify } from 'leapify'\r\n * export default createLeapify({ allowedOrigins: ['https://yourdomain.com'] })\r\n */\r\nexport function createLeapify(options: LeapifyOptions = {}) {\r\n const app = createApp(options);\r\n let loggedEmailConfig = false;\r\n let migrated = false;\r\n\r\n return {\r\n /**\r\n * Cloudflare Workers fetch handler.\r\n * Handles all HTTP requests routed through Leapify.\r\n */\r\n async fetch(\r\n request: Request,\r\n env: LeapifyBindings,\r\n ctx: ExecutionContext,\r\n ): Promise<Response> {\r\n if (!loggedEmailConfig) {\r\n loggedEmailConfig = true;\r\n const hasSes = !!(\r\n env.SES_REGION &&\r\n env.SES_ACCESS_KEY_ID &&\r\n env.SES_SECRET_ACCESS_KEY\r\n );\r\n const hasResend = !!env.RESEND_API_KEY;\r\n\r\n if (!hasSes && !hasResend) {\r\n console.warn(\r\n \"[leapify] Email functionality is DISABLED (no SES or Resend credentials).\",\r\n );\r\n }\r\n }\r\n\r\n if (options.autoMigrate && !migrated) {\r\n migrated = true;\r\n try {\r\n await ensureDatabase(env.DB);\r\n } catch (err) {\r\n console.error(\"[leapify] Auto-migration failed:\", err);\r\n }\r\n }\r\n\r\n return app.fetch(request, env, ctx);\r\n },\r\n\r\n // Cloudflare Workers scheduled handler. Routes cron triggers by schedule string.\r\n // Cron schedule (configured in wrangler.toml):\r\n // \"* * * * *\" → batch-release\r\n // \"*/5 * * * *\" → reconcile-slots\r\n // \"0 * * * *\" → reminder-emails + lifecycle-check\r\n // \"0 0 * * *\" → renew-watches\r\n async scheduled(\r\n event: ScheduledEvent,\r\n env: LeapifyBindings,\r\n ctx: ExecutionContext,\r\n ): Promise<void> {\r\n const { cron } = event;\r\n\r\n if (cron === \"* * * * *\") await batchRelease(env);\r\n if (cron === \"*/5 * * * *\") await reconcileSlots(env);\r\n if (cron === \"0 * * * *\") {\r\n ctx.waitUntil(reminderEmails(env));\r\n }\r\n if (cron === \"0 0 * * *\") await renewWatches(env);\r\n },\r\n\r\n /**\r\n * Cloudflare Queue consumer.\r\n * Processes async jobs (emails, audit logs, snapshots, watch renewals).\r\n */\r\n async queue(\r\n batch: MessageBatch<LeapifyJob>,\r\n env: LeapifyBindings,\r\n ): Promise<void> {\r\n const handler = createQueueHandler(env);\r\n return handler(batch);\r\n },\r\n };\r\n}\r\n\r\n// Re-exports\r\n\r\nexport { createQueueHandler } from \"./queues/handlers\";\r\nexport { createDb } from \"./db\";\r\nexport { ensureDatabase } from \"./db/migrate\";\r\nexport { createWorkerHandler, type CreateWorkerHandlerOptions } from \"./worker-handler\";\r\n\r\nexport type {\r\n LeapifyBindings,\r\n LeapifyEnv,\r\n SiteConfigKey,\r\n SiteConfigMap,\r\n} from \"./types\";\r\nexport type { LeapifyUser } from \"./auth/types\";\r\nexport type { LeapifyDb } from \"./db\";\r\nexport type { LeapifyJob } from \"./queues/jobs\";\r\nexport type { SlotInfo } from \"./services/slots\";\r\n\r\n/**\r\n * Runtime config shape injected into HTML pages by the worker.\r\n * Use on the server side to build the config object.\r\n */\r\nexport interface RuntimeConfig {\r\n production: boolean;\r\n leapifyApiUrl: string;\r\n turnstileSiteKey?: string;\r\n}\r\n\r\n/**\r\n * Build the runtime config from Worker bindings.\r\n * Used by createWorkerHandler() and standalone workers.\r\n */\r\nexport function getRuntimeConfig(env: LeapifyBindings): RuntimeConfig {\r\n return {\r\n production: true,\r\n leapifyApiUrl: \"\",\r\n ...(env.TURNSTILE_SITE_KEY ? { turnstileSiteKey: env.TURNSTILE_SITE_KEY } : {}),\r\n };\r\n}\r\n\r\n/**\r\n * Inject runtime config into an HTML string as a window.__CONFIG__ script tag.\r\n * Used by createWorkerHandler() and standalone workers.\r\n */\r\nexport function injectConfig(html: string, config: RuntimeConfig): string {\r\n const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)};</script>`;\r\n return html.replace(\"</head>\", `${configScript}</head>`);\r\n}\r\n\r\n// Schema re-exports for consumers running drizzle-kit migrations\r\nexport * from \"./db/schema\";\r\n","/**\r\n * Leapify — Standalone Cloudflare Worker entry point.\r\n *\r\n * ─────────────────────────────────────────────────────────────────────\r\n * MODE 1: Standalone Server (this file)\r\n * ─────────────────────────────────────────────────────────────────────\r\n * Deploy Leapify directly as a Cloudflare Worker — no frontend code\r\n * required. Configure wrangler.toml with your bindings + secrets and run:\r\n *\r\n * wrangler deploy\r\n *\r\n * CORS is controlled via the ALLOWED_ORIGINS Worker secret:\r\n * wrangler secret put ALLOWED_ORIGINS\r\n * # value: \"https://yoursite.com,https://www.yoursite.com\"\r\n *\r\n * ─────────────────────────────────────────────────────────────────────\r\n * MODE 2: npm module (src/index.ts)\r\n * ─────────────────────────────────────────────────────────────────────\r\n * Install leapify into your own project and mount it:\r\n *\r\n * npm install leapify\r\n *\r\n * import { createLeapify } from 'leapify'\r\n * export default createLeapify({ allowedOrigins: ['https://yoursite.com'] })\r\n *\r\n * See README.md for full mode comparison.\r\n */\r\n\r\nimport { createLeapify } from './index'\r\nimport type { LeapifyBindings } from './types'\r\nimport type { LeapifyJob } from './queues/jobs'\r\n\r\n/**\r\n * Parse ALLOWED_ORIGINS env var.\r\n * Falls back to [] which blocks all cross-origin requests (safest default).\r\n * Set to \"*\" to allow all origins (only appropriate for public-read APIs\r\n * without sensitive user data).\r\n */\r\nfunction parseAllowedOrigins(raw: string | undefined): string[] {\r\n if (!raw) return []\r\n return raw\r\n .split(',')\r\n .map((s) => s.trim())\r\n .filter(Boolean)\r\n}\r\n\r\nlet app: ReturnType<typeof createLeapify> | null = null;\r\n\r\n/**\r\n * Singleton Leapify app instance.\r\n * Ensures that startup logic (like email warnings) only runs once per Worker lifecycle.\r\n */\r\nfunction getApp(env: LeapifyBindings): NonNullable<typeof app> {\r\n if (!app) {\r\n const allowedOrigins = parseAllowedOrigins(env.ALLOWED_ORIGINS);\r\n app = createLeapify({ allowedOrigins });\r\n }\r\n return app;\r\n}\r\n\r\nconst leapify = {\r\n fetch(request: Request, env: LeapifyBindings, ctx: ExecutionContext): Promise<Response> {\r\n return getApp(env).fetch(request, env, ctx);\r\n },\r\n\r\n async scheduled(event: ScheduledEvent, env: LeapifyBindings, ctx: ExecutionContext): Promise<void> {\r\n return getApp(env).scheduled(event, env, ctx);\r\n },\r\n\r\n async queue(batch: MessageBatch<LeapifyJob>, env: LeapifyBindings): Promise<void> {\r\n return getApp(env).queue(batch, env);\r\n },\r\n};\r\n\r\nexport default leapify;\r\n"]}
|