using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; using MongoDB.Entities; using PrivaPub.ClientModels; using PrivaPub.ClientModels.Admin; using PrivaPub.Domain.Moderation; using PrivaPub.Extensions; using PrivaPub.Federation.Moderation; using PrivaPub.Models; using PrivaPub.Models.Federation; using PrivaPub.Resources; namespace PrivaPub.Controllers.ClientToServer { [ApiController, Route("clientapi/admin/domainblocks"), Authorize(Policy = Policies.IsAdmin)] public class DomainBlockController : ControllerBase { readonly IDomainBlocks _domainBlocks; readonly IStringLocalizer _localizer; readonly Domain.Media.IMediaProxy _proxy; readonly IModerationLog _log; public DomainBlockController(IDomainBlocks domainBlocks, IStringLocalizer localizer, Domain.Media.IMediaProxy proxy, IModerationLog log) { _domainBlocks = domainBlocks; _localizer = localizer; _proxy = proxy; _log = log; } [HttpGet, Route("/clientapi/admin/domainblocks/list")] public async Task List(CancellationToken token) => Ok((await DB.Default.Find().Sort(b => b.Domain, Order.Ascending).ExecuteAsync(token)).Select(ToView).ToList()); [HttpPost, Route("/clientapi/admin/domainblocks/insert")] public async Task Insert(DomainBlockForm form, CancellationToken token) { var domain = DomainBlocks.Normalise(form.Domain); if (!ModelState.IsValid || Uri.CheckHostName(domain) != UriHostNameType.Dns) return BadRequest(new WebResult().Invalidate(_localizer["Invalid model."])); var block = await DB.Default.UpdateAndGet() .Match(b => b.Domain == domain) .Modify(b => b.Domain, domain) .Modify(b => b.Severity, form.Suspend ? DomainBlockSeverity.Suspend : DomainBlockSeverity.Silence) .Modify(b => b.RejectMedia, form.RejectMedia) .Modify(b => b.PublicComment, form.PublicComment) .Modify(b => b.PrivateComment, form.PrivateComment) .Modify(b => b.Published, form.Published) .Modify(b => b.Obfuscate, form.Obfuscate) .Modify(b => b.SetOnInsert(x => x.CreatedAt, DateTime.UtcNow)) .Option(o => o.IsUpsert = true) .ExecuteAsync(token); await _domainBlocks.Reload(token); // what the media proxy holds of it goes with the block if (block.Severity == DomainBlockSeverity.Suspend || block.RejectMedia) _proxy.Purge(domain); await _log.Write(User.GetUserId(), AdminActions.DomainBlockInsert, AdminTargets.Domain, domain, domain, note: string.Join(", ", new[] { block.Severity.ToString().ToLowerInvariant(), block.RejectMedia ? "media rejected" : default, block.Published ? (block.Obfuscate ? "published, obfuscated" : "published") : default }.Where(n => n != default)), token: token); return Ok(ToView(block)); } [HttpPost, Route("/clientapi/admin/domainblocks/delete")] public async Task Delete([FromQuery] string domain, CancellationToken token) { domain = DomainBlocks.Normalise(domain); var deleted = await DB.Default.DeleteAsync(b => b.Domain == domain); await _domainBlocks.Reload(token); if (deleted.DeletedCount > 0) await _log.Write(User.GetUserId(), AdminActions.DomainBlockDelete, AdminTargets.Domain, domain, domain, token: token); return Ok(); } static ViewDomainBlock ToView(DomainBlock block) => new() { Id = block.ID, Domain = block.Domain, Severity = block.Severity.ToString(), RejectMedia = block.RejectMedia, PublicComment = block.PublicComment, PrivateComment = block.PrivateComment, Published = block.Published, Obfuscate = block.Obfuscate, CreatedAt = block.CreatedAt }; } }