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
87 lines
2.5 KiB
Dart
87 lines
2.5 KiB
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
import 'package:worldhopper/services/server_software/server_software_type.dart';
|
|
import 'package:worldhopper/services/url_helper.dart';
|
|
|
|
part 'server.freezed.dart';
|
|
part 'server.g.dart';
|
|
|
|
/// Represents a server configuration
|
|
@freezed
|
|
class Server with _$Server {
|
|
const factory Server({
|
|
/// Unique identifier for the server
|
|
required String id,
|
|
|
|
/// Display name for the server
|
|
required String name,
|
|
|
|
/// Root URL of the server
|
|
required String url,
|
|
|
|
/// Optional username for authentication
|
|
String? username,
|
|
|
|
/// Optional password for authentication
|
|
String? password,
|
|
|
|
/// Timestamp when the server was added
|
|
required DateTime createdAt,
|
|
|
|
/// Timestamp of the last successful sync
|
|
DateTime? lastSyncedAt,
|
|
|
|
/// Server software type (null = not yet detected)
|
|
ServerSoftwareType? softwareType,
|
|
}) = _Server;
|
|
|
|
/// Create Server from JSON
|
|
factory Server.fromJson(Map<String, dynamic> json) => _$ServerFromJson(json);
|
|
|
|
/// Create Server from database row
|
|
factory Server.fromDatabase(Map<String, dynamic> row) {
|
|
final softwareTypeStr = row['software_type'] as String?;
|
|
return Server(
|
|
id: row['id'] as String,
|
|
name: row['name'] as String,
|
|
url: row['url'] as String,
|
|
username: row['username'] as String?,
|
|
password: row['password'] as String?,
|
|
createdAt: DateTime.fromMillisecondsSinceEpoch(row['created_at'] as int),
|
|
lastSyncedAt: row['last_synced_at'] != null
|
|
? DateTime.fromMillisecondsSinceEpoch(row['last_synced_at'] as int)
|
|
: null,
|
|
softwareType: softwareTypeStr != null
|
|
? ServerSoftwareType.values.asNameMap()[softwareTypeStr]
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
extension ServerX on Server {
|
|
/// Convert Server to database row
|
|
Map<String, dynamic> toDatabase() {
|
|
return {
|
|
'id': id,
|
|
'name': name,
|
|
'url': url,
|
|
'username': username,
|
|
'password': password,
|
|
'created_at': createdAt.millisecondsSinceEpoch,
|
|
'last_synced_at': lastSyncedAt?.millisecondsSinceEpoch,
|
|
'software_type': softwareType?.name,
|
|
};
|
|
}
|
|
|
|
/// Check if server requires authentication
|
|
bool get requiresAuth => username != null && password != null;
|
|
|
|
/// Resolve a relative URL against this server's base URL
|
|
String resolveUrl(String relativeUrl) {
|
|
return UrlHelper.resolveUrl(url, relativeUrl);
|
|
}
|
|
|
|
/// Make a request URL by resolving it against the server base
|
|
String makeRequest(String path) {
|
|
return UrlHelper.resolveUrl(url, path);
|
|
}
|
|
}
|