fix(dashboard): F-U-008 + F-U-014 back-link to app slug, public_base_url for webhook

F-U-008: scripts/[id]'s "← Scripts" back-link used `base + '/'` which
the root page redirects to /apps. The breadcrumb already resolves
appSlug; switch the back-link to `{base}/apps/{appSlug}` (falling back
to `{base}/apps` when appSlug isn't loaded yet).

F-U-014: emailInboundUrl built the webhook URL from
window.location.origin — wrong when the admin browses via an internal
LAN address but the public webhook URL is on a different host.
VersionInfo.public_base_url already exists for exactly this case; the
apps/[slug] page now loads /version alongside the app fetch and
prefers public_base_url for the webhook URL display. Falls back to
window.location.origin if /version hasn't loaded.

AUDIT.md anchors: F-U-008, F-U-014.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-07 20:58:35 +02:00
parent 6b5da50a38
commit 86698afc24
2 changed files with 19 additions and 2 deletions

View File

@@ -54,6 +54,10 @@
let myRole = $state<AppRole | null>(null);
let loadError = $state<string | null>(null);
let loading = $state(true);
/// F-U-014: the canonical public origin (from `/version`). Used to
/// build webhook URLs the operator shares with external systems so
/// they don't accidentally inherit the admin's LAN address.
let publicBaseUrl = $state<string | null>(null);
let activeTab = $state<Tab>('scripts');
let scripts = $state<Script[]>([]);
@@ -379,9 +383,14 @@
// The inbound-email webhook URL for a given email trigger (shown so
// the operator can configure their provider).
// F-U-014: prefer the server-known public_base_url (from /version)
// so the URL we hand the operator works from outside the admin's
// network. Fall back to window.location.origin only if /version
// hasn't loaded yet (visibly distinguishable in the UI).
function emailInboundUrl(triggerId: string): string {
if (!app) return '';
return `${window.location.origin}/api/v1/email-inbound/${app.id}/${triggerId}`;
const origin = publicBaseUrl ?? window.location.origin;
return `${origin.replace(/\/$/, '')}/api/v1/email-inbound/${app.id}/${triggerId}`;
}
async function confirmRemoveTrigger() {
@@ -601,6 +610,12 @@
loading = true;
loadError = null;
try {
// F-U-014: fire the /version request alongside the app fetch so
// emailInboundUrl can prefer the server-known public_base_url.
api
.version()
.then((v) => (publicBaseUrl = v.public_base_url || null))
.catch(() => (publicBaseUrl = null));
const fetched = await api.apps.get(slug);
if (fetched.redirect_to && fetched.redirect_to !== slug) {
await goto(`${base}/apps/${fetched.redirect_to}`, { replaceState: true });