175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
import type { PageId, PageParams, PageRoute } from "../contracts/page";
|
|
import type { CurrentUserView } from "../contracts/workspace";
|
|
import { isPlatformAdmin } from "../contracts/workspace";
|
|
|
|
export const firstPartyRoutes: PageRoute[] = [
|
|
{
|
|
id: "profileSettings",
|
|
label: "个人设置",
|
|
path: "/profile",
|
|
hash: "#/profile",
|
|
description: "个人资料与界面偏好",
|
|
requiredCapability: "profile.settings.manage",
|
|
showInNav: false
|
|
},
|
|
{
|
|
id: "home",
|
|
label: "平台概览",
|
|
path: "/",
|
|
hash: "#/home",
|
|
description: "平台运行健康概览",
|
|
requiredCapability: "platform.overview.read",
|
|
showInNav: true
|
|
},
|
|
{
|
|
id: "servers",
|
|
label: "服务器管理",
|
|
path: "/servers",
|
|
hash: "#/servers",
|
|
description: "服务器列表、状态与生命周期",
|
|
requiredCapability: "servers.read",
|
|
showInNav: true
|
|
},
|
|
{
|
|
id: "serverDetail",
|
|
label: "服务器详情",
|
|
path: "/servers/:serverId",
|
|
hash: "#/servers/:serverId",
|
|
description: "单服务器日常运维工作台",
|
|
requiredCapability: "servers.read",
|
|
showInNav: false
|
|
},
|
|
{
|
|
id: "pluginPage",
|
|
label: "插件页面",
|
|
path: "/plugin-pages/:pluginId/:routeKey",
|
|
hash: "#/plugin-pages/:pluginId/:routeKey",
|
|
description: "平台托管的插件运维页面",
|
|
requiredCapability: "servers.read",
|
|
showInNav: false
|
|
},
|
|
{
|
|
id: "plugins",
|
|
label: "插件市场",
|
|
path: "/plugins",
|
|
hash: "#/plugins",
|
|
description: "插件发现、安装状态与文档",
|
|
requiredCapability: "plugins.market.read",
|
|
showInNav: true
|
|
},
|
|
{
|
|
id: "users",
|
|
label: "用户管理",
|
|
path: "/users",
|
|
hash: "#/users",
|
|
description: "用户、角色和访问审核",
|
|
requiredCapability: "users.manage",
|
|
showInNav: true
|
|
},
|
|
{
|
|
id: "aiProviders",
|
|
label: "AI 提供商管理",
|
|
path: "/ai-providers",
|
|
hash: "#/aiProviders",
|
|
description: "平台中介模型配置",
|
|
requiredCapability: "aiProviders.manage",
|
|
showInNav: true
|
|
},
|
|
{
|
|
id: "maintenance",
|
|
label: "系统维护",
|
|
path: "/maintenance",
|
|
hash: "#/maintenance",
|
|
description: "服务器异常与失败任务",
|
|
requiredCapability: "system.maintenance",
|
|
showInNav: true
|
|
}
|
|
];
|
|
|
|
const routesById = new Map(firstPartyRoutes.map((route) => [route.id, route]));
|
|
|
|
export interface ResolvedRoute {
|
|
route: PageRoute;
|
|
params: PageParams;
|
|
}
|
|
|
|
export function routeForPage(pageId: PageId): PageRoute {
|
|
return routesById.get(pageId) ?? firstPartyRoutes[0];
|
|
}
|
|
|
|
export function hashForPage(pageId: PageId, params: PageParams = {}): string {
|
|
const route = routeForPage(pageId);
|
|
if (pageId === "serverDetail" && params.serverId) {
|
|
const query = new URLSearchParams();
|
|
if (params.routeKey) query.set("focus", params.routeKey);
|
|
const suffix = query.toString();
|
|
return `#/servers/${encodeURIComponent(params.serverId)}${suffix ? `?${suffix}` : ""}`;
|
|
}
|
|
if (pageId === "pluginPage" && params.pluginId && params.routeKey) {
|
|
const query = new URLSearchParams();
|
|
if (params.serverId) {
|
|
query.set("serverInstanceId", params.serverId);
|
|
}
|
|
const suffix = query.toString();
|
|
return `#/plugin-pages/${encodeURIComponent(params.pluginId)}/${encodeURIComponent(params.routeKey)}${suffix ? `?${suffix}` : ""}`;
|
|
}
|
|
return route.hash;
|
|
}
|
|
|
|
export function defaultPageForUser(user: CurrentUserView): PageId {
|
|
return isPlatformAdmin(user) ? "home" : "servers";
|
|
}
|
|
|
|
export function navigationRoutesForUser(user: CurrentUserView): PageRoute[] {
|
|
return firstPartyRoutes.filter((route) => route.showInNav && user.capabilities.includes(route.requiredCapability));
|
|
}
|
|
|
|
export function canAccessRoute(user: CurrentUserView, pageId: PageId): boolean {
|
|
const route = routesById.get(pageId);
|
|
if (!route) {
|
|
return false;
|
|
}
|
|
return user.capabilities.includes(route.requiredCapability);
|
|
}
|
|
|
|
export function resolveRouteHash(hash: string, user?: CurrentUserView): ResolvedRoute {
|
|
const fallback: ResolvedRoute = {
|
|
route: routeForPage(user ? defaultPageForUser(user) : "servers"),
|
|
params: {}
|
|
};
|
|
const normalized = hash.replace(/^#/, "");
|
|
if (!normalized || normalized === "/") {
|
|
return fallback;
|
|
}
|
|
const [normalizedPath, rawQuery = ""] = normalized.split("?", 2);
|
|
const segments = normalizedPath.replace(/^\//, "").split("/").filter(Boolean);
|
|
if (segments.length === 0) {
|
|
return fallback;
|
|
}
|
|
if (segments[0] === "servers" && segments.length > 1) {
|
|
const routeKey = new URLSearchParams(rawQuery).get("focus") ?? undefined;
|
|
return { route: routeForPage("serverDetail"), params: { serverId: decodeURIComponent(segments[1]), routeKey } };
|
|
}
|
|
if (segments[0] === "plugin-pages" && segments.length > 2) {
|
|
const serverId = new URLSearchParams(rawQuery).get("serverInstanceId") ?? undefined;
|
|
return {
|
|
route: routeForPage("pluginPage"),
|
|
params: {
|
|
pluginId: decodeURIComponent(segments[1]),
|
|
routeKey: decodeURIComponent(segments[2]),
|
|
serverId
|
|
}
|
|
};
|
|
}
|
|
const key = segments[0];
|
|
const byId = routesById.get(key as PageId);
|
|
if (byId) {
|
|
return { route: byId, params: {} };
|
|
}
|
|
const byPath = firstPartyRoutes.find((route) => route.path === `/${key}`);
|
|
if (byPath) {
|
|
return { route: byPath, params: {} };
|
|
}
|
|
return fallback;
|
|
}
|