mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Add public view-config endpoint for dashboard with access control
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user