Files
gpustack-ui/src/request-config.tsx
T
gitlawrandjialin 2889ba7c78 feat: plugin extension slots for access, request interceptors, Users actions, and form fields
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.
2026-05-08 18:29:52 +08:00

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;
}
]
};