mirror of
https://github.com/temetro/temetro.git
synced 2026-08-09 18:20:00 +00:00
2454bb4c2b
Add public GET /api/version (current version + GitHub-release update check, cached, fail-soft) and GET /api/network (detected LAN addresses). Accept localhost/private-LAN origins in CORS and Better Auth trusted origins via a shared isAllowedOrigin helper, so staff can reach the app over the clinic network. The seed-demo.ts fake test data is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
// Decides which browser origins may call the API with credentials.
|
|
//
|
|
// temetro is self-hosted: a clinic runs it on one machine and other departments
|
|
// reach it over the LAN by the host's IP (e.g. http://192.168.1.20:3000). The
|
|
// browser there sends that LAN origin, which must be allowed for both CORS and
|
|
// Better Auth's CSRF/trusted-origin check. We allow:
|
|
// - FRONTEND_URL (the configured origin),
|
|
// - any explicitly listed TRUSTED_ORIGINS (or "*" for any),
|
|
// - localhost and private/LAN hosts (so LAN access works with no config).
|
|
import { env } from "../env.js";
|
|
|
|
const configured = (env.TRUSTED_ORIGINS ?? "")
|
|
.split(",")
|
|
.map((s) => s.trim())
|
|
.filter(Boolean);
|
|
|
|
const allowAll = configured.includes("*");
|
|
|
|
function isPrivateHost(hostname: string): boolean {
|
|
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
|
|
return true;
|
|
}
|
|
// Private IPv4 ranges (RFC 1918) + link-local.
|
|
if (/^10\./.test(hostname)) return true;
|
|
if (/^192\.168\./.test(hostname)) return true;
|
|
if (/^172\.(1[6-9]|2\d|3[01])\./.test(hostname)) return true;
|
|
if (/^169\.254\./.test(hostname)) return true;
|
|
// mDNS .local hostnames (e.g. clinic-pc.local).
|
|
if (hostname.endsWith(".local")) return true;
|
|
return false;
|
|
}
|
|
|
|
/** True if `origin` (an `Origin` header value) is allowed to call the API. */
|
|
export function isAllowedOrigin(origin: string | undefined | null): boolean {
|
|
if (!origin) return false;
|
|
if (allowAll) return true;
|
|
if (origin === env.FRONTEND_URL) return true;
|
|
if (configured.includes(origin)) return true;
|
|
try {
|
|
return isPrivateHost(new URL(origin).hostname);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|