sync: bring over remaining release/custom-0.1.115 changes
- Extract PublicSettingsInjectionPayload named struct with drift test - Add channel_monitor_default_interval_seconds to SSR injection - Add image_output_price to SupportedModelChip - Simplify AppSidebar buildSelfNavItems (admins see available channels) - Add gateway WARN logs for 503 no-available-accounts branches - Wire ChannelMonitorRunner into provideCleanup for graceful shutdown - Add migrations 130/131 (CC template userid fix + mimicry field cleanup) - Clean up fork-only features (sora, claude max simulation, client affinity) - Remove ~320 obsolete i18n keys - Add codexUsage utility, WechatServiceButton, BulkEditAccountModal - Tidy go.sum
This commit is contained in:
@@ -3751,91 +3751,90 @@
|
||||
|
||||
<!-- Tab: Features (功能开关) -->
|
||||
<div v-show="activeTab === 'features'" class="space-y-6">
|
||||
|
||||
<div class="card">
|
||||
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ t('admin.settings.features.channelMonitor.title') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.features.channelMonitor.description') }}
|
||||
</p>
|
||||
<p class="mt-1.5 text-xs">
|
||||
<router-link
|
||||
to="/admin/channels/monitor"
|
||||
class="inline-flex items-center gap-1 text-primary-600 hover:underline dark:text-primary-400"
|
||||
>
|
||||
{{ t('admin.settings.features.channelMonitor.configureLink') }}
|
||||
<span aria-hidden="true">→</span>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-5 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ t('admin.settings.features.channelMonitor.enabled') }}
|
||||
</label>
|
||||
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.features.channelMonitor.enabledHint') }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle v-model="form.channel_monitor_enabled" />
|
||||
</div>
|
||||
|
||||
<div v-if="form.channel_monitor_enabled">
|
||||
<label class="input-label">
|
||||
{{ t('admin.settings.features.channelMonitor.defaultInterval') }}
|
||||
<span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model.number="form.channel_monitor_default_interval_seconds"
|
||||
type="number"
|
||||
min="15"
|
||||
max="3600"
|
||||
class="input"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
{{ t('admin.settings.features.channelMonitor.defaultIntervalHint') }}
|
||||
<div class="card">
|
||||
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ t("admin.settings.features.channelMonitor.title") }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t("admin.settings.features.channelMonitor.description") }}
|
||||
</p>
|
||||
<p class="mt-1.5 text-xs">
|
||||
<router-link
|
||||
to="/admin/channels/monitor"
|
||||
class="inline-flex items-center gap-1 text-primary-600 hover:underline dark:text-primary-400"
|
||||
>
|
||||
{{ t("admin.settings.features.channelMonitor.configureLink") }}
|
||||
<span aria-hidden="true">→</span>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-5 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ t("admin.settings.features.channelMonitor.enabled") }}
|
||||
</label>
|
||||
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t("admin.settings.features.channelMonitor.enabledHint") }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle v-model="form.channel_monitor_enabled" />
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ t('admin.settings.features.availableChannels.title') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.features.availableChannels.description') }}
|
||||
</p>
|
||||
<p class="mt-1.5 text-xs">
|
||||
<router-link
|
||||
to="/admin/channels/pricing"
|
||||
class="inline-flex items-center gap-1 text-primary-600 hover:underline dark:text-primary-400"
|
||||
>
|
||||
{{ t('admin.settings.features.availableChannels.configureLink') }}
|
||||
<span aria-hidden="true">→</span>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-5 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ t('admin.settings.features.availableChannels.enabled') }}
|
||||
<div v-if="form.channel_monitor_enabled">
|
||||
<label class="input-label">
|
||||
{{ t("admin.settings.features.channelMonitor.defaultInterval") }}
|
||||
<span class="text-red-500">*</span>
|
||||
</label>
|
||||
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.features.availableChannels.enabledHint') }}
|
||||
<input
|
||||
v-model.number="form.channel_monitor_default_interval_seconds"
|
||||
type="number"
|
||||
min="15"
|
||||
max="3600"
|
||||
class="input"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
{{ t("admin.settings.features.channelMonitor.defaultIntervalHint") }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle v-model="form.available_channels_enabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ t("admin.settings.features.availableChannels.title") }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t("admin.settings.features.availableChannels.description") }}
|
||||
</p>
|
||||
<p class="mt-1.5 text-xs">
|
||||
<router-link
|
||||
to="/admin/channels/pricing"
|
||||
class="inline-flex items-center gap-1 text-primary-600 hover:underline dark:text-primary-400"
|
||||
>
|
||||
{{ t("admin.settings.features.availableChannels.configureLink") }}
|
||||
<span aria-hidden="true">→</span>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-5 p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ t("admin.settings.features.availableChannels.enabled") }}
|
||||
</label>
|
||||
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t("admin.settings.features.availableChannels.enabledHint") }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle v-model="form.available_channels_enabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /Tab: Features -->
|
||||
<!-- /Tab: Features -->
|
||||
|
||||
<!-- Tab: Email -->
|
||||
<!-- Tab: Payment -->
|
||||
@@ -4254,8 +4253,6 @@
|
||||
}}</label>
|
||||
<ImageUpload
|
||||
v-model="form.payment_help_image_url"
|
||||
:upload-label="t('admin.settings.site.uploadImage')"
|
||||
:remove-label="t('admin.settings.site.remove')"
|
||||
:placeholder="
|
||||
t('admin.settings.payment.helpImagePlaceholder')
|
||||
"
|
||||
|
||||
@@ -155,8 +155,6 @@ vi.mock("vue-i18n", async () => {
|
||||
"admin.settings.payment.findProvider": "查看支持的支付方式",
|
||||
"admin.settings.openaiExperimentalScheduler.title": "OpenAI 实验调度策略",
|
||||
"admin.settings.openaiExperimentalScheduler.description": "默认关闭。开启后仅影响本网关在 OpenAI 账号间的实验性调度选择逻辑,不代表上游 OpenAI 官方能力。",
|
||||
"admin.settings.site.uploadImage": "上传图片",
|
||||
"admin.settings.site.remove": "移除",
|
||||
};
|
||||
return {
|
||||
...actual,
|
||||
@@ -242,37 +240,6 @@ const SelectStub = defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
const ImageUploadStub = defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
uploadLabel: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
removeLabel: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
return () =>
|
||||
h("div", {
|
||||
class: "image-upload-stub",
|
||||
"data-model-value": props.modelValue,
|
||||
"data-upload-label": props.uploadLabel,
|
||||
"data-remove-label": props.removeLabel,
|
||||
"data-placeholder": props.placeholder,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const baseSettingsResponse = {
|
||||
registration_enabled: true,
|
||||
email_verify_enabled: false,
|
||||
@@ -408,7 +375,7 @@ function mountView() {
|
||||
GroupBadge: true,
|
||||
GroupOptionItem: true,
|
||||
ProxySelector: true,
|
||||
ImageUpload: ImageUploadStub,
|
||||
ImageUpload: true,
|
||||
BackupSettings: true,
|
||||
},
|
||||
},
|
||||
@@ -615,7 +582,7 @@ describe("admin SettingsView payment visible method controls", () => {
|
||||
GroupBadge: true,
|
||||
GroupOptionItem: true,
|
||||
ProxySelector: true,
|
||||
ImageUpload: ImageUploadStub,
|
||||
ImageUpload: true,
|
||||
BackupSettings: true,
|
||||
},
|
||||
},
|
||||
@@ -641,24 +608,6 @@ describe("admin SettingsView payment visible method controls", () => {
|
||||
);
|
||||
expect(wrapper.text()).not.toContain("OpenAI 高级调度器");
|
||||
});
|
||||
|
||||
it("passes translated upload and remove labels to the payment help image uploader", async () => {
|
||||
const wrapper = mountView();
|
||||
|
||||
await flushPromises();
|
||||
await openPaymentTab(wrapper);
|
||||
|
||||
const imageUploads = wrapper.findAll(".image-upload-stub");
|
||||
expect(imageUploads.length).toBeGreaterThan(0);
|
||||
|
||||
const paymentHelpImageUpload = imageUploads.find(
|
||||
(node) => node.attributes("data-placeholder") === "admin.settings.payment.helpImagePlaceholder",
|
||||
);
|
||||
|
||||
expect(paymentHelpImageUpload).toBeDefined();
|
||||
expect(paymentHelpImageUpload?.attributes("data-upload-label")).toBe("上传图片");
|
||||
expect(paymentHelpImageUpload?.attributes("data-remove-label")).toBe("移除");
|
||||
});
|
||||
});
|
||||
|
||||
describe("admin SettingsView wechat connect controls", () => {
|
||||
|
||||
@@ -122,6 +122,7 @@ const platformRows = computed((): SummaryRow[] => {
|
||||
available_accounts: availableAccounts,
|
||||
rate_limited_accounts: safeNumber(avail.rate_limit_count),
|
||||
|
||||
|
||||
error_accounts: safeNumber(avail.error_count),
|
||||
total_concurrency: totalConcurrency,
|
||||
used_concurrency: usedConcurrency,
|
||||
@@ -161,7 +162,6 @@ const groupRows = computed((): SummaryRow[] => {
|
||||
total_accounts: totalAccounts,
|
||||
available_accounts: availableAccounts,
|
||||
rate_limited_accounts: safeNumber(avail.rate_limit_count),
|
||||
|
||||
error_accounts: safeNumber(avail.error_count),
|
||||
total_concurrency: totalConcurrency,
|
||||
used_concurrency: usedConcurrency,
|
||||
@@ -329,6 +329,7 @@ function formatDuration(seconds: number): string {
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => realtimeEnabled.value,
|
||||
async (enabled) => {
|
||||
|
||||
Reference in New Issue
Block a user