Files
krowworkforce_controltower/src/lib/app-params.js
Aravind 3c649fc123 Fix blank page on self-hosted deploys
The app reads its Base44 app id and backend URL from VITE_BASE44_APP_ID and
VITE_BASE44_BACKEND_URL, but neither was set anywhere, so Vite compiled both
defaults to undefined and app-params resolved them to null at runtime. On
base44's own host these arrive as query params; on a custom domain they never
do. A null serverUrl skipped the SDK's default (it only applies to undefined),
making every API baseURL relative — requests hit our own nginx, which answered
with index.html, and the app rendered against HTML and died. Deploys reported
success while serving an empty page.

appBaseUrl was never passed either, so login/logout redirects were relative too
and bounced between /login and the SPA fallback. redirectToLogin also omits
app_id, which base44.app/login 404s on when the request comes from our domain
rather than a *.base44.app one, so login now goes through loginWithProvider.

Set both vars as build args in the Dockerfile — they must be present at build
time, since runtime env vars in Dokploy arrive after Vite has already inlined
them. Guard the build against an empty app id and a missing dist/index.html.

Surface failures instead of hiding them: an ErrorBoundary around the tree, and
a diagnostic panel for auth errors that previously fell through to a crash.

In nginx, serve a real 404 for missing /assets/ files. Falling through to the
SPA rule returned index.html as text/html, which the browser refuses to run as
a module — another silent blank page. Also add /health and stop the SPA
fallback from being cached under the requested path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 20:31:04 +05:30

59 lines
2.1 KiB
JavaScript

const isNode = typeof window === 'undefined';
const windowObj = isNode ? { localStorage: new Map() } : window;
const storage = windowObj.localStorage;
const toSnakeCase = (str) => {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
const getAppParamValue = (paramName, { defaultValue = undefined, removeFromUrl = false } = {}) => {
if (isNode) {
return defaultValue;
}
const storageKey = `base44_${toSnakeCase(paramName)}`;
const urlParams = new URLSearchParams(window.location.search);
const searchParam = urlParams.get(paramName);
if (removeFromUrl) {
urlParams.delete(paramName);
const newUrl = `${window.location.pathname}${urlParams.toString() ? `?${urlParams.toString()}` : ""
}${window.location.hash}`;
window.history.replaceState({}, document.title, newUrl);
}
if (searchParam) {
storage.setItem(storageKey, searchParam);
return searchParam;
}
if (defaultValue) {
storage.setItem(storageKey, defaultValue);
return defaultValue;
}
const storedValue = storage.getItem(storageKey);
if (storedValue) {
return storedValue;
}
return null;
}
// Base44's own host. On a base44-hosted app these arrive as ?app_id=/?server_url=
// query params, but a self-hosted build (Docker/Dokploy on a custom domain) never
// gets them, so the build-time env vars below are the only source. If both are
// missing the SDK falls back to a null serverUrl, every request becomes a
// same-origin relative URL, nginx answers it with index.html, and the app renders
// a blank page — so serverUrl must never be allowed to end up null.
const DEFAULT_SERVER_URL = "https://base44.app";
const getAppParams = () => {
return {
appId: getAppParamValue("app_id", { defaultValue: import.meta.env.VITE_BASE44_APP_ID }),
serverUrl: getAppParamValue("server_url", { defaultValue: import.meta.env.VITE_BASE44_BACKEND_URL }) || DEFAULT_SERVER_URL,
token: getAppParamValue("access_token", { removeFromUrl: true }),
fromUrl: getAppParamValue("from_url", { defaultValue: window.location.href }),
functionsVersion: getAppParamValue("functions_version"),
}
}
export const appParams = {
...getAppParams()
}