mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
feat(pluginStore): add support for third-party plugin sources with localization and configuration updates
This commit is contained in:
@@ -37,6 +37,7 @@ import {
|
||||
ApiKeysCardEditor,
|
||||
PayloadFilterRulesEditor,
|
||||
PayloadRulesEditor,
|
||||
StringListEditor,
|
||||
} from './VisualConfigEditorBlocks';
|
||||
import styles from './VisualConfigEditor.module.scss';
|
||||
|
||||
@@ -218,6 +219,10 @@ export function VisualConfigEditor({
|
||||
(apiKeysText: string) => onChange({ apiKeysText }),
|
||||
[onChange]
|
||||
);
|
||||
const handlePluginStoreSourcesChange = useCallback(
|
||||
(pluginStoreSources: string[]) => onChange({ pluginStoreSources }),
|
||||
[onChange]
|
||||
);
|
||||
const handlePayloadDefaultRulesChange = useCallback(
|
||||
(payloadDefaultRules: PayloadRule[]) => onChange({ payloadDefaultRules }),
|
||||
[onChange]
|
||||
@@ -667,6 +672,33 @@ export function VisualConfigEditor({
|
||||
/>
|
||||
</SectionGrid>
|
||||
|
||||
<SectionSubsection
|
||||
title={t('config_management.visual.sections.system.plugin_store_sources')}
|
||||
description={t(
|
||||
'config_management.visual.sections.system.plugin_store_sources_desc'
|
||||
)}
|
||||
>
|
||||
<div className={styles.fieldShell}>
|
||||
<label className={styles.fieldLabel}>
|
||||
{t('config_management.visual.sections.system.plugin_store_sources_label')}
|
||||
</label>
|
||||
<StringListEditor
|
||||
value={values.pluginStoreSources}
|
||||
disabled={disabled}
|
||||
placeholder={t(
|
||||
'config_management.visual.sections.system.plugin_store_sources_placeholder'
|
||||
)}
|
||||
inputAriaLabel={t(
|
||||
'config_management.visual.sections.system.plugin_store_sources_label'
|
||||
)}
|
||||
onChange={handlePluginStoreSourcesChange}
|
||||
/>
|
||||
<div className={styles.fieldHint}>
|
||||
{t('config_management.visual.sections.system.plugin_store_sources_hint')}
|
||||
</div>
|
||||
</div>
|
||||
</SectionSubsection>
|
||||
|
||||
<SectionGrid>
|
||||
<Input
|
||||
label={t('config_management.visual.sections.system.logs_max_size')}
|
||||
|
||||
@@ -389,7 +389,7 @@ export const ApiKeysCardEditor = memo(function ApiKeysCardEditor({
|
||||
);
|
||||
});
|
||||
|
||||
const StringListEditor = memo(function StringListEditor({
|
||||
export const StringListEditor = memo(function StringListEditor({
|
||||
value,
|
||||
disabled,
|
||||
placeholder,
|
||||
|
||||
@@ -120,6 +120,15 @@ function setStringInDoc(doc: YamlDocument, path: YamlPath, value: unknown): void
|
||||
}
|
||||
}
|
||||
|
||||
function setStringListInDoc(doc: YamlDocument, path: YamlPath, values: string[]): void {
|
||||
const nextValues = values.map((value) => value.trim()).filter(Boolean);
|
||||
if (nextValues.length > 0) {
|
||||
doc.setIn(path, nextValues);
|
||||
return;
|
||||
}
|
||||
if (docHas(doc, path)) doc.deleteIn(path);
|
||||
}
|
||||
|
||||
function setIntFromStringInDoc(doc: YamlDocument, path: YamlPath, value: unknown): void {
|
||||
const safe = typeof value === 'string' ? value : '';
|
||||
const trimmed = safe.trim();
|
||||
@@ -790,6 +799,13 @@ function getNextDirtyFields(
|
||||
] as Array<keyof VisualConfigValues>
|
||||
).forEach(updateScalarDirty);
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'pluginStoreSources')) {
|
||||
updateDirty(
|
||||
'pluginStoreSources',
|
||||
areStringArraysEqual(nextValues.pluginStoreSources, baselineValues.pluginStoreSources)
|
||||
);
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'payloadDefaultRules')) {
|
||||
updateDirty(
|
||||
'payloadDefaultRules',
|
||||
@@ -957,6 +973,7 @@ export function useVisualConfig() {
|
||||
authDir: typeof parsed['auth-dir'] === 'string' ? parsed['auth-dir'] : '',
|
||||
apiKeysText: resolveApiKeysText(parsed),
|
||||
pluginsEnabled: Boolean(plugins?.enabled),
|
||||
pluginStoreSources: parseStringList(plugins?.['store-sources']),
|
||||
|
||||
debug: Boolean(parsed.debug),
|
||||
commercialMode: Boolean(parsed['commercial-mode']),
|
||||
@@ -1126,10 +1143,28 @@ export function useVisualConfig() {
|
||||
if (
|
||||
docHas(doc, ['plugins']) ||
|
||||
values.pluginsEnabled ||
|
||||
shouldWriteManagedField(doc, ['plugins', 'enabled'], dirtyFields, 'pluginsEnabled')
|
||||
values.pluginStoreSources.length > 0 ||
|
||||
shouldWriteManagedField(doc, ['plugins', 'enabled'], dirtyFields, 'pluginsEnabled') ||
|
||||
shouldWriteManagedField(
|
||||
doc,
|
||||
['plugins', 'store-sources'],
|
||||
dirtyFields,
|
||||
'pluginStoreSources'
|
||||
)
|
||||
) {
|
||||
ensureMapInDoc(doc, ['plugins']);
|
||||
setBooleanInDoc(doc, ['plugins', 'enabled'], values.pluginsEnabled);
|
||||
if (
|
||||
values.pluginStoreSources.length > 0 ||
|
||||
shouldWriteManagedField(
|
||||
doc,
|
||||
['plugins', 'store-sources'],
|
||||
dirtyFields,
|
||||
'pluginStoreSources'
|
||||
)
|
||||
) {
|
||||
setStringListInDoc(doc, ['plugins', 'store-sources'], values.pluginStoreSources);
|
||||
}
|
||||
deleteIfMapEmpty(doc, ['plugins']);
|
||||
}
|
||||
|
||||
|
||||
@@ -920,6 +920,11 @@
|
||||
"logging_to_file_desc": "Save logs to files",
|
||||
"plugins_enabled": "Enable Plugin System",
|
||||
"plugins_enabled_desc": "Enable standard dynamic-library plugin loading; individual plugin instances are still managed on the Plugins page",
|
||||
"plugin_store_sources": "Third-party Plugin Sources",
|
||||
"plugin_store_sources_desc": "Append plugin-store registry sources; the built-in official source is always kept",
|
||||
"plugin_store_sources_label": "Plugin source registry URL (plugins.store-sources)",
|
||||
"plugin_store_sources_placeholder": "https://example.com/cliproxy-plugins/registry.json",
|
||||
"plugin_store_sources_hint": "One registry.json URL per row. Empty rows are filtered on save",
|
||||
"logs_max_size": "Log File Size Limit (MB)",
|
||||
"error_logs_max_files": "Retained Error Log Files",
|
||||
"usage_statistics_enabled": "Enable In-memory Usage Statistics",
|
||||
|
||||
@@ -907,6 +907,11 @@
|
||||
"logging_to_file_desc": "Сохранять журналы в файлы",
|
||||
"plugins_enabled": "Включить систему плагинов",
|
||||
"plugins_enabled_desc": "Включает загрузку стандартных dynamic-library плагинов; отдельные экземпляры управляются на странице плагинов",
|
||||
"plugin_store_sources": "Сторонние источники плагинов",
|
||||
"plugin_store_sources_desc": "Добавляет registry-источники магазина плагинов; встроенный официальный источник всегда сохраняется",
|
||||
"plugin_store_sources_label": "URL registry источника плагинов (plugins.store-sources)",
|
||||
"plugin_store_sources_placeholder": "https://example.com/cliproxy-plugins/registry.json",
|
||||
"plugin_store_sources_hint": "Один URL registry.json в строке. Пустые строки отфильтровываются при сохранении",
|
||||
"logs_max_size": "Максимальный размер файла журнала (МБ)",
|
||||
"error_logs_max_files": "Файлов журнала ошибок",
|
||||
"usage_statistics_enabled": "Включить статистику использования в памяти",
|
||||
|
||||
@@ -920,6 +920,11 @@
|
||||
"logging_to_file_desc": "将日志保存到文件",
|
||||
"plugins_enabled": "启用插件系统",
|
||||
"plugins_enabled_desc": "启用标准动态库插件加载;具体插件实例仍在插件管理页启停",
|
||||
"plugin_store_sources": "第三方插件源",
|
||||
"plugin_store_sources_desc": "追加插件商店 registry 源;内置官方源始终保留",
|
||||
"plugin_store_sources_label": "插件源 registry URL (plugins.store-sources)",
|
||||
"plugin_store_sources_placeholder": "https://example.com/cliproxy-plugins/registry.json",
|
||||
"plugin_store_sources_hint": "每行一个 registry.json URL,保存时会过滤空行",
|
||||
"logs_max_size": "日志文件大小限制 (MB)",
|
||||
"error_logs_max_files": "错误日志保留文件数",
|
||||
"usage_statistics_enabled": "启用内存用量统计",
|
||||
|
||||
@@ -946,6 +946,11 @@
|
||||
"logging_to_file_desc": "將記錄儲存到檔案",
|
||||
"plugins_enabled": "啟用插件系統",
|
||||
"plugins_enabled_desc": "啟用標準動態庫插件載入;具體插件實例仍在插件管理頁啟停",
|
||||
"plugin_store_sources": "第三方插件源",
|
||||
"plugin_store_sources_desc": "追加插件商店 registry 源;內建官方源始終保留",
|
||||
"plugin_store_sources_label": "插件源 registry URL(plugins.store-sources)",
|
||||
"plugin_store_sources_placeholder": "https://example.com/cliproxy-plugins/registry.json",
|
||||
"plugin_store_sources_hint": "每列一個 registry.json URL,儲存時會過濾空列",
|
||||
"logs_max_size": "記錄檔大小限制(MB)",
|
||||
"error_logs_max_files": "錯誤記錄保留檔案數",
|
||||
"usage_statistics_enabled": "啟用記憶體用量統計",
|
||||
|
||||
@@ -81,6 +81,7 @@ export type VisualConfigValues = {
|
||||
authDir: string;
|
||||
apiKeysText: string;
|
||||
pluginsEnabled: boolean;
|
||||
pluginStoreSources: string[];
|
||||
debug: boolean;
|
||||
commercialMode: boolean;
|
||||
loggingToFile: boolean;
|
||||
@@ -145,6 +146,7 @@ export const DEFAULT_VISUAL_VALUES: VisualConfigValues = {
|
||||
authDir: '',
|
||||
apiKeysText: '',
|
||||
pluginsEnabled: false,
|
||||
pluginStoreSources: [],
|
||||
debug: false,
|
||||
commercialMode: false,
|
||||
loggingToFile: false,
|
||||
|
||||
Reference in New Issue
Block a user