feat(gpu-service): scope create forms by the selected organization
In the platform-admin "all organizations" view, add an organization picker (the CreateOrgScopeField slot) to the SSH public key, storage type, storage, GPU instance and instance-template create forms, placed below the name / display-name fields. Drop the hidden owner field and let the owner derive from the request context, matching the model-route form. For GPU instances, scope the instance-type list to clusters the chosen org owns (client-side, by cluster owner) so an instance can't be scheduled onto another org's cluster; when the org owns none, show "no instance type available" and clear the selection, cluster and CPU/memory fields. Instance templates gain a Global level (NULL owner) and an owner tag on the card.
This commit is contained in:
@@ -13,6 +13,7 @@ import theadLogoZH from '@/assets/logo/t-head-zh.png';
|
||||
import tensorflowkLogo from '@/assets/logo/tensorflow.svg';
|
||||
import ubuntuLogo from '@/assets/logo/ubuntu_logo.png';
|
||||
import vllmLogo from '@/assets/logo/vllm.png';
|
||||
import PluginExtraFields from '@/components/plugin-extra-fields';
|
||||
import {
|
||||
GPUsConfigs,
|
||||
manfacturerValueMap
|
||||
@@ -257,6 +258,10 @@ const TemplateCardItem: React.FC<TemplateCardProps> = ({ data, onSelect }) => {
|
||||
{data.displayName || data.name || '-'}
|
||||
</AutoTooltip>
|
||||
{renderManufacturerTag()}
|
||||
<PluginExtraFields
|
||||
name="OwnerScopeTag"
|
||||
context={{ ownerPrincipalId: data.owner_principal_id }}
|
||||
/>
|
||||
</CardName>
|
||||
<InfoItem>
|
||||
<span>
|
||||
|
||||
@@ -35,6 +35,11 @@ export interface FormData {
|
||||
export interface ListItem extends FormData {
|
||||
id: number;
|
||||
status?: string;
|
||||
// NULL = a shared, admin-managed preset visible to everyone; a
|
||||
// non-NULL id scopes the template to a single owner. Surfaced via
|
||||
// the `OwnerScopeTag` slot on the card so the owner is visible at a
|
||||
// glance. Optional on the wire — absent in single-owner builds.
|
||||
owner_principal_id?: number | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import PluginExtraFields from '@/components/plugin-extra-fields';
|
||||
import { validateLabelNameRegxFor63 } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import {
|
||||
Input as CInput,
|
||||
InputNumber,
|
||||
@@ -28,12 +30,14 @@ export interface BasicResourceMax {
|
||||
|
||||
interface BasicProps {
|
||||
page?: 'template' | 'instance';
|
||||
action?: PageActionType;
|
||||
disabled?: boolean;
|
||||
onceMaxRequest?: BasicResourceMax;
|
||||
}
|
||||
|
||||
const Basic: React.FC<BasicProps> = ({
|
||||
page = 'template',
|
||||
action,
|
||||
onceMaxRequest,
|
||||
disabled
|
||||
}) => {
|
||||
@@ -105,6 +109,10 @@ const Basic: React.FC<BasicProps> = ({
|
||||
maxLength={63}
|
||||
/>
|
||||
</Form.Item>
|
||||
<PluginExtraFields
|
||||
name="CreateOrgScopeField"
|
||||
context={{ action, allowGlobal: true }}
|
||||
/>
|
||||
<Form.Item<FormData>
|
||||
name="manufacturer"
|
||||
rules={[
|
||||
|
||||
@@ -82,7 +82,7 @@ const GPUServiceTemplateForm: React.FC<TemplateFormProps> = forwardRef(
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Basic />
|
||||
<Basic action={action} />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user