317 lines
10 KiB
Dart
317 lines
10 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
|
|
/// Represents the available OAuth providers configured on the server.
|
|
@immutable
|
|
class OAuthProviders {
|
|
const OAuthProviders({
|
|
this.google,
|
|
this.microsoft,
|
|
this.github,
|
|
this.oidc,
|
|
this.feishu,
|
|
});
|
|
|
|
/// Google OAuth provider name (if enabled).
|
|
final String? google;
|
|
|
|
/// Microsoft OAuth provider name (if enabled).
|
|
final String? microsoft;
|
|
|
|
/// GitHub OAuth provider name (if enabled).
|
|
final String? github;
|
|
|
|
/// Generic OIDC provider name (if enabled).
|
|
final String? oidc;
|
|
|
|
/// Feishu OAuth provider name (if enabled).
|
|
final String? feishu;
|
|
|
|
/// Whether any OAuth provider is enabled.
|
|
bool get hasAnyProvider =>
|
|
google != null ||
|
|
microsoft != null ||
|
|
github != null ||
|
|
oidc != null ||
|
|
feishu != null;
|
|
|
|
/// Returns the list of enabled provider keys.
|
|
List<String> get enabledProviders => [
|
|
if (google != null) 'google',
|
|
if (microsoft != null) 'microsoft',
|
|
if (github != null) 'github',
|
|
if (oidc != null) 'oidc',
|
|
if (feishu != null) 'feishu',
|
|
];
|
|
|
|
/// Returns the display name for a provider.
|
|
String getProviderDisplayName(String key) {
|
|
return switch (key) {
|
|
'google' => google ?? 'Google',
|
|
'microsoft' => microsoft ?? 'Microsoft',
|
|
'github' => github ?? 'GitHub',
|
|
'oidc' => oidc ?? 'SSO',
|
|
'feishu' => feishu ?? 'Feishu',
|
|
_ => key,
|
|
};
|
|
}
|
|
|
|
factory OAuthProviders.fromJson(Map<String, dynamic>? json) {
|
|
if (json == null) return const OAuthProviders();
|
|
return OAuthProviders(
|
|
google: json['google'] as String?,
|
|
microsoft: json['microsoft'] as String?,
|
|
github: json['github'] as String?,
|
|
oidc: json['oidc'] as String?,
|
|
feishu: json['feishu'] as String?,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() => {
|
|
if (google != null) 'google': google,
|
|
if (microsoft != null) 'microsoft': microsoft,
|
|
if (github != null) 'github': github,
|
|
if (oidc != null) 'oidc': oidc,
|
|
if (feishu != null) 'feishu': feishu,
|
|
};
|
|
}
|
|
|
|
/// Subset of the backend `/api/config` response the app cares about.
|
|
@immutable
|
|
class BackendConfig {
|
|
const BackendConfig({
|
|
this.enableWebsocket,
|
|
this.enableAudioInput,
|
|
this.enableAudioOutput,
|
|
this.sttProvider,
|
|
this.ttsProvider,
|
|
this.ttsVoice,
|
|
this.defaultSttLocale,
|
|
this.audioSampleRate,
|
|
this.audioFrameSize,
|
|
this.vadEnabled,
|
|
this.oauthProviders = const OAuthProviders(),
|
|
this.enableLdap = false,
|
|
this.enableLoginForm = true,
|
|
});
|
|
|
|
/// Mirrors `features.enable_websocket` from OpenWebUI.
|
|
final bool? enableWebsocket;
|
|
final bool? enableAudioInput;
|
|
final bool? enableAudioOutput;
|
|
final String? sttProvider;
|
|
final String? ttsProvider;
|
|
final String? ttsVoice;
|
|
final String? defaultSttLocale;
|
|
final int? audioSampleRate;
|
|
final int? audioFrameSize;
|
|
final bool? vadEnabled;
|
|
|
|
/// OAuth providers configured on the server.
|
|
final OAuthProviders oauthProviders;
|
|
|
|
/// Whether LDAP authentication is enabled on the server.
|
|
final bool enableLdap;
|
|
|
|
/// Whether the standard login form (email/password) is enabled.
|
|
final bool enableLoginForm;
|
|
|
|
/// Whether SSO (OAuth) login is available.
|
|
bool get hasSsoEnabled => oauthProviders.hasAnyProvider;
|
|
|
|
/// Returns a copy with updated fields.
|
|
BackendConfig copyWith({
|
|
bool? enableWebsocket,
|
|
bool? enableAudioInput,
|
|
bool? enableAudioOutput,
|
|
String? sttProvider,
|
|
String? ttsProvider,
|
|
String? ttsVoice,
|
|
String? defaultSttLocale,
|
|
int? audioSampleRate,
|
|
int? audioFrameSize,
|
|
bool? vadEnabled,
|
|
OAuthProviders? oauthProviders,
|
|
bool? enableLdap,
|
|
bool? enableLoginForm,
|
|
}) {
|
|
return BackendConfig(
|
|
enableWebsocket: enableWebsocket ?? this.enableWebsocket,
|
|
enableAudioInput: enableAudioInput ?? this.enableAudioInput,
|
|
enableAudioOutput: enableAudioOutput ?? this.enableAudioOutput,
|
|
sttProvider: sttProvider ?? this.sttProvider,
|
|
ttsProvider: ttsProvider ?? this.ttsProvider,
|
|
ttsVoice: ttsVoice ?? this.ttsVoice,
|
|
defaultSttLocale: defaultSttLocale ?? this.defaultSttLocale,
|
|
audioSampleRate: audioSampleRate ?? this.audioSampleRate,
|
|
audioFrameSize: audioFrameSize ?? this.audioFrameSize,
|
|
vadEnabled: vadEnabled ?? this.vadEnabled,
|
|
oauthProviders: oauthProviders ?? this.oauthProviders,
|
|
enableLdap: enableLdap ?? this.enableLdap,
|
|
enableLoginForm: enableLoginForm ?? this.enableLoginForm,
|
|
);
|
|
}
|
|
|
|
/// Whether the backend only allows WebSocket transport.
|
|
bool get websocketOnly => enableWebsocket == true;
|
|
|
|
/// Whether the backend only allows HTTP polling transport.
|
|
bool get pollingOnly => enableWebsocket == false;
|
|
|
|
/// Whether the backend permits choosing WebSocket-only mode.
|
|
bool get supportsWebsocketOnly => !pollingOnly;
|
|
|
|
/// Whether the backend permits choosing polling fallback.
|
|
bool get supportsPolling => !websocketOnly;
|
|
|
|
/// Returns the enforced transport mode derived from backend policy.
|
|
String? get enforcedTransportMode {
|
|
if (websocketOnly) return 'ws';
|
|
if (pollingOnly) return 'polling';
|
|
return null;
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return <String, dynamic>{
|
|
'enable_websocket': enableWebsocket,
|
|
'enable_audio_input': enableAudioInput,
|
|
'enable_audio_output': enableAudioOutput,
|
|
'stt_provider': sttProvider,
|
|
'tts_provider': ttsProvider,
|
|
'tts_voice': ttsVoice,
|
|
'default_stt_locale': defaultSttLocale,
|
|
'audio_sample_rate': audioSampleRate,
|
|
'audio_frame_size': audioFrameSize,
|
|
'vad_enabled': vadEnabled,
|
|
'oauth': {'providers': oauthProviders.toJson()},
|
|
'enable_ldap': enableLdap,
|
|
'enable_login_form': enableLoginForm,
|
|
};
|
|
}
|
|
|
|
static BackendConfig fromJson(Map<String, dynamic> json) {
|
|
bool? enableWebsocket;
|
|
bool? enableAudioInput;
|
|
bool? enableAudioOutput;
|
|
String? sttProvider;
|
|
String? ttsProvider;
|
|
String? ttsVoice;
|
|
String? defaultSttLocale;
|
|
int? audioSampleRate;
|
|
int? audioFrameSize;
|
|
bool? vadEnabled;
|
|
OAuthProviders oauthProviders = const OAuthProviders();
|
|
bool enableLdap = false;
|
|
bool enableLoginForm = true;
|
|
|
|
// Try canonical format first
|
|
final value = json['enable_websocket'];
|
|
if (value is bool) {
|
|
enableWebsocket = value;
|
|
}
|
|
|
|
final audioIn = json['enable_audio_input'];
|
|
if (audioIn is bool) enableAudioInput = audioIn;
|
|
final audioOut = json['enable_audio_output'];
|
|
if (audioOut is bool) enableAudioOutput = audioOut;
|
|
|
|
final stt = json['stt_provider'];
|
|
if (stt is String) sttProvider = stt;
|
|
final tts = json['tts_provider'];
|
|
if (tts is String) ttsProvider = tts;
|
|
final ttsVoiceValue = json['tts_voice'];
|
|
if (ttsVoiceValue is String) ttsVoice = ttsVoiceValue;
|
|
|
|
final defaultLocale = json['default_stt_locale'];
|
|
if (defaultLocale is String) defaultSttLocale = defaultLocale;
|
|
|
|
final sampleRate = json['audio_sample_rate'];
|
|
if (sampleRate is int) audioSampleRate = sampleRate;
|
|
final frameSize = json['audio_frame_size'];
|
|
if (frameSize is int) audioFrameSize = frameSize;
|
|
|
|
final vad = json['vad_enabled'];
|
|
if (vad is bool) vadEnabled = vad;
|
|
|
|
// Parse OAuth providers from top-level oauth.providers
|
|
final oauth = json['oauth'];
|
|
if (oauth is Map<String, dynamic>) {
|
|
final providers = oauth['providers'];
|
|
if (providers is Map<String, dynamic>) {
|
|
oauthProviders = OAuthProviders.fromJson(providers);
|
|
}
|
|
}
|
|
|
|
// Parse auth features from top-level
|
|
final ldapValue = json['enable_ldap'];
|
|
if (ldapValue is bool) enableLdap = ldapValue;
|
|
final loginFormValue = json['enable_login_form'];
|
|
if (loginFormValue is bool) enableLoginForm = loginFormValue;
|
|
|
|
// Fallback to nested format for backwards compatibility
|
|
final features = json['features'];
|
|
if (features is Map<String, dynamic>) {
|
|
final nestedValue = features['enable_websocket'];
|
|
if (nestedValue is bool && enableWebsocket == null) {
|
|
enableWebsocket = nestedValue;
|
|
}
|
|
final nestedAudioIn = features['enable_audio_input'];
|
|
if (nestedAudioIn is bool && enableAudioInput == null) {
|
|
enableAudioInput = nestedAudioIn;
|
|
}
|
|
final nestedAudioOut = features['enable_audio_output'];
|
|
if (nestedAudioOut is bool && enableAudioOutput == null) {
|
|
enableAudioOutput = nestedAudioOut;
|
|
}
|
|
final nestedStt = features['stt_provider'];
|
|
if (nestedStt is String && sttProvider == null) {
|
|
sttProvider = nestedStt;
|
|
}
|
|
final nestedTts = features['tts_provider'];
|
|
if (nestedTts is String && ttsProvider == null) {
|
|
ttsProvider = nestedTts;
|
|
}
|
|
final nestedVoice = features['tts_voice'];
|
|
if (nestedVoice is String && ttsVoice == null) {
|
|
ttsVoice = nestedVoice;
|
|
}
|
|
final nestedLocale = features['default_stt_locale'];
|
|
if (nestedLocale is String && defaultSttLocale == null) {
|
|
defaultSttLocale = nestedLocale;
|
|
}
|
|
final nestedSample = features['audio_sample_rate'];
|
|
if (nestedSample is int && audioSampleRate == null) {
|
|
audioSampleRate = nestedSample;
|
|
}
|
|
final nestedFrame = features['audio_frame_size'];
|
|
if (nestedFrame is int && audioFrameSize == null) {
|
|
audioFrameSize = nestedFrame;
|
|
}
|
|
final nestedVad = features['vad_enabled'];
|
|
if (nestedVad is bool && vadEnabled == null) {
|
|
vadEnabled = nestedVad;
|
|
}
|
|
// Auth features in nested format
|
|
final nestedLdap = features['enable_ldap'];
|
|
if (nestedLdap is bool) enableLdap = nestedLdap;
|
|
final nestedLoginForm = features['enable_login_form'];
|
|
if (nestedLoginForm is bool) enableLoginForm = nestedLoginForm;
|
|
}
|
|
|
|
return BackendConfig(
|
|
enableWebsocket: enableWebsocket,
|
|
enableAudioInput: enableAudioInput,
|
|
enableAudioOutput: enableAudioOutput,
|
|
sttProvider: sttProvider,
|
|
ttsProvider: ttsProvider,
|
|
ttsVoice: ttsVoice,
|
|
defaultSttLocale: defaultSttLocale,
|
|
audioSampleRate: audioSampleRate,
|
|
audioFrameSize: audioFrameSize,
|
|
vadEnabled: vadEnabled,
|
|
oauthProviders: oauthProviders,
|
|
enableLdap: enableLdap,
|
|
enableLoginForm: enableLoginForm,
|
|
);
|
|
}
|
|
}
|