using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PrivaPub.ClientModels; using PrivaPub.Domain.Statistics; using PrivaPub.Infrastructure.Jobs; using PrivaPub.Infrastructure.Statistics; using PrivaPub.Models.Jobs; using System.Globalization; namespace PrivaPub.Controllers.ClientToServer { [ApiController, Route("clientapi/admin/statistics"), Authorize(Policy = Policies.IsAdmin)] public class StatisticsController : ControllerBase { readonly StatisticsQueries _queries; readonly IJobQueue _queue; readonly Microsoft.Extensions.Options.IOptionsMonitor _options; public StatisticsController(StatisticsQueries queries, IJobQueue queue, Microsoft.Extensions.Options.IOptionsMonitor options) { _queries = queries; _queue = queue; _options = options; } [HttpGet, Route("/clientapi/admin/statistics/crawler")] public async Task Crawler(CancellationToken token) => Ok(await _queries.Crawler(_options.CurrentValue.Crawler.Enabled, _options.CurrentValue.Crawler.RevisitDays, token)); [HttpGet, Route("/clientapi/admin/statistics/overview")] public async Task Overview([FromQuery] int days = 30, CancellationToken token = default) => Ok(await _queries.Overview(days, token)); [HttpGet, Route("/clientapi/admin/statistics/hosts")] public async Task Hosts([FromQuery] int days = 30, [FromQuery] string sort = default, [FromQuery] string software = default, [FromQuery] int page = 1, [FromQuery] int limit = 50, CancellationToken token = default) => Ok(await _queries.Hosts(days, sort, software, page, limit, token)); [HttpGet, Route("/clientapi/admin/statistics/hosts/{host}")] public async Task Host(string host, [FromQuery] int days = 90, CancellationToken token = default) => await _queries.Host(host, days, token) is { } found ? Ok(found) : NotFound(); [HttpGet, Route("/clientapi/admin/statistics/events")] public async Task Events([FromQuery] string host = default, [FromQuery] string channel = default, [FromQuery] string outcome = default, [FromQuery] string reason = default, [FromQuery] DateTime? before = default, [FromQuery] int limit = 100, CancellationToken token = default) => Ok(await _queries.Events(host, channel, outcome, reason, before, limit, token)); [HttpGet, Route("/clientapi/admin/statistics/server")] public async Task Server([FromQuery] int days = 30, CancellationToken token = default) => Ok(await _queries.Server(days, token)); [HttpPost, Route("/clientapi/admin/statistics/rollups/{day}")] public async Task Rollup(string day, CancellationToken token) { if (!DateTime.TryParseExact(day, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var parsed) || parsed.Date >= DateTime.UtcNow.Date) return BadRequest(); var job = RollupJob.For(parsed.Date, $"rollup|{day}|rerun|{DateTime.UtcNow.Ticks}"); job.RunAt = DateTime.UtcNow; await _queue.EnqueueMany(new[] { job }, token); return Accepted(); } [HttpPost, Route("/clientapi/admin/statistics/hosts/{host}/describe")] public async Task Describe(string host, CancellationToken token) { host = Interactions.Host(host); if (host == Interactions.Unknown) return BadRequest(); await _queue.Enqueue(JobKind.DescribeInstance, host, host, $"describe|{host}|admin|{DateTime.UtcNow.Ticks}", token); return Accepted(); } } }