1
0
forked from boos/BoosAPI

Merge pull request #518 from kevinye13/fix/settings-allowlist-json-array

fix(settings): persist model allowlist as JSON array
This commit is contained in:
Cita
2026-05-08 16:05:15 +08:00
committed by GitHub
4 changed files with 48 additions and 2 deletions
@@ -81,6 +81,8 @@ describe('settings and auth events', () => {
(config as any).telegramChatId = ''; (config as any).telegramChatId = '';
(config as any).telegramUseSystemProxy = false; (config as any).telegramUseSystemProxy = false;
(config as any).telegramMessageThreadId = ''; (config as any).telegramMessageThreadId = '';
config.globalBlockedBrands = [];
config.globalAllowedModels = [];
}); });
afterAll(async () => { afterAll(async () => {
@@ -745,6 +747,32 @@ describe('settings and auth events', () => {
expect(runtime.proxyEmptyContentFailEnabled).toBe(true); expect(runtime.proxyEmptyContentFailEnabled).toBe(true);
}); });
it('persists global model and brand filters as JSON arrays', async () => {
const updateResponse = await app.inject({
method: 'PUT',
url: '/api/settings/runtime',
payload: {
globalAllowedModels: ['model-alpha', ' model-beta ', 'model-alpha', 'model-gamma'],
globalBlockedBrands: ['Codex', ' Codex ', 'Gemini'],
},
});
expect(updateResponse.statusCode).toBe(200);
const updated = updateResponse.json() as {
globalAllowedModels?: string[];
globalBlockedBrands?: string[];
};
expect(updated.globalAllowedModels).toEqual(['model-alpha', 'model-beta', 'model-gamma']);
expect(updated.globalBlockedBrands).toEqual(['Codex', 'Gemini']);
const rows = await db.select().from(schema.settings).all();
const settingsMap = new Map(rows.map((row) => [row.key, row.value]));
expect(settingsMap.get('global_allowed_models')).toBe(JSON.stringify(['model-alpha', 'model-beta', 'model-gamma']));
expect(JSON.parse(settingsMap.get('global_allowed_models') || 'null')).toEqual(['model-alpha', 'model-beta', 'model-gamma']);
expect(settingsMap.get('global_blocked_brands')).toBe(JSON.stringify(['Codex', 'Gemini']));
expect(JSON.parse(settingsMap.get('global_blocked_brands') || 'null')).toEqual(['Codex', 'Gemini']);
});
it('persists and returns log cleanup settings from runtime settings', async () => { it('persists and returns log cleanup settings from runtime settings', async () => {
const updateResponse = await app.inject({ const updateResponse = await app.inject({
method: 'PUT', method: 'PUT',
+2 -2
View File
@@ -1425,7 +1425,7 @@ export async function settingsRoutes(app: FastifyInstance) {
changedLabels.push('全局品牌屏蔽'); changedLabels.push('全局品牌屏蔽');
} }
config.globalBlockedBrands = uniqueBrands; config.globalBlockedBrands = uniqueBrands;
upsertSetting('global_blocked_brands', JSON.stringify(uniqueBrands)); upsertSetting('global_blocked_brands', uniqueBrands);
if (prev !== next) { if (prev !== next) {
startBackgroundTask( startBackgroundTask(
{ {
@@ -1450,7 +1450,7 @@ export async function settingsRoutes(app: FastifyInstance) {
changedLabels.push('全局模型白名单'); changedLabels.push('全局模型白名单');
} }
config.globalAllowedModels = uniqueModels; config.globalAllowedModels = uniqueModels;
upsertSetting('global_allowed_models', JSON.stringify(uniqueModels)); upsertSetting('global_allowed_models', uniqueModels);
if (prev !== next) { if (prev !== next) {
startBackgroundTask( startBackgroundTask(
{ {
@@ -44,4 +44,14 @@ describe('applyRuntimeSettings', () => {
expect(config.smtpPort).toBe(587); expect(config.smtpPort).toBe(587);
}); });
it('hydrates legacy double-encoded global model allowlist values', () => {
config.globalAllowedModels = [];
applyRuntimeSettings(new Map([
['global_allowed_models', JSON.stringify(JSON.stringify(['model-alpha', ' model-beta ', 'model-gamma']))],
]));
expect(config.globalAllowedModels).toEqual(['model-alpha', 'model-beta', 'model-gamma']);
});
}); });
+8
View File
@@ -22,6 +22,14 @@ function toStringList(value: unknown): string[] {
.filter((item) => item.length > 0); .filter((item) => item.length > 0);
} }
if (typeof value === 'string') { if (typeof value === 'string') {
try {
const parsed = JSON.parse(value);
if (Array.isArray(parsed)) {
return toStringList(parsed);
}
} catch {
// Fall back to comma splitting for legacy plain-string lists.
}
return value return value
.split(',') .split(',')
.map((item) => item.trim()) .map((item) => item.trim())