223 lines
7.9 KiB
TypeScript
223 lines
7.9 KiB
TypeScript
import type { AdminSection, ApiDocSection, PageKey, PlaygroundMode, WorkspaceSection, WorkspaceTaskQuery } from './types';
|
|
|
|
export interface AppRouteState {
|
|
activePage: PageKey;
|
|
adminSection: AdminSection;
|
|
apiDocSection: ApiDocSection;
|
|
playgroundMode: PlaygroundMode;
|
|
workspaceSection: WorkspaceSection;
|
|
workspaceTaskQuery: WorkspaceTaskQuery;
|
|
}
|
|
|
|
export const defaultRouteState: AppRouteState = {
|
|
activePage: 'home',
|
|
adminSection: 'overview',
|
|
apiDocSection: 'chat',
|
|
playgroundMode: 'chat',
|
|
workspaceSection: 'overview',
|
|
workspaceTaskQuery: defaultWorkspaceTaskQuery(),
|
|
};
|
|
|
|
const workspacePaths: Record<WorkspaceSection, string> = {
|
|
overview: '/workspace/overview',
|
|
billing: '/workspace/billing',
|
|
apiKeys: '/workspace/api-keys',
|
|
tasks: '/workspace/tasks',
|
|
transactions: '/workspace/transactions',
|
|
};
|
|
|
|
const adminPaths: Record<AdminSection, string> = {
|
|
overview: '/admin/overview',
|
|
globalModels: '/admin/providers',
|
|
baseModels: '/admin/base-models',
|
|
pricing: '/admin/pricing',
|
|
platforms: '/admin/platforms',
|
|
realtimeLoad: '/admin/realtime-load',
|
|
tenants: '/admin/tenants',
|
|
users: '/admin/users',
|
|
userGroups: '/admin/user-groups',
|
|
auditLogs: '/admin/audit-logs',
|
|
runtime: '/admin/runtime',
|
|
accessRules: '/admin/access-rules',
|
|
systemSettings: '/admin/system-settings',
|
|
};
|
|
|
|
const docsPaths: Record<ApiDocSection, string> = {
|
|
guideBaseUrl: '/docs/auth',
|
|
guideWebhook: '/docs/webhook',
|
|
guideErrors: '/docs/errors',
|
|
guideTesting: '/docs/testing',
|
|
chat: '/docs/chat',
|
|
responses: '/docs/responses',
|
|
embeddings: '/docs/embeddings',
|
|
reranks: '/docs/reranks',
|
|
imageGeneration: '/docs/images/generations',
|
|
imageEdit: '/docs/images/edits',
|
|
videoGeneration: '/docs/videos/generations',
|
|
asyncMode: '/docs/async',
|
|
taskRetrieve: '/docs/tasks/retrieve',
|
|
pricing: '/docs/pricing',
|
|
files: '/docs/files',
|
|
};
|
|
|
|
const playgroundPaths: Record<PlaygroundMode, string> = {
|
|
chat: '/playground/chat',
|
|
image: '/playground/image',
|
|
video: '/playground/video',
|
|
};
|
|
|
|
const workspaceSections = reverseMap(workspacePaths);
|
|
const adminSections = reverseMap(adminPaths);
|
|
const docsSections = reverseMap(docsPaths);
|
|
const playgroundSections = reverseMap(playgroundPaths);
|
|
|
|
export function parseAppRoute(input = currentLocationPath()): AppRouteState {
|
|
const origin = typeof window === 'undefined' ? 'http://localhost' : window.location.origin;
|
|
const url = new URL(input, origin);
|
|
const path = normalizePath(url.pathname);
|
|
const workspaceTaskQuery = parseWorkspaceTaskQuery(url.searchParams);
|
|
if (path === '/') return { ...defaultRouteState };
|
|
if (path.startsWith('/playground')) {
|
|
return { ...defaultRouteState, activePage: 'playground', playgroundMode: parsePlaygroundMode(path), workspaceTaskQuery };
|
|
}
|
|
if (path === '/models') return { ...defaultRouteState, activePage: 'models', workspaceTaskQuery };
|
|
if (path.startsWith('/workspace')) {
|
|
return { ...defaultRouteState, activePage: 'workspace', workspaceSection: parseWorkspaceSection(path), workspaceTaskQuery };
|
|
}
|
|
if (path.startsWith('/admin')) {
|
|
return { ...defaultRouteState, activePage: 'admin', adminSection: parseAdminSection(path), workspaceTaskQuery };
|
|
}
|
|
if (path.startsWith('/docs')) {
|
|
return { ...defaultRouteState, activePage: 'docs', apiDocSection: parseDocSection(path), workspaceTaskQuery };
|
|
}
|
|
return { ...defaultRouteState };
|
|
}
|
|
|
|
function currentLocationPath() {
|
|
if (typeof window === 'undefined') return '/';
|
|
return `${window.location.pathname}${window.location.search}`;
|
|
}
|
|
|
|
export function pathForPage(page: PageKey, route: AppRouteState): string {
|
|
if (page === 'playground') return pathForPlaygroundMode(route.playgroundMode);
|
|
if (page === 'models') return '/models';
|
|
if (page === 'workspace') return pathForWorkspaceSection(route.workspaceSection);
|
|
if (page === 'admin') return pathForAdminSection(route.adminSection);
|
|
if (page === 'docs') return pathForApiDocSection(route.apiDocSection);
|
|
return '/';
|
|
}
|
|
|
|
export function pathForWorkspaceSection(section: WorkspaceSection) {
|
|
return workspacePaths[section] ?? workspacePaths.overview;
|
|
}
|
|
|
|
export function pathForWorkspaceTaskQuery(query: WorkspaceTaskQuery) {
|
|
const normalized = normalizeWorkspaceTaskQuery(query);
|
|
const search = new URLSearchParams();
|
|
if (normalized.query) search.set('q', normalized.query);
|
|
if (normalized.modelType) search.set('type', normalized.modelType);
|
|
if (normalized.createdFrom) search.set('from', normalized.createdFrom);
|
|
if (normalized.createdTo) search.set('to', normalized.createdTo);
|
|
if (normalized.page !== 1) search.set('page', String(normalized.page));
|
|
if (normalized.pageSize !== 10) search.set('pageSize', String(normalized.pageSize));
|
|
const suffix = search.toString();
|
|
return `${workspacePaths.tasks}${suffix ? `?${suffix}` : ''}`;
|
|
}
|
|
|
|
export function pathForAdminSection(section: AdminSection) {
|
|
return adminPaths[section] ?? adminPaths.overview;
|
|
}
|
|
|
|
export function pathForApiDocSection(section: ApiDocSection) {
|
|
return docsPaths[section] ?? docsPaths.chat;
|
|
}
|
|
|
|
export function pathForPlaygroundMode(mode: PlaygroundMode) {
|
|
return playgroundPaths[mode] ?? playgroundPaths.chat;
|
|
}
|
|
|
|
function parseWorkspaceSection(path: string): WorkspaceSection {
|
|
return workspaceSections[path] ?? (path === '/workspace' ? 'overview' : 'overview');
|
|
}
|
|
|
|
function parseAdminSection(path: string): AdminSection {
|
|
if (path === '/admin') return 'overview';
|
|
if (path === '/admin/models/global') return 'baseModels';
|
|
if (path === '/admin/usergroups') return 'userGroups';
|
|
return adminSections[path] ?? 'overview';
|
|
}
|
|
|
|
function parseDocSection(path: string): ApiDocSection {
|
|
if (path === '/docs') return 'chat';
|
|
if (path === '/docs/playground') return 'guideTesting';
|
|
if (path === '/docs/api/chat') return 'chat';
|
|
if (path === '/docs/api/responses') return 'responses';
|
|
if (path === '/docs/api/embeddings') return 'embeddings';
|
|
if (path === '/docs/api/reranks') return 'reranks';
|
|
if (path === '/docs/api/media') return 'imageGeneration';
|
|
if (path === '/docs/api/videos') return 'videoGeneration';
|
|
if (path === '/docs/api/tasks') return 'taskRetrieve';
|
|
return docsSections[path] ?? 'chat';
|
|
}
|
|
|
|
function parsePlaygroundMode(path: string): PlaygroundMode {
|
|
if (path === '/playground') return 'chat';
|
|
return playgroundSections[path] ?? 'chat';
|
|
}
|
|
|
|
function normalizePath(pathname: string) {
|
|
const path = pathname.replace(/\/+$/, '');
|
|
return path || '/';
|
|
}
|
|
|
|
function reverseMap<T extends string>(value: Record<T, string>) {
|
|
return Object.fromEntries(Object.entries(value).map(([key, path]) => [path, key])) as Record<string, T>;
|
|
}
|
|
|
|
export function defaultWorkspaceTaskQuery(): WorkspaceTaskQuery {
|
|
return {
|
|
query: '',
|
|
modelType: '',
|
|
createdFrom: '',
|
|
createdTo: '',
|
|
page: 1,
|
|
pageSize: 10,
|
|
};
|
|
}
|
|
|
|
export function normalizeWorkspaceTaskQuery(query: WorkspaceTaskQuery): WorkspaceTaskQuery {
|
|
return {
|
|
query: query.query.trim(),
|
|
modelType: query.modelType.trim(),
|
|
createdFrom: query.createdFrom.trim(),
|
|
createdTo: query.createdTo.trim(),
|
|
page: positiveInt(query.page, 1),
|
|
pageSize: clampPageSize(query.pageSize),
|
|
};
|
|
}
|
|
|
|
export function workspaceTaskQueryKey(query: WorkspaceTaskQuery) {
|
|
const normalized = normalizeWorkspaceTaskQuery(query);
|
|
return JSON.stringify(normalized);
|
|
}
|
|
|
|
function parseWorkspaceTaskQuery(search: URLSearchParams): WorkspaceTaskQuery {
|
|
return normalizeWorkspaceTaskQuery({
|
|
query: search.get('q') ?? search.get('query') ?? '',
|
|
modelType: search.get('type') ?? search.get('modelType') ?? '',
|
|
createdFrom: search.get('from') ?? search.get('createdFrom') ?? '',
|
|
createdTo: search.get('to') ?? search.get('createdTo') ?? '',
|
|
page: Number(search.get('page') ?? 1),
|
|
pageSize: Number(search.get('pageSize') ?? search.get('limit') ?? 10),
|
|
});
|
|
}
|
|
|
|
function positiveInt(value: number, fallback: number) {
|
|
return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
}
|
|
|
|
function clampPageSize(value: number) {
|
|
const normalized = positiveInt(value, 10);
|
|
return Math.min(100, Math.max(1, normalized));
|
|
}
|