From aac1decc71d6e5077d69ecbf83f748cc9eb50aea Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sat, 20 Jun 2026 15:42:21 +1000 Subject: [PATCH] fix: support LAN access in local dev with wildcard host matching - PUBLIC_HOST/PRIVATE_HOST='*' matches any hostname (localhost, LAN IPs) - Public route falls through to private on 404 (shared host in local dev) - If both return 404, keep original public 404 response - APP_BASE_URL updated to http://localhost:8787 for local dev --- package-lock.json | 33 +++++++++++-------------------- worker/index.ts | 19 ++++++++++++++++-- worker/routes/private-redirect.ts | 1 + worker/routes/redirect.ts | 1 + wrangler.jsonc | 6 +++--- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7cb2471..cea1946 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,7 +146,8 @@ "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260619.1.tgz", "integrity": "sha512-bprsNzG0DapQPFwU2AvQlQ6FUO7Y4bKWaPBzLNI7nBSqlHsK0P62xx2NaNT6i/htzAgQspKZm1D32y0RxofrRQ==", "dev": true, - "license": "MIT OR Apache-2.0" + "license": "MIT OR Apache-2.0", + "peer": true }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -161,27 +162,6 @@ "node": ">=12" } }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", @@ -3381,6 +3361,7 @@ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3391,6 +3372,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3674,6 +3656,7 @@ "devOptional": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -4139,6 +4122,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4256,6 +4240,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4265,6 +4250,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -4589,6 +4575,7 @@ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -4641,6 +4628,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -4827,6 +4815,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, diff --git a/worker/index.ts b/worker/index.ts index 86a850d..c570676 100644 --- a/worker/index.ts +++ b/worker/index.ts @@ -55,12 +55,27 @@ export default { return withPrivateHostNoStoreHeaders(url, json({ error: 'Not found' }, { status: 404 }), env.PRIVATE_HOST); } + // In local dev (PUBLIC_HOST=*), public and private share the same host. + // Try public first; if it returns 404 (alias not found), fall through to + // private so authenticated users can resolve their personal shortlinks. + // If private also returns 404, keep the original public 404 response. if (isHeygoPublicHost(url.hostname, env.PUBLIC_HOST) && !isReservedPublicPath(url.pathname)) { - return handlePublicShortlink(request, env, ctx); + const publicResponse = await handlePublicShortlink(request, env, ctx); + if (publicResponse.status !== 404) { + return withPrivateHostNoStoreHeaders(url, publicResponse, env.PRIVATE_HOST); + } + // Public alias not found — try private route (for local dev with shared host) + if (isHeygoPrivateHost(url.hostname, env.PRIVATE_HOST)) { + const privateResponse = await handlePrivateShortlink(request, env, ctx); + if (privateResponse.status !== 404) { + return withPrivateHostNoStoreHeaders(url, privateResponse, env.PRIVATE_HOST); + } + } + return withPrivateHostNoStoreHeaders(url, publicResponse, env.PRIVATE_HOST); } if (isHeygoPrivateHost(url.hostname, env.PRIVATE_HOST) && !isReservedPublicPath(url.pathname)) { - return handlePrivateShortlink(request, env, ctx); + return withPrivateHostNoStoreHeaders(url, await handlePrivateShortlink(request, env, ctx), env.PRIVATE_HOST); } return withPrivateHostNoStoreHeaders(url, json({ error: 'Not found' }, { status: 404 }), env.PRIVATE_HOST); diff --git a/worker/routes/private-redirect.ts b/worker/routes/private-redirect.ts index baf5939..b9ecf5a 100644 --- a/worker/routes/private-redirect.ts +++ b/worker/routes/private-redirect.ts @@ -31,6 +31,7 @@ LIMIT 1`; const CLICK_COUNT_UPDATE = `UPDATE links SET click_count = click_count + 1, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id=?`; export function isHeygoPrivateHost(hostname: string, privateHost: string): boolean { + if (privateHost === '*') return true; // local dev: match any host (LAN access) return hostname === privateHost; } diff --git a/worker/routes/redirect.ts b/worker/routes/redirect.ts index 16db5ec..64acc15 100644 --- a/worker/routes/redirect.ts +++ b/worker/routes/redirect.ts @@ -27,6 +27,7 @@ export function isReservedPublicPath(pathname: string): boolean { } export function isHeygoPublicHost(hostname: string, publicHost: string): boolean { + if (publicHost === '*') return true; // local dev: match any host (LAN access) return hostname === publicHost; } diff --git a/wrangler.jsonc b/wrangler.jsonc index 247eed3..ea5af15 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -11,9 +11,9 @@ "enabled": true }, "vars": { - "PUBLIC_HOST": "localhost", - "PRIVATE_HOST": "localhost", - "APP_BASE_URL": "http://localhost:5173", + "PUBLIC_HOST": "*", + "PRIVATE_HOST": "*", + "APP_BASE_URL": "http://localhost:8787", "COOKIE_DOMAIN": "" }, "d1_databases": [