feat: api token login

This commit is contained in:
Dr-Blank 2024-09-06 15:10:00 -04:00
parent 880960c745
commit 682631fb8e
No known key found for this signature in database
GPG key ID: 7452CC63F210A266
17 changed files with 993 additions and 254 deletions

View file

@ -2,6 +2,7 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shelfsdk/audiobookshelf_api.dart';
@ -10,6 +11,12 @@ import 'package:vaani/settings/api_settings_provider.dart';
part 'api_provider.g.dart';
// TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718
typedef ResponseErrorHandler = void Function(
Response response, [
Object? error,
]);
final _logger = Logger('api_provider');
Uri makeBaseUrl(String address) {
@ -52,25 +59,33 @@ AudiobookshelfApi authenticatedApi(AuthenticatedApiRef ref) {
/// ping the server to check if it is reachable
@riverpod
FutureOr<bool> isServerAlive(IsServerAliveRef ref, String address) async {
// return (await ref.watch(audiobookshelfApiProvider).server.ping()) ?? false;
// if address not starts with http or https, add https
// !remove this line
// return true;
if (address.isEmpty) {
return false;
}
if (!address.startsWith('http') && !address.startsWith('https')) {
address = 'https://$address';
}
// check url is valid
if (!Uri.parse(address).isAbsolute) {
try {
return await AudiobookshelfApi(baseUrl: makeBaseUrl(address))
.server
.ping() ??
false;
} catch (e) {
return false;
}
return await AudiobookshelfApi(baseUrl: Uri.parse(address)).server.ping() ??
false;
}
/// fetch status of server
@riverpod
FutureOr<ServerStatusResponse?> serverStatus(
ServerStatusRef ref,
Uri baseUrl, [
ResponseErrorHandler? responseErrorHandler,
]) async {
_logger.fine('fetching server status: $baseUrl');
final api = ref.watch(audiobookshelfApiProvider(baseUrl));
final res =
await api.server.status(responseErrorHandler: responseErrorHandler);
_logger.fine('server status: $res');
return res;
}
/// fetch the personalized view
@ -97,13 +112,18 @@ class PersonalizedView extends _$PersonalizedView {
) ??
await apiResponseCacheManager.getFileFromCache(key);
if (cachedRes != null) {
final resJson = jsonDecode(await cachedRes.file.readAsString()) as List;
final res = [
for (final item in resJson)
Shelf.fromJson(item as Map<String, dynamic>),
];
_logger.fine('reading from cache: $cachedRes');
yield res;
_logger.fine('reading from cache: $cachedRes for key: $key');
try {
final resJson = jsonDecode(await cachedRes.file.readAsString()) as List;
final res = [
for (final item in resJson)
Shelf.fromJson(item as Map<String, dynamic>),
];
_logger.fine('successfully read from cache key: $key');
yield res;
} catch (e) {
_logger.warning('error reading from cache: $e\n$cachedRes');
}
}
// ! exagerated delay
@ -112,14 +132,20 @@ class PersonalizedView extends _$PersonalizedView {
.getPersonalized(libraryId: apiSettings.activeLibraryId!);
// debugPrint('personalizedView: ${res!.map((e) => e).toSet()}');
// save to cache
final newFile = await apiResponseCacheManager.putFile(
key,
utf8.encode(jsonEncode(res)),
fileExtension: 'json',
key: key,
);
_logger.fine('writing to cache: $newFile');
yield res!;
if (res != null) {
final newFile = await apiResponseCacheManager.putFile(
key,
utf8.encode(jsonEncode(res)),
fileExtension: 'json',
key: key,
);
_logger.fine('writing to cache: $newFile');
yield res;
} else {
_logger.warning('failed to fetch personalized view');
yield [];
}
}
// method to force refresh the view and ignore the cache

View file

@ -187,7 +187,7 @@ final authenticatedApiProvider = Provider<AudiobookshelfApi>.internal(
);
typedef AuthenticatedApiRef = ProviderRef<AudiobookshelfApi>;
String _$isServerAliveHash() => r'f839350795fbdeb0ca1d5f0c84a9065cac4dd40a';
String _$isServerAliveHash() => r'6ff90b6e0febd2cd4a4d3a5209a59afc778cd3b6';
/// ping the server to check if it is reachable
///
@ -327,6 +327,166 @@ class _IsServerAliveProviderElement
String get address => (origin as IsServerAliveProvider).address;
}
String _$serverStatusHash() => r'2739906a1862d09b098588ebd16749a09032ee99';
/// fetch status of server
///
/// Copied from [serverStatus].
@ProviderFor(serverStatus)
const serverStatusProvider = ServerStatusFamily();
/// fetch status of server
///
/// Copied from [serverStatus].
class ServerStatusFamily extends Family<AsyncValue<ServerStatusResponse?>> {
/// fetch status of server
///
/// Copied from [serverStatus].
const ServerStatusFamily();
/// fetch status of server
///
/// Copied from [serverStatus].
ServerStatusProvider call(
Uri baseUrl, [
void Function(Response, [Object?])? responseErrorHandler,
]) {
return ServerStatusProvider(
baseUrl,
responseErrorHandler,
);
}
@override
ServerStatusProvider getProviderOverride(
covariant ServerStatusProvider provider,
) {
return call(
provider.baseUrl,
provider.responseErrorHandler,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'serverStatusProvider';
}
/// fetch status of server
///
/// Copied from [serverStatus].
class ServerStatusProvider
extends AutoDisposeFutureProvider<ServerStatusResponse?> {
/// fetch status of server
///
/// Copied from [serverStatus].
ServerStatusProvider(
Uri baseUrl, [
void Function(Response, [Object?])? responseErrorHandler,
]) : this._internal(
(ref) => serverStatus(
ref as ServerStatusRef,
baseUrl,
responseErrorHandler,
),
from: serverStatusProvider,
name: r'serverStatusProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$serverStatusHash,
dependencies: ServerStatusFamily._dependencies,
allTransitiveDependencies:
ServerStatusFamily._allTransitiveDependencies,
baseUrl: baseUrl,
responseErrorHandler: responseErrorHandler,
);
ServerStatusProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.baseUrl,
required this.responseErrorHandler,
}) : super.internal();
final Uri baseUrl;
final void Function(Response, [Object?])? responseErrorHandler;
@override
Override overrideWith(
FutureOr<ServerStatusResponse?> Function(ServerStatusRef provider) create,
) {
return ProviderOverride(
origin: this,
override: ServerStatusProvider._internal(
(ref) => create(ref as ServerStatusRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
baseUrl: baseUrl,
responseErrorHandler: responseErrorHandler,
),
);
}
@override
AutoDisposeFutureProviderElement<ServerStatusResponse?> createElement() {
return _ServerStatusProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is ServerStatusProvider &&
other.baseUrl == baseUrl &&
other.responseErrorHandler == responseErrorHandler;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, baseUrl.hashCode);
hash = _SystemHash.combine(hash, responseErrorHandler.hashCode);
return _SystemHash.finish(hash);
}
}
mixin ServerStatusRef on AutoDisposeFutureProviderRef<ServerStatusResponse?> {
/// The parameter `baseUrl` of this provider.
Uri get baseUrl;
/// The parameter `responseErrorHandler` of this provider.
void Function(Response, [Object?])? get responseErrorHandler;
}
class _ServerStatusProviderElement
extends AutoDisposeFutureProviderElement<ServerStatusResponse?>
with ServerStatusRef {
_ServerStatusProviderElement(super.provider);
@override
Uri get baseUrl => (origin as ServerStatusProvider).baseUrl;
@override
void Function(Response, [Object?])? get responseErrorHandler =>
(origin as ServerStatusProvider).responseErrorHandler;
}
String _$fetchContinueListeningHash() =>
r'f65fe3ac3a31b8ac074330525c5d2cc4b526802d';
@ -361,7 +521,7 @@ final meProvider = AutoDisposeFutureProvider<User>.internal(
);
typedef MeRef = AutoDisposeFutureProviderRef<User>;
String _$personalizedViewHash() => r'dada8d72845ffd516f731f88193941f7ebdd47ed';
String _$personalizedViewHash() => r'4c392ece4650bdc36d7195a0ddb8810e8fe4caa9';
/// fetch the personalized view
///

View file

@ -52,8 +52,16 @@ class AuthenticatedUser extends _$AuthenticatedUser {
_logger.fine('writing state to box: $state');
}
void addUser(model.AuthenticatedUser user) {
void addUser(model.AuthenticatedUser user, {bool setActive = false}) {
state = state..add(user);
if (setActive) {
final apiSettings = ref.read(apiSettingsProvider);
ref.read(apiSettingsProvider.notifier).updateState(
apiSettings.copyWith(
activeUser: user,
),
);
}
}
void removeUsersOfServer(AudiobookShelfServer registeredServer) {

View file

@ -6,7 +6,7 @@ part of 'authenticated_user_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$authenticatedUserHash() => r'8578d7fda1755ecacce6853076da4149e4ebe3e7';
String _$authenticatedUserHash() => r'308f19b33ae04af6340fb83167fa64aa23400a09';
/// provides with a set of authenticated users
///