@access-dlsu/leapify 0.260531.1 → 0.260601.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/auth.d.ts +2 -2
- package/dist/client/auth.d.ts +41 -41
- package/dist/client/index.cjs +2 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/index.cjs +126 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +124 -59
- package/dist/index.js.map +1 -1
- package/dist/lib/middleware/cors.d.ts.map +1 -1
- package/dist/lib/middleware/referer-guard.d.ts +1 -1
- package/dist/lib/middleware/referer-guard.d.ts.map +1 -1
- package/dist/routes/site-config.d.ts +2 -2
- package/dist/routes/site-config.d.ts.map +1 -1
- package/dist/worker.js +278 -213
- package/dist/worker.js.map +1 -1
- package/package.json +155 -155
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/auth.ts","../../src/client/turnstile.ts","../../src/client/session.ts","../../src/client/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Better Auth client helper for Leapify API consumers.\r\n *\r\n * This module is **browser-safe** — no Cloudflare, Drizzle, or Hono deps.\r\n * It wraps Better Auth's client SDK with the bearer plugin so that tokens\r\n * can be stored and retrieved as plain strings (no cookie dependency on\r\n * the consumer's frontend).\r\n *\r\n * @example\r\n * // lib/auth.ts (frontend)\r\n * import { createLeapifyAuthClient, signInWithGoogleRedirect } from 'leapify/client'\r\n *\r\n * export const authClient = createLeapifyAuthClient(process.env.NEXT_PUBLIC_API_URL!)\r\n *\r\n * // Redirect-based Google sign-in:\r\n * await signInWithGoogleRedirect(authClient, '/dashboard')\r\n */\r\n\r\nimport { createAuthClient } from 'better-auth/client'\r\n\r\nconst AUTH_TOKEN_KEY = 'better-auth.session_token'\r\n\r\n/**\r\n * Create a Better Auth client bound to the Leapify Worker URL.\r\n *\r\n * It uses the 'Bearer' auth type to send the stored session token\r\n * in the Authorization header.\r\n */\r\nexport function createLeapifyAuthClient(baseUrl: string) {\r\n return createAuthClient({\r\n baseURL: baseUrl,\r\n fetchOptions: {\r\n auth: {\r\n type: 'Bearer',\r\n token: () => {\r\n if (typeof window !== 'undefined') {\r\n return localStorage.getItem(AUTH_TOKEN_KEY) || ''\r\n }\r\n return ''\r\n }\r\n }\r\n }\r\n })\r\n}\r\n\r\nexport type LeapifyAuthClient = ReturnType<typeof createLeapifyAuthClient>\r\n\r\n/**\r\n * Sign in with Google via OAuth redirect flow.\r\n *\r\n * Redirects the browser to Google's OAuth page. After authentication,\r\n * Google redirects back to the Better Auth callback endpoint, which\r\n * creates a session and redirects to `callbackURL`.\r\n *\r\n * Call `syncCookieSessionToStorage()` on app init to restore the\r\n * session from the cookie after a redirect-based sign-in.\r\n *\r\n * @param authClient - Client created by createLeapifyAuthClient\r\n * @param callbackURL - Path or URL to redirect to after successful auth (e.g. '/dashboard')\r\n *\r\n * @example\r\n * import { signInWithGoogleRedirect } from 'leapify/client'\r\n *\r\n * document.getElementById('google-btn').onclick = () => {\r\n * signInWithGoogleRedirect(authClient, '/dashboard')\r\n * }\r\n */\r\nexport async function signInWithGoogleRedirect(\r\n authClient: LeapifyAuthClient,\r\n callbackURL: string,\r\n): Promise<void> {\r\n await authClient.signIn.social({\r\n provider: 'google',\r\n callbackURL,\r\n })\r\n}\r\n\r\n/**\r\n * Sync a cookie-based Better Auth session into localStorage.\r\n *\r\n * After an OAuth redirect flow, Better Auth stores the session in an\r\n * HTTP-only cookie. This function reads that session via `getSession()`\r\n * and stores the token in localStorage so that subsequent API calls\r\n * using the Bearer token work correctly.\r\n *\r\n * Call this once on app initialization, before `initializeSession()`.\r\n *\r\n * @param authClient - Client created by createLeapifyAuthClient\r\n *\r\n * @example\r\n * import { syncCookieSessionToStorage, initializeSession } from 'leapify/client'\r\n *\r\n * // On app mount:\r\n * await syncCookieSessionToStorage(authClient)\r\n * const user = await initializeSession(API_URL, getToken)\r\n */\r\nexport async function syncCookieSessionToStorage(\r\n authClient: LeapifyAuthClient,\r\n): Promise<void> {\r\n try {\r\n const result = await authClient.getSession()\r\n const data = result?.data as { session?: { token?: string } } | undefined\r\n const token = data?.session?.token\r\n if (token) {\r\n localStorage.setItem(AUTH_TOKEN_KEY, token)\r\n }\r\n } catch {\r\n // No cookie session — user is a guest.\r\n }\r\n}\r\n\r\n/**\r\n * Get the current bearer token from storage, or null for guests.\r\n * Pass this to `createLeapifyClient` as the `getToken` option.\r\n *\r\n * @example\r\n * import { createLeapifyClient } from 'leapify/client'\r\n * import { createLeapifyAuthClient, getLeapifyToken } from 'leapify/client'\r\n *\r\n * const authClient = createLeapifyAuthClient(API_URL)\r\n * const api = createLeapifyClient(API_URL, () => getLeapifyToken(authClient))\r\n */\r\nexport async function getLeapifyToken(\r\n // @ts-ignore - Kept for backwards compatibility with previous signature\r\n authClient?: LeapifyAuthClient,\r\n): Promise<string | null> {\r\n if (typeof window !== 'undefined') {\r\n return localStorage.getItem(AUTH_TOKEN_KEY)\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * Sign out the current user.\r\n */\r\nexport async function signOut(authClient: LeapifyAuthClient) {\r\n const result = await authClient.signOut()\r\n if (typeof window !== 'undefined') {\r\n localStorage.removeItem(AUTH_TOKEN_KEY)\r\n }\r\n return result\r\n}\r\n","declare global {\r\n interface Window {\r\n turnstile: {\r\n render: (\r\n container: string | HTMLElement,\r\n opts: { sitekey: string; callback: (token: string) => void },\r\n ) => string;\r\n remove: (widgetId: string) => void;\r\n };\r\n }\r\n}\r\n\r\nconst TURNSTILE_VERIFY_PATH = \"/.well-known/leapify/turnstile/verify\";\r\n\r\nfunction getTurnstileSiteKey(): string | undefined {\r\n const config = (window as unknown as Record<string, unknown>).__CONFIG__ as\r\n | { turnstileSiteKey?: string }\r\n | undefined;\r\n return config?.turnstileSiteKey;\r\n}\r\n\r\nfunction loadTurnstileScript(): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n if (typeof window.turnstile !== \"undefined\") {\r\n resolve();\r\n return;\r\n }\r\n const script = document.createElement(\"script\");\r\n script.src =\r\n \"https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit\";\r\n script.async = true;\r\n script.defer = true;\r\n script.onload = () => resolve();\r\n script.onerror = () => reject(new Error(\"Failed to load Turnstile script\"));\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\nfunction executeTurnstile(siteKey: string): Promise<string> {\r\n let widgetId: string | undefined;\r\n\r\n const cleanup = () => {\r\n if (widgetId && typeof window.turnstile?.remove === \"function\") {\r\n window.turnstile.remove(widgetId);\r\n }\r\n const el = document.getElementById(\"leapify-turnstile-container\");\r\n el?.remove();\r\n };\r\n\r\n return new Promise((resolve) => {\r\n const container = document.createElement(\"div\");\r\n container.id = \"leapify-turnstile-container\";\r\n container.style.display = \"none\";\r\n document.body.appendChild(container);\r\n\r\n // Timeout guard — Turnstile iframe can hang if postMessage origin mismatch\r\n // or other widget issues prevent the callback from firing.\r\n // After 3s, continue without the cookie; the server-side auth middleware\r\n // will handle verified sessions via the Authorization header instead.\r\n const timer = setTimeout(() => {\r\n cleanup();\r\n resolve(\"\");\r\n }, 3_000);\r\n\r\n widgetId = window.turnstile.render(`#${container.id}`, {\r\n sitekey: siteKey,\r\n callback: (token: string) => {\r\n clearTimeout(timer);\r\n cleanup();\r\n resolve(token);\r\n },\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Solve a Turnstile challenge and obtain a signed cookie from the backend.\r\n *\r\n * Loads the Turnstile script (if not already loaded), executes an invisible\r\n * challenge, and posts the token to the backend verify endpoint. The server\r\n * sets a signed cookie that bypasses Turnstile for subsequent requests.\r\n *\r\n * Call once on app initialization before any API requests.\r\n *\r\n * @param baseUrl - The Leapify Worker URL. If omitted, uses the current origin.\r\n * @param siteKey - Turnstile site key. If omitted, reads from window.__CONFIG__.\r\n * @returns `true` if the challenge was solved and cookie was set.\r\n */\r\nexport async function solveTurnstileChallenge(\r\n baseUrl?: string,\r\n siteKey?: string,\r\n): Promise<boolean> {\r\n siteKey = siteKey ?? getTurnstileSiteKey();\r\n if (!siteKey) return false;\r\n\r\n const base = baseUrl?.replace(/\\/$/, \"\") ?? \"\";\r\n\r\n try {\r\n await loadTurnstileScript();\r\n const token = await executeTurnstile(siteKey);\r\n\r\n if (!token) return false;\r\n\r\n const res = await fetch(`${base}${TURNSTILE_VERIFY_PATH}`, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ token }),\r\n credentials: \"include\",\r\n });\r\n\r\n return res.ok;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","/**\r\n * Browser-safe session initialization helper.\r\n *\r\n * Checks for an existing session token and fetches the user profile.\r\n * Callers should run solveTurnstileChallenge() separately if the server\r\n * enforces Turnstile for unauthenticated requests.\r\n *\r\n * Import from 'leapify/client' — no Cloudflare, Drizzle, or Hono deps.\r\n *\r\n * @example\r\n * import { initializeSession, createLeapifyClient } from 'leapify/client'\r\n *\r\n * const user = await initializeSession(\r\n * 'https://api.leap.yourdomain.com',\r\n * () => getLeapifyToken(),\r\n * )\r\n * if (user) {\r\n * console.log(`Welcome ${user.name} (${user.role})`)\r\n * }\r\n */\r\n\r\nimport type { UserProfile } from \"./types\";\r\n\r\n/**\r\n * Initialize a browser session: restore existing token and fetch profile.\r\n *\r\n * @param baseUrl - The Leapify Worker URL.\r\n * @param getToken - Async function returning the current session token, or null.\r\n * @returns The authenticated user profile, or null if not signed in.\r\n */\r\nexport async function initializeSession(\r\n baseUrl: string,\r\n getToken: () => Promise<string | null>,\r\n): Promise<UserProfile | null> {\r\n const token = await getToken();\r\n if (!token) return null;\r\n\r\n const base = baseUrl.replace(/\\/$/, \"\");\r\n const res = await fetch(`${base}/api/users/me`, {\r\n headers: { Authorization: `Bearer ${token}` },\r\n });\r\n\r\n if (!res.ok) return null;\r\n\r\n const body = await res.json().catch(() => ({}));\r\n return (body as { data: UserProfile | null }).data ?? null;\r\n}\r\n","/**\r\n * Leapify browser-safe API client.\r\n *\r\n * Import from 'leapify/client' — no Cloudflare, Drizzle, or Hono dependencies.\r\n *\r\n * @example\r\n * import { createLeapifyClient, createLeapifyAuthClient, getLeapifyToken } from 'leapify/client'\r\n *\r\n * const authClient = createLeapifyAuthClient(process.env.NEXT_PUBLIC_API_URL!)\r\n * const api = createLeapifyClient(\r\n * process.env.NEXT_PUBLIC_API_URL!,\r\n * () => getLeapifyToken(authClient),\r\n * )\r\n *\r\n * const events = await api.getEvents()\r\n */\r\n\r\nexport type {\r\n LeapEvent,\r\n SlotInfo,\r\n UserProfile,\r\n BookmarkEntry,\r\n Faq,\r\n Theme,\r\n Organization,\r\n SiteConfig,\r\n ToggleBookmarkResult,\r\n LeapifyErrorBody,\r\n UserRole,\r\n EventStatus,\r\n CreateEventBody,\r\n CreateFaqBody,\r\n HealthResponse,\r\n RuntimeConfig,\r\n} from \"./types\";\r\n\r\nexport {\r\n createLeapifyAuthClient,\r\n signInWithGoogleRedirect,\r\n syncCookieSessionToStorage,\r\n getLeapifyToken,\r\n signOut,\r\n} from \"./auth\";\r\nexport type { LeapifyAuthClient } from \"./auth\";\r\n\r\nexport { solveTurnstileChallenge } from \"./turnstile\";\r\nexport { initializeSession } from \"./session\";\r\n\r\n/**\r\n * Read the runtime config injected by the worker into HTML pages.\r\n * Returns null if not running in a browser or config not injected.\r\n */\r\nexport function getClientConfig(): RuntimeConfig | null {\r\n if (typeof window === \"undefined\") return null;\r\n const config = (window as unknown as Record<string, unknown>).__CONFIG__;\r\n if (!config || typeof config !== \"object\") return null;\r\n return config as RuntimeConfig;\r\n}\r\n\r\nimport type { RuntimeConfig } from \"./types\";\r\n\r\n/**\r\n * Structured error thrown by all client methods on non-2xx responses.\r\n *\r\n * @example\r\n * import { LeapifyApiError } from 'leapify/client'\r\n *\r\n * try {\r\n * await api.toggleBookmark(eventId)\r\n * } catch (err) {\r\n * if (err instanceof LeapifyApiError && err.code === 'UNAUTHORIZED') {\r\n * // redirect to sign-in\r\n * }\r\n * }\r\n */\r\nexport class LeapifyApiError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n public readonly code: string,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"LeapifyApiError\";\r\n }\r\n}\r\n\r\n// ─── Error code constants ───────────────────────────────────────────────────\r\n\r\nexport const LEAPIFY_ERROR_CODES = {\r\n UNAUTHORIZED: \"UNAUTHORIZED\",\r\n DOMAIN_RESTRICTED: \"DOMAIN_RESTRICTED\",\r\n FORBIDDEN: \"FORBIDDEN\",\r\n NOT_FOUND: \"NOT_FOUND\",\r\n CONFLICT: \"CONFLICT\",\r\n TOO_MANY_REQUESTS: \"TOO_MANY_REQUESTS\",\r\n SERVICE_UNAVAILABLE: \"SERVICE_UNAVAILABLE\",\r\n INTERNAL_ERROR: \"INTERNAL_ERROR\",\r\n} as const;\r\n\r\nexport type LeapifyErrorCode = keyof typeof LEAPIFY_ERROR_CODES;\r\n\r\n// ─── Client factory ─────────────────────────────────────────────────────────\r\n\r\nimport type {\r\n LeapEvent,\r\n SlotInfo,\r\n UserProfile,\r\n BookmarkEntry,\r\n Faq,\r\n Theme,\r\n Organization,\r\n SiteConfig,\r\n ToggleBookmarkResult,\r\n LeapifyErrorBody,\r\n CreateEventBody,\r\n CreateFaqBody,\r\n HealthResponse,\r\n} from \"./types\";\r\n\r\ntype GetTokenFn = () => Promise<string | null>;\r\n\r\nasync function buildHeaders(\r\n getToken: GetTokenFn | undefined,\r\n extra: Record<string, string> = {},\r\n): Promise<Record<string, string>> {\r\n const headers: Record<string, string> = {\r\n \"Content-Type\": \"application/json\",\r\n ...extra,\r\n };\r\n if (getToken) {\r\n const token = await getToken();\r\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\r\n }\r\n return headers;\r\n}\r\n\r\nasync function parseResponse<T>(res: Response): Promise<T> {\r\n if (res.status === 204) return undefined as T;\r\n\r\n const body = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const err = (body as LeapifyErrorBody)?.error;\r\n throw new LeapifyApiError(\r\n res.status,\r\n err?.code ?? \"UNKNOWN\",\r\n err?.message ?? res.statusText,\r\n );\r\n }\r\n\r\n return (body as { data: T }).data;\r\n}\r\n\r\n/**\r\n * Creates a typed Leapify API client bound to a base URL.\r\n *\r\n * @param baseUrl - The deployed Leapify Worker URL (e.g. `https://api.leap.yourdomain.com`).\r\n * @param getToken - Optional async function that returns a session token string,\r\n * or null for guest requests. Use `getLeapifyToken()` from this module.\r\n *\r\n * @example\r\n * // lib/api.ts\r\n * import { createLeapifyClient, getLeapifyToken } from 'leapify/client'\r\n *\r\n * export const api = createLeapifyClient(\r\n * process.env.NEXT_PUBLIC_API_URL!,\r\n * () => getLeapifyToken(),\r\n * )\r\n */\r\nexport function createLeapifyClient(baseUrl: string, getToken?: GetTokenFn) {\r\n const base = baseUrl.replace(/\\/$/, \"\");\r\n\r\n async function get<T>(path: string, init?: RequestInit): Promise<T> {\r\n const headers = await buildHeaders(getToken, init?.headers as Record<string, string>);\r\n const res = await fetch(`${base}${path}`, { ...init, method: \"GET\", headers });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function post<T>(path: string, body?: unknown): Promise<T> {\r\n const headers = await buildHeaders(getToken);\r\n const res = await fetch(`${base}${path}`, {\r\n method: \"POST\",\r\n headers,\r\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\r\n });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function postFormData<T>(path: string, formData: FormData): Promise<T> {\r\n const headers: Record<string, string> = {};\r\n if (getToken) {\r\n const token = await getToken();\r\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\r\n }\r\n const res = await fetch(`${base}${path}`, {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function patch<T>(path: string, body: unknown): Promise<T> {\r\n const headers = await buildHeaders(getToken);\r\n const res = await fetch(`${base}${path}`, {\r\n method: \"PATCH\",\r\n headers,\r\n body: JSON.stringify(body),\r\n });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function del<T>(path: string): Promise<T> {\r\n const headers = await buildHeaders(getToken);\r\n const res = await fetch(`${base}${path}`, { method: \"DELETE\", headers });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n return {\r\n // ── Site Config ────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /config\r\n * Returns site-wide configuration. Check `maintenanceMode` and\r\n * `comingSoonUntil` on app load to gate the UI appropriately.\r\n * Use `now` (server unix epoch) for timestamp comparisons.\r\n */\r\n getConfig(): Promise<SiteConfig> {\r\n return get<SiteConfig>(\"/api/config\");\r\n },\r\n\r\n /**\r\n * PATCH /api/config/:key — admin only.\r\n * Upserts a site config value. Requires admin or super_admin role.\r\n */\r\n updateConfig<K extends string>(key: K, value: unknown): Promise<{ key: K; value: unknown }> {\r\n return patch(`/api/config/${encodeURIComponent(key)}`, { value });\r\n },\r\n\r\n // ── Events ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/classes\r\n * Returns all published classes. Response is ETag-cached for 7 days.\r\n */\r\n getEvents(): Promise<LeapEvent[]> {\r\n return get<LeapEvent[]>(\"/api/classes\");\r\n },\r\n\r\n /**\r\n * GET /api/classes/admin — admin only.\r\n * Returns all classes regardless of status.\r\n */\r\n getAdminEvents(): Promise<LeapEvent[]> {\r\n return get<LeapEvent[]>(\"/api/classes/admin\");\r\n },\r\n\r\n /**\r\n * POST /api/classes/admin/publish — admin only.\r\n * Batch publish queued classes immediately or schedule them for later.\r\n */\r\n batchPublish(ids: string[], releaseAt?: number): Promise<{ updated: number }> {\r\n return post(\"/api/classes/admin/publish\", { ids, releaseAt });\r\n },\r\n\r\n /**\r\n * GET /api/classes/:slug\r\n * Returns a single published class by slug.\r\n */\r\n getEvent(slug: string): Promise<LeapEvent> {\r\n return get<LeapEvent>(`/api/classes/${encodeURIComponent(slug)}`);\r\n },\r\n\r\n /**\r\n * GET /api/classes/:slug/slots\r\n * Returns real-time slot availability. CF edge caches this for 5 seconds.\r\n * Poll every 8–10 seconds on class detail pages.\r\n */\r\n getSlots(slug: string): Promise<SlotInfo> {\r\n return get<SlotInfo>(`/api/classes/${encodeURIComponent(slug)}/slots`);\r\n },\r\n\r\n /**\r\n * POST /api/classes — admin only.\r\n * Creates a new class. Auto-generates slug from title.\r\n */\r\n createEvent(data: CreateEventBody): Promise<LeapEvent> {\r\n return post<LeapEvent>(\"/api/classes\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/classes/:slug — admin only.\r\n * Updates an existing class by slug.\r\n */\r\n updateEvent(slug: string, data: Partial<CreateEventBody>): Promise<LeapEvent> {\r\n return patch<LeapEvent>(`/api/classes/${encodeURIComponent(slug)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/classes/:slug — admin only.\r\n * Deletes a class.\r\n */\r\n deleteEvent(slug: string): Promise<void> {\r\n return del<void>(`/api/classes/${encodeURIComponent(slug)}`);\r\n },\r\n\r\n // ── Themes ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/themes\r\n * Returns all themes.\r\n */\r\n getThemes(): Promise<Theme[]> {\r\n return get<Theme[]>(\"/api/themes\");\r\n },\r\n\r\n /**\r\n * POST /api/themes — admin only.\r\n */\r\n createTheme(data: Omit<Theme, \"id\" | \"createdAt\">): Promise<Theme> {\r\n return post<Theme>(\"/api/themes\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/themes/:id — admin only.\r\n */\r\n updateTheme(id: string, data: Partial<Omit<Theme, \"id\" | \"createdAt\">>): Promise<Theme> {\r\n return patch<Theme>(`/api/themes/${encodeURIComponent(id)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/themes/:id — admin only.\r\n */\r\n deleteTheme(id: string): Promise<void> {\r\n return del<void>(`/api/themes/${encodeURIComponent(id)}`);\r\n },\r\n\r\n // ── Organizations ──────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/organizations\r\n * Returns all organizations.\r\n */\r\n getOrganizations(): Promise<Organization[]> {\r\n return get<Organization[]>(\"/api/organizations\");\r\n },\r\n\r\n /**\r\n * POST /api/organizations — admin only.\r\n */\r\n createOrganization(data: Omit<Organization, \"id\" | \"createdAt\">): Promise<Organization> {\r\n return post<Organization>(\"/api/organizations\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/organizations/:id — admin only.\r\n */\r\n updateOrganization(id: string, data: Partial<Omit<Organization, \"id\" | \"createdAt\">>): Promise<Organization> {\r\n return patch<Organization>(`/api/organizations/${encodeURIComponent(id)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/organizations/:id — admin only.\r\n */\r\n deleteOrganization(id: string): Promise<void> {\r\n return del<void>(`/api/organizations/${encodeURIComponent(id)}`);\r\n },\r\n\r\n // ── Users ──────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/users/me\r\n * Returns the authenticated user's profile, or null for guests.\r\n * Use `profile.role` to gate admin UI.\r\n */\r\n getMe(): Promise<UserProfile | null> {\r\n return get<UserProfile | null>(\"/api/users/me\");\r\n },\r\n\r\n // ── Admin: User Management ────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/users — admin only.\r\n * Returns all registered users.\r\n */\r\n getUsers(): Promise<UserProfile[]> {\r\n return get<UserProfile[]>(\"/api/users\");\r\n },\r\n\r\n /**\r\n * PATCH /api/users/:id/role — admin only.\r\n * Changes a user's role.\r\n */\r\n updateUserRole(id: string, role: string): Promise<UserProfile> {\r\n return patch<UserProfile>(`/api/users/${encodeURIComponent(id)}/role`, { role });\r\n },\r\n\r\n /**\r\n * POST /api/users/by-email — admin only.\r\n * Finds or creates a user by email and sets their role.\r\n */\r\n upsertUserByEmail(email: string, role: string): Promise<UserProfile> {\r\n return post<UserProfile>(\"/api/users/by-email\", { email, role });\r\n },\r\n\r\n // ── Bookmarks ──────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/users/me/bookmarks\r\n * Returns the authenticated user's bookmarked events.\r\n * Returns an empty array for unauthenticated users.\r\n */\r\n getBookmarks(): Promise<BookmarkEntry[]> {\r\n return get<BookmarkEntry[]>(\"/api/users/me/bookmarks\");\r\n },\r\n\r\n /**\r\n * POST /api/users/me/bookmarks/:eventId\r\n * Toggles a bookmark on/off. Requires authentication.\r\n * Returns `{ bookmarked: true }` (201) on add, `{ bookmarked: false }` (200) on remove.\r\n */\r\n toggleBookmark(eventId: string): Promise<ToggleBookmarkResult> {\r\n return post<ToggleBookmarkResult>(\r\n `/api/users/me/bookmarks/${encodeURIComponent(eventId)}`,\r\n );\r\n },\r\n\r\n /**\r\n * DELETE /api/users/me/bookmarks/:eventId\r\n * Removes a bookmark. Requires authentication.\r\n */\r\n deleteBookmark(eventId: string): Promise<ToggleBookmarkResult> {\r\n return del<ToggleBookmarkResult>(\r\n `/api/users/me/bookmarks/${encodeURIComponent(eventId)}`,\r\n );\r\n },\r\n\r\n // ── FAQs ───────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/faqs\r\n * Returns all active FAQs. Cached in KV for 10 minutes.\r\n * The `answer` field is markdown — render with a markdown library.\r\n */\r\n getFaqs(): Promise<Faq[]> {\r\n return get<Faq[]>(\"/api/faqs\");\r\n },\r\n\r\n /**\r\n * POST /api/faqs — admin only.\r\n * Creates a new FAQ item.\r\n */\r\n createFaq(data: CreateFaqBody): Promise<Faq> {\r\n return post<Faq>(\"/api/faqs\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/faqs/:id — admin only.\r\n * Updates an existing FAQ item.\r\n */\r\n updateFaq(id: string, data: Partial<CreateFaqBody>): Promise<Faq> {\r\n return patch<Faq>(`/api/faqs/${encodeURIComponent(id)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/faqs/:id — admin only.\r\n * Soft-deletes a FAQ (sets isActive: false).\r\n */\r\n deleteFaq(id: string): Promise<{ deleted: boolean }> {\r\n return del<{ deleted: boolean }>(`/api/faqs/${encodeURIComponent(id)}`);\r\n },\r\n\r\n // ── Uploads ────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * POST /api/uploads/images — admin only.\r\n * Uploads an image file to R2. Accepts multipart/form-data.\r\n * Returns the public URL, storage key, size, and content type.\r\n */\r\n uploadImage(file: File | Blob): Promise<{\r\n url: string;\r\n key: string;\r\n size: number;\r\n contentType: string;\r\n }> {\r\n const formData = new FormData();\r\n formData.append(\"file\", file);\r\n return postFormData(\"/api/uploads/images\", formData);\r\n },\r\n\r\n // ── Health ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /health\r\n * Public health check. Returns provider availability status.\r\n */\r\n healthCheck(): Promise<HealthResponse> {\r\n return get<HealthResponse>(\"/health\");\r\n },\r\n };\r\n}\r\n\r\nexport type LeapifyClient = ReturnType<typeof createLeapifyClient>;\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/auth.ts","../../src/client/turnstile.ts","../../src/client/session.ts","../../src/client/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Better Auth client helper for Leapify API consumers.\r\n *\r\n * This module is **browser-safe** — no Cloudflare, Drizzle, or Hono deps.\r\n * It wraps Better Auth's client SDK with the bearer plugin so that tokens\r\n * can be stored and retrieved as plain strings (no cookie dependency on\r\n * the consumer's frontend).\r\n *\r\n * @example\r\n * // lib/auth.ts (frontend)\r\n * import { createLeapifyAuthClient, signInWithGoogleRedirect } from 'leapify/client'\r\n *\r\n * export const authClient = createLeapifyAuthClient(process.env.NEXT_PUBLIC_API_URL!)\r\n *\r\n * // Redirect-based Google sign-in:\r\n * await signInWithGoogleRedirect(authClient, '/dashboard')\r\n */\r\n\r\nimport { createAuthClient } from 'better-auth/client'\r\n\r\nconst AUTH_TOKEN_KEY = 'better-auth.session_token'\r\n\r\n/**\r\n * Create a Better Auth client bound to the Leapify Worker URL.\r\n *\r\n * It uses the 'Bearer' auth type to send the stored session token\r\n * in the Authorization header.\r\n */\r\nexport function createLeapifyAuthClient(baseUrl: string) {\r\n return createAuthClient({\r\n baseURL: baseUrl,\r\n fetchOptions: {\r\n auth: {\r\n type: 'Bearer',\r\n token: () => {\r\n if (typeof window !== 'undefined') {\r\n return localStorage.getItem(AUTH_TOKEN_KEY) || ''\r\n }\r\n return ''\r\n }\r\n }\r\n }\r\n })\r\n}\r\n\r\nexport type LeapifyAuthClient = ReturnType<typeof createLeapifyAuthClient>\r\n\r\n/**\r\n * Sign in with Google via OAuth redirect flow.\r\n *\r\n * Redirects the browser to Google's OAuth page. After authentication,\r\n * Google redirects back to the Better Auth callback endpoint, which\r\n * creates a session and redirects to `callbackURL`.\r\n *\r\n * Call `syncCookieSessionToStorage()` on app init to restore the\r\n * session from the cookie after a redirect-based sign-in.\r\n *\r\n * @param authClient - Client created by createLeapifyAuthClient\r\n * @param callbackURL - Path or URL to redirect to after successful auth (e.g. '/dashboard')\r\n *\r\n * @example\r\n * import { signInWithGoogleRedirect } from 'leapify/client'\r\n *\r\n * document.getElementById('google-btn').onclick = () => {\r\n * signInWithGoogleRedirect(authClient, '/dashboard')\r\n * }\r\n */\r\nexport async function signInWithGoogleRedirect(\r\n authClient: LeapifyAuthClient,\r\n callbackURL: string,\r\n): Promise<void> {\r\n await authClient.signIn.social({\r\n provider: 'google',\r\n callbackURL,\r\n })\r\n}\r\n\r\n/**\r\n * Sync a cookie-based Better Auth session into localStorage.\r\n *\r\n * After an OAuth redirect flow, Better Auth stores the session in an\r\n * HTTP-only cookie. This function reads that session via `getSession()`\r\n * and stores the token in localStorage so that subsequent API calls\r\n * using the Bearer token work correctly.\r\n *\r\n * Call this once on app initialization, before `initializeSession()`.\r\n *\r\n * @param authClient - Client created by createLeapifyAuthClient\r\n *\r\n * @example\r\n * import { syncCookieSessionToStorage, initializeSession } from 'leapify/client'\r\n *\r\n * // On app mount:\r\n * await syncCookieSessionToStorage(authClient)\r\n * const user = await initializeSession(API_URL, getToken)\r\n */\r\nexport async function syncCookieSessionToStorage(\r\n authClient: LeapifyAuthClient,\r\n): Promise<void> {\r\n try {\r\n const result = await authClient.getSession()\r\n const data = result?.data as { session?: { token?: string } } | undefined\r\n const token = data?.session?.token\r\n if (token) {\r\n localStorage.setItem(AUTH_TOKEN_KEY, token)\r\n }\r\n } catch {\r\n // No cookie session — user is a guest.\r\n }\r\n}\r\n\r\n/**\r\n * Get the current bearer token from storage, or null for guests.\r\n * Pass this to `createLeapifyClient` as the `getToken` option.\r\n *\r\n * @example\r\n * import { createLeapifyClient } from 'leapify/client'\r\n * import { createLeapifyAuthClient, getLeapifyToken } from 'leapify/client'\r\n *\r\n * const authClient = createLeapifyAuthClient(API_URL)\r\n * const api = createLeapifyClient(API_URL, () => getLeapifyToken(authClient))\r\n */\r\nexport async function getLeapifyToken(\r\n // @ts-ignore - Kept for backwards compatibility with previous signature\r\n authClient?: LeapifyAuthClient,\r\n): Promise<string | null> {\r\n if (typeof window !== 'undefined') {\r\n return localStorage.getItem(AUTH_TOKEN_KEY)\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * Sign out the current user.\r\n */\r\nexport async function signOut(authClient: LeapifyAuthClient) {\r\n const result = await authClient.signOut()\r\n if (typeof window !== 'undefined') {\r\n localStorage.removeItem(AUTH_TOKEN_KEY)\r\n }\r\n return result\r\n}\r\n","declare global {\r\n interface Window {\r\n turnstile: {\r\n render: (\r\n container: string | HTMLElement,\r\n opts: { sitekey: string; callback: (token: string) => void },\r\n ) => string;\r\n remove: (widgetId: string) => void;\r\n };\r\n }\r\n}\r\n\r\nconst TURNSTILE_VERIFY_PATH = \"/.well-known/leapify/turnstile/verify\";\r\n\r\nfunction getTurnstileSiteKey(): string | undefined {\r\n const config = (window as unknown as Record<string, unknown>).__CONFIG__ as\r\n | { turnstileSiteKey?: string }\r\n | undefined;\r\n return config?.turnstileSiteKey;\r\n}\r\n\r\nfunction loadTurnstileScript(): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n if (typeof window.turnstile !== \"undefined\") {\r\n resolve();\r\n return;\r\n }\r\n const script = document.createElement(\"script\");\r\n script.src =\r\n \"https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit\";\r\n script.async = true;\r\n script.defer = true;\r\n script.onload = () => resolve();\r\n script.onerror = () => reject(new Error(\"Failed to load Turnstile script\"));\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\nfunction executeTurnstile(siteKey: string): Promise<string> {\r\n let widgetId: string | undefined;\r\n\r\n const cleanup = () => {\r\n if (widgetId && typeof window.turnstile?.remove === \"function\") {\r\n window.turnstile.remove(widgetId);\r\n }\r\n const el = document.getElementById(\"leapify-turnstile-container\");\r\n el?.remove();\r\n };\r\n\r\n return new Promise((resolve) => {\r\n const container = document.createElement(\"div\");\r\n container.id = \"leapify-turnstile-container\";\r\n container.style.display = \"none\";\r\n document.body.appendChild(container);\r\n\r\n // Timeout guard — Turnstile iframe can hang if postMessage origin mismatch\r\n // or other widget issues prevent the callback from firing.\r\n // After 3s, continue without the cookie; the server-side auth middleware\r\n // will handle verified sessions via the Authorization header instead.\r\n const timer = setTimeout(() => {\r\n cleanup();\r\n resolve(\"\");\r\n }, 3_000);\r\n\r\n widgetId = window.turnstile.render(`#${container.id}`, {\r\n sitekey: siteKey,\r\n callback: (token: string) => {\r\n clearTimeout(timer);\r\n cleanup();\r\n resolve(token);\r\n },\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Solve a Turnstile challenge and obtain a signed cookie from the backend.\r\n *\r\n * Loads the Turnstile script (if not already loaded), executes an invisible\r\n * challenge, and posts the token to the backend verify endpoint. The server\r\n * sets a signed cookie that bypasses Turnstile for subsequent requests.\r\n *\r\n * Call once on app initialization before any API requests.\r\n *\r\n * @param baseUrl - The Leapify Worker URL. If omitted, uses the current origin.\r\n * @param siteKey - Turnstile site key. If omitted, reads from window.__CONFIG__.\r\n * @returns `true` if the challenge was solved and cookie was set.\r\n */\r\nexport async function solveTurnstileChallenge(\r\n baseUrl?: string,\r\n siteKey?: string,\r\n): Promise<boolean> {\r\n siteKey = siteKey ?? getTurnstileSiteKey();\r\n if (!siteKey) return false;\r\n\r\n const base = baseUrl?.replace(/\\/$/, \"\") ?? \"\";\r\n\r\n try {\r\n await loadTurnstileScript();\r\n const token = await executeTurnstile(siteKey);\r\n\r\n if (!token) return false;\r\n\r\n const res = await fetch(`${base}${TURNSTILE_VERIFY_PATH}`, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ token }),\r\n credentials: \"include\",\r\n });\r\n\r\n return res.ok;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","/**\r\n * Browser-safe session initialization helper.\r\n *\r\n * Checks for an existing session token and fetches the user profile.\r\n * Callers should run solveTurnstileChallenge() separately if the server\r\n * enforces Turnstile for unauthenticated requests.\r\n *\r\n * Import from 'leapify/client' — no Cloudflare, Drizzle, or Hono deps.\r\n *\r\n * @example\r\n * import { initializeSession, createLeapifyClient } from 'leapify/client'\r\n *\r\n * const user = await initializeSession(\r\n * 'https://api.leap.yourdomain.com',\r\n * () => getLeapifyToken(),\r\n * )\r\n * if (user) {\r\n * console.log(`Welcome ${user.name} (${user.role})`)\r\n * }\r\n */\r\n\r\nimport type { UserProfile } from \"./types\";\r\n\r\n/**\r\n * Initialize a browser session: restore existing token and fetch profile.\r\n *\r\n * @param baseUrl - The Leapify Worker URL.\r\n * @param getToken - Async function returning the current session token, or null.\r\n * @returns The authenticated user profile, or null if not signed in.\r\n */\r\nexport async function initializeSession(\r\n baseUrl: string,\r\n getToken: () => Promise<string | null>,\r\n): Promise<UserProfile | null> {\r\n const token = await getToken();\r\n if (!token) return null;\r\n\r\n const base = baseUrl.replace(/\\/$/, \"\");\r\n const res = await fetch(`${base}/api/users/me`, {\r\n headers: { Authorization: `Bearer ${token}` },\r\n });\r\n\r\n if (!res.ok) return null;\r\n\r\n const body = await res.json().catch(() => ({}));\r\n return (body as { data: UserProfile | null }).data ?? null;\r\n}\r\n","/**\r\n * Leapify browser-safe API client.\r\n *\r\n * Import from 'leapify/client' — no Cloudflare, Drizzle, or Hono dependencies.\r\n *\r\n * @example\r\n * import { createLeapifyClient, createLeapifyAuthClient, getLeapifyToken } from 'leapify/client'\r\n *\r\n * const authClient = createLeapifyAuthClient(process.env.NEXT_PUBLIC_API_URL!)\r\n * const api = createLeapifyClient(\r\n * process.env.NEXT_PUBLIC_API_URL!,\r\n * () => getLeapifyToken(authClient),\r\n * )\r\n *\r\n * const events = await api.getEvents()\r\n */\r\n\r\nexport type {\r\n LeapEvent,\r\n SlotInfo,\r\n UserProfile,\r\n BookmarkEntry,\r\n Faq,\r\n Theme,\r\n Organization,\r\n SiteConfig,\r\n ToggleBookmarkResult,\r\n LeapifyErrorBody,\r\n UserRole,\r\n EventStatus,\r\n CreateEventBody,\r\n CreateFaqBody,\r\n HealthResponse,\r\n RuntimeConfig,\r\n} from \"./types\";\r\n\r\nexport {\r\n createLeapifyAuthClient,\r\n signInWithGoogleRedirect,\r\n syncCookieSessionToStorage,\r\n getLeapifyToken,\r\n signOut,\r\n} from \"./auth\";\r\nexport type { LeapifyAuthClient } from \"./auth\";\r\n\r\nexport { solveTurnstileChallenge } from \"./turnstile\";\r\nexport { initializeSession } from \"./session\";\r\n\r\n/**\r\n * Read the runtime config injected by the worker into HTML pages.\r\n * Returns null if not running in a browser or config not injected.\r\n */\r\nexport function getClientConfig(): RuntimeConfig | null {\r\n if (typeof window === \"undefined\") return null;\r\n const config = (window as unknown as Record<string, unknown>).__CONFIG__;\r\n if (!config || typeof config !== \"object\") return null;\r\n return config as RuntimeConfig;\r\n}\r\n\r\nimport type { RuntimeConfig } from \"./types\";\r\n\r\n/**\r\n * Structured error thrown by all client methods on non-2xx responses.\r\n *\r\n * @example\r\n * import { LeapifyApiError } from 'leapify/client'\r\n *\r\n * try {\r\n * await api.toggleBookmark(eventId)\r\n * } catch (err) {\r\n * if (err instanceof LeapifyApiError && err.code === 'UNAUTHORIZED') {\r\n * // redirect to sign-in\r\n * }\r\n * }\r\n */\r\nexport class LeapifyApiError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n public readonly code: string,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"LeapifyApiError\";\r\n }\r\n}\r\n\r\n// ─── Error code constants ───────────────────────────────────────────────────\r\n\r\nexport const LEAPIFY_ERROR_CODES = {\r\n UNAUTHORIZED: \"UNAUTHORIZED\",\r\n DOMAIN_RESTRICTED: \"DOMAIN_RESTRICTED\",\r\n FORBIDDEN: \"FORBIDDEN\",\r\n NOT_FOUND: \"NOT_FOUND\",\r\n CONFLICT: \"CONFLICT\",\r\n TOO_MANY_REQUESTS: \"TOO_MANY_REQUESTS\",\r\n SERVICE_UNAVAILABLE: \"SERVICE_UNAVAILABLE\",\r\n INTERNAL_ERROR: \"INTERNAL_ERROR\",\r\n} as const;\r\n\r\nexport type LeapifyErrorCode = keyof typeof LEAPIFY_ERROR_CODES;\r\n\r\n// ─── Client factory ─────────────────────────────────────────────────────────\r\n\r\nimport type {\r\n LeapEvent,\r\n SlotInfo,\r\n UserProfile,\r\n BookmarkEntry,\r\n Faq,\r\n Theme,\r\n Organization,\r\n SiteConfig,\r\n ToggleBookmarkResult,\r\n LeapifyErrorBody,\r\n CreateEventBody,\r\n CreateFaqBody,\r\n HealthResponse,\r\n} from \"./types\";\r\n\r\ntype GetTokenFn = () => Promise<string | null>;\r\n\r\nasync function buildHeaders(\r\n getToken: GetTokenFn | undefined,\r\n extra: Record<string, string> = {},\r\n): Promise<Record<string, string>> {\r\n const headers: Record<string, string> = {\r\n \"Content-Type\": \"application/json\",\r\n ...extra,\r\n };\r\n if (getToken) {\r\n const token = await getToken();\r\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\r\n }\r\n return headers;\r\n}\r\n\r\nasync function parseResponse<T>(res: Response): Promise<T> {\r\n if (res.status === 204) return undefined as T;\r\n\r\n const body = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const err = (body as LeapifyErrorBody)?.error;\r\n throw new LeapifyApiError(\r\n res.status,\r\n err?.code ?? \"UNKNOWN\",\r\n err?.message ?? res.statusText,\r\n );\r\n }\r\n\r\n return (body as { data: T }).data;\r\n}\r\n\r\n/**\r\n * Creates a typed Leapify API client bound to a base URL.\r\n *\r\n * @param baseUrl - The deployed Leapify Worker URL (e.g. `https://api.leap.yourdomain.com`).\r\n * @param getToken - Optional async function that returns a session token string,\r\n * or null for guest requests. Use `getLeapifyToken()` from this module.\r\n *\r\n * @example\r\n * // lib/api.ts\r\n * import { createLeapifyClient, getLeapifyToken } from 'leapify/client'\r\n *\r\n * export const api = createLeapifyClient(\r\n * process.env.NEXT_PUBLIC_API_URL!,\r\n * () => getLeapifyToken(),\r\n * )\r\n */\r\nexport function createLeapifyClient(baseUrl: string, getToken?: GetTokenFn) {\r\n const base = baseUrl.replace(/\\/$/, \"\");\r\n\r\n async function get<T>(path: string, init?: RequestInit): Promise<T> {\r\n const headers = await buildHeaders(getToken, init?.headers as Record<string, string>);\r\n const res = await fetch(`${base}${path}`, { ...init, method: \"GET\", headers });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function post<T>(path: string, body?: unknown): Promise<T> {\r\n const headers = await buildHeaders(getToken);\r\n const res = await fetch(`${base}${path}`, {\r\n method: \"POST\",\r\n headers,\r\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\r\n });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function postFormData<T>(path: string, formData: FormData): Promise<T> {\r\n const headers: Record<string, string> = {};\r\n if (getToken) {\r\n const token = await getToken();\r\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\r\n }\r\n const res = await fetch(`${base}${path}`, {\r\n method: \"POST\",\r\n headers,\r\n body: formData,\r\n });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function patch<T>(path: string, body: unknown): Promise<T> {\r\n const headers = await buildHeaders(getToken);\r\n const res = await fetch(`${base}${path}`, {\r\n method: \"PATCH\",\r\n headers,\r\n body: JSON.stringify(body),\r\n });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n async function del<T>(path: string): Promise<T> {\r\n const headers = await buildHeaders(getToken);\r\n const res = await fetch(`${base}${path}`, { method: \"DELETE\", headers });\r\n return parseResponse<T>(res);\r\n }\r\n\r\n return {\r\n // ── Site Config ────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /config\r\n * Returns site-wide configuration. Check `maintenanceMode` and\r\n * `comingSoonUntil` on app load to gate the UI appropriately.\r\n * Use `now` (server unix epoch) for timestamp comparisons.\r\n */\r\n getConfig(): Promise<SiteConfig> {\r\n return get<SiteConfig>(\"/api/config\");\r\n },\r\n\r\n /**\r\n * PATCH /api/config/:key — admin only.\r\n * Upserts a site config value. Requires admin or super_admin role.\r\n */\r\n updateConfig<K extends string>(key: K, value: unknown): Promise<{ key: K; value: unknown }> {\r\n return patch(`/api/config/${encodeURIComponent(key)}`, { value });\r\n },\r\n\r\n // ── Events ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/classes\r\n * Returns all published classes. Response is ETag-cached for 7 days.\r\n */\r\n getEvents(): Promise<LeapEvent[]> {\r\n return get<LeapEvent[]>(\"/api/classes\");\r\n },\r\n\r\n /**\r\n * GET /api/classes/admin — admin only.\r\n * Returns all classes regardless of status.\r\n */\r\n getAdminEvents(): Promise<LeapEvent[]> {\r\n return get<LeapEvent[]>(\"/api/classes/admin\");\r\n },\r\n\r\n /**\r\n * POST /api/classes/admin/publish — admin only.\r\n * Batch publish queued classes immediately or schedule them for later.\r\n */\r\n batchPublish(ids: string[], releaseAt?: number): Promise<{ updated: number }> {\r\n return post(\"/api/classes/admin/publish\", { ids, releaseAt });\r\n },\r\n\r\n /**\r\n * GET /api/classes/:slug\r\n * Returns a single published class by slug.\r\n */\r\n getEvent(slug: string): Promise<LeapEvent> {\r\n return get<LeapEvent>(`/api/classes/${encodeURIComponent(slug)}`);\r\n },\r\n\r\n /**\r\n * GET /api/classes/:slug/slots\r\n * Returns real-time slot availability. CF edge caches this for 5 seconds.\r\n * Poll every 8–10 seconds on class detail pages.\r\n */\r\n getSlots(slug: string): Promise<SlotInfo> {\r\n return get<SlotInfo>(`/api/classes/${encodeURIComponent(slug)}/slots`);\r\n },\r\n\r\n /**\r\n * POST /api/classes — admin only.\r\n * Creates a new class. Auto-generates slug from title.\r\n */\r\n createEvent(data: CreateEventBody): Promise<LeapEvent> {\r\n return post<LeapEvent>(\"/api/classes\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/classes/:slug — admin only.\r\n * Updates an existing class by slug.\r\n */\r\n updateEvent(slug: string, data: Partial<CreateEventBody>): Promise<LeapEvent> {\r\n return patch<LeapEvent>(`/api/classes/${encodeURIComponent(slug)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/classes/:slug — admin only.\r\n * Deletes a class.\r\n */\r\n deleteEvent(slug: string): Promise<void> {\r\n return del<void>(`/api/classes/${encodeURIComponent(slug)}`);\r\n },\r\n\r\n // ── Themes ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/themes\r\n * Returns all themes.\r\n */\r\n getThemes(): Promise<Theme[]> {\r\n return get<Theme[]>(\"/api/themes\");\r\n },\r\n\r\n /**\r\n * POST /api/themes — admin only.\r\n */\r\n createTheme(data: Omit<Theme, \"id\" | \"createdAt\">): Promise<Theme> {\r\n return post<Theme>(\"/api/themes\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/themes/:id — admin only.\r\n */\r\n updateTheme(id: string, data: Partial<Omit<Theme, \"id\" | \"createdAt\">>): Promise<Theme> {\r\n return patch<Theme>(`/api/themes/${encodeURIComponent(id)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/themes/:id — admin only.\r\n */\r\n deleteTheme(id: string): Promise<void> {\r\n return del<void>(`/api/themes/${encodeURIComponent(id)}`);\r\n },\r\n\r\n // ── Organizations ──────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/organizations\r\n * Returns all organizations.\r\n */\r\n getOrganizations(): Promise<Organization[]> {\r\n return get<Organization[]>(\"/api/organizations\");\r\n },\r\n\r\n /**\r\n * POST /api/organizations — admin only.\r\n */\r\n createOrganization(data: Omit<Organization, \"id\" | \"createdAt\">): Promise<Organization> {\r\n return post<Organization>(\"/api/organizations\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/organizations/:id — admin only.\r\n */\r\n updateOrganization(id: string, data: Partial<Omit<Organization, \"id\" | \"createdAt\">>): Promise<Organization> {\r\n return patch<Organization>(`/api/organizations/${encodeURIComponent(id)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/organizations/:id — admin only.\r\n */\r\n deleteOrganization(id: string): Promise<void> {\r\n return del<void>(`/api/organizations/${encodeURIComponent(id)}`);\r\n },\r\n\r\n // ── Users ──────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/users/me\r\n * Returns the authenticated user's profile, or null for guests.\r\n * Use `profile.role` to gate admin UI.\r\n */\r\n getMe(): Promise<UserProfile | null> {\r\n return get<UserProfile | null>(\"/api/users/me\");\r\n },\r\n\r\n // ── Admin: User Management ────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/users — admin only.\r\n * Returns all registered users.\r\n */\r\n getUsers(): Promise<UserProfile[]> {\r\n return get<UserProfile[]>(\"/api/users\");\r\n },\r\n\r\n /**\r\n * PATCH /api/users/:id/role — admin only.\r\n * Changes a user's role.\r\n */\r\n updateUserRole(id: string, role: string): Promise<UserProfile> {\r\n return patch<UserProfile>(`/api/users/${encodeURIComponent(id)}/role`, { role });\r\n },\r\n\r\n /**\r\n * POST /api/users/by-email — admin only.\r\n * Finds or creates a user by email and sets their role.\r\n */\r\n upsertUserByEmail(email: string, role: string): Promise<UserProfile> {\r\n return post<UserProfile>(\"/api/users/by-email\", { email, role });\r\n },\r\n\r\n // ── Bookmarks ──────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/users/me/bookmarks\r\n * Returns the authenticated user's bookmarked events.\r\n * Returns an empty array for unauthenticated users.\r\n */\r\n getBookmarks(): Promise<BookmarkEntry[]> {\r\n return get<BookmarkEntry[]>(\"/api/users/me/bookmarks\");\r\n },\r\n\r\n /**\r\n * POST /api/users/me/bookmarks/:eventId\r\n * Toggles a bookmark on/off. Requires authentication.\r\n * Returns `{ bookmarked: true }` (201) on add, `{ bookmarked: false }` (200) on remove.\r\n */\r\n toggleBookmark(eventId: string): Promise<ToggleBookmarkResult> {\r\n return post<ToggleBookmarkResult>(\r\n `/api/users/me/bookmarks/${encodeURIComponent(eventId)}`,\r\n );\r\n },\r\n\r\n /**\r\n * DELETE /api/users/me/bookmarks/:eventId\r\n * Removes a bookmark. Requires authentication.\r\n */\r\n deleteBookmark(eventId: string): Promise<ToggleBookmarkResult> {\r\n return del<ToggleBookmarkResult>(\r\n `/api/users/me/bookmarks/${encodeURIComponent(eventId)}`,\r\n );\r\n },\r\n\r\n // ── FAQs ───────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /api/faqs\r\n * Returns all active FAQs. Cached in KV for 10 minutes.\r\n * The `answer` field is markdown — render with a markdown library.\r\n */\r\n getFaqs(): Promise<Faq[]> {\r\n return get<Faq[]>(\"/api/faqs\");\r\n },\r\n\r\n /**\r\n * POST /api/faqs — admin only.\r\n * Creates a new FAQ item.\r\n */\r\n createFaq(data: CreateFaqBody): Promise<Faq> {\r\n return post<Faq>(\"/api/faqs\", data);\r\n },\r\n\r\n /**\r\n * PATCH /api/faqs/:id — admin only.\r\n * Updates an existing FAQ item.\r\n */\r\n updateFaq(id: string, data: Partial<CreateFaqBody>): Promise<Faq> {\r\n return patch<Faq>(`/api/faqs/${encodeURIComponent(id)}`, data);\r\n },\r\n\r\n /**\r\n * DELETE /api/faqs/:id — admin only.\r\n * Soft-deletes a FAQ (sets isActive: false).\r\n */\r\n deleteFaq(id: string): Promise<{ deleted: boolean }> {\r\n return del<{ deleted: boolean }>(`/api/faqs/${encodeURIComponent(id)}`);\r\n },\r\n\r\n // ── Uploads ────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * POST /api/uploads/images — admin only.\r\n * Uploads an image file to R2. Accepts multipart/form-data.\r\n * Returns the public URL, storage key, size, and content type.\r\n */\r\n uploadImage(file: File | Blob): Promise<{\r\n url: string;\r\n key: string;\r\n size: number;\r\n contentType: string;\r\n }> {\r\n const formData = new FormData();\r\n formData.append(\"file\", file);\r\n return postFormData(\"/api/uploads/images\", formData);\r\n },\r\n\r\n // ── Health ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * GET /health\r\n * Public health check. Returns provider availability status.\r\n */\r\n healthCheck(): Promise<HealthResponse> {\r\n return get<HealthResponse>(\"/health\");\r\n },\r\n };\r\n}\r\n\r\nexport type LeapifyClient = ReturnType<typeof createLeapifyClient>;\r\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -4,13 +4,13 @@ var chunkX4OB4DZ3_cjs = require('./chunk-X4OB4DZ3.cjs');
|
|
|
4
4
|
var chunkQ7SFCCGT_cjs = require('./chunk-Q7SFCCGT.cjs');
|
|
5
5
|
var hono = require('hono');
|
|
6
6
|
var cors = require('hono/cors');
|
|
7
|
+
var d1 = require('drizzle-orm/d1');
|
|
8
|
+
var sqliteCore = require('drizzle-orm/sqlite-core');
|
|
9
|
+
var drizzleOrm = require('drizzle-orm');
|
|
7
10
|
var factory = require('hono/factory');
|
|
8
11
|
var betterAuth = require('better-auth');
|
|
9
12
|
var drizzle = require('better-auth/adapters/drizzle');
|
|
10
13
|
var plugins = require('better-auth/plugins');
|
|
11
|
-
var drizzleOrm = require('drizzle-orm');
|
|
12
|
-
var d1 = require('drizzle-orm/d1');
|
|
13
|
-
var sqliteCore = require('drizzle-orm/sqlite-core');
|
|
14
14
|
var zodValidator = require('@hono/zod-validator');
|
|
15
15
|
var zod = require('zod');
|
|
16
16
|
|
|
@@ -22,6 +22,8 @@ var LeapifyError = class extends Error {
|
|
|
22
22
|
this.code = code;
|
|
23
23
|
this.name = "LeapifyError";
|
|
24
24
|
}
|
|
25
|
+
statusCode;
|
|
26
|
+
code;
|
|
25
27
|
};
|
|
26
28
|
var unauthorized = (message = "Unauthorized") => new LeapifyError(401, "UNAUTHORIZED", message);
|
|
27
29
|
var domainRestricted = () => new LeapifyError(
|
|
@@ -50,58 +52,6 @@ var errorHandler = (err, c) => {
|
|
|
50
52
|
500
|
|
51
53
|
);
|
|
52
54
|
};
|
|
53
|
-
function createCorsMiddleware(allowedOrigins) {
|
|
54
|
-
return async (c, next) => {
|
|
55
|
-
const origin = c.req.header("origin");
|
|
56
|
-
const dynamicOriginsJson = await c.env.KV.get("config:allowed_origins", "json");
|
|
57
|
-
const currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
58
|
-
if (c.req.path.startsWith("/api/uploads/images")) {
|
|
59
|
-
c.header("Access-Control-Allow-Origin", "*");
|
|
60
|
-
c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
61
|
-
if (c.req.method === "OPTIONS") {
|
|
62
|
-
return c.body(null, 204);
|
|
63
|
-
}
|
|
64
|
-
return next();
|
|
65
|
-
}
|
|
66
|
-
if (!c.req.path.startsWith("/health") && !c.req.path.startsWith("/api/auth") && !c.req.path.startsWith("/internal") && origin && !currentAllowedOrigins.includes("*") && !currentAllowedOrigins.includes(origin)) {
|
|
67
|
-
return c.json(
|
|
68
|
-
{
|
|
69
|
-
error: {
|
|
70
|
-
code: "DOMAIN_RESTRICTED",
|
|
71
|
-
message: `Origin ${origin} is not allowed`
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
403
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
const honoCors = cors.cors({
|
|
78
|
-
origin: currentAllowedOrigins,
|
|
79
|
-
allowMethods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
|
|
80
|
-
allowHeaders: ["Content-Type", "Authorization"],
|
|
81
|
-
exposeHeaders: ["ETag", "Last-Modified", "Cache-Control"],
|
|
82
|
-
maxAge: 86400,
|
|
83
|
-
credentials: true
|
|
84
|
-
});
|
|
85
|
-
return honoCors(c, next);
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
function createRefererGuard(allowedOrigins) {
|
|
89
|
-
const MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
90
|
-
const SKIP_PREFIXES = ["/health", "/internal", "/api/auth", "/.well-known"];
|
|
91
|
-
return factory.createMiddleware(async (c, next) => {
|
|
92
|
-
if (!MUTATION_METHODS.has(c.req.method)) return next();
|
|
93
|
-
if (SKIP_PREFIXES.some((p) => c.req.path.startsWith(p))) return next();
|
|
94
|
-
const dynamicOriginsJson = await c.env.KV.get("config:allowed_origins", "json");
|
|
95
|
-
const currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
96
|
-
if (currentAllowedOrigins.includes("*")) return next();
|
|
97
|
-
const referer = c.req.header("referer") ?? "";
|
|
98
|
-
const isAllowed = currentAllowedOrigins.some((origin) => referer.startsWith(origin));
|
|
99
|
-
if (!isAllowed) {
|
|
100
|
-
throw forbidden("Request origin not permitted");
|
|
101
|
-
}
|
|
102
|
-
return next();
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
55
|
|
|
106
56
|
// src/db/schema/index.ts
|
|
107
57
|
var schema_exports = {};
|
|
@@ -334,8 +284,112 @@ var authVerification = sqliteCore.sqliteTable(
|
|
|
334
284
|
function createDb(d1$1) {
|
|
335
285
|
return d1.drizzle(d1$1, { schema: schema_exports });
|
|
336
286
|
}
|
|
337
|
-
|
|
338
|
-
|
|
287
|
+
async function getOriginsFromDb(env) {
|
|
288
|
+
try {
|
|
289
|
+
const db = createDb(env.DB);
|
|
290
|
+
const row = await db.query.siteConfig.findFirst({
|
|
291
|
+
where: drizzleOrm.eq(siteConfig.key, "allowed_origins")
|
|
292
|
+
});
|
|
293
|
+
if (row) return JSON.parse(row.value);
|
|
294
|
+
} catch {
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
function createCorsMiddleware(allowedOrigins) {
|
|
299
|
+
return async (c, next) => {
|
|
300
|
+
const origin = c.req.header("origin");
|
|
301
|
+
const dynamicOriginsJson = await c.env.KV.get(
|
|
302
|
+
"config:allowed_origins",
|
|
303
|
+
"json"
|
|
304
|
+
);
|
|
305
|
+
let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
306
|
+
if (!dynamicOriginsJson) {
|
|
307
|
+
const dbOrigins = await getOriginsFromDb(c.env);
|
|
308
|
+
if (dbOrigins) {
|
|
309
|
+
currentAllowedOrigins = dbOrigins;
|
|
310
|
+
await c.env.KV.put(
|
|
311
|
+
"config:allowed_origins",
|
|
312
|
+
JSON.stringify(dbOrigins),
|
|
313
|
+
{ expirationTtl: 86400 }
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (c.req.path.startsWith("/api/uploads/images")) {
|
|
318
|
+
c.header("Access-Control-Allow-Origin", "*");
|
|
319
|
+
c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
320
|
+
if (c.req.method === "OPTIONS") {
|
|
321
|
+
return c.body(null, 204);
|
|
322
|
+
}
|
|
323
|
+
return next();
|
|
324
|
+
}
|
|
325
|
+
if (!c.req.path.startsWith("/health") && !c.req.path.startsWith("/api/auth") && !c.req.path.startsWith("/internal") && origin && !currentAllowedOrigins.includes("*") && !currentAllowedOrigins.includes(origin) && origin !== new URL(c.req.url).origin) {
|
|
326
|
+
return c.json(
|
|
327
|
+
{
|
|
328
|
+
error: {
|
|
329
|
+
code: "DOMAIN_RESTRICTED",
|
|
330
|
+
message: `Origin ${origin} is not allowed`
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
403
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
const honoCors = cors.cors({
|
|
337
|
+
origin: currentAllowedOrigins,
|
|
338
|
+
allowMethods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
|
|
339
|
+
allowHeaders: ["Content-Type", "Authorization"],
|
|
340
|
+
exposeHeaders: ["ETag", "Last-Modified", "Cache-Control"],
|
|
341
|
+
maxAge: 86400,
|
|
342
|
+
credentials: true
|
|
343
|
+
});
|
|
344
|
+
return honoCors(c, next);
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
async function getOriginsFromDb2(env) {
|
|
348
|
+
try {
|
|
349
|
+
const db = createDb(env.DB);
|
|
350
|
+
const row = await db.query.siteConfig.findFirst({
|
|
351
|
+
where: drizzleOrm.eq(siteConfig.key, "allowed_origins")
|
|
352
|
+
});
|
|
353
|
+
if (row) return JSON.parse(row.value);
|
|
354
|
+
} catch {
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
function createRefererGuard(allowedOrigins) {
|
|
359
|
+
const MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
|
|
360
|
+
const SKIP_PREFIXES = ["/health", "/internal", "/api/auth", "/.well-known"];
|
|
361
|
+
return factory.createMiddleware(async (c, next) => {
|
|
362
|
+
if (!MUTATION_METHODS.has(c.req.method)) return next();
|
|
363
|
+
if (SKIP_PREFIXES.some((p) => c.req.path.startsWith(p))) return next();
|
|
364
|
+
const dynamicOriginsJson = await c.env.KV.get(
|
|
365
|
+
"config:allowed_origins",
|
|
366
|
+
"json"
|
|
367
|
+
);
|
|
368
|
+
let currentAllowedOrigins = dynamicOriginsJson ?? allowedOrigins;
|
|
369
|
+
if (!dynamicOriginsJson) {
|
|
370
|
+
const dbOrigins = await getOriginsFromDb2(c.env);
|
|
371
|
+
if (dbOrigins) {
|
|
372
|
+
currentAllowedOrigins = dbOrigins;
|
|
373
|
+
await c.env.KV.put(
|
|
374
|
+
"config:allowed_origins",
|
|
375
|
+
JSON.stringify(dbOrigins),
|
|
376
|
+
{ expirationTtl: 86400 }
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (currentAllowedOrigins.includes("*")) return next();
|
|
381
|
+
const referer = c.req.header("referer") ?? "";
|
|
382
|
+
const requestOrigin = new URL(c.req.url).origin;
|
|
383
|
+
if (referer.startsWith(requestOrigin)) return next();
|
|
384
|
+
const isAllowed = currentAllowedOrigins.some(
|
|
385
|
+
(origin) => referer.startsWith(origin)
|
|
386
|
+
);
|
|
387
|
+
if (!isAllowed) {
|
|
388
|
+
throw forbidden("Request origin not permitted");
|
|
389
|
+
}
|
|
390
|
+
return next();
|
|
391
|
+
});
|
|
392
|
+
}
|
|
339
393
|
var DLSU_DOMAIN = "@dlsu.edu.ph";
|
|
340
394
|
function createAuth(env) {
|
|
341
395
|
const db = createDb(env.DB);
|
|
@@ -631,7 +685,14 @@ healthRoute.get("/", async (c) => {
|
|
|
631
685
|
const env = c.env;
|
|
632
686
|
const hasSes = Boolean(env.SES_REGION) && Boolean(env.SES_ACCESS_KEY_ID) && Boolean(env.SES_SECRET_ACCESS_KEY);
|
|
633
687
|
const hasResend = Boolean(env.RESEND_API_KEY);
|
|
634
|
-
|
|
688
|
+
let hasGForms = false;
|
|
689
|
+
if (env.GFORMS_SERVICE_ACCOUNT_JSON) {
|
|
690
|
+
try {
|
|
691
|
+
const parsed = JSON.parse(env.GFORMS_SERVICE_ACCOUNT_JSON);
|
|
692
|
+
hasGForms = Boolean(parsed.client_email && parsed.private_key);
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
}
|
|
635
696
|
const probes = [];
|
|
636
697
|
if (hasSes) {
|
|
637
698
|
probes.push(
|
|
@@ -689,6 +750,7 @@ var CacheService = class {
|
|
|
689
750
|
constructor(kv) {
|
|
690
751
|
this.kv = kv;
|
|
691
752
|
}
|
|
753
|
+
kv;
|
|
692
754
|
async get(key) {
|
|
693
755
|
return this.kv.get(key, "json");
|
|
694
756
|
}
|
|
@@ -734,6 +796,8 @@ var SlotsService = class {
|
|
|
734
796
|
this.db = db;
|
|
735
797
|
this.cache = cache;
|
|
736
798
|
}
|
|
799
|
+
db;
|
|
800
|
+
cache;
|
|
737
801
|
kvKey(slug) {
|
|
738
802
|
return `${SLOT_KV_PREFIX}${slug}`;
|
|
739
803
|
}
|
|
@@ -1348,7 +1412,7 @@ siteConfigRoute.patch("/:key", authMiddleware, adminMiddleware, async (c) => {
|
|
|
1348
1412
|
set: { value: JSON.stringify(value), updatedAt: now }
|
|
1349
1413
|
});
|
|
1350
1414
|
await c.env.KV.put(`config:${key}`, JSON.stringify(value), {
|
|
1351
|
-
expirationTtl:
|
|
1415
|
+
expirationTtl: 86400
|
|
1352
1416
|
});
|
|
1353
1417
|
return c.json({ data: { key, value } });
|
|
1354
1418
|
});
|
|
@@ -1840,6 +1904,7 @@ var SesError = class extends Error {
|
|
|
1840
1904
|
this.status = status;
|
|
1841
1905
|
this.name = "SesError";
|
|
1842
1906
|
}
|
|
1907
|
+
status;
|
|
1843
1908
|
/**
|
|
1844
1909
|
* True for errors that are permanent (not worth retrying via SES again).
|
|
1845
1910
|
* 400 BadRequest, 403 Forbidden, 404 NotFound → non-retryable.
|
|
@@ -2532,7 +2597,7 @@ function defaultGetRuntimeConfig(env) {
|
|
|
2532
2597
|
};
|
|
2533
2598
|
}
|
|
2534
2599
|
function injectConfig(html, config) {
|
|
2535
|
-
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)}
|
|
2600
|
+
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)};</script>`;
|
|
2536
2601
|
return html.replace("</head>", `${configScript}</head>`);
|
|
2537
2602
|
}
|
|
2538
2603
|
function createWorkerHandler(options) {
|
|
@@ -2654,7 +2719,7 @@ function getRuntimeConfig(env) {
|
|
|
2654
2719
|
};
|
|
2655
2720
|
}
|
|
2656
2721
|
function injectConfig2(html, config) {
|
|
2657
|
-
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)}
|
|
2722
|
+
const configScript = `<script>window.__CONFIG__=${JSON.stringify(config)};</script>`;
|
|
2658
2723
|
return html.replace("</head>", `${configScript}</head>`);
|
|
2659
2724
|
}
|
|
2660
2725
|
|