diff --git a/assets/translations/en.json b/assets/translations/en.json index ad200033..66f21bf7 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -599,7 +599,10 @@ "start_server_upgrade": "Start the server upgrade", "change_auto_upgrade_settings": "Change auto-upgrade settings", "change_server_timezone": "Change server timezone", - "change_ssh_settings": "Change SSH settings" + "change_ssh_settings": "Change SSH settings", + "update_dns_records": "Update DNS records", + "dns_records_did_not_change": "No changes needed", + "dns_records_changed": "DNS records updated" }, "validations": { "required": "Required", diff --git a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart index 86d99fb2..14ea6670 100644 --- a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart +++ b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart @@ -5,8 +5,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/job.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; export 'package:provider/provider.dart'; @@ -130,11 +133,20 @@ class JobsCubit extends Cubit { Future applyAll() async { if (state is JobsStateWithJobs) { final List jobs = (state as JobsStateWithJobs).clientJobList; + + final rebuildRequired = jobs.any((final job) => job.requiresRebuild); + final dnsUpdateRequired = jobs.any((final job) => job.requiresDnsUpdate); + + if (dnsUpdateRequired) { + jobs.add(UpdateDnsRecordsJob(status: JobStatusEnum.created)); + } + emit(JobsStateLoading(jobs, null, const [])); await Future.delayed(Duration.zero); - final rebuildRequired = jobs.any((final job) => job.requiresRebuild); + final List oldDnsRecords = + await getIt().api.getDnsRecords(); for (final ClientJob job in jobs) { emit( @@ -158,6 +170,50 @@ class JobsCubit extends Cubit { } } + if (dnsUpdateRequired) { + emit( + (state as JobsStateLoading).updateJobStatus( + UpdateDnsRecordsJob.jobId, + JobStatusEnum.running, + ), + ); + final List newDnsRecords = + await getIt().api.getDnsRecords(); + + if (const UnorderedIterableEquality() + .equals(oldDnsRecords, newDnsRecords)) { + emit( + (state as JobsStateLoading).updateJobStatus( + UpdateDnsRecordsJob.jobId, + JobStatusEnum.finished, + message: 'jobs.dns_records_did_not_change'.tr(), + ), + ); + } else { + final ServerDomain? domain = + getIt().serverDomain; + + final dnsCreateResult = + await ProvidersController.currentDnsProvider!.updateDnsRecords( + newRecords: + newDnsRecords.where((final r) => r.content != null).toList(), + oldRecords: oldDnsRecords, + domain: domain!, + ); + + emit( + (state as JobsStateLoading).updateJobStatus( + UpdateDnsRecordsJob.jobId, + dnsCreateResult.success + ? JobStatusEnum.finished + : JobStatusEnum.error, + message: + dnsCreateResult.message ?? 'jobs.dns_records_changed'.tr(), + ), + ); + } + } + if (!rebuildRequired) { emit((state as JobsStateLoading).finished()); return; diff --git a/lib/logic/cubit/client_jobs/client_jobs_state.dart b/lib/logic/cubit/client_jobs/client_jobs_state.dart index c7ab2c0c..757885f3 100644 --- a/lib/logic/cubit/client_jobs/client_jobs_state.dart +++ b/lib/logic/cubit/client_jobs/client_jobs_state.dart @@ -27,6 +27,9 @@ class JobsStateWithJobs extends JobsState { bool get rebuildRequired => clientJobList.any((final job) => job.requiresRebuild); + bool get dnsUpdateRequired => + clientJobList.any((final job) => job.requiresDnsUpdate); + JobsState removeById(final String id) { final List newJobsList = clientJobList.where((final element) => element.id != id).toList(); @@ -74,6 +77,9 @@ class JobsStateLoading extends JobsState { bool get rebuildRequired => clientJobList.any((final job) => job.requiresRebuild); + bool get dnsUpdateRequired => + clientJobList.any((final job) => job.requiresDnsUpdate); + final List postponedJobs; JobsStateLoading updateJobStatus( @@ -139,6 +145,9 @@ class JobsStateFinished extends JobsState { bool get rebuildRequired => clientJobList.any((final job) => job.requiresRebuild); + bool get dnsUpdateRequired => + clientJobList.any((final job) => job.requiresDnsUpdate); + final List postponedJobs; @override diff --git a/lib/logic/models/job.dart b/lib/logic/models/job.dart index 02ac9d84..41226e7e 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/job.dart @@ -14,12 +14,14 @@ abstract class ClientJob extends Equatable { final String? id, this.requiresRebuild = true, this.status = JobStatusEnum.created, + this.requiresDnsUpdate = false, this.message, }) : id = id ?? StringGenerators.simpleId(); final String title; final String id; final bool requiresRebuild; + final bool requiresDnsUpdate; final JobStatusEnum status; final String? message; @@ -62,6 +64,32 @@ class UpgradeServerJob extends ClientJob { ); } +class UpdateDnsRecordsJob extends ClientJob { + UpdateDnsRecordsJob({ + super.status, + super.message, + }) : super(title: 'jobs.update_dns_records'.tr(), id: jobId); + + static String jobId = 'dns_update'; + + @override + bool canAddTo(final List jobs) => + !jobs.any((final job) => job is UpdateDnsRecordsJob); + + @override + Future<(bool, String)> execute() async => (false, 'unimplemented'); + + @override + UpdateDnsRecordsJob copyWithNewStatus({ + required final JobStatusEnum status, + final String? message, + }) => + UpdateDnsRecordsJob( + status: status, + message: message, + ); +} + class CollectNixGarbageJob extends ClientJob { CollectNixGarbageJob({ super.status, @@ -223,6 +251,7 @@ class ServiceToggleJob extends ClientJob { }) : super( title: '${needToTurnOn ? "jobs.service_turn_on".tr() : "jobs.service_turn_off".tr()} ${service.displayName}', + requiresDnsUpdate: true, ); final bool needToTurnOn;