add admin page, fix no legs infinite load on dashboard
All checks were successful
Release / meta (push) Successful in 8s
Release / linux-build (push) Successful in 6m49s
Release / web-build (push) Successful in 6m23s
Release / android-build (push) Successful in 16m56s
Release / release-master (push) Successful in 30s
Release / release-dev (push) Successful in 32s

This commit is contained in:
2026-01-02 18:49:14 +00:00
parent 29cecf0ded
commit 2fa66ff9c6
11 changed files with 691 additions and 89 deletions

View File

@@ -21,6 +21,9 @@ class AuthService extends ChangeNotifier {
String? get userId => _user?.userId;
String? get username => _user?.username;
String? get fullName => _user?.fullName;
bool get isElevated => _user?.isElevated ?? false;
bool get isAdmin => isElevated; // alias for old name
bool get isDisabled => _user?.disabled ?? false;
void setLoginData({
required String userId,
@@ -28,6 +31,8 @@ class AuthService extends ChangeNotifier {
required String fullName,
required String accessToken,
required String email,
bool isElevated = false,
bool isDisabled = false,
}) {
_user = AuthenticatedUserData(
userId: userId,
@@ -35,6 +40,8 @@ class AuthService extends ChangeNotifier {
fullName: fullName,
accessToken: accessToken,
email: email,
isElevated: isElevated,
disabled: isDisabled,
);
_persistToken(accessToken);
notifyListeners();
@@ -70,6 +77,8 @@ class AuthService extends ChangeNotifier {
fullName: userResponse['full_name'],
accessToken: accessToken,
email: userResponse['email'],
isElevated: _parseIsElevated(userResponse),
isDisabled: _parseIsDisabled(userResponse),
);
}
@@ -95,6 +104,8 @@ class AuthService extends ChangeNotifier {
fullName: userResponse['full_name'],
accessToken: token,
email: userResponse['email'],
isElevated: _parseIsElevated(userResponse),
isDisabled: _parseIsDisabled(userResponse),
);
} catch (_) {
await _clearToken();
@@ -157,4 +168,60 @@ class AuthService extends ChangeNotifier {
void logout() {
handleTokenExpired(); // reuse
}
bool _parseIsElevated(Map<String, dynamic> json) {
dynamic value = json['is_elevated'] ??
json['elevated'] ??
json['is_admin'] ??
json['admin'] ??
json['isAdmin'] ??
json['admin_user'] ??
json['role'] ??
json['roles'] ??
json['permissions'] ??
json['scopes'] ??
json['is_staff'] ??
json['staff'] ??
json['is_superuser'] ??
json['superuser'] ??
json['groups'];
bool parseBoolish(dynamic v) {
if (v is bool) return v;
if (v is num) return v != 0;
final str = v?.toString().toLowerCase().trim();
if (str == null || str.isEmpty) return false;
if (['1', 'true', 'yes', 'y', 'admin', 'superuser', 'staff', 'elevated'].contains(str)) {
return true;
}
return str.contains('admin') || str.contains('superuser') || str.contains('staff');
}
if (value is List) {
for (final entry in value) {
if (parseBoolish(entry)) return true;
final s = entry?.toString().toLowerCase();
if (s != null &&
(s.contains('admin') ||
s.contains('superuser') ||
s.contains('staff') ||
s.contains('elevated') ||
s == 'root')) {
return true;
}
}
return false;
}
return parseBoolish(value);
}
bool _parseIsDisabled(Map<String, dynamic> json) {
dynamic value = json['disabled'] ?? json['is_disabled'];
if (value is bool) return value;
if (value is num) return value != 0;
final str = value?.toString().toLowerCase().trim();
if (str == null || str.isEmpty) return false;
return ['1', 'true', 'yes', 'y', 'disabled'].contains(str);
}
}