Initial commit
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../contracts/contracts.dart';
|
||||
import '../errors.dart';
|
||||
import '../ports/emby_gateway.dart';
|
||||
import '../ports/server_repository.dart';
|
||||
import '../ports/session_repository.dart';
|
||||
|
||||
const _uuid = Uuid();
|
||||
|
||||
class ListServersUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
ListServersUseCase({required this.serverRepository});
|
||||
|
||||
Future<List<EmbyServer>> execute() => serverRepository.list();
|
||||
}
|
||||
|
||||
class ReorderServersUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
|
||||
ReorderServersUseCase({required this.serverRepository});
|
||||
|
||||
Future<void> execute(List<EmbyServer> orderedServers) {
|
||||
return serverRepository.replaceAll(orderedServers);
|
||||
}
|
||||
}
|
||||
|
||||
class ProbeServerUseCase {
|
||||
final EmbyGateway embyGateway;
|
||||
ProbeServerUseCase({required this.embyGateway});
|
||||
|
||||
Future<ProbePublicInfoRes> execute(EmbyServerProbeReq input) async {
|
||||
final baseUrl = input.baseUrl.trim();
|
||||
if (baseUrl.isEmpty) {
|
||||
throw DomainError(ErrorCode.invalidParams, '服务器地址不能为空');
|
||||
}
|
||||
return embyGateway.probePublicInfo(baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
class SaveServerUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
final DateTime Function() now;
|
||||
final String Function() idGenerator;
|
||||
|
||||
SaveServerUseCase({
|
||||
required this.serverRepository,
|
||||
DateTime Function()? now,
|
||||
String Function()? idGenerator,
|
||||
}) : now = (now ?? DateTime.now),
|
||||
idGenerator = (idGenerator ?? _defaultIdGenerator);
|
||||
|
||||
static String _defaultIdGenerator() => _uuid.v4();
|
||||
|
||||
Future<EmbyServer> execute(EmbyServerSaveReq input) async {
|
||||
final name = input.name.trim();
|
||||
if (name.isEmpty) {
|
||||
throw DomainError(ErrorCode.invalidParams, '服务器名称不能为空');
|
||||
}
|
||||
|
||||
final baseUrl = _normalizeBaseUrl(input.baseUrl);
|
||||
final nowIso = now().toUtc().toIso8601String();
|
||||
|
||||
final duplicated = await serverRepository.findByBaseUrl(baseUrl);
|
||||
if (duplicated != null && duplicated.id != input.id) {
|
||||
throw DomainError(
|
||||
ErrorCode.storeConflict,
|
||||
'服务器地址已存在',
|
||||
details: {'baseUrl': baseUrl},
|
||||
);
|
||||
}
|
||||
|
||||
final iconUrl = input.iconUrl.trim();
|
||||
|
||||
if (input.id != null) {
|
||||
final current = await serverRepository.findById(input.id!);
|
||||
if (current == null) {
|
||||
throw DomainError(
|
||||
ErrorCode.storeNotFound,
|
||||
'服务器不存在',
|
||||
details: {'id': input.id},
|
||||
);
|
||||
}
|
||||
return serverRepository.update(
|
||||
current.copyWith(
|
||||
name: name,
|
||||
baseUrl: baseUrl,
|
||||
updatedAt: nowIso,
|
||||
iconUrl: iconUrl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return serverRepository.create(
|
||||
EmbyServer(
|
||||
id: idGenerator(),
|
||||
name: name,
|
||||
baseUrl: baseUrl,
|
||||
createdAt: nowIso,
|
||||
updatedAt: nowIso,
|
||||
iconUrl: iconUrl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
String _normalizeBaseUrl(String raw) {
|
||||
final trimmed = raw.trim();
|
||||
if (trimmed.isEmpty) {
|
||||
throw DomainError(ErrorCode.invalidParams, '服务器地址不能为空');
|
||||
}
|
||||
|
||||
Uri parsed;
|
||||
try {
|
||||
parsed = Uri.parse(trimmed);
|
||||
} catch (_) {
|
||||
throw DomainError(
|
||||
ErrorCode.invalidParams,
|
||||
'服务器地址格式不正确',
|
||||
details: {'baseUrl': raw},
|
||||
);
|
||||
}
|
||||
|
||||
if (parsed.scheme != 'http' && parsed.scheme != 'https') {
|
||||
throw DomainError(
|
||||
ErrorCode.invalidParams,
|
||||
'仅支持 HTTP/HTTPS 协议',
|
||||
details: {'baseUrl': raw},
|
||||
);
|
||||
}
|
||||
|
||||
final origin =
|
||||
'${parsed.scheme}://${parsed.host}'
|
||||
'${parsed.hasPort ? ':${parsed.port}' : ''}';
|
||||
final cleanPath = parsed.path.replaceAll(RegExp(r'/+$'), '');
|
||||
final normalizedPath = cleanPath.isEmpty ? '/' : cleanPath;
|
||||
|
||||
if (RegExp(r'/emby$', caseSensitive: false).hasMatch(normalizedPath)) {
|
||||
return '$origin$normalizedPath';
|
||||
}
|
||||
if (normalizedPath == '/') {
|
||||
return '$origin/emby';
|
||||
}
|
||||
return '$origin$normalizedPath/emby';
|
||||
}
|
||||
}
|
||||
|
||||
class TogglePauseServerUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
|
||||
TogglePauseServerUseCase({required this.serverRepository});
|
||||
|
||||
Future<EmbyServer> execute(String id) async {
|
||||
final current = await serverRepository.findById(id);
|
||||
if (current == null) {
|
||||
throw DomainError(ErrorCode.storeNotFound, '服务器不存在', details: {'id': id});
|
||||
}
|
||||
return serverRepository.update(current.copyWith(paused: !current.paused));
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteServerUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
final SessionRepository sessionRepository;
|
||||
|
||||
DeleteServerUseCase({
|
||||
required this.serverRepository,
|
||||
required this.sessionRepository,
|
||||
});
|
||||
|
||||
Future<void> execute(String id) async {
|
||||
final exists = await serverRepository.findById(id);
|
||||
if (exists == null) {
|
||||
throw DomainError(ErrorCode.storeNotFound, '服务器不存在', details: {'id': id});
|
||||
}
|
||||
await serverRepository.deleteById(id);
|
||||
await sessionRepository.clear(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user