Under /clientapi/admin/statistics, admin only (the root JWT's IsAdmin policy, like the domain
blocks; /api tokens are persona tokens):
- GET overview?days: totals per channel, inbound and outbound outcomes, the delivery
success rate, delivery latency p50/p95 from the buckets, active and known servers,
distinct accounts, and the ledger's written/dropped/failed counts;
- GET hosts?days&sort=volume|failures|latency|host&software&page&limit: per server
traffic, refusals, failures, latency, accounts, software and delivery health;
- GET hosts/{host}?days: the server's description, its daily series and its last 100
events;
- GET events?host&channel&outcome&reason&before&limit, and GET server?days for the
ServerDay rows;
- POST rollups/{day} refolds a past day; POST hosts/{host}/describe describes a server
again.
Days not yet rolled up, today included, are folded live from the events, so the numbers are
current. Answers that may carry locations carry the DB-IP attribution.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELjqpznMFMNrJoJUj6K5p2
237 lines
9.7 KiB
C#
237 lines
9.7 KiB
C#
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.ResponseCompression;
|
|
|
|
using OpenIddict.Validation.AspNetCore;
|
|
|
|
using PrivaPub.ClientModels;
|
|
using PrivaPub.Extensions;
|
|
using PrivaPub.Models;
|
|
using PrivaPub.Services;
|
|
using PrivaPub.StaticServices;
|
|
|
|
using System.Text.Json.Serialization;
|
|
using Microsoft.OpenApi;
|
|
using PrivaPub.Services.ClientToServer.Private;
|
|
using PrivaPub.Services.ClientToServer.Public;
|
|
using PrivaPub.Federation.Actors;
|
|
using PrivaPub.Federation.Outbox;
|
|
using PrivaPub.Federation.Signing;
|
|
using PrivaPub.Federation.Inbox;
|
|
using PrivaPub.Federation.Objects;
|
|
using PrivaPub.Federation.Moderation;
|
|
using PrivaPub.Federation.Inbox.Handlers;
|
|
using PrivaPub.Domain.Content;
|
|
using PrivaPub.Domain.Relationships;
|
|
using PrivaPub.Domain.Social;
|
|
using PrivaPub.Domain.Statuses;
|
|
using PrivaPub.Domain.Timelines;
|
|
using PrivaPub.Infrastructure.Http;
|
|
using PrivaPub.Infrastructure.Jobs;
|
|
using PrivaPub.Infrastructure.Geo;
|
|
using PrivaPub.Infrastructure.Statistics;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace PrivaPub.Middleware
|
|
{
|
|
public static class PrivaPubConfigurations
|
|
{
|
|
const string SchemeSelector = "PrivaPub";
|
|
|
|
public static IServiceCollection PrivaPubAppSettingsConfiguration(this IServiceCollection service, IConfiguration configuration)
|
|
{
|
|
return service
|
|
.Configure<MongoSettings>(configuration.GetSection(nameof(MongoSettings)))
|
|
.Configure<AppConfiguration>(configuration.GetSection(nameof(AppConfiguration)));
|
|
}
|
|
public static IServiceCollection PrivaPubWorkersConfiguration(this IServiceCollection service)
|
|
{
|
|
return service;
|
|
//.AddHostedService<DiscussionsWorker>()
|
|
//.AddHostedService<GroupsCleanerWorker>()
|
|
//.AddHostedService<PoliciesCleanerWorker>();
|
|
}
|
|
public static IServiceCollection PrivaPubFederationConfiguration(this IServiceCollection service, IConfiguration configuration)
|
|
{
|
|
service.Configure<FederationOptions>(configuration.GetSection("Federation"));
|
|
service.AddHttpClient(FederationHttp.ClientName, (provider, client) =>
|
|
{
|
|
var baseAddress = provider.GetRequiredService<IOptionsMonitor<AppConfiguration>>().CurrentValue.BackendBaseAddress?.TrimEnd('/');
|
|
client.Timeout = FederationHttp.RequestTimeout;
|
|
client.DefaultRequestHeaders.UserAgent.ParseAdd($"PrivaPub/{BuildInfo.Ref} (+{baseAddress}/)");
|
|
})
|
|
.ConfigurePrimaryHttpMessageHandler(provider =>
|
|
SafeHttpHandlerFactory.Create(provider.GetRequiredService<IOptions<FederationOptions>>().Value, provider.GetRequiredService<IConnectedAddresses>()));
|
|
return service
|
|
.AddSingleton<IConnectedAddresses, ConnectedAddresses>()
|
|
.AddSingleton<IFederationHttp, FederationHttp>()
|
|
.AddSingleton<IDomainBlocks, DomainBlocks>()
|
|
.AddSingleton<IContentRenderer, ContentRenderer>()
|
|
.AddSingleton<ILocalActorService, LocalActorService>()
|
|
.AddSingleton<IRemoteActorService, RemoteActorService>()
|
|
.AddSingleton<IDeliveryService, DeliveryService>()
|
|
.AddSingleton<IOutboxPublisher, OutboxPublisher>()
|
|
.AddSingleton<IGroupDistributor, GroupDistributor>()
|
|
.AddSingleton<ISignedFetchAuthorizer, SignedFetchAuthorizer>()
|
|
.AddSingleton<IFanout, Fanout>()
|
|
.AddSingleton<IInboxReceiver, InboxReceiver>()
|
|
.AddSingleton<IActivityHandler, FollowHandler>()
|
|
.AddSingleton<IActivityHandler, AcceptHandler>()
|
|
.AddSingleton<IActivityHandler, RejectHandler>()
|
|
.AddSingleton<IActivityHandler, UndoHandler>()
|
|
.AddSingleton<IActivityHandler, LikeHandler>()
|
|
.AddSingleton<IActivityHandler, DislikeHandler>()
|
|
.AddSingleton<IActivityHandler, EmojiReactHandler>()
|
|
.AddSingleton<IActivityHandler, QuoteRequestHandler>()
|
|
.AddSingleton<IReactions, Reactions>()
|
|
.AddSingleton<LinkPreviews>()
|
|
.AddSingleton<ILinkPreviews>(services => services.GetRequiredService<LinkPreviews>())
|
|
.AddSingleton<IJobHandler>(services => services.GetRequiredService<LinkPreviews>())
|
|
.AddSingleton<IActivityHandler, JoinHandler>()
|
|
.AddSingleton<IActivityHandler, AnnounceHandler>()
|
|
.AddSingleton<IActivityHandler, FlagHandler>()
|
|
.AddSingleton<IActivityHandler, CreateHandler>()
|
|
.AddSingleton<IActivityHandler, DeleteHandler>()
|
|
.AddSingleton<IActivityHandler, UpdateHandler>()
|
|
.AddSingleton<IJobHandler, InboxProcessor>()
|
|
.AddSingleton<IRemotePosts, RemotePosts>()
|
|
.AddSingleton<IObjectRecords, ObjectRecords>()
|
|
.AddSingleton<IPollService, PollService>()
|
|
.AddSingleton<IQuoteService, QuoteService>()
|
|
.AddSingleton<IJobHandler, PollRefreshJob>()
|
|
.AddSingleton<IJobHandler, PollCloseJob>()
|
|
.AddSingleton<IJobHandler, InstanceDescriber>()
|
|
.AddSingleton<IJobHandler, AncestorsJobHandler>()
|
|
.AddSingleton<IJobQueue, JobQueue>()
|
|
.AddSingleton<IHostCircuitBreaker, HostCircuitBreaker>()
|
|
.AddSingleton<IJobHandler, DeliveryJobHandler>()
|
|
.AddHostedService<JobWorker>();
|
|
}
|
|
public static IServiceCollection PrivaPubStatisticsConfiguration(this IServiceCollection service, IConfiguration configuration) =>
|
|
service
|
|
.Configure<StatisticsOptions>(configuration.GetSection("Statistics"))
|
|
.AddSingleton<InteractionSalts>()
|
|
.AddSingleton<InteractionLedger>()
|
|
.AddSingleton<IInteractionLedger>(services => services.GetRequiredService<InteractionLedger>())
|
|
.AddHostedService(services => services.GetRequiredService<InteractionLedger>())
|
|
.AddSingleton<IJobHandler, RollupJob>()
|
|
.AddSingleton<IGeoLocator, DbIpLocator>()
|
|
.AddSingleton<Domain.Statistics.StatisticsQueries>()
|
|
.AddHostedService<StatisticsSchedule>();
|
|
|
|
public static IServiceCollection PrivaPubAuthServicesConfiguration(this IServiceCollection service, IConfiguration configuration)
|
|
{
|
|
return service
|
|
.AddAuthorization(options =>
|
|
{
|
|
options.AddPolicy(Policies.IsUser, Extensions.Extensions.IsUserPolicy());
|
|
options.AddPolicy(Policies.IsAdmin, Extensions.Extensions.IsAdminPolicy());
|
|
options.AddPolicy(Policies.IsModerator, Extensions.Extensions.IsModeratorPolicy());
|
|
})
|
|
.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = SchemeSelector;
|
|
options.DefaultChallengeScheme = SchemeSelector;
|
|
options.DefaultScheme = SchemeSelector;
|
|
})
|
|
.AddPolicyScheme(SchemeSelector, SchemeSelector, options => options.ForwardDefaultSelector = context =>
|
|
context.Request.Path.StartsWithSegments("/api")
|
|
? OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme
|
|
: JwtBearerDefaults.AuthenticationScheme)
|
|
.AddPrivaPubAuth(configuration)
|
|
.Services
|
|
.AddSingleton<AuthTokenManager>()
|
|
.AddSingleton<IPasswordHasher, PasswordHasher>();
|
|
}
|
|
public static IServiceCollection PrivaPubInternalizationConfiguration(this IServiceCollection service, IConfiguration configuration)
|
|
{
|
|
return service
|
|
.AddLocalization()
|
|
.AddSingleton<RequestLocalizationOptionsService>();
|
|
}
|
|
|
|
public static IServiceCollection PrivaPubOptimizationConfiguration(this IServiceCollection service)
|
|
{
|
|
return service.AddResponseCompression(opts =>
|
|
{
|
|
opts.Providers.Add<BrotliCompressionProvider>();
|
|
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream" });
|
|
});
|
|
}
|
|
|
|
public static IServiceCollection PrivaPubDataBaseConfiguration(this IServiceCollection service)
|
|
{
|
|
return service.AddSingleton<DbEntities>();
|
|
}
|
|
|
|
public static IServiceCollection PrivaPubServicesConfiguration(this IServiceCollection service)
|
|
{
|
|
return service
|
|
.AddTransient<IDataService, DataService>()
|
|
.AddTransient<IRootUsersService, RootUsersService>()
|
|
.AddTransient<IPublicAvatarUsersService, PublicAvatarUsersService>()
|
|
.AddTransient<IPrivateAvatarUsersService, PrivateAvatarUsersService>()
|
|
.AddTransient<IGroupUsersService, GroupUsersService>()
|
|
.AddTransient<IPostsService, PostsService>()
|
|
.AddTransient<IStatusService, StatusService>()
|
|
.AddTransient<IRelationshipService, RelationshipService>()
|
|
.AddTransient<IReportService, ReportService>()
|
|
.AddTransient<IFollowService, FollowService>()
|
|
.AddTransient<ITimelineService, TimelineService>()
|
|
.AddSingleton<AppConfigurationService>()
|
|
.AddHttpContextAccessor()
|
|
.AddMemoryCache()
|
|
.AddSingleton<IPasswordHasher, PasswordHasher>();
|
|
}
|
|
|
|
public static IServiceCollection PrivaPubMiddlewareConfiguration(this IServiceCollection service)
|
|
{
|
|
return service
|
|
.AddEndpointsApiExplorer()
|
|
.AddSwaggerGen(c =>
|
|
{
|
|
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
|
{
|
|
In = ParameterLocation.Header,
|
|
Description = "Please enter a valid token",
|
|
Name = "Authorization",
|
|
Type = SecuritySchemeType.Http,
|
|
BearerFormat = "JWT",
|
|
Scheme = "bearer"
|
|
});
|
|
c.AddSecurityRequirement(document => new OpenApiSecurityRequirement
|
|
{
|
|
[new OpenApiSecuritySchemeReference("Bearer", document)] = []
|
|
});
|
|
})
|
|
.AddRazorPages(options => options.RootDirectory = "/Web/Pages")
|
|
.Services
|
|
.AddControllers(options => { options.Filters.Add<OperationCancelledExceptionFilter>(); })
|
|
.AddJsonOptions(options =>
|
|
{
|
|
options.JsonSerializerOptions.IgnoreReadOnlyFields = false;
|
|
options.JsonSerializerOptions.IgnoreReadOnlyProperties = false;
|
|
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
|
|
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
|
}).Services;
|
|
}
|
|
|
|
public static IServiceCollection PrivaPubCORSConfiguration(this IServiceCollection service)
|
|
{
|
|
return service.AddCors(options =>
|
|
{
|
|
options.DefaultPolicyName = "DefaultCORS";
|
|
options.AddDefaultPolicy(configure =>
|
|
{
|
|
configure.AllowAnyMethod()
|
|
.AllowAnyHeader()
|
|
.AllowAnyOrigin()
|
|
.WithExposedHeaders("Link", "X-RateLimit-Remaining", "X-RateLimit-Reset")
|
|
.DisallowCredentials();
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
}
|