Rename all OPDS-prefixed models to generic names (Server, Feed, Entry, Link, StreamLink), expand the ServerSoftware interface to cover all server interactions, and implement a full Kavita REST API client that replaces the OPDS delegation. - Rename OPDS* models to generic names across ~60 files - Add database migrations 13 (opds_id → entry_id) and 14 (unify credentials) - Create OPDSServerSoftware wrapping existing OPDS services - Create KavitaApiClient for direct Kavita REST API calls - Create KavitaFeedMapper to convert Kavita JSON to Feed/Entry models - Rewrite KavitaServerSoftware to use native API (no OPDS delegation) - Unify server credentials (remove softwareUsername/softwarePassword) - Simplify add/edit server UI to single auth section - Add test connection button to server add/edit screens - Add progress indicator to PublicationCard using local and server data - Eliminate softwareType branching in reader screens - Add EntryProgress and fetchEntryProgress to ServerSoftware interface - Fix Entry.acquisitionLink crash on empty links - Add 59 new tests covering models, services, and providers
86 lines
2.5 KiB
Dart
86 lines
2.5 KiB
Dart
import 'dart:convert';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:worldhopper/models/server.dart';
|
|
import 'package:worldhopper/services/dio_factory.dart';
|
|
|
|
/// Service for handling HTTP Basic Authentication
|
|
class AuthService {
|
|
/// Create Dio client with authentication for a server
|
|
Dio createAuthenticatedClient(Server server) {
|
|
final dio = createDio(
|
|
BaseOptions(
|
|
connectTimeout: const Duration(seconds: 30),
|
|
receiveTimeout: const Duration(seconds: 30),
|
|
headers: {
|
|
'User-Agent': 'Worldhopper/1.0',
|
|
'Accept': 'application/atom+xml, application/xml, text/xml, */*',
|
|
},
|
|
),
|
|
);
|
|
|
|
// Add authentication interceptor if credentials are provided
|
|
if (server.requiresAuth) {
|
|
dio.interceptors.add(
|
|
InterceptorsWrapper(
|
|
onRequest: (options, handler) {
|
|
final authHeader = _generateBasicAuthHeader(
|
|
server.username!,
|
|
server.password!,
|
|
);
|
|
options.headers['Authorization'] = authHeader;
|
|
return handler.next(options);
|
|
},
|
|
onError: (error, handler) {
|
|
// Handle 401 Unauthorized errors
|
|
if (error.response?.statusCode == 401) {
|
|
// Could trigger re-authentication flow here
|
|
}
|
|
return handler.next(error);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
// Add logging interceptor for debugging
|
|
dio.interceptors.add(
|
|
LogInterceptor(
|
|
requestBody: false,
|
|
responseBody: false,
|
|
logPrint: (obj) {
|
|
// Custom logging (can be integrated with a logging package)
|
|
// print(obj);
|
|
},
|
|
),
|
|
);
|
|
|
|
return dio;
|
|
}
|
|
|
|
/// Generate HTTP Basic Auth header
|
|
String _generateBasicAuthHeader(String username, String password) {
|
|
final credentials = '$username:$password';
|
|
final encoded = base64Encode(utf8.encode(credentials));
|
|
return 'Basic $encoded';
|
|
}
|
|
|
|
/// Get authentication headers for image requests
|
|
Map<String, String> getAuthHeaders(Server server) {
|
|
if (!server.requiresAuth) {
|
|
return {};
|
|
}
|
|
|
|
return {
|
|
'Authorization': _generateBasicAuthHeader(
|
|
server.username!,
|
|
server.password!,
|
|
),
|
|
};
|
|
}
|
|
|
|
/// Generate Basic Auth header from username and password
|
|
static String generateBasicAuthHeader(String username, String password) {
|
|
final credentials = '$username:$password';
|
|
final encoded = base64Encode(utf8.encode(credentials));
|
|
return 'Basic $encoded';
|
|
}
|
|
}
|