@access-dlsu/leapify 0.260507.1 → 0.260507.5

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.
Files changed (57) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/auth/auth.d.ts.map +1 -1
  3. package/dist/auth/middleware.d.ts.map +1 -1
  4. package/dist/{chunk-ANNHE3PZ.js → chunk-5YYVBPAE.js} +21 -5
  5. package/dist/chunk-5YYVBPAE.js.map +1 -0
  6. package/dist/{chunk-QARF2YFF.cjs → chunk-LVKPYSXI.cjs} +21 -5
  7. package/dist/chunk-LVKPYSXI.cjs.map +1 -0
  8. package/dist/{chunk-63CUZGSZ.js → chunk-OZ6HZKR5.js} +21 -5
  9. package/dist/chunk-OZ6HZKR5.js.map +1 -0
  10. package/dist/{chunk-YFJBE3AU.cjs → chunk-S5DBMZVP.cjs} +21 -5
  11. package/dist/chunk-S5DBMZVP.cjs.map +1 -0
  12. package/dist/client/auth.d.ts +1 -13
  13. package/dist/client/auth.d.ts.map +1 -1
  14. package/dist/client/index.cjs +25 -25
  15. package/dist/client/index.cjs.map +1 -1
  16. package/dist/client/index.d.ts +17 -17
  17. package/dist/client/index.d.ts.map +1 -1
  18. package/dist/client/index.js +25 -25
  19. package/dist/client/index.js.map +1 -1
  20. package/dist/client/types.d.ts +4 -2
  21. package/dist/client/types.d.ts.map +1 -1
  22. package/dist/db/migrate.d.ts.map +1 -1
  23. package/dist/db/schema/{events.d.ts → classes.d.ts} +3 -3
  24. package/dist/db/schema/classes.d.ts.map +1 -0
  25. package/dist/db/schema/index.d.ts +1 -1
  26. package/dist/db/schema/index.d.ts.map +1 -1
  27. package/dist/db/schema/site-config.d.ts +83 -0
  28. package/dist/db/schema/site-config.d.ts.map +1 -1
  29. package/dist/index.cjs +679 -59
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.ts +2 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +655 -40
  34. package/dist/index.js.map +1 -1
  35. package/dist/lib/middleware/cors.d.ts.map +1 -1
  36. package/dist/lib/middleware/pow-challenge.cjs +6 -6
  37. package/dist/lib/middleware/pow-challenge.d.ts.map +1 -1
  38. package/dist/lib/middleware/pow-challenge.js +1 -1
  39. package/dist/routes/classes.d.ts +4 -0
  40. package/dist/routes/classes.d.ts.map +1 -0
  41. package/dist/routes/contentful-sync.d.ts +4 -0
  42. package/dist/routes/contentful-sync.d.ts.map +1 -0
  43. package/dist/services/snapshot.d.ts +1 -1
  44. package/dist/services/snapshot.d.ts.map +1 -1
  45. package/dist/types.d.ts +19 -0
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/worker-handler.d.ts.map +1 -1
  48. package/dist/worker.js +662 -39
  49. package/dist/worker.js.map +1 -1
  50. package/package.json +153 -153
  51. package/dist/chunk-63CUZGSZ.js.map +0 -1
  52. package/dist/chunk-ANNHE3PZ.js.map +0 -1
  53. package/dist/chunk-QARF2YFF.cjs.map +0 -1
  54. package/dist/chunk-YFJBE3AU.cjs.map +0 -1
  55. package/dist/db/schema/events.d.ts.map +0 -1
  56. package/dist/routes/events.d.ts +0 -4
  57. package/dist/routes/events.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,153 +1,153 @@
1
- {
2
- "name": "@access-dlsu/leapify",
3
- "version": "0.260507.1",
4
- "description": "DLSU CSO LEAP backend (run as a standalone Cloudflare Worker or install as an npm module)",
5
- "type": "module",
6
- "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.cjs"
14
- },
15
- "./worker": {
16
- "import": "./dist/worker.js"
17
- },
18
- "./client": {
19
- "types": "./dist/client/index.d.ts",
20
- "import": "./dist/client/index.js",
21
- "require": "./dist/client/index.cjs"
22
- },
23
- "./types": {
24
- "types": "./dist/client/types.d.ts",
25
- "import": "./dist/client/types.js",
26
- "require": "./dist/client/types.cjs"
27
- },
28
- "./middleware/pow-challenge": {
29
- "types": "./dist/lib/middleware/pow-challenge.d.ts",
30
- "import": "./dist/lib/middleware/pow-challenge.js",
31
- "require": "./dist/lib/middleware/pow-challenge.cjs"
32
- }
33
- },
34
- "files": [
35
- "dist"
36
- ],
37
- "sideEffects": false,
38
- "scripts": {
39
- "build": "npx tsup src/index.ts src/client/index.ts src/client/types.ts src/lib/middleware/pow-challenge.ts --format esm,cjs --outDir dist --target es2022 --splitting --sourcemap false --no-dts --no-clean --external @opentelemetry/api && npx tsup src/worker.ts --format esm --outDir dist --target es2022 --no-splitting --sourcemap false --no-dts --no-clean --external @opentelemetry/api && tsc -p tsconfig.build.json --emitDeclarationOnly --declaration --declarationDir dist",
40
- "dev": "tsup --watch",
41
- "start": "npm run build && wrangler dev",
42
- "deploy": "npm run build && wrangler deploy",
43
- "test": "vitest",
44
- "test:run": "vitest run",
45
- "cf-typegen": "wrangler types",
46
- "typecheck": "tsc --noEmit",
47
- "db:generate": "drizzle-kit generate",
48
- "db:migrate": "drizzle-kit migrate",
49
- "clean": "node scripts/clean.mjs",
50
- "version:auto": "node scripts/auto-version.mjs",
51
- "pack": "npm run clean && npm run version:auto && npm run build && npm pack",
52
- "pub": "npm run clean && npm run build && npm publish --access public"
53
- },
54
- "cloudflare": {
55
- "bindings": {
56
- "ALLOWED_ORIGINS": {
57
- "description": "Comma-separated allowed origins (e.g., https://yoursite.com). Leave empty to block all cross-origin requests."
58
- },
59
- "BETTER_AUTH_SECRET": {
60
- "description": "Better Auth signing secret. Generate with: openssl rand -base64 32"
61
- },
62
- "BETTER_AUTH_URL": {
63
- "description": "Public HTTPS base URL of this Worker (e.g. https://leap.yourdomain.com). Used for OAuth redirects."
64
- },
65
- "GOOGLE_CLIENT_ID": {
66
- "description": "Google OAuth 2.0 Client ID from Google Cloud Console."
67
- },
68
- "GOOGLE_CLIENT_SECRET": {
69
- "description": "Google OAuth 2.0 Client Secret from Google Cloud Console."
70
- },
71
- "GFORMS_SERVICE_ACCOUNT_JSON": {
72
- "description": "Google Forms Service Account JSON."
73
- },
74
- "GFORMS_WEBHOOK_SECRET": {
75
- "description": "Secret for Google Forms Webhook."
76
- },
77
- "CONTENTFUL_SPACE_ID": {
78
- "description": "Contentful Space ID."
79
- },
80
- "CONTENTFUL_ACCESS_TOKEN": {
81
- "description": "Contentful Access Token."
82
- },
83
- "CONTENTFUL_ENVIRONMENT": {
84
- "description": "Contentful Environment (usually `master`)."
85
- },
86
- "SES_REGION": {
87
- "description": "Amazon SES Region (e.g., `us-east-1`)."
88
- },
89
- "SES_ACCESS_KEY_ID": {
90
- "description": "Amazon SES Access Key ID."
91
- },
92
- "SES_SECRET_ACCESS_KEY": {
93
- "description": "Amazon SES Secret Access Key."
94
- },
95
- "SES_FROM_ADDRESS": {
96
- "description": "Verified from address for Amazon SES."
97
- },
98
- "RESEND_API_KEY": {
99
- "description": "Resend API Key for fallback email (Optional)."
100
- },
101
- "RESEND_FROM_ADDRESS": {
102
- "description": "Resend From Address (Optional)."
103
- },
104
- "INTERNAL_API_SECRET": {
105
- "description": "Secret key for internal API routes. Also used as HMAC signing key for PoW challenge cookies."
106
- },
107
- "POW_DIFFICULTY": {
108
- "description": "Proof-of-work challenge difficulty (leading zero bits). Default: 4. Range: 1-8. Higher = harder challenge."
109
- }
110
- }
111
- },
112
- "peerDependencies": {
113
- "@cloudflare/workers-types": "^4.0.0",
114
- "drizzle-orm": "^0.45.2",
115
- "hono": "^4.0.0"
116
- },
117
- "peerDependenciesMeta": {
118
- "@cloudflare/workers-types": {
119
- "optional": true
120
- }
121
- },
122
- "dependencies": {
123
- "@hono/zod-validator": "^0.7.6",
124
- "better-auth": "^1.6.9",
125
- "contentful-management": "^12.3.2",
126
- "wrangler": "^4.80.0",
127
- "zod": "^4.3.6"
128
- },
129
- "devDependencies": {
130
- "@cloudflare/workers-types": "^4.20260402.1",
131
- "@types/better-sqlite3": "^7.6.13",
132
- "better-sqlite3": "^12.8.0",
133
- "drizzle-kit": "^0.31.10",
134
- "hono": "^4.12.0",
135
- "prettier": "3.8.3",
136
- "tsup": "^8.4.0",
137
- "typescript": "^6.0.3",
138
- "vitest": "^4.1.2"
139
- },
140
- "overrides": {
141
- "esbuild": "^0.25.0"
142
- },
143
- "homepage": "https://github.com/access-dlsu/leapify#readme",
144
- "bugs": {
145
- "url": "https://github.com/access-dlsu/leapify/issues"
146
- },
147
- "repository": {
148
- "type": "git",
149
- "url": "git+https://github.com/access-dlsu/leapify.git"
150
- },
151
- "license": "MIT",
152
- "author": "ACCESS DLSU"
153
- }
1
+ {
2
+ "name": "@access-dlsu/leapify",
3
+ "version": "0.260507.5",
4
+ "description": "DLSU CSO LEAP backend (run as a standalone Cloudflare Worker or install as an npm module)",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./worker": {
16
+ "import": "./dist/worker.js"
17
+ },
18
+ "./client": {
19
+ "types": "./dist/client/index.d.ts",
20
+ "import": "./dist/client/index.js",
21
+ "require": "./dist/client/index.cjs"
22
+ },
23
+ "./types": {
24
+ "types": "./dist/client/types.d.ts",
25
+ "import": "./dist/client/types.js",
26
+ "require": "./dist/client/types.cjs"
27
+ },
28
+ "./middleware/pow-challenge": {
29
+ "types": "./dist/lib/middleware/pow-challenge.d.ts",
30
+ "import": "./dist/lib/middleware/pow-challenge.js",
31
+ "require": "./dist/lib/middleware/pow-challenge.cjs"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "sideEffects": false,
38
+ "scripts": {
39
+ "build": "npx tsup src/index.ts src/client/index.ts src/client/types.ts src/lib/middleware/pow-challenge.ts --format esm,cjs --outDir dist --target es2022 --splitting --sourcemap false --no-dts --no-clean --external @opentelemetry/api && npx tsup src/worker.ts --format esm --outDir dist --target es2022 --no-splitting --sourcemap false --no-dts --no-clean --external @opentelemetry/api && tsc -p tsconfig.build.json --emitDeclarationOnly --declaration --declarationDir dist",
40
+ "dev": "tsup --watch",
41
+ "start": "npm run build && wrangler dev",
42
+ "deploy": "npm run build && wrangler deploy",
43
+ "test": "vitest",
44
+ "test:run": "vitest run",
45
+ "cf-typegen": "wrangler types",
46
+ "typecheck": "tsc --noEmit",
47
+ "db:generate": "drizzle-kit generate",
48
+ "db:migrate": "drizzle-kit migrate",
49
+ "clean": "node scripts/clean.mjs",
50
+ "version:auto": "node scripts/auto-version.mjs",
51
+ "pack": "npm run clean && npm run version:auto && npm run build && npm pack",
52
+ "pub": "npm whoami && npm run clean && npm run build && npm publish --access public"
53
+ },
54
+ "cloudflare": {
55
+ "bindings": {
56
+ "ALLOWED_ORIGINS": {
57
+ "description": "Comma-separated allowed origins (e.g., https://yoursite.com). Leave empty to block all cross-origin requests."
58
+ },
59
+ "BETTER_AUTH_SECRET": {
60
+ "description": "Better Auth signing secret. Generate with: openssl rand -base64 32"
61
+ },
62
+ "BETTER_AUTH_URL": {
63
+ "description": "Public HTTPS base URL of this Worker (e.g. https://leap.yourdomain.com). Used for OAuth redirects."
64
+ },
65
+ "GOOGLE_CLIENT_ID": {
66
+ "description": "Google OAuth 2.0 Client ID from Google Cloud Console."
67
+ },
68
+ "GOOGLE_CLIENT_SECRET": {
69
+ "description": "Google OAuth 2.0 Client Secret from Google Cloud Console."
70
+ },
71
+ "GFORMS_SERVICE_ACCOUNT_JSON": {
72
+ "description": "Google Forms Service Account JSON."
73
+ },
74
+ "GFORMS_WEBHOOK_SECRET": {
75
+ "description": "Secret for Google Forms Webhook."
76
+ },
77
+ "CONTENTFUL_SPACE_ID": {
78
+ "description": "Contentful Space ID."
79
+ },
80
+ "CONTENTFUL_ACCESS_TOKEN": {
81
+ "description": "Contentful Access Token."
82
+ },
83
+ "CONTENTFUL_ENVIRONMENT": {
84
+ "description": "Contentful Environment (usually `master`)."
85
+ },
86
+ "SES_REGION": {
87
+ "description": "Amazon SES Region (e.g., `us-east-1`)."
88
+ },
89
+ "SES_ACCESS_KEY_ID": {
90
+ "description": "Amazon SES Access Key ID."
91
+ },
92
+ "SES_SECRET_ACCESS_KEY": {
93
+ "description": "Amazon SES Secret Access Key."
94
+ },
95
+ "SES_FROM_ADDRESS": {
96
+ "description": "Verified from address for Amazon SES."
97
+ },
98
+ "RESEND_API_KEY": {
99
+ "description": "Resend API Key for fallback email (Optional)."
100
+ },
101
+ "RESEND_FROM_ADDRESS": {
102
+ "description": "Resend From Address (Optional)."
103
+ },
104
+ "INTERNAL_API_SECRET": {
105
+ "description": "Secret key for internal API routes. Also used as HMAC signing key for PoW challenge cookies."
106
+ },
107
+ "POW_DIFFICULTY": {
108
+ "description": "Proof-of-work challenge difficulty (leading zero bits). Default: 4. Range: 1-8. Higher = harder challenge."
109
+ }
110
+ }
111
+ },
112
+ "peerDependencies": {
113
+ "@cloudflare/workers-types": "^4.0.0",
114
+ "drizzle-orm": "^0.45.2",
115
+ "hono": "^4.0.0"
116
+ },
117
+ "peerDependenciesMeta": {
118
+ "@cloudflare/workers-types": {
119
+ "optional": true
120
+ }
121
+ },
122
+ "dependencies": {
123
+ "@hono/zod-validator": "^0.7.6",
124
+ "better-auth": "^1.6.9",
125
+ "contentful-management": "^12.3.2",
126
+ "wrangler": "^4.80.0",
127
+ "zod": "^4.3.6"
128
+ },
129
+ "devDependencies": {
130
+ "@cloudflare/workers-types": "^4.20260402.1",
131
+ "@types/better-sqlite3": "^7.6.13",
132
+ "better-sqlite3": "^12.8.0",
133
+ "drizzle-kit": "^0.31.10",
134
+ "hono": "^4.12.0",
135
+ "prettier": "3.8.3",
136
+ "tsup": "^8.4.0",
137
+ "typescript": "^6.0.3",
138
+ "vitest": "^4.1.2"
139
+ },
140
+ "overrides": {
141
+ "esbuild": "^0.25.0"
142
+ },
143
+ "homepage": "https://github.com/access-dlsu/leapify#readme",
144
+ "bugs": {
145
+ "url": "https://github.com/access-dlsu/leapify/issues"
146
+ },
147
+ "repository": {
148
+ "type": "git",
149
+ "url": "git+https://github.com/access-dlsu/leapify.git"
150
+ },
151
+ "license": "MIT",
152
+ "author": "ACCESS DLSU"
153
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/middleware/pow-challenge.ts"],"names":[],"mappings":"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","file":"chunk-63CUZGSZ.js","sourcesContent":["/**\r\n * Proof-of-Work Challenge Middleware (ADR-006, Layer 7).\r\n *\r\n * Anubis-inspired PoW challenge, implemented natively as Hono middleware.\r\n * Requires browsers to solve a SHA-256 PoW puzzle before accessing API endpoints.\r\n * After solving, a signed cookie is issued so subsequent requests bypass the challenge.\r\n *\r\n * Flow:\r\n * 1. Request arrives → no valid PoW cookie → serve challenge HTML page\r\n * 2. Browser runs JS PoW (find nonce where SHA-256(challengeId:nonce) meets difficulty)\r\n * 3. Client POSTs solution to /.well-known/leapify/pow/verify\r\n * 4. Server validates → sets signed cookie → redirects back to original URL\r\n * 5. Subsequent requests include cookie → pass through immediately\r\n *\r\n * Signing key: INTERNAL_API_SECRET (reused — same HMAC purpose as internal route auth)\r\n * Difficulty: POW_DIFFICULTY env var or DEFAULT_POW_DIFFICULTY (leading zero bits)\r\n */\r\n\r\nimport { createMiddleware } from 'hono/factory'\r\nimport type { Context } from 'hono'\r\nimport type { LeapifyBindings } from '../../types'\r\n\r\n// ─── Constants ──────────────────────────────────────────────────────────────────\r\n\r\n/** Base path for PoW challenge routes */\r\nexport const POW_PATH = '/.well-known/leapify/pow'\r\n\r\n/** Challenge verification endpoint */\r\nexport const POW_VERIFY_PATH = `${POW_PATH}/verify`\r\n\r\n/** Cookie name for PoW auth token */\r\nexport const POW_COOKIE_NAME = 'leapify-pow'\r\n\r\n/** KV key prefix for stored challenges */\r\nconst CHALLENGE_KV_PREFIX = 'pow:challenge:'\r\n\r\n/** Default difficulty (leading zero bits required in SHA-256 hash) */\r\nconst DEFAULT_POW_DIFFICULTY = 4\r\n\r\n/** Challenge expiration time in seconds */\r\nconst CHALLENGE_TTL_SEC = 120\r\n\r\n/** Cookie expiration time in seconds (1 hour) */\r\nconst COOKIE_MAX_AGE_SEC = 3600\r\n\r\n/** Paths exempt from PoW challenge */\r\nconst EXEMPT_PATHS = ['/health', '/internal', '/api/auth']\r\n\r\n// ─── Base64url Utilities ────────────────────────────────────────────────────────\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\n// ─── Crypto Helpers ─────────────────────────────────────────────────────────────\r\n\r\nasync function generateChallengeId(): Promise<string> {\r\n const bytes = crypto.getRandomValues(new Uint8Array(16))\r\n return base64urlEncode(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 // Verify HMAC signature\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 // Parse payload: ip:ts:nonce\r\n const payload = new TextDecoder().decode(payloadBytes)\r\n const [cookieIp, tsStr] = payload.split(':')\r\n\r\n // Verify IP matches (prevent cookie sharing)\r\n if (cookieIp !== ip) return false\r\n\r\n // Verify not expired\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 getDifficulty(env: LeapifyBindings): number {\r\n const raw = env.POW_DIFFICULTY\r\n if (!raw) return DEFAULT_POW_DIFFICULTY\r\n const parsed = parseInt(raw, 10)\r\n return isNaN(parsed)\r\n ? DEFAULT_POW_DIFFICULTY\r\n : Math.max(1, Math.min(parsed, 8))\r\n}\r\n\r\n// ─── Challenge Page HTML ────────────────────────────────────────────────────────\r\n\r\nfunction challengePageHtml(\r\n challengeId: string,\r\n difficulty: number,\r\n originalUrl: string\r\n): string {\r\n return `<!DOCTYPE html>\r\n<html>\r\n<head>\r\n <meta charset=\"utf-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n <title>Verifying your browser</title>\r\n <style>\r\n * { margin: 0; padding: 0; box-sizing: border-box; }\r\n body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #f5f5f5; color: #333; }\r\n .card { background: #fff; border-radius: 12px; padding: 2rem; box-shadow: 0 2px 12px rgba(0,0,0,0.08); text-align: center; max-width: 400px; }\r\n .spinner { width: 40px; height: 40px; border: 3px solid #e0e0e0; border-top-color: #333; border-radius: 50%; animation: spin 0.8s linear infinite; margin: 0 auto 1rem; }\r\n @keyframes spin { to { transform: rotate(360deg); } }\r\n h1 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.5rem; }\r\n p { font-size: 0.9rem; color: #666; }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"card\">\r\n <div class=\"spinner\"></div>\r\n <h1>Verifying your browser</h1>\r\n <p>This should only take a moment&hellip;</p>\r\n </div>\r\n <script>\r\n (async () => {\r\n const challengeId = ${JSON.stringify(challengeId)};\r\n const difficulty = ${difficulty};\r\n const prefix = '0'.repeat(Math.ceil(difficulty / 4));\r\n let nonce = 0;\r\n const t0 = performance.now();\r\n while (true) {\r\n const input = challengeId + ':' + nonce;\r\n const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input));\r\n const hex = Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');\r\n if (hex.startsWith(prefix)) {\r\n const elapsed = performance.now() - t0;\r\n const res = await fetch(${JSON.stringify(POW_VERIFY_PATH)}, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ id: challengeId, nonce, elapsed, redir: ${JSON.stringify(originalUrl)} }),\r\n });\r\n const data = await res.json();\r\n if (data.redirect) window.location.href = data.redirect;\r\n else window.location.reload();\r\n break;\r\n }\r\n nonce++;\r\n }\r\n })();\r\n </script>\r\n</body>\r\n</html>`\r\n}\r\n\r\n// ─── Verify Handler ─────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * POST /.well-known/leapify/pow/verify\r\n *\r\n * Validates a completed PoW challenge and issues a signed cookie.\r\n * Exported for mounting in app.ts.\r\n */\r\nexport async function handlePowVerify(\r\n c: Context<{ Bindings: LeapifyBindings }>\r\n) {\r\n const body = await c.req.json<{\r\n id?: string\r\n nonce?: number\r\n elapsed?: number\r\n redir?: string\r\n }>()\r\n\r\n const { id, nonce, redir } = body\r\n\r\n if (!id || typeof nonce !== 'number') {\r\n return c.json(\r\n {\r\n error: {\r\n code: 'VALIDATION_ERROR',\r\n message: 'Missing challenge id or nonce'\r\n }\r\n },\r\n 422\r\n )\r\n }\r\n\r\n // Retrieve challenge from KV\r\n const challenge = await c.env.KV.get(`${CHALLENGE_KV_PREFIX}${id}`)\r\n if (!challenge) {\r\n return c.json(\r\n { error: { code: 'NOT_FOUND', message: 'Challenge expired or invalid' } },\r\n 404\r\n )\r\n }\r\n\r\n const { difficulty } = JSON.parse(challenge)\r\n\r\n // Verify PoW: SHA-256(challengeId:nonce) must have required leading zeros\r\n const input = new TextEncoder().encode(`${id}:${nonce}`)\r\n const hash = await crypto.subtle.digest('SHA-256', input)\r\n const hex = Array.from(new Uint8Array(hash))\r\n .map((b) => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n\r\n const requiredPrefix = '0'.repeat(Math.ceil(difficulty / 4))\r\n if (!hex.startsWith(requiredPrefix)) {\r\n return c.json(\r\n { error: { code: 'VALIDATION_ERROR', message: 'Invalid proof of work' } },\r\n 422\r\n )\r\n }\r\n\r\n // Invalidate challenge (single-use)\r\n await c.env.KV.delete(`${CHALLENGE_KV_PREFIX}${id}`)\r\n\r\n // Issue signed cookie\r\n const secret = c.env.INTERNAL_API_SECRET\r\n const ip = getClientIp(c)\r\n const token = await signCookie(secret, ip)\r\n\r\n c.header(\r\n 'Set-Cookie',\r\n `${POW_COOKIE_NAME}=${token}; Path=/; Max-Age=${COOKIE_MAX_AGE_SEC}; Secure; HttpOnly; SameSite=Lax`\r\n )\r\n\r\n return c.json({ redirect: redir || '/' })\r\n}\r\n\r\n// ─── Main Middleware ─────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * PoW challenge middleware.\r\n *\r\n * Mount AFTER cors, BEFORE everything else:\r\n * app.use('*', createCorsMiddleware(...))\r\n * app.use('*', createPowChallengeMiddleware()) ← here\r\n * app.use('*', createRefererGuard(...))\r\n */\r\nexport function createPowChallengeMiddleware() {\r\n return createMiddleware<{ Bindings: LeapifyBindings }>(async (c, next) => {\r\n // Always pass through the verify endpoint itself\r\n if (c.req.path === POW_VERIFY_PATH) return next()\r\n\r\n // Skip exempt paths (health, internal webhooks)\r\n if (EXEMPT_PATHS.some((p) => c.req.path.startsWith(p))) return next()\r\n\r\n // Skip if client has a valid Authorization header (Firebase JWT — auth middleware will handle)\r\n if (c.req.header('Authorization')) return next()\r\n\r\n // Check for valid PoW cookie\r\n const cookieHeader = c.req.header('Cookie') ?? ''\r\n const cookieMatch = cookieHeader.match(\r\n new RegExp(`${POW_COOKIE_NAME}=([^;]+)`)\r\n )\r\n if (cookieMatch) {\r\n const secret = c.env.INTERNAL_API_SECRET\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 // ── Issue challenge ──────────────────────────────────────────────────────\r\n\r\n const difficulty = getDifficulty(c.env)\r\n const challengeId = await generateChallengeId()\r\n\r\n // Store challenge in KV with TTL\r\n await c.env.KV.put(\r\n `${CHALLENGE_KV_PREFIX}${challengeId}`,\r\n JSON.stringify({ difficulty, createdAt: Date.now() }),\r\n { expirationTtl: CHALLENGE_TTL_SEC }\r\n )\r\n\r\n // Serve challenge page\r\n const originalUrl = c.req.path + (c.req.query('?') ? c.req.query('?') : '')\r\n const html = challengePageHtml(challengeId, difficulty, originalUrl)\r\n\r\n return c.html(html, 200)\r\n })\r\n}\r\n"]}