fix connection and release regressions

This commit is contained in:
cita
2026-03-18 18:00:59 +08:00
parent d928e067c1
commit 38e46e165b
16 changed files with 528 additions and 63 deletions
+3 -1
View File
@@ -358,7 +358,9 @@ export default function Accounts() {
skipModelFetch: tokenForm.skipModelFetch,
});
closeAddPanel();
if (result.tokenType === 'apikey') {
if (result.queued) {
toast.info(result.message || '账号已添加,后台正在同步初始化信息。');
} else if (result.tokenType === 'apikey') {
toast.success('已添加为 API Key 账号(可用于代理转发)');
} else {
const parts: string[] = [];
+1 -1
View File
@@ -162,7 +162,7 @@ function isExactModelPattern(modelPattern: string): boolean {
const normalized = modelPattern.trim();
if (!normalized) return false;
if (normalized.toLowerCase().startsWith('re:')) return false;
return !/[\*\?\[]/.test(normalized);
return !/[\*\?]/.test(normalized);
}
function routeTitle(route: RouteSelectorItem): string {
+1 -1
View File
@@ -159,7 +159,7 @@ function isExactModelPattern(modelPattern: string): boolean {
const normalized = modelPattern.trim();
if (!normalized) return false;
if (isRegexModelPattern(normalized)) return false;
return !/[\*\?\[]/.test(normalized);
return !/[\*\?]/.test(normalized);
}
function routeTitle(route: RouteSelectorItem): string {
+1 -1
View File
@@ -226,7 +226,7 @@ const isExactModelPattern = (modelPattern: string): boolean => {
const normalized = modelPattern.trim();
if (!normalized) return false;
if (normalized.toLowerCase().startsWith('re:')) return false;
return !/[\*\?\[]/.test(normalized);
return !/[\*\?]/.test(normalized);
};
const splitCommaSeparated = (value: string): string[] =>
@@ -0,0 +1,8 @@
import { describe, expect, it } from 'vitest';
import { isExactModelPattern } from './utils.js';
describe('isExactModelPattern', () => {
it('treats bracket-prefixed literal model names as exact patterns', () => {
expect(isExactModelPattern('[NV]deepseek-v3.1-terminus')).toBe(true);
});
});
+1 -1
View File
@@ -45,7 +45,7 @@ export function isExactModelPattern(modelPattern: string): boolean {
const normalized = modelPattern.trim();
if (!normalized) return false;
if (isRegexModelPattern(normalized)) return false;
return !/[\*\?\[]/.test(normalized);
return !/[\*\?]/.test(normalized);
}
export function parseRegexModelPattern(modelPattern: string): { regex: RegExp | null; error: string | null } {
@@ -79,6 +79,54 @@ describe('TokenRoutes grouped source models', () => {
vi.clearAllMocks();
});
it('does not treat bracket-prefixed exact model routes as group filters', async () => {
apiMock.getRoutesSummary.mockResolvedValue([
{
id: 4386, modelPattern: '[NV]deepseek-v3.1-terminus', displayName: null,
displayIcon: null, modelMapping: null, enabled: true,
channelCount: 1, enabledChannelCount: 1, siteNames: ['test'],
decisionSnapshot: null, decisionRefreshedAt: null,
},
{
id: 3383, modelPattern: 're:^claude-(opus|sonnet)-4-5$', displayName: 'claude-opus-4-6',
displayIcon: null, modelMapping: null, enabled: true,
channelCount: 4, enabledChannelCount: 4, siteNames: ['site-a', 'site-b'],
decisionSnapshot: null, decisionRefreshedAt: null,
},
]);
let root: ReturnType<typeof create> | null = null;
try {
await act(async () => {
root = create(
<MemoryRouter initialEntries={['/routes']}>
<ToastProvider>
<TokenRoutes />
</ToastProvider>
</MemoryRouter>,
);
});
await flushMicrotasks();
const filterToggle = findButtonByText(root.root, '筛选');
await act(async () => {
filterToggle.props.onClick();
});
await flushMicrotasks();
const text = collectText(root.root);
expect(text).toContain('全部群组1');
const bracketGroupButtons = root.root.findAll((node) => (
node.type === 'button'
&& collectText(node).includes('[NV]deepseek-v3.1-terminus')
));
expect(bracketGroupButtons).toHaveLength(0);
} finally {
root?.unmount();
}
});
it('collapses source-model groups by default for wildcard routes', async () => {
const channels = [
{