R6 moderation in decePub: users and personas, accounts, report actions, deletions, suspended personas

Administration's Users & personas (administrators): logins searched and opened into their personas and groups; roles,
bans, two-factor reset and deletion of a login; silence, suspension and deletion of a persona; a group deleted. Accounts
(moderators): local personas and remote accounts searched or listed as moderated, silenced and suspended with a staff
note and a warning. Reports resolve with an action. ActDialog asks for the note, the warning or the password and is the
act's confirmation. A login deletes its own persona and the groups it owns with its password. Suspended personas can't
be chosen; one refused a token mid-session moves the session to another, or to none, once. Warnings show in
notifications, limited accounts on their profile, and moderators remove a post from its card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-17 05:41:10 +02:00
1 parent 4c88980374
commit c572471b88
30 files changed
+2604 -67

No files matched your search

+26
View File
@@ -23,6 +23,12 @@ namespace decePubClient.Services
AuthData authData;
bool loaded;
/// <summary>The personas PrivaPub refused a token to while the JWT was good (suspended, or gone): no request acts as
/// them again until the reader picks one anew or the session is read again.</summary>
readonly HashSet<string> refusedPersonas = [];
/// <summary>Raised once for a persona PrivaPub refused a token to (Refuse); CascadingState moves the session on.</summary>
public event Action<string> PersonaRefused;
/// <summary>The stored session, or an empty one.</summary>
public async Task<AuthData> GetAuthData(CancellationToken cancellationToken = default)
@@ -89,6 +95,25 @@ namespace decePubClient.Services
await Save(session, cancellationToken);
}
/// <summary>Whether PrivaPub refused the persona a token in this session (Refuse).</summary>
public bool IsRefused(string personaId) => personaId is not null && refusedPersonas.Contains(personaId);
/// <summary>PrivaPub refused the persona a token while the JWT was good: no request acts as it from now on, and
/// PersonaRefused tells the session, once.</summary>
public void Refuse(string personaId)
{
if (personaId is null || !refusedPersonas.Add(personaId))
return;
PersonaRefused?.Invoke(personaId);
}
/// <summary>The persona may be tried again: the reader picked it, or PrivaPub lists it as usable again.</summary>
public void Unrefuse(string personaId)
{
if (personaId is not null)
refusedPersonas.Remove(personaId);
}
/// <summary>Drops every persona's token: PrivaPub revoked them all (a password change), and each is exchanged anew when used.</summary>
public async Task ForgetPersonaTokens(CancellationToken cancellationToken = default)
{
@@ -104,6 +129,7 @@ namespace decePubClient.Services
logger.LogInformation($"set null({nameof(LogoutAsync)})");
authData = null;
loaded = true;
refusedPersonas.Clear();
await storage.RemoveItemAsync(nameof(AuthData), cancellationToken);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());