Build / Build (push) Successful in 1m3s
A singleton ServerSettings (title, descriptions, contact, thumbnail, privacy policy, terms, rules, relays, media overrides, PublishActivity), cached by InstanceSettings and read by the instance API, NodeInfo, the instance actor, the web pages and the mailer; its defaults are the old literals, so nothing changes until an administrator edits. /clientapi/admin/settings (and /thumbnail), /announcements, /relays for administrators; /clientapi/moderator/log, the staff's log (AdminAction, written by domain blocks, backups, settings and the CLI; no IP, never a root paired with its personas). The Mastodon API's rules, extended description, privacy policy, terms of service, domain blocks (Published, Obfuscate), announcements (dismiss, reactions, streaming) and activity (only when published). Reports carry rule ids. Media overrides clamped to hard caps. Relays named by the settings reconcile next to the configured ones. Verified: interop.sh instance accounts relay with mastodon, relay, aoderelay and mailpit, 119 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
60 lines
2.2 KiB
C#
60 lines
2.2 KiB
C#
using MongoDB.Entities;
|
|
|
|
using PrivaPub.Models;
|
|
using PrivaPub.Models.User;
|
|
|
|
namespace PrivaPub.Domain.Moderation
|
|
{
|
|
public interface IModerationLog
|
|
{
|
|
/// <summary>Keeps one act of the staff (Models/AdminAction.cs). Never throws: an act that was done stays done when
|
|
/// its entry can't be written, and the failure is logged.</summary>
|
|
Task Write(string byRootId, string action, string targetKind, string targetId, string targetLabel, string reportId = default, string note = default,
|
|
CancellationToken token = default);
|
|
}
|
|
|
|
// The staff's log (completion program R5): administrators read all of it, moderators what isn't about a login
|
|
// (ModeratorController's /clientapi/moderator/log). An entry never holds an IP address, and never pairs a root with its
|
|
// own personas: an act on a persona that the acting root owns is kept without who acted.
|
|
public class ModerationLog : IModerationLog
|
|
{
|
|
const int MaxLabel = 200;
|
|
const int MaxNote = 1000;
|
|
|
|
readonly ILogger<ModerationLog> _logger;
|
|
|
|
public ModerationLog(ILogger<ModerationLog> logger)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task Write(string byRootId, string action, string targetKind, string targetId, string targetLabel, string reportId = default,
|
|
string note = default, CancellationToken token = default)
|
|
{
|
|
try
|
|
{
|
|
if (targetKind == AdminTargets.Persona && byRootId != default && byRootId != AdminAction.Cli && targetId != default
|
|
&& await DB.Default.Find<RootToAvatar>().Match(r => r.RootId == byRootId && r.AvatarId == targetId).ExecuteAnyAsync(token))
|
|
byRootId = default;
|
|
await DB.Default.SaveAsync(new AdminAction
|
|
{
|
|
ByRootId = byRootId,
|
|
Action = action,
|
|
TargetKind = targetKind,
|
|
TargetId = targetId,
|
|
TargetLabel = Cut(targetLabel, MaxLabel),
|
|
ReportId = reportId,
|
|
Note = Cut(note, MaxNote)
|
|
}, CancellationToken.None);//an act that was done is logged even when its request went away
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "The staff's log could not keep a {Action}", action);
|
|
}
|
|
}
|
|
|
|
static string Cut(string text, int max) =>
|
|
string.IsNullOrEmpty(text) ? text : text.Length <= max ? text : text[..(max - 1)] + "…";
|
|
}
|
|
}
|