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:
1 parent
4c88980374
commit
c572471b88
30 files changed
+2604
-67
No files matched your search
@@ -160,8 +160,15 @@ Below `md` the app is laid out for a phone, and a touch screen gets thumb-sized
|
||||
- No history entries are pushed.
|
||||
- **What can't be undone asks first** through `Services/ConfirmService.cs` (`Ask`, shown by `ConfirmHost` in
|
||||
`MainLayout`): deleting a post, taking it off a wall, muting, blocking, removing a follower, revoking a quote, leaving a
|
||||
group, dropping a scheduled post, signing out, and for an administrator deleting an announcement and removing the
|
||||
server's thumbnail.
|
||||
group, dropping a scheduled post, signing out, and for an administrator deleting an announcement, removing the
|
||||
server's thumbnail, and changing a login's role, banning or unbanning it, resetting its two-factor sign-in and deleting
|
||||
it. An act that takes words or a password asks in `Components/ActDialog.razor` instead, which is its confirmation:
|
||||
what it does, the fields, Cancel (`data-act-cancel`) and the act (`data-act-submit`); a failure (a wrong password) stays
|
||||
in the dialog. The staff's (`Staff`: `data-moderate-confirm="{act}"`, a note `data-moderate-note`, a warning
|
||||
`data-moderate-warning`, the administrator's own password `data-admin-password`) are silencing and suspending an account
|
||||
and undoing it, removing a post, resolving a report, deleting a persona or a group; the login's own
|
||||
(`data-own-confirm="{act}"`, its password `data-own-password`) are deleting one of its personas and a group a persona
|
||||
owns.
|
||||
- **On a touch screen:**
|
||||
- a tap on a post opens its thread (`main.js`, `data-thread` on the card, its `data-thread-link`), and cards are
|
||||
pinned to the globe with a mouse only;
|
||||
@@ -207,9 +214,16 @@ all of them in every theme. When you add or change one:
|
||||
- `PublicCacheData` (page settings: theme colours, dark/light/system theming, language), persisted in localStorage;
|
||||
- the theme, applied through `AppStatusService.ApplyTheme` (`wwwroot/js/theme.js`);
|
||||
- `IsOnline` / `IsOffline`, polled every 10 seconds;
|
||||
- the session: `IsSignedIn`, `Personas` (the root's), `PersonaId` and `Persona` (its Mastodon `Account`), with
|
||||
`LoadSession`, `SwitchPersona` and `SignOut`. A persona change re-renders every subscriber; pages compare
|
||||
`PersonaId` and reload what belongs to the persona. The JWT is renewed hourly when needed;
|
||||
- the session: `IsSignedIn`, `Personas` (the root's), `PersonaId` and `Persona` (its Mastodon `Account`), `IsModerator`
|
||||
and `IsAdmin` (the JWT's policies), with `LoadSession`, `SwitchPersona`, `PersonaDeleted` and `SignOut`. A persona
|
||||
change re-renders every subscriber; pages compare `PersonaId` and reload what belongs to the persona. The JWT is renewed
|
||||
hourly when needed;
|
||||
- a persona the staff suspended (`ViewAvatar.AccountState` `Suspended` in `/clientapi/avatar/private/list`, `CanUse`) is
|
||||
never acted as: `LoadSession` skips it, `SwitchPersona` refuses it, and the switcher and Login's chooser show it
|
||||
disabled (`data-persona-state`). When PrivaPub refuses the current persona a token while the JWT is good (suspended
|
||||
meanwhile, or gone), `HttpService` marks it refused (`TokenAuthStateProvider.Refuse`; no request acts as it again, each
|
||||
answered `FailureCodes.PersonaRefused` without asking PrivaPub, until it is picked anew or the session is read again)
|
||||
and `CascadingState` reads the personas again and moves to another usable one, or to none, saying why once;
|
||||
- `ProcessError` / `ProcessWarning`, which log through `ILoggingService` and show a toast;
|
||||
- `LogFromJs`, which JS calls back into through a `DotNetObjectReference`.
|
||||
|
||||
@@ -300,7 +314,10 @@ Never redeclare the base's members in a component.
|
||||
following them, trends, the directory), `PortabilityService` (Mastodon's CSV exports and imports), `AdminService`
|
||||
(`/clientapi/moderator`, the staff's log among it, and `/clientapi/admin`'s blocks, statistics and backups),
|
||||
`InstanceAdminService` (the server's own settings, thumbnail, announcements and relays: PrivaPub's
|
||||
`InstanceAdminController`), `AboutService` (what the server says of itself to anyone: the instance API, its documents
|
||||
`InstanceAdminController`), `ModerationService` (the staff's acts: accounts found, silenced, suspended and given back,
|
||||
posts removed and reports resolved on `/clientapi/moderator`; logins read with their personas and groups, their role,
|
||||
ban, two-factor reset and deletion, and a persona's or a group's deletion with the administrator's password on
|
||||
`/clientapi/admin`), `AboutService` (what the server says of itself to anyone: the instance API, its documents
|
||||
and published domain blocks, never with credentials), `AnnouncementService` (`/api/v1/announcements`: dismissing and
|
||||
reacting), `MediaService` (uploads, and deleting those no post holds), `GroupService`, `InstanceService` and
|
||||
`ProvenanceService`.
|
||||
@@ -334,7 +351,8 @@ Never redeclare the base's members in a component.
|
||||
A profile shows the posts the account pins above its posts (`pinned=true`: what an account elsewhere features); the
|
||||
persona pins and unpins its own public and unlisted posts from the card's menu.
|
||||
An account that moved says where to (`Account.Moved`, which PrivaPub fills once the new account names the old one),
|
||||
linking the new account's profile.
|
||||
linking the new account's profile. One the staff silenced, or whose server is silenced, says Limited
|
||||
(`Account.Limited`, `data-account-limited`).
|
||||
Another's profile and posts can be reported (`Components/ReportDialog.razor`, `/api/v1/reports`): a category, words,
|
||||
and for a remote account a copy to its server, which PrivaPub sends from its own actor, never naming the persona. A
|
||||
violation (`ReportCategory.Violation`, offered while the server has rules) ticks the server's rules it breaks
|
||||
@@ -352,8 +370,9 @@ Never redeclare the base's members in a component.
|
||||
A card in a fixed-height flex column with `overflow-hidden` needs `flex-none`, or it collapses to nothing.
|
||||
- **Groups** (`Pages/Groups.razor` at `/groups`): the persona's communities and circles (`GroupService`, `/clientapi/group`):
|
||||
made (a circle unless it is a community, with an invitation code and an optional password), joined by invitation and
|
||||
left; the ones it runs edited, their invitation shown, the requests to join let in or declined, members taken out.
|
||||
Only a group's owner and moderators see who is in it.
|
||||
left; the ones it runs edited, their invitation shown, the requests to join let in or declined, members taken out; the
|
||||
ones it owns deleted with the login's password (`data-group-delete-own`, `ActDialog`; `/clientapi/group/delete`,
|
||||
`DeleteGroupForm`: its members are told, its name stays taken). Only a group's owner and moderators see who is in it.
|
||||
- **Lists** (`Pages/Lists.razor` at `/lists`): made, renamed, given a replies policy and made exclusive (their members
|
||||
leave the home feed) there, filled from the accounts the persona follows; a followed account's profile has a Lists
|
||||
menu too. A list is read as the feed's Lists tab (`FeedFilter.ListId`). Lists never leave PrivaPub.
|
||||
@@ -415,7 +434,11 @@ Never redeclare the base's members in a component.
|
||||
password and a code from the app or a recovery code (`/totp/disable`)); every session of it ended ("Sign out
|
||||
everywhere", see **One sign-in**); or the whole of it
|
||||
deleted, every persona with it, through `/clientapi/user/update/password` and `/clientapi/user/delete`; the client then
|
||||
signs out), the persona's profile (`ProfileEditor`:
|
||||
signs out), its personas (`PersonasEditor`: each used, shown silenced or suspended (`data-persona-state`; a suspended
|
||||
one can't be used), and deleted with the login's password (`data-persona-delete-own`, `ActDialog`;
|
||||
`/clientapi/avatar/private/delete`, `DeletePersonaForm`), the groups it owns with it; `CascadingState.PersonaDeleted`
|
||||
forgets its token and, when it was the current one, acts as another, or as none, and a new one is made below), the
|
||||
persona's profile (`ProfileEditor`:
|
||||
`update_credentials` as multipart, its words as written from `Account.Source`, pictures (streamed from the file,
|
||||
and removed with `DELETE /api/v1/profile/avatar|header`), fields, locked, bot,
|
||||
discoverable, indexable and posting defaults; the composer starts a new post at its default visibility), its mutes,
|
||||
@@ -430,9 +453,29 @@ Never redeclare the base's members in a component.
|
||||
`data-admin-section`, and its panel loads when the page opens. Moderators see Reports (`reports`) and the Moderation
|
||||
log (`log`); administrators also see Instance (`instance`), Rules (`rules`), Announcements (`announcements`),
|
||||
Blocked servers (`blocks`), Relays (`relays`), Media limits (`media`), Statistics (`statistics`) and Backups
|
||||
(`backups`). PrivaPub checks the role on every call (moderators: `/clientapi/moderator`, administrators:
|
||||
`/clientapi/admin`, checked against its database too). Users & personas come with R6, custom emoji with R7: until
|
||||
then they have no section.
|
||||
(`backups`). Moderators also see Accounts (`accounts`), administrators Users & personas (`users`). PrivaPub checks the
|
||||
role on every call (moderators: `/clientapi/moderator`, administrators: `/clientapi/admin`, checked against its database
|
||||
too). Custom emoji come with R7: until then they have no section.
|
||||
- **Users & personas** (`UsersPanel`, `data-users`, `/clientapi/admin/roots`; the one place a login is shown with its
|
||||
personas): newest first, 50 at a time (`data-users-more`), found by a login's or a persona's name (`data-users-search`;
|
||||
`data-users-reload`). Each login (`data-user="{login name}"`, `data-user-role` user|moderator|admin,
|
||||
`data-user-banned` true|false) shows its badges (`data-user-you`, `data-user-banned-badge`, `data-user-totp`,
|
||||
`data-user-email` none|verified|unverified) and, unless it is the administrator's own, its role
|
||||
(`data-user-role-select`), `data-user-ban` or `data-user-unban`, `data-user-totp-reset` (with two-factor on) and
|
||||
`data-user-delete`, each asked first (ConfirmService; a role PrivaPub refuses, one's own or the last administrator's,
|
||||
is a 409 and the select shows the role again). `data-user-open` shows its personas (`data-user-personas`): each
|
||||
(`data-persona="{userName}"`, `data-persona-state` normal|silenced|suspended) with `data-persona-silence` or
|
||||
`-unsilence`, `-suspend` or `-unsuspend` and `data-persona-delete` (the administrator's password), and the groups it
|
||||
owns (`data-group="{userName}"`, `data-group-delete`, the password). An act on the administrator's own persona moves
|
||||
the session with it (`CascadingState.PersonaDeleted`, `ReloadPersonas`).
|
||||
- **Accounts** (`AccountsPanel`, `data-accounts`, `/clientapi/moderator/accounts`): accounts here and elsewhere found by a
|
||||
name, `user@host` or an address (`data-accounts-search`, results in `data-accounts-results`), and the ones silenced or
|
||||
suspended, most recently first (`data-accounts-moderated`, 50 at a time, `data-accounts-more`). Each
|
||||
(`data-moderated-account="{handle}"`, `data-account-state` normal|silenced|suspended, `data-account-id`) shows what the
|
||||
staff did (`data-account-silenced`, `-suspended`, its server's block `data-account-domain-silenced`, `-suspended`, the
|
||||
staff's note `data-account-note`) and `data-account-silence` or `-unsilence`, `-suspend` or `-unsuspend`, asked in
|
||||
`ActDialog` with a note and, for a persona here and a silence or suspension, a warning it is told (Mastodon's
|
||||
`moderation_warning`; an account elsewhere is told nothing).
|
||||
- **The settings save whole.** `POST /clientapi/admin/settings` replaces everything `ServerSettingsForm` carries (a rule
|
||||
left out is removed), so Instance, Rules and Media limits each save through `InstanceAdminService.SaveSettings`: it
|
||||
reads the settings PrivaPub has now, builds the form from them (`FormOf`: the rules not deleted, in order, the media
|
||||
@@ -475,7 +518,14 @@ Never redeclare the base's members in a component.
|
||||
(`data-log-by`: you, a login name to administrators, `cli`, or none), what, to what (`data-log-target`) and its note
|
||||
(`data-log-note`). `data-log-reload`.
|
||||
- **Reports** (`ReportsPanel`): a violation's rules (`data-report-rules`, `data-report-rule-id`) as PrivaPub keeps their
|
||||
texts, a removed rule's too.
|
||||
texts, a removed rule's too. "Resolve and…" (`data-report-resolve`, a `DDropdown SheetOnNarrow`) resolves it with
|
||||
`data-report-resolve-action` none, silence, suspend (with an account) or remove_posts (with posts), asked in
|
||||
`ActDialog` with a note and a warning (`ResolveReportForm`; the act comes first, so one that fails leaves the report
|
||||
open). A resolved report shows what was done and its note (`data-report-resolution="{action}"`,
|
||||
`data-report-resolution-note`).
|
||||
- **A post from the feed:** a moderator removes another's post from its card's menu (`data-moderate-remove-post`,
|
||||
`ActDialog`, `/clientapi/moderator/posts/remove`): one written here is deleted as its author would, and its author
|
||||
warned; a copy of one from elsewhere is hidden here. The page takes the card out.
|
||||
- **Statistics** (`Components/Admin/StatisticsPanel.razor`, `/clientapi/admin/statistics`, typed in ClientModels'
|
||||
`Admin/ViewStatistics.cs`): the period's overview, the servers (by traffic, failures, latency or name), the crawler, the
|
||||
ledger's events and the server's own days. Counted per server, never per account.
|
||||
@@ -499,7 +549,9 @@ Never redeclare the base's members in a component.
|
||||
what notifications from people the persona does not follow, people who do not follow it, new accounts, private
|
||||
mentions it did not ask for and limited accounts become (accept, filter, drop; `/api/v2/notifications/policy`), and the
|
||||
filtered ones, one request per account, let in (with that account from then on) or dismissed. The Notifications tab
|
||||
links there while requests wait. A notification is dismissed from its line (`data-notification-dismiss`,
|
||||
links there while requests wait. A warning from the staff (`moderation_warning`, `Notification.ModerationWarning`) shows
|
||||
what they did and what they wrote (`data-notification-warning="{action}"`, `data-notification-warning-text`). A
|
||||
notification is dismissed from its line (`data-notification-dismiss`,
|
||||
`TimelineService.DismissNotification`: `/api/v2/notifications/:group_key/dismiss`, its whole group, `ungrouped-{id}` for
|
||||
one alone), and the page takes that item out (`Content.OnDismissed`).
|
||||
- **Scheduled posts** (`Pages/Scheduled.razor` at `/scheduled`): the composer's clock button gives a Mastodon API post a
|
||||
|
||||
Reference in new issue
Block a user