feat: Add public view-config endpoint for dashboard with access control

This commit is contained in:
Nawaz Dhandala
2026-03-26 19:55:16 +00:00
parent 7419ff4437
commit 028212731f
2 changed files with 75 additions and 14 deletions

View File

@@ -13,11 +13,12 @@ import DashboardViewConfig, {
getAutoRefreshIntervalInMs,
getAutoRefreshIntervalLabel,
} from "Common/Types/Dashboard/DashboardViewConfig";
import { ObjectType } from "Common/Types/JSON";
import { JSONObject, ObjectType } from "Common/Types/JSON";
import ObjectID from "Common/Types/ObjectID";
import Dashboard from "Common/Models/DatabaseModels/Dashboard";
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
import API from "../../Utils/API";
import { PUBLIC_DASHBOARD_API_URL } from "../../Utils/Config";
import URL from "Common/Types/API/URL";
import HTTPResponse from "Common/Types/API/HTTPResponse";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import PageLoader from "Common/UI/Components/Loader/PageLoader";
import DashboardViewConfigUtil from "Common/Utils/Dashboard/DashboardViewConfig";
@@ -104,28 +105,25 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
const fetchDashboardViewConfig: PromiseVoidFunction =
async (): Promise<void> => {
const dashboard: Dashboard | null = await ModelAPI.getItem({
modelType: Dashboard,
id: props.dashboardId,
select: {
dashboardViewConfig: true,
name: true,
description: true,
},
const response: HTTPResponse<JSONObject> = await API.post<JSONObject>({
url: URL.fromString(PUBLIC_DASHBOARD_API_URL.toString()).addRoute(
`/view-config/${props.dashboardId.toString()}`,
),
data: {},
});
if (!dashboard) {
if (response.isFailure() || !response.data) {
setError("Dashboard not found");
return;
}
const config: DashboardViewConfig = JSONFunctions.deserializeValue(
dashboard.dashboardViewConfig ||
response.data["dashboardViewConfig"] ||
DashboardViewConfigUtil.createDefaultDashboardViewConfig(),
) as DashboardViewConfig;
setDashboardViewConfig(config);
setDashboardName(dashboard.name || "Untitled Dashboard");
setDashboardName((response.data["name"] as string) || "Untitled Dashboard");
if (config.refreshInterval) {
setAutoRefreshInterval(config.refreshInterval);

View File

@@ -20,6 +20,9 @@ import Dashboard from "../../Models/DatabaseModels/Dashboard";
import DashboardDomain from "../../Models/DatabaseModels/DashboardDomain";
import { EncryptionSecret } from "../EnvironmentConfig";
import { DASHBOARD_MASTER_PASSWORD_INVALID_MESSAGE } from "../../Types/Dashboard/MasterPassword";
import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
import ForbiddenException from "../../Types/Exception/ForbiddenException";
import JSONFunctions from "../../Types/JSONFunctions";
export default class DashboardAPI extends BaseAPI<
Dashboard,
@@ -202,6 +205,66 @@ export default class DashboardAPI extends BaseAPI<
},
);
// Public view-config endpoint - returns dashboard view config for the public viewer
this.router.post(
`${new this.entityType()
.getCrudApiPath()
?.toString()}/view-config/:dashboardId`,
UserMiddleware.getUserMiddleware,
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const dashboardId: ObjectID = new ObjectID(
req.params["dashboardId"] as string,
);
// Check read access (handles public check, IP whitelist, master password)
const accessResult: {
hasReadAccess: boolean;
error?: NotAuthenticatedException | ForbiddenException;
} = await DashboardService.hasReadAccess({
dashboardId,
req,
});
if (!accessResult.hasReadAccess) {
throw (
accessResult.error ||
new BadDataException("Access denied to this dashboard.")
);
}
const dashboard: Dashboard | null =
await DashboardService.findOneById({
id: dashboardId,
select: {
_id: true,
name: true,
description: true,
dashboardViewConfig: true,
},
props: {
isRoot: true,
},
});
if (!dashboard) {
throw new NotFoundException("Dashboard not found");
}
return Response.sendJsonObjectResponse(req, res, {
_id: dashboard._id?.toString() || "",
name: dashboard.name || "Dashboard",
description: dashboard.description || "",
dashboardViewConfig: dashboard.dashboardViewConfig
? JSONFunctions.serialize(dashboard.dashboardViewConfig as any)
: null,
});
} catch (err) {
next(err);
}
},
);
this.router.post(
`${new this.entityType()
.getCrudApiPath()