using System.Net; using collAnon.Client.Services; using decePubClient.Extensions; using decePubClient.Models; using PrivaPub.ClientModels; using PrivaPub.ClientModels.Admin; namespace decePubClient.Services { /// /// What PrivaPub's moderators and administrators do as the root: reports (/clientapi/moderator), servers blocked /// for the whole instance and the federation statistics (/clientapi/admin). PrivaPub checks the root's role on every /// call. Lists come back empty and values null on failure; nothing throws. /// public class AdminService(IHttpService httpService, ILoggingService logger, CoalescingStringLocalizer localizer) { /// The newest 100 reports, open or resolved. public async Task> Reports(bool resolved, CancellationToken cancellationToken = default) { using var response = await httpService.Get(APIs.ClientApi.GET_ModeratorReports, [$"resolved={(resolved ? "true" : "false")}"], cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.DefaultReadFromJsonAsync>(cancellationToken) ?? []; await LogFailure(response, nameof(Reports), cancellationToken); return []; } public async Task Resolve(string reportId, CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_M_ResolveReport(reportId), cancellationToken: cancellationToken); return response.IsSuccessStatusCode ? new WebResult() : await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); } /// The servers blocked for everyone here, by name. public async Task> ServerBlocks(CancellationToken cancellationToken = default) { using var response = await httpService.Get(APIs.ClientApi.GET_AdminDomainBlocks, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.DefaultReadFromJsonAsync>(cancellationToken) ?? []; await LogFailure(response, nameof(ServerBlocks), cancellationToken); return []; } /// Blocks a server for everyone, or changes how; Data is the block. public async Task BlockServer(DomainBlockForm form, CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_AdminDomainBlock, form, cancellationToken: cancellationToken); if (!response.IsSuccessStatusCode) return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); return new WebResult { Data = await response.DefaultReadFromJsonAsync(cancellationToken) }; } public async Task UnblockServer(string domain, CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_M_AdminDomainUnblock(domain), cancellationToken: cancellationToken); return response.IsSuccessStatusCode ? new WebResult() : await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); } /// The server's backups, what runs and the last restore; null on failure. public async Task Backups(CancellationToken cancellationToken = default) { using var response = await httpService.Get(APIs.ClientApi.GET_AdminBackups, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.DefaultReadFromJsonAsync(cancellationToken); await LogFailure(response, nameof(Backups), cancellationToken); return null; } /// Starts a backup; it runs on while the list says so. public async Task BackUp(CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_AdminBackup, cancellationToken: cancellationToken); return response.IsSuccessStatusCode ? new WebResult() : await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); } public async Task DeleteBackup(string id, CancellationToken cancellationToken = default) { using var response = await httpService.Delete(APIs.ClientApi.DELETE_M_AdminBackup(id), cancellationToken: cancellationToken); return response.IsSuccessStatusCode ? new WebResult() : await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); } /// A download link for a backup, for the password; Data is the ViewBackupTicket. public async Task BackupTicket(string id, string password, CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_M_AdminBackupTicket(id), new BackupPasswordForm { Password = password }, cancellationToken: cancellationToken); if (!response.IsSuccessStatusCode) return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); return new WebResult { Data = await response.DefaultReadFromJsonAsync(cancellationToken) }; } /// Asks for a backup to be restored: PrivaPub stops within seconds and restores it as it starts again. public async Task RestoreBackup(string id, RestoreBackupForm form, CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_M_AdminBackupRestore(id), form, cancellationToken: cancellationToken); return response.IsSuccessStatusCode ? new WebResult() : await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); } /// An upload to send a backup's pieces to (transfer.js); Data is the ViewBackupUpload. public async Task StartBackupUpload(CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_AdminBackupUpload, cancellationToken: cancellationToken); if (!response.IsSuccessStatusCode) return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); return new WebResult { Data = await response.DefaultReadFromJsonAsync(cancellationToken) }; } /// The whole upload read into a backup; Data is the ViewBackup. public async Task FinishBackupUpload(string id, CancellationToken cancellationToken = default) { using var response = await httpService.Post(APIs.ClientApi.POST_M_AdminBackupUploadFinish(id), cancellationToken: cancellationToken); if (!response.IsSuccessStatusCode) return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); return new WebResult { Data = await response.DefaultReadFromJsonAsync(cancellationToken) }; } public async Task CancelBackupUpload(string id, CancellationToken cancellationToken = default) { using var response = await httpService.Delete(APIs.ClientApi.DELETE_M_AdminBackupUpload(id), cancellationToken: cancellationToken); return response.IsSuccessStatusCode ? new WebResult() : await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); } public async Task Overview(int days, CancellationToken cancellationToken = default) { using var response = await httpService.Get(APIs.ClientApi.GET_StatisticsOverview, [$"days={days}"], cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.DefaultReadFromJsonAsync(cancellationToken); await LogFailure(response, nameof(Overview), cancellationToken); return null; } /// One page of the servers we exchanged with; sort is traffic (default), failures, latency or host. public async Task Hosts(int days, string sort, int page, CancellationToken cancellationToken = default) { using var response = await httpService.Get(APIs.ClientApi.GET_StatisticsHosts, [$"days={days}", $"sort={Uri.EscapeDataString(sort ?? "")}", $"page={page}", "limit=50"], cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.DefaultReadFromJsonAsync(cancellationToken); await LogFailure(response, nameof(Hosts), cancellationToken); return null; } public async Task Crawler(CancellationToken cancellationToken = default) { using var response = await httpService.Get(APIs.ClientApi.GET_StatisticsCrawler, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.DefaultReadFromJsonAsync(cancellationToken); await LogFailure(response, nameof(Crawler), cancellationToken); return null; } async Task LogFailure(HttpResponseMessage response, string caller, CancellationToken cancellationToken) { var result = await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); if (result.StatusCode is not ((int)HttpStatusCode.Gone or (int)HttpStatusCode.Forbidden)) await logger.ProcessWarning(result.ToString(localizer), $"{nameof(AdminService)}.{caller}", cancellationToken); } } }