Files
gpustack-ui/src/config/global.d.ts
T
gitlawrandjialin 6b47bacfac feat: gate GPU Service on Kubernetes cluster availability
GPU Service today only schedules on Kubernetes clusters; Docker / cloud
clusters can't host the CRDs. Without any awareness of that, Org
members whose Org has no K8s cluster (and no cluster_access grant on
one) saw a menu they couldn't use and a form that bottomed out with
backend errors.

Two changes lock the UX down:

- Boot probes the caller's cluster list once and stashes
  hasKubernetesCluster in initialState. A new canSeeGpuService
  predicate gates the menu — admins and Org owners always see it
  (they can add the cluster); everyone else only sees it when a
  reachable K8s cluster actually exists.

- The instances page filters its own cluster list to Kubernetes
  before deciding what to show. With nothing reachable we render the
  Deployments-style 'No clusters available. Add a Kubernetes cluster
  to get started.' empty state and hide the create-instance CTA;
  admins and Org owners additionally get the 'Add cluster' button
  that jumps to cluster management.
2026-05-27 13:41:32 +08:00

132 lines
3.0 KiB
TypeScript

type PageActionType = 'create' | 'update' | 'view' | 'edit' | 'copy';
declare namespace Global {
type WithFalse<T> = T | false;
interface Pagination {
page: number;
perPage?: number;
watch?: boolean;
}
interface PageResponse<T> {
items: T[];
pagination: {
total: number;
totalPage: number;
page: number;
perPage: number;
};
}
interface K8sPageResponse<T> {
apiVersion: string;
items: T[];
kind: string;
metadata: {
continue?: string;
resourceVersion: string;
selfLink?: string;
[key: string]: any;
};
}
interface K8sCommonData {
kind: string;
apiVersion: string;
metadata: {
name: string;
namespace: string;
[key: string]: any;
};
[key: string]: any;
}
interface K8sSearchParams {
allowWatchBookmarks?: boolean;
continue?: string;
fieldSelector?: string;
labelSelector?: string;
limit?: number;
resourceVersion?: string;
resourceVersionMatch?: string;
sendInitialEvents?: boolean;
timeoutSeconds?: number;
watch?: boolean;
[key: string]: any;
}
interface UserInfo {
username: string;
is_admin: boolean;
full_name: string;
require_password_change: boolean;
id: number;
source: string;
avatar_url: string;
org_name?: string;
}
type EmptyObject = Record<never, never>;
type BaseListItem<T, U extends object = EmptyObject> = {
key: string;
locale?: boolean;
value: T;
} & Partial<U>;
type BaseOption<T, U extends object = EmptyObject> = {
label: string;
locale?: boolean;
value: T;
meta?: Record<string, any>;
} & Partial<U>;
type BaseOptionGroup<T, U extends object = EmptyObject> = {
label: string;
options?: BaseOption<T, U>[];
};
interface HintOptions {
label: string;
value: string;
opts?: Array<BaseOption<string | number>>;
}
interface InitialStateType {
fetchUserInfo: () => Promise<UserInfo>;
currentUser?: UserInfo;
// Captured at app boot so access predicates can gate GPU Service —
// the feature is Kubernetes-only, and Org members without a K8s
// cluster they can schedule on shouldn't see the menu. Refreshed
// by full page reload (e.g. OrgSwitcher) which re-runs
// getInitialState.
hasKubernetesCluster?: boolean;
}
type SearchParams = Pagination & { search?: string; [key: string]: any };
type MessageType = 'transition' | 'warning' | 'danger' | 'success' | 'info';
interface ScrollerModalProps<T = unknown, U = unknown> {
title?: string;
action?: PageActionType;
open: boolean;
currentData?: T | null;
onOk?: (values: U) => void;
onCancel: () => void;
}
interface ActionItem<T> {
label: string;
key: string;
icon: React.ReactNode;
locale?: boolean;
props?: {
danger?: boolean;
};
visible?: (record: T) => boolean;
disabled?: (record: T) => boolean;
}
}
interface Window {
__GPUSTACK_BODY_SCROLLER__?: any;
}