This commit is contained in:
seb
2026-10-06 17:57:50 +02:00
parent 07f0d5ba80
commit 2e8c36fa01
6 changed files with 152 additions and 22 deletions

View File

@@ -8,8 +8,25 @@ const ALLOWED_KEYS = [
'systemprompt-prefix',
'systemprompt-suffix',
'default-model',
'modelslug',
'modelslugflag',
'modelreasoning',
'modelslugimage',
];
export const DEFAULT_SETTINGS = {
'modelslug': 'openai/gpt-6-luna',
'modelslugflag': 'floor',
'modelreasoning': 'medium',
'modelslugimage': 'meta/muse-image',
};
const WRITEONLY_KEYS = new Set([
'keys.tavily',
'keys.openrouter',
'keys.serper',
]);
function requireSelectedCompany(req) {
const companyId = req.user.selectedCompanyId;
if (companyId == null) {
@@ -108,9 +125,12 @@ export function register(app, basePath = '') {
if (s.frontend) visibleKeys.add(s.key);
}
const settings = [...visibleKeys].map((key) =>
byKey.has(key) ? shapeSetting(byKey.get(key)) : shapeSetting({ key, value: null })
);
const settings = [...visibleKeys].map((key) => {
if (byKey.has(key)) {
return shapeSetting(byKey.get(key));
}
return shapeSetting({ key, value: DEFAULT_SETTINGS[key] ?? null });
});
res.status(200).json({ settings });
} catch (error) {
@@ -186,11 +206,18 @@ export function register(app, basePath = '') {
try {
const setting = await db.getByKey(req.user.id, key);
// Only frontend-visible settings are exposed; others are treated as absent.
if (!setting || !setting.frontend) {
return res.status(404).json({ error: 'Company setting not found' });
// Stored setting that is frontend-visible
if (setting && setting.frontend) {
return res.status(200).json({ setting: shapeSetting(setting) });
}
res.status(200).json({ setting: shapeSetting(setting) });
// If unset, but it's an allowed key with a default or allowed frontend key
if (!setting && ALLOWED_KEYS.includes(key) && !WRITEONLY_KEYS.has(key)) {
return res.status(200).json({
setting: shapeSetting({ key, value: DEFAULT_SETTINGS[key] ?? null }),
});
}
// Only frontend-visible settings are exposed; others are treated as absent.
return res.status(404).json({ error: 'Company setting not found' });
} catch (error) {
const status = settingErrorStatus(error);
if (status === 500) console.error('Get company setting failed:', error);
@@ -208,7 +235,8 @@ export function register(app, basePath = '') {
* Sets (upserts) a company setting for the selected company.
* Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`,
* `keys.openrouter`, `keys.serper`, `systemprompt-prefix`,
* `systemprompt-suffix`, `default-model`.
* `systemprompt-suffix`, `default-model`, `modelslug`,
* `modelslugflag`, `modelreasoning`, `modelslugimage`.
* security:
* - cookieAuth: []
* x-markdown: companysetting/set.md
@@ -279,15 +307,35 @@ export function register(app, basePath = '') {
return res.status(400).json({ error: 'key is required' });
}
if (!ALLOWED_KEYS.includes(key)) {
return res.status(400).json({ error: 'Invalid setting' });
}
const value = req.body?.value;
const frontend = req.body?.frontend;
const input = { key };
if (typeof value === 'string') {
input.value = value;
if (typeof value !== 'string') {
return res.status(400).json({ error: 'Invalid setting' });
}
const trimmedVal = value.trim();
if (key === 'modelslugflag' && !['', 'floor', 'nitro'].includes(trimmedVal)) {
return res.status(400).json({ error: 'Invalid setting' });
}
if (key === 'modelreasoning' && !['none', 'low', 'medium', 'high'].includes(trimmedVal)) {
return res.status(400).json({ error: 'Invalid setting' });
}
let valToSave = value;
if (key === 'modelslug' && !trimmedVal) {
valToSave = DEFAULT_SETTINGS.modelslug;
}
const input = { key, value: valToSave };
if (typeof frontend === 'boolean') {
input.frontend = frontend;
} else if (!WRITEONLY_KEYS.has(key)) {
input.frontend = true;
}
try {