fix(gpu-service): show global instance templates under custom scopes

The Instance Templates management page requested the `mine` list scope,
which drops admin-curated Global templates for non-admin callers. The
Inference Backend page uses the default scope and shows Global + own
rows, so the two pages behaved inconsistently.

Use the default scope on the templates page so Global templates are
visible alongside the caller's own, and gate Edit/Delete on the card so
Global rows a non-admin can't modify render read-only.
This commit is contained in:
gitlawr
2026-07-08 16:27:40 +08:00
committed by jialin
parent f18c12662b
commit fdafdfc3d1
3 changed files with 18 additions and 10 deletions
@@ -6,8 +6,8 @@ import useUserDirectory from '../hooks/use-user-directory';
/** /**
* Owner tag for template cards, disambiguating same-name templates in * Owner tag for template cards, disambiguating same-name templates in
* the admin's cross-tenant view. Renders nothing for non-admin callers * the admin's cross-tenant view. Renders nothing for non-admin callers
* (the management page is `mine`-scoped for them) and when a plugin * (gated on `canSeeAdmin`) and when a plugin provides its own
* provides its own `OwnerScopeTag` slot. * `OwnerScopeTag` slot.
*/ */
const OwnerTag: React.FC<{ ownerId?: number | null }> = ({ ownerId }) => { const OwnerTag: React.FC<{ ownerId?: number | null }> = ({ ownerId }) => {
const intl = useIntl(); const intl = useIntl();
@@ -28,7 +28,7 @@ import {
TemplateCard, TemplateCard,
ThemeTag ThemeTag
} from '@gpustack/core-ui'; } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max'; import { useAccess, useIntl } from '@umijs/max';
import { Button, Tag } from 'antd'; import { Button, Tag } from 'antd';
import { useMemo } from 'react'; import { useMemo } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@@ -144,8 +144,15 @@ interface TemplateCardProps {
const TemplateCardItem: React.FC<TemplateCardProps> = ({ data, onSelect }) => { const TemplateCardItem: React.FC<TemplateCardProps> = ({ data, onSelect }) => {
const intl = useIntl(); const intl = useIntl();
const access = useAccess();
const { isDarkTheme } = useUserSettings(); const { isDarkTheme } = useUserSettings();
// Global templates (owner_principal_id NULL) are admin-curated and
// only editable by an admin. Principal-owned rows only reach a
// non-admin's list when they own them, so those are always
// manageable by the caller.
const canManage = !!access.canSeeAdmin || data.owner_principal_id != null;
const manufacturerLabelMap: Record<string, string> = useMemo(() => { const manufacturerLabelMap: Record<string, string> = useMemo(() => {
return Object.values(GPUsConfigs).reduce( return Object.values(GPUsConfigs).reduce(
(acc, item) => { (acc, item) => {
@@ -234,6 +241,9 @@ const TemplateCardItem: React.FC<TemplateCardProps> = ({ data, onSelect }) => {
}; };
const renderActions = () => { const renderActions = () => {
if (!canManage) {
return null;
}
return ( return (
<span onClick={handleonClickAction} className="operations"> <span onClick={handleonClickAction} className="operations">
<DropdownActions <DropdownActions
+5 -7
View File
@@ -45,13 +45,11 @@ const GPUServiceTemplates: React.FC = () => {
isInfiniteScroll: true, isInfiniteScroll: true,
contentForDelete: intl.formatMessage({ id: 'gpuservice.template' }), contentForDelete: intl.formatMessage({ id: 'gpuservice.template' }),
defaultQueryParams: { defaultQueryParams: {
perPage: 24, // Default (non-``mine``) scope: Global rows plus rows owned by the
// Management view: drop Global rows for non-admin callers — the // caller's current principal, matching the Inference Backend page.
// page is a CRUD surface, and admin-curated Global templates // Global rows a non-admin can't edit render read-only — the card
// they can't edit only add visual noise. The instance-create // gates Edit/Delete on ownership.
// picker (uses ``useQueryTemplates`` separately) doesn't set perPage: 24
// this and so still sees Global presets.
mine: true
} }
}); });
const { openTemplateModalStatus, openTemplateModal, closeTemplateModal } = const { openTemplateModalStatus, openTemplateModal, closeTemplateModal } =