2026-10-03 11:35:40 +02:00
|
|
|
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;
|
2026-10-03 12:19:29 +02:00
|
|
|
readonly Microsoft.Extensions.Options.IOptionsMonitor<StatisticsOptions> _options;
|
2026-10-03 11:35:40 +02:00
|
|
|
|
2026-10-03 12:19:29 +02:00
|
|
|
public StatisticsController(StatisticsQueries queries, IJobQueue queue, Microsoft.Extensions.Options.IOptionsMonitor<StatisticsOptions> options)
|
2026-10-03 11:35:40 +02:00
|
|
|
{
|
|
|
|
|
_queries = queries;
|
|
|
|
|
_queue = queue;
|
2026-10-03 12:19:29 +02:00
|
|
|
_options = options;
|
2026-10-03 11:35:40 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-03 12:19:29 +02:00
|
|
|
[HttpGet, Route("/clientapi/admin/statistics/crawler")]
|
|
|
|
|
public async Task<IActionResult> Crawler(CancellationToken token) =>
|
|
|
|
|
Ok(await _queries.Crawler(_options.CurrentValue.Crawler.Enabled, _options.CurrentValue.Crawler.RevisitDays, token));
|
|
|
|
|
|
2026-10-03 11:35:40 +02:00
|
|
|
[HttpGet, Route("/clientapi/admin/statistics/overview")]
|
|
|
|
|
public async Task<IActionResult> Overview([FromQuery] int days = 30, CancellationToken token = default) =>
|
|
|
|
|
Ok(await _queries.Overview(days, token));
|
|
|
|
|
|
|
|
|
|
[HttpGet, Route("/clientapi/admin/statistics/hosts")]
|
|
|
|
|
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> Server([FromQuery] int days = 30, CancellationToken token = default) =>
|
|
|
|
|
Ok(await _queries.Server(days, token));
|
|
|
|
|
|
|
|
|
|
[HttpPost, Route("/clientapi/admin/statistics/rollups/{day}")]
|
|
|
|
|
public async Task<IActionResult> 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<IActionResult> 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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|