feat(teamsbot): per-user settings API, system bot admin API, credentials removed from settings UI

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-02-16 00:16:03 +01:00
parent dffebc8d73
commit 7366da06ac
2 changed files with 60 additions and 36 deletions

View file

@ -180,7 +180,7 @@ export async function deleteSession(instanceId: string, sessionId: string): Prom
} }
/** /**
* Get teamsbot configuration. * Get teamsbot configuration (instance-level defaults).
*/ */
export async function getConfig(instanceId: string): Promise<{ config: TeamsbotConfig }> { export async function getConfig(instanceId: string): Promise<{ config: TeamsbotConfig }> {
const response = await api.get(`/api/teamsbot/${instanceId}/config`); const response = await api.get(`/api/teamsbot/${instanceId}/config`);
@ -188,13 +188,54 @@ export async function getConfig(instanceId: string): Promise<{ config: TeamsbotC
} }
/** /**
* Update teamsbot configuration. * Update teamsbot configuration (instance-level defaults).
*/ */
export async function updateConfig(instanceId: string, updates: ConfigUpdateRequest): Promise<{ config: TeamsbotConfig }> { export async function updateConfig(instanceId: string, updates: ConfigUpdateRequest): Promise<{ config: TeamsbotConfig }> {
const response = await api.put(`/api/teamsbot/${instanceId}/config`, updates); const response = await api.put(`/api/teamsbot/${instanceId}/config`, updates);
return response.data; return response.data;
} }
/**
* Get per-user settings merged with instance defaults.
*/
export async function getUserSettings(instanceId: string): Promise<{ settings: any; effectiveConfig: TeamsbotConfig }> {
const response = await api.get(`/api/teamsbot/${instanceId}/settings`);
return response.data;
}
/**
* Update per-user settings.
*/
export async function updateUserSettings(instanceId: string, updates: ConfigUpdateRequest): Promise<{ settings: any; effectiveConfig: TeamsbotConfig }> {
const response = await api.put(`/api/teamsbot/${instanceId}/settings`, updates);
return response.data;
}
/**
* Reset per-user settings to instance defaults.
*/
export async function resetUserSettings(instanceId: string): Promise<{ settings: null; effectiveConfig: TeamsbotConfig }> {
const response = await api.delete(`/api/teamsbot/${instanceId}/settings`);
return response.data;
}
export interface SystemBot {
id: string;
mandateId: string;
name: string;
email: string;
isActive: boolean;
creationDate?: string;
}
/**
* List system bot accounts for this mandate.
*/
export async function listSystemBots(instanceId: string): Promise<{ bots: SystemBot[] }> {
const response = await api.get(`/api/teamsbot/${instanceId}/system-bots`);
return response.data;
}
/** /**
* Test TTS voice with AI-generated sample text. Returns base64-encoded audio. * Test TTS voice with AI-generated sample text. Returns base64-encoded audio.
*/ */

View file

@ -41,15 +41,17 @@ export const TeamsbotSettingsView: React.FC = () => {
if (!instanceId) return; if (!instanceId) return;
try { try {
setLoading(true); setLoading(true);
const [configResult, languagesResult] = await Promise.all([ // Load per-user settings (merged with instance defaults)
teamsbotApi.getConfig(instanceId), const [settingsResult, languagesResult] = await Promise.all([
teamsbotApi.getUserSettings(instanceId),
teamsbotApi.fetchLanguages(), teamsbotApi.fetchLanguages(),
]); ]);
setConfig(configResult.config); const effectiveConfig = settingsResult.effectiveConfig;
setFormData(configResult.config); setConfig(effectiveConfig);
setFormData(effectiveConfig);
setLanguages(languagesResult); setLanguages(languagesResult);
// Load voices for the current language // Load voices for the current language
const lang = configResult.config.language || 'de-DE'; const lang = effectiveConfig.language || 'de-DE';
const voicesResult = await teamsbotApi.fetchVoices(lang); const voicesResult = await teamsbotApi.fetchVoices(lang);
setVoices(voicesResult); setVoices(voicesResult);
setError(null); setError(null);
@ -71,10 +73,11 @@ export const TeamsbotSettingsView: React.FC = () => {
setSuccessMsg(null); setSuccessMsg(null);
try { try {
const result = await teamsbotApi.updateConfig(instanceId, formData); // Save as per-user settings (not instance config)
setConfig(result.config); const result = await teamsbotApi.updateUserSettings(instanceId, formData);
setFormData(result.config); setConfig(result.effectiveConfig);
setSuccessMsg('Konfiguration gespeichert'); setFormData(result.effectiveConfig);
setSuccessMsg('Einstellungen gespeichert');
setTimeout(() => setSuccessMsg(null), 3000); setTimeout(() => setSuccessMsg(null), 3000);
} catch (err: any) { } catch (err: any) {
setError(err.message || 'Fehler beim Speichern'); setError(err.message || 'Fehler beim Speichern');
@ -282,33 +285,13 @@ export const TeamsbotSettingsView: React.FC = () => {
</div> </div>
</div> </div>
{/* Bot Account */} {/* Bot Account - managed by admin, read-only display */}
<div className={styles.settingsSection}> <div className={styles.settingsSection}>
<h4 className={styles.sectionTitle}>Bot-Account (Microsoft)</h4> <h4 className={styles.sectionTitle}>Bot-Account (Microsoft)</h4>
<span className={styles.hint}>
<div className={styles.formGroup}> System-Bot-Accounts werden vom Administrator verwaltet und sind nicht direkt editierbar.
<label className={styles.label}>E-Mail</label> Waehle beim Session-Start den gewuenschten Join-Modus.
<input </span>
type="email"
className={styles.input}
value={formData.botAccountEmail || ''}
onChange={(e) => _updateField('botAccountEmail', e.target.value)}
placeholder="bot@ihrefirma.ch"
/>
<span className={styles.hint}>Dedizierter Microsoft-Account fuer authentifizierten Bot-Zugang. Leer = anonymer Gast.</span>
</div>
<div className={styles.formGroup}>
<label className={styles.label}>Passwort</label>
<input
type="password"
className={styles.input}
value={formData.botAccountPassword || ''}
onChange={(e) => _updateField('botAccountPassword', e.target.value)}
placeholder="••••••••"
/>
<span className={styles.hint}>MFA muss fuer diesen Account deaktiviert sein. Passwort wird verschluesselt gespeichert.</span>
</div>
</div> </div>
{/* Advanced Settings */} {/* Advanced Settings */}