Point the panel link at the installation, not at the browser's origin
Both links out of the dashboard editor were built root-relative, so a portal
serving the app under `/i/{id}` got a URL to itself: the hub has no route
there and answers a bare 404. That is what a device link and "open what a
wall panel sees" both landed on.
They want different answers. The view link is for the person already looking,
so it takes the router's basepath — `appPath` in `lib/portal` is the same
prefix the router applies to every `Link`, for the places that step outside
it. The device link is for a screen, which cannot go through the portal at
all: the shell is served only to a portal session, and the credential that
page carries is the portal's rather than the panel's. So the server now says
where it answers, and `FRONTEND_HOST` is that answer — the same setting the
password-reset links already use.
Also fixes the panel branch in the query error handler, which compared a raw
pathname and so never fired under a portal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AHpLJHozysQXjsxAyU1WHj
This commit is contained in:
@@ -2054,6 +2054,32 @@ export const PanelsConfigSchema = {
|
||||
description: 'Every panel this installation knows about.'
|
||||
} as const;
|
||||
|
||||
export const PanelsPublicSchema = {
|
||||
properties: {
|
||||
panels: {
|
||||
items: {
|
||||
'$ref': '#/components/schemas/PanelDef'
|
||||
},
|
||||
type: 'array',
|
||||
title: 'Panels'
|
||||
},
|
||||
frontend_host: {
|
||||
type: 'string',
|
||||
title: 'Frontend Host',
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
title: 'PanelsPublic',
|
||||
description: `The panels, and the address a device should be pointed at.
|
||||
|
||||
The address is the server's own, because the browser's origin is not a
|
||||
reliable answer to it: an admin working through the portal is on the
|
||||
portal's origin, and a screen cannot be sent there — the portal serves a
|
||||
page only to someone holding a portal session, and the credential it hands
|
||||
that page is the portal's rather than the panel's.`
|
||||
} as const;
|
||||
|
||||
export const PlacementSchema = {
|
||||
properties: {
|
||||
x: {
|
||||
|
||||
@@ -1395,8 +1395,8 @@ export class ObservabilityService {
|
||||
export class PanelsService {
|
||||
/**
|
||||
* Read Panels
|
||||
* Every panel, and what each one shows.
|
||||
* @returns PanelsConfig Successful Response
|
||||
* Every panel, what each one shows, and where to point a device.
|
||||
* @returns PanelsPublic Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static readPanels(): CancelablePromise<PanelsReadPanelsResponse> {
|
||||
@@ -1414,7 +1414,7 @@ export class PanelsService {
|
||||
* how a device is unpaired.
|
||||
* @param data The data for the request.
|
||||
* @param data.requestBody
|
||||
* @returns PanelsConfig Successful Response
|
||||
* @returns PanelsPublic Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static savePanels(data: PanelsSavePanelsData): CancelablePromise<PanelsSavePanelsResponse> {
|
||||
|
||||
@@ -744,6 +744,20 @@ export type PanelsConfig = {
|
||||
panels?: Array<PanelDef>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The panels, and the address a device should be pointed at.
|
||||
*
|
||||
* The address is the server's own, because the browser's origin is not a
|
||||
* reliable answer to it: an admin working through the portal is on the
|
||||
* portal's origin, and a screen cannot be sent there — the portal serves a
|
||||
* page only to someone holding a portal session, and the credential it hands
|
||||
* that page is the portal's rather than the panel's.
|
||||
*/
|
||||
export type PanelsPublic = {
|
||||
panels?: Array<PanelDef>;
|
||||
frontend_host?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Where a widget sits in its section's grid, in grid units.
|
||||
*/
|
||||
@@ -1393,13 +1407,13 @@ export type ObservabilityReadDeadLettersData = {
|
||||
|
||||
export type ObservabilityReadDeadLettersResponse = (Array<DeadLetter>);
|
||||
|
||||
export type PanelsReadPanelsResponse = (PanelsConfig);
|
||||
export type PanelsReadPanelsResponse = (PanelsPublic);
|
||||
|
||||
export type PanelsSavePanelsData = {
|
||||
requestBody: PanelsConfig;
|
||||
};
|
||||
|
||||
export type PanelsSavePanelsResponse = (PanelsConfig);
|
||||
export type PanelsSavePanelsResponse = (PanelsPublic);
|
||||
|
||||
export type PanelsStartPairingResponse = (PairStarted);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user