Four purely-additive seams that let build-time tooling extend host behaviour without forking files: * `src/access.extensions.ts` — identity `applyAccessExtensions` (mirrors `config/routes.extensions.ts`). `src/access.ts` runs the resolved predicate object through it. Adds two new predicates, `canSeeOrgAdmin` and `canManageCurrentOrg`, alongside the existing `canSeeAdmin`. Resources, Models children, Cluster Management, and Resources/Backends are retagged from `canSeeAdmin` to `canSeeOrgAdmin`. Users and Dashboard stay strict. * `src/request.extensions.ts` — identity-empty `extraRequestInterceptors`. `src/request-config.tsx` spreads it into the existing `requestInterceptors` list so extensions can inject context-aware headers without forking the request config. * Users page action column — renders `getGPUStackPlugin()?.components?.UserRowActions` next to the existing DropdownButtons inside a Space when a plugin component is registered. If absent, the cell renders exactly as before. * `src/components/plugin-extra-fields.tsx` — generic component-slot helper. Renders `pluginManager.components.<name>` if registered, forwarding a `context` payload. Used by create/edit forms to let plugins inject extra `Form.Item` fields. Mounted in the relevant create forms — API Keys, Cloud Credentials, Clusters, Model Routes, Model Providers, and Inference Backends — under the slot name `CreateOrgScopeField`. Resources whose org is implicit from a parent (Models / Workers / Benchmarks / Worker Pools / Model Files inherit from the chosen Cluster) deliberately don't mount the slot.
59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
import { userAtom } from '@/atoms/user';
|
|
import { clearAtomStorage } from '@/atoms/utils';
|
|
import { history, RequestConfig } from '@umijs/max';
|
|
import { message } from 'antd';
|
|
import { DEFAULT_ENTER_PAGE } from './config/settings';
|
|
import ErrorMessageContent from './pages/_components/error-message-content';
|
|
import { extraRequestInterceptors } from './request.extensions';
|
|
|
|
// these APIs do not via the GPUSTACK_API_BASE_URL
|
|
const NoBaseURLAPIs = ['/auth', '/v1', '/version', '/proxy', '/update'];
|
|
|
|
export const requestConfig: RequestConfig = {
|
|
headers: {
|
|
'Content-Security-Policy': "frame-ancestors 'self'",
|
|
'X-Frame-Options': 'SAMEORIGIN'
|
|
},
|
|
errorConfig: {
|
|
errorThrower: (res: any) => {
|
|
// to do something
|
|
},
|
|
errorHandler: (error: any, opts: any) => {
|
|
const { message: errorMessage, response } = error;
|
|
const errMsg =
|
|
response?.data?.error?.message ||
|
|
response?.data?.message ||
|
|
errorMessage;
|
|
|
|
if (!opts?.skipErrorHandler && response?.status) {
|
|
message.error({
|
|
content: <ErrorMessageContent errMsg={errMsg}></ErrorMessageContent>
|
|
});
|
|
}
|
|
if (response?.status === 401) {
|
|
clearAtomStorage(userAtom);
|
|
|
|
history.push(DEFAULT_ENTER_PAGE.login, { replace: true });
|
|
}
|
|
}
|
|
},
|
|
requestInterceptors: [
|
|
(url, options) => {
|
|
if (NoBaseURLAPIs.some((api) => url.startsWith(api))) {
|
|
options.baseURL = '';
|
|
return { url, options };
|
|
}
|
|
return { url, options };
|
|
},
|
|
// Build-time tooling can plug additional interceptors via
|
|
// `request.extensions.ts`. Default is an empty list.
|
|
...extraRequestInterceptors
|
|
],
|
|
responseInterceptors: [
|
|
(response) => {
|
|
// to do something
|
|
return response;
|
|
}
|
|
]
|
|
};
|