Files
umbrix/lib/data/repository/profiles_repository_impl.dart

156 lines
4.2 KiB
Dart
Raw Normal View History

2023-07-06 17:18:41 +03:30
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:fpdart/fpdart.dart';
import 'package:hiddify/data/local/dao/dao.dart';
import 'package:hiddify/data/repository/exception_handlers.dart';
import 'package:hiddify/domain/clash/clash.dart';
import 'package:hiddify/domain/profiles/profiles.dart';
import 'package:hiddify/services/files_editor_service.dart';
import 'package:hiddify/utils/utils.dart';
import 'package:meta/meta.dart';
class ProfilesRepositoryImpl
with ExceptionHandler, InfraLogger
implements ProfilesRepository {
ProfilesRepositoryImpl({
required this.profilesDao,
required this.filesEditor,
required this.clashFacade,
required this.dio,
});
final ProfilesDao profilesDao;
final FilesEditorService filesEditor;
final ClashFacade clashFacade;
final Dio dio;
@override
TaskEither<ProfileFailure, Profile?> get(String id) {
return TaskEither.tryCatch(
() => profilesDao.getById(id),
ProfileUnexpectedFailure.new,
);
}
@override
Stream<Either<ProfileFailure, Profile?>> watchActiveProfile() {
return profilesDao
.watchActiveProfile()
.handleExceptions(ProfileUnexpectedFailure.new);
}
@override
Stream<Either<ProfileFailure, bool>> watchHasAnyProfile() {
return profilesDao
.watchProfileCount()
.map((event) => event != 0)
.handleExceptions(ProfileUnexpectedFailure.new);
}
@override
Stream<Either<ProfileFailure, List<Profile>>> watchAll() {
return profilesDao
.watchAll()
.handleExceptions(ProfileUnexpectedFailure.new);
}
@override
TaskEither<ProfileFailure, Unit> add(Profile baseProfile) {
return exceptionHandler(
() async {
return fetch(baseProfile.url, baseProfile.id)
.flatMap(
(subInfo) => TaskEither(() async {
await profilesDao.create(
baseProfile.copyWith(
subInfo: subInfo,
lastUpdate: DateTime.now(),
),
);
return right(unit);
}),
)
.run();
},
ProfileUnexpectedFailure.new,
);
}
@override
TaskEither<ProfileFailure, Unit> update(Profile baseProfile) {
return exceptionHandler(
() async {
return fetch(baseProfile.url, baseProfile.id)
.flatMap(
(subInfo) => TaskEither(() async {
await profilesDao.edit(
baseProfile.copyWith(
subInfo: subInfo,
lastUpdate: DateTime.now(),
),
);
return right(unit);
}),
)
.run();
},
ProfileUnexpectedFailure.new,
);
}
@override
TaskEither<ProfileFailure, Unit> setAsActive(String id) {
return TaskEither.tryCatch(
() async {
await profilesDao.setAsActive(id);
return unit;
},
ProfileUnexpectedFailure.new,
);
}
@override
TaskEither<ProfileFailure, Unit> delete(String id) {
return TaskEither.tryCatch(
() async {
await profilesDao.removeById(id);
await filesEditor.deleteConfig(id);
return unit;
},
ProfileUnexpectedFailure.new,
);
}
@visibleForTesting
TaskEither<ProfileFailure, SubscriptionInfo?> fetch(
String url,
String fileName,
) {
return TaskEither(
() async {
final path = filesEditor.configPath(fileName);
final response = await dio.download(url, path);
if (response.statusCode != 200) {
await File(path).delete();
return left(const ProfileUnexpectedFailure());
}
final isValid = await clashFacade
.validateConfig(fileName)
.getOrElse((_) => false)
.run();
if (!isValid) {
await File(path).delete();
return left(const ProfileFailure.invalidConfig());
}
final subInfoString =
response.headers.map['subscription-userinfo']?.single;
final subInfo = subInfoString != null
? SubscriptionInfo.fromResponseHeader(subInfoString)
: null;
return right(subInfo);
},
);
}
}