chore: commit workspace updates
This commit is contained in:
@@ -8,11 +8,9 @@ import { useSearchParams } from "react-router";
|
||||
import { ChannelHeadersEditor, validateChannelHeaders } from "@/components/channel-headers-editor";
|
||||
import { PaginationBar } from "@/components/layout/workspace-page";
|
||||
import { useDebouncedValue } from "@/hooks/use-debounced-value";
|
||||
import { desktopLocalChannelFormState, desktopLocalChannelPayloadValue, DESKTOP_LOCAL_CHANNEL_EXAMPLE_BASE_URL } from "@/lib/desktop-local-channel";
|
||||
import { refreshSystemChannels } from "@/lib/user-session";
|
||||
import { createAdminChannel, deleteAdminChannel, listAdminChannels, updateAdminChannel } from "@/services/api/auth";
|
||||
import { type ChannelHeader, type ModelChannel } from "@/stores/use-config-store";
|
||||
import { useUserStore } from "@/stores/use-user-store";
|
||||
import { useAdminContext } from "../admin-context";
|
||||
import { AdminPageFrame } from "../components/admin-shell";
|
||||
import { AdminDataTable, AdminRowActions, AdminStatusBadge, AdminTableEmpty, configuredSecretText } from "../components/admin-ui";
|
||||
@@ -23,7 +21,6 @@ type ChannelFormValues = {
|
||||
name: string;
|
||||
publicAlias?: string;
|
||||
baseUrl: string;
|
||||
allowLocalChannel?: boolean;
|
||||
apiKey?: string;
|
||||
secretKey?: string;
|
||||
headers?: ChannelHeader[];
|
||||
@@ -32,37 +29,11 @@ type ChannelFormValues = {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export function adminLocalChannelFormOwner(desktopLocalChannelsEnabled: boolean, hostname: string, requestedAllowLocalChannel?: boolean) {
|
||||
const state = desktopLocalChannelFormState(desktopLocalChannelsEnabled, hostname, requestedAllowLocalChannel);
|
||||
return { ...state, payloadValue: desktopLocalChannelPayloadValue(desktopLocalChannelsEnabled, hostname, requestedAllowLocalChannel) };
|
||||
}
|
||||
|
||||
export function AdminLocalChannelSwitch({ visible, checked, onChange }: { visible: boolean; checked: boolean; onChange: (checked: boolean) => void }) {
|
||||
if (!visible) return null;
|
||||
return (
|
||||
<Form.Item name="allowLocalChannel" label="允许本机渠道" valuePropName="checked" extra={`仅放行精确 localhost 或 127.0.0.1;示例:${DESKTOP_LOCAL_CHANNEL_EXAMPLE_BASE_URL}`}>
|
||||
<Switch checked={checked} onChange={onChange} />
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
||||
export function AdminLocalChannelFields({ visible, checked, form }: { visible: boolean; checked: boolean; form: Pick<FormInstance<ChannelFormValues>, "setFieldValue"> }) {
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="baseUrl" label="Base URL" rules={[{ required: true, message: "请填写 Base URL" }]}>
|
||||
<Input placeholder={checked ? DESKTOP_LOCAL_CHANNEL_EXAMPLE_BASE_URL : "填写渠道 Base URL"} />
|
||||
</Form.Item>
|
||||
<AdminLocalChannelSwitch visible={visible} checked={checked} onChange={(value) => form.setFieldValue("allowLocalChannel", value)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function adminChannelSavePayload(values: ChannelFormValues, desktopLocalChannelsEnabled: boolean, hostname: string) {
|
||||
export function adminChannelSavePayload(values: ChannelFormValues) {
|
||||
return {
|
||||
name: values.name.trim(),
|
||||
publicAlias: values.publicAlias?.trim() || "",
|
||||
baseUrl: values.baseUrl.trim(),
|
||||
allowLocalChannel: adminLocalChannelFormOwner(desktopLocalChannelsEnabled, hostname, values.allowLocalChannel).payloadValue,
|
||||
apiKey: values.apiKey?.trim() || "",
|
||||
secretKey: values.secretKey?.trim() || "",
|
||||
headers: values.headers || [],
|
||||
@@ -91,12 +62,6 @@ export default function ChannelsPage() {
|
||||
const requestSequence = useRef(0);
|
||||
const [form] = Form.useForm<ChannelFormValues>();
|
||||
const useGlobalConcurrency = Form.useWatch("useGlobalConcurrency", form) !== false;
|
||||
const requestedAllowLocalChannel = Form.useWatch("allowLocalChannel", form) === true;
|
||||
const desktopLocalChannelsEnabled = useUserStore((state) => state.features.desktopLocalChannelsEnabled);
|
||||
const desktopLocalChannelHostname = typeof window === "undefined" ? "" : window.location.hostname;
|
||||
const desktopLocalChannelControl = adminLocalChannelFormOwner(desktopLocalChannelsEnabled, desktopLocalChannelHostname, requestedAllowLocalChannel);
|
||||
const allowLocalChannel = desktopLocalChannelControl.checked;
|
||||
const showDesktopLocalChannelControl = desktopLocalChannelControl.visible;
|
||||
const hasFilters = Boolean(keyword || status !== "all");
|
||||
|
||||
const updateUrl = (patch: Record<string, string | number>, replace = false) => {
|
||||
@@ -146,7 +111,6 @@ export default function ChannelsPage() {
|
||||
? {
|
||||
name: channel.name,
|
||||
baseUrl: channel.baseUrl,
|
||||
allowLocalChannel: adminLocalChannelFormOwner(desktopLocalChannelsEnabled, desktopLocalChannelHostname, channel.allowLocalChannel).checked,
|
||||
apiKey: "",
|
||||
secretKey: "",
|
||||
headers: channel.headers || [],
|
||||
@@ -154,7 +118,7 @@ export default function ChannelsPage() {
|
||||
concurrencyLimit: channel.concurrencyLimit || undefined,
|
||||
enabled: channel.enabled !== false,
|
||||
}
|
||||
: { name: "", baseUrl: "", allowLocalChannel: false, apiKey: "", secretKey: "", headers: [], useGlobalConcurrency: true, concurrencyLimit: undefined, enabled: true },
|
||||
: { name: "", baseUrl: "", apiKey: "", secretKey: "", headers: [], useGlobalConcurrency: true, concurrencyLimit: undefined, enabled: true },
|
||||
);
|
||||
setDrawerOpen(true);
|
||||
form.setFieldsValue({ publicAlias: channel?.publicAlias || "" });
|
||||
@@ -182,7 +146,7 @@ export default function ChannelsPage() {
|
||||
}
|
||||
setSaving(true);
|
||||
try {
|
||||
const payload = adminChannelSavePayload(values, desktopLocalChannelsEnabled, desktopLocalChannelHostname);
|
||||
const payload = adminChannelSavePayload(values);
|
||||
await (editingChannel ? updateAdminChannel(editingChannel.id, payload) : createAdminChannel(payload));
|
||||
await syncChannels();
|
||||
setDrawerOpen(false);
|
||||
@@ -382,7 +346,9 @@ export default function ChannelsPage() {
|
||||
<Form.Item name="publicAlias" label="前台显示别名" extra="留空时显示渠道名称;填写后用户端只显示此别名,后台仍保留原渠道名称。" rules={[{ max: 80, message: "别名不能超过 80 个字符" }]}>
|
||||
<Input maxLength={80} placeholder="可选,例如:精选图片" />
|
||||
</Form.Item>
|
||||
<AdminLocalChannelFields visible={showDesktopLocalChannelControl} checked={allowLocalChannel} form={form} />
|
||||
<Form.Item name="baseUrl" label="Base URL" rules={[{ required: true, message: "请填写 Base URL" }]}>
|
||||
<Input placeholder="填写云端渠道 Base URL" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="apiKey"
|
||||
label={editingChannel ? `API Key / Access Key(${configuredSecretText})` : "API Key / Access Key"}
|
||||
|
||||
@@ -246,7 +246,7 @@ function taskStatusTone(value?: string): AdminStatusTone {
|
||||
}
|
||||
|
||||
function quotaUsageItems(detail: AdminUserDetail) {
|
||||
const structuredBytes = detail.storageUsage.assetBytes + detail.storageUsage.canvasBytes + detail.storageUsage.sessionBytes;
|
||||
const structuredBytes = detail.storageUsage.assetBytes + detail.storageUsage.canvasBytes;
|
||||
const bytes = (value: number) => value >= 1024 ** 3 ? `${(value / 1024 ** 3).toFixed(2)} GB` : `${(value / 1024 ** 2).toFixed(1)} MB`;
|
||||
const number = (value: number) => new Intl.NumberFormat("zh-CN").format(value);
|
||||
return [
|
||||
@@ -256,7 +256,6 @@ function quotaUsageItems(detail: AdminUserDetail) {
|
||||
{ label: "任务与请求日志数据", value: detail.storageUsage.taskBytes, limit: detail.quota.taskDataGB * 1024 ** 3, display: `${bytes(detail.storageUsage.taskBytes)} / ${detail.quota.taskDataGB} GB` },
|
||||
{ label: "素材数量", value: detail.storageUsage.assetCount, limit: detail.quota.assetCount, display: `${number(detail.storageUsage.assetCount)} / ${number(detail.quota.assetCount)}` },
|
||||
{ label: "画布数量", value: detail.storageUsage.canvasCount, limit: detail.quota.canvasCount, display: `${number(detail.storageUsage.canvasCount)} / ${number(detail.quota.canvasCount)}` },
|
||||
{ label: "Agent 会话数量", value: detail.storageUsage.sessionCount, limit: detail.quota.sessionCount, display: `${number(detail.storageUsage.sessionCount)} / ${number(detail.quota.sessionCount)}` },
|
||||
{ label: "任务历史数量", value: detail.storageUsage.taskCount, limit: detail.quota.taskCount, display: `${number(detail.storageUsage.taskCount)} / ${number(detail.quota.taskCount)}` },
|
||||
{ label: "上游请求日志数量", value: detail.storageUsage.apiCallCount, limit: detail.quota.apiCallLogCount, display: `${number(detail.storageUsage.apiCallCount)} / ${number(detail.quota.apiCallLogCount)}` },
|
||||
];
|
||||
|
||||
@@ -323,7 +323,7 @@ export function ChannelModelEditor({
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
{modelCapability === "image" || modelCapability === "video" ? (
|
||||
{modelCapability === "text" || modelCapability === "image" || modelCapability === "video" ? (
|
||||
<section className="admin-model-editor-section admin-model-editor-section-stacked admin-model-editor-parameters">
|
||||
<SectionHeading title="协议参数" description="配置可发送参数、支持值与默认值;仅影响当前模型。" />
|
||||
<div className="admin-model-editor-section-content">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { App, Button, Skeleton } from "antd";
|
||||
import { Switch } from "@/components/ui/base/switch";
|
||||
import { AlertTriangle, Clapperboard, Coins, ListChecks, LockKeyhole, MonitorCog, PlugZap, RadioTower, RefreshCw, ShieldCheck, Sparkles } from "lucide-react";
|
||||
import { AlertTriangle, Clapperboard, Coins, ListChecks, MonitorCog, PlugZap, RadioTower, RefreshCw, ShieldCheck, Sparkles } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getAdminFeatureAvailability, updateAdminFeatureAvailability } from "@/services/api/auth";
|
||||
@@ -269,7 +269,6 @@ export default function FeatureAvailabilityPanel() {
|
||||
status={<AdminStatusBadge label={draftFeatures.frontendModelsEnabled ? "前台模型目录" : "系统渠道"} tone="info" />}
|
||||
>
|
||||
<FeatureSourceRow row={modelFeatureRows[0]} saved={savedFeatures} draft={draftFeatures} saving={saving} onChange={requestFeatureChange} />
|
||||
<FeatureRuntimeRow enabled={draftFeatures.desktopLocalChannelsEnabled} />
|
||||
</FeatureDomainPanel>
|
||||
</div>
|
||||
</div>
|
||||
@@ -341,18 +340,6 @@ function FeatureSourceRow({ row, saved, draft, saving, onChange }: { row: Featur
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureRuntimeRow({ enabled }: { enabled: boolean }) {
|
||||
return (
|
||||
<aside className="admin-feature-runtime-note" aria-label="桌面本地渠道部署状态">
|
||||
<LockKeyhole className="size-4" aria-hidden="true" />
|
||||
<div>
|
||||
<strong>桌面本地渠道由部署环境控制</strong>
|
||||
</div>
|
||||
<AdminStatusBadge label={enabled ? "当前可用" : "当前不可用"} tone={enabled ? "success" : "neutral"} />
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
function effectiveFeatureValue(features: FeatureAvailability, key: FeatureKey) {
|
||||
if (key === "systemPluginsVisibleToUsers") return features.pluginCenterEnabled && features.systemPluginsVisibleToUsers;
|
||||
return features[key];
|
||||
@@ -388,7 +375,6 @@ function parseFeatureAvailability(value: unknown): FeatureAvailability {
|
||||
frontendModelsEnabled: record.frontendModelsEnabled as boolean,
|
||||
pluginCenterEnabled: record.pluginCenterEnabled as boolean,
|
||||
systemPluginsVisibleToUsers: record.systemPluginsVisibleToUsers as boolean,
|
||||
desktopLocalChannelsEnabled: record.desktopLocalChannelsEnabled === true,
|
||||
configured: typeof record.configured === "boolean" ? record.configured : undefined,
|
||||
updatedBy: typeof record.updatedBy === "string" ? record.updatedBy : undefined,
|
||||
updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : undefined,
|
||||
|
||||
@@ -9,8 +9,7 @@ import { PaginationBar } from "@/components/layout/workspace-page";
|
||||
import "@/lib/plugins/builtin";
|
||||
import { EAGLE_PLUGIN_ID } from "@/lib/plugins/builtin/eagle";
|
||||
import { PROMPT_OPTIMIZER_PLUGIN_ID } from "@/lib/plugins/builtin/prompt-optimizer";
|
||||
import { COMFYUI_PLUGIN_ID, RUNNINGHUB_PLUGIN_ID } from "@/lib/plugins/builtin/workflows";
|
||||
import { MEDIA_CONVERSION_PLUGIN_ID } from "@/lib/plugins/builtin/media-conversion";
|
||||
import { RUNNINGHUB_PLUGIN_ID } from "@/lib/plugins/builtin/workflows";
|
||||
import { listRegisteredPlugins } from "@/lib/plugins/plugin-registry";
|
||||
import type { PluginManifest, PluginManifestV2 } from "@/lib/plugins/plugin-types";
|
||||
import { fetchAdminPlugins, setPluginPlatformAvailability, uninstallPlugin, uploadPlugin, type AdminPluginState, type BackendPlugin, type PluginManagement } from "@/services/api/plugins";
|
||||
@@ -27,7 +26,7 @@ type AdminPluginItem = {
|
||||
error?: string;
|
||||
};
|
||||
|
||||
const officialApplicationIds = new Set([RUNNINGHUB_PLUGIN_ID, COMFYUI_PLUGIN_ID, EAGLE_PLUGIN_ID, PROMPT_OPTIMIZER_PLUGIN_ID, "portrait-clearance", MEDIA_CONVERSION_PLUGIN_ID]);
|
||||
const officialApplicationIds = new Set([RUNNINGHUB_PLUGIN_ID, EAGLE_PLUGIN_ID, PROMPT_OPTIMIZER_PLUGIN_ID]);
|
||||
|
||||
export default function AdminPluginsPage() {
|
||||
const { message, modal } = App.useApp();
|
||||
@@ -371,7 +370,7 @@ function mergePlugins(remote: BackendPlugin[]): AdminPluginItem[] {
|
||||
origin: "official",
|
||||
kind: application ? "application" : "protocol",
|
||||
activationScope: application ? "user" : "system",
|
||||
configurationScope: application ? (plugin.manifest.id === EAGLE_PLUGIN_ID || plugin.manifest.id === RUNNINGHUB_PLUGIN_ID || plugin.manifest.id === COMFYUI_PLUGIN_ID ? "user" : "none") : "system",
|
||||
configurationScope: application ? (plugin.manifest.id === EAGLE_PLUGIN_ID || plugin.manifest.id === RUNNINGHUB_PLUGIN_ID ? "user" : "none") : "system",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ type PolicySectionDefinition = {
|
||||
|
||||
const resourceFields: PolicyField[] = [
|
||||
{ group: "resource", name: "resourceUploadMB", label: "普通资源单文件", extra: "素材上传和远程导入的单文件业务上限。", unit: "MB", max: 999 },
|
||||
{ group: "resource", name: "sessionUploadMB", label: "Agent 会话附件", extra: "单个会话附件的大小上限。", unit: "MB", max: 999 },
|
||||
{ group: "resource", name: "generatedFileMB", label: "单个生成资源", extra: "上游生成响应和落库资源的单文件上限。", unit: "MB", max: 999 },
|
||||
{ group: "resource", name: "dailyUploadMB", label: "每日上传总量", extra: "按 UTC 自然日累计资源与附件上传。", unit: "MB", max: 999_999 },
|
||||
{ group: "resource", name: "storedFileGB", label: "账号文件总量", extra: "资源文件与 Agent 会话附件合计。", unit: "GB", max: 999 },
|
||||
@@ -32,7 +31,6 @@ const resourceFields: PolicyField[] = [
|
||||
{ group: "resource", name: "taskDataGB", label: "任务数据总量", extra: "任务历史、结果和上游请求日志合计。", unit: "GB", max: 999 },
|
||||
{ group: "resource", name: "assetCount", label: "素材数量", extra: "单账号可保存的素材记录数。", unit: "条", max: 999_999_999 },
|
||||
{ group: "resource", name: "canvasCount", label: "画布数量", extra: "单账号可保存的画布数量。", unit: "个", max: 999_999_999 },
|
||||
{ group: "resource", name: "sessionCount", label: "Agent 会话数量", extra: "单账号可保存的 Agent 会话数量。", unit: "个", max: 999_999_999 },
|
||||
{ group: "resource", name: "taskCount", label: "任务历史数量", extra: "单账号保留的任务历史记录数。", unit: "条", max: 999_999_999 },
|
||||
{ group: "resource", name: "apiCallLogCount", label: "请求日志数量", extra: "单账号保留的上游请求日志数。", unit: "条", max: 999_999_999 },
|
||||
{ group: "resource", name: "recycleBinRetentionDays", label: "回收站自动清理时间", extra: "素材移入回收站后自动彻底删除的天数,0 表示不自动清理。", unit: "天", min: 0, max: 365 },
|
||||
@@ -55,10 +53,8 @@ const timeoutFields: PolicyField[] = [
|
||||
|
||||
const rateFields: PolicyField[] = [
|
||||
{ group: "request", name: "taskCreatePerMinute", label: "任务创建", extra: "每账号每分钟允许创建的任务数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "sessionCreatePerMinute", label: "会话创建", extra: "每账号每分钟允许创建的会话数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "resourceUploadPerMinute", label: "资源上传", extra: "每账号每分钟上传资源的次数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "resourceImportPerMinute", label: "资源导入", extra: "每账号每分钟导入远程资源的次数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "sessionFilePerMinute", label: "会话附件", extra: "每账号每分钟上传会话附件的次数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "assetWritePerMinute", label: "素材写入", extra: "每账号每分钟写入素材的次数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "canvasWritePerMinute", label: "画布写入", extra: "每账号每分钟写入画布的次数。", unit: "次/分钟", max: 999_999 },
|
||||
{ group: "request", name: "registerPerHour", label: "账号注册", extra: "每 IP 每小时允许的注册请求次数,包含失败尝试;多人共用网络时建议至少 30 次。", unit: "次/小时", max: 999_999 },
|
||||
|
||||
Reference in New Issue
Block a user