feat: public profiles, a handle on the account domain with name, bio, links and picture
Stage 1 of the public tier (ADR 0005). A person with an identity has a public profile: a handle by Mastodon's rules (1 to 30 letters, digits or underscores, unique whatever its case, a few kept for the instance), changeable until they first publish, on the instance's account domain (ACCOUNT_DOMAIN, else the server's host; the instance info names it), so `raisondetredev@halkyon.eu`; a display name, a bio, up to four links and an avatar. The table is server only: the app gets a Profile without the person's id. ProfileEndpoint (identified only) reads, saves and removes the caller's own; ProfilesEndpoint finds anyone's by handle. An avatar is a PNG of at most 1 MB that the app draws anew from the picture chosen, its middle square at 400 pixels, so a photo's metadata never leaves the device. The server keeps it in the public storage named by its content, so its URL tells nothing about whose it is, and deletes it once no profile shows it. Deleting a person deletes their profile and its picture. In the app, the Profile tab holds the profile as a form, laid out as the old app's; anonymous people are told GitHub sign-in gives them one. Migration 20261009184924904-public-profiles creates public_profile with a unique index on the person and one on the handle in lower case; it touches no other table, and the old server never sees it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
b8462d650f
commit
7f6147d689
1 changed files with 16609 additions and 3 deletions
|
|
@ -156,6 +156,7 @@ A spaced-repetition learning app: studysets of flashcards, reviewed on an FSRS s
|
|||
- **Where sync stands** is `SyncService.status` (syncing, the last sync that went through, the last failure; since the app started), which `SyncStatusBloc` (`core/sync/`, provided by `PersonApp`) hands to the trouble dot on the Settings cogs (on Home and Profile; no tab carries it), the Sync card in Settings and the banner on Home. Only the last outcome counts: one sync that goes through clears a failure. The Library's Refresh and pulling it down sync now (`syncAndWait`, which a pull's spinner waits on).
|
||||
- **Serverpod's version moves together with `serverpod_offline_sync`**, which allows only a narrow range of Serverpod (v0.0.10: `>=4.0.4 <4.1.0`; v0.0.8 pinned v4.0.0 exactly, which the root `pubspec.yaml` overrode until 2026-10-09). Every package pins Serverpod's packages to one version: upgrade them all together with the sync packages and the CLI version CI generates code with (`SERVERPOD_VERSION` in `.forgejo/workflows/ci.yaml`), after reading the sync package's changelog. Never override a version the sync package doesn't allow without checking what changed in `serverpod_database`, which it builds on.
|
||||
- **The source-code link**: both licences require offering the source of what an instance runs to the people who use it. `instance.info().sourceCodeUrl` names it (the operator's `SOURCE_URL`, else this project's repository) and the app's About dialog shows it next to the licences and every package's notice. Keep it working when changing the dialog or the endpoint.
|
||||
- **Public profiles** (ADR 0005, stage 1 of the public tier): `public_profile` (`serverOnly`, never sent; the app gets a `Profile`, without the person's id) holds a handle (Mastodon's rules: 1 to 30 letters, digits or underscores, unique whatever their case through `handleKey`, a few reserved for the instance; changeable until the first publish, `firstPublishedAt`), a display name, a bio, up to 4 links and an avatar. `ProfileEndpoint` (`identified` only: a profile is for others to see) reads, saves and removes the caller's own; `ProfilesEndpoint` finds anyone's by handle. Handles are on the account domain (`ACCOUNT_DOMAIN`, else the server's public host; `instance.info().accountDomain`), which can never change once anyone follows a handle on it. An avatar is a PNG of at most 1 MB that the app draws anew from the picture chosen (`avatarPng`, so a photo's metadata never leaves the device), kept in the public storage at `avatars/<SHA-256>.png`, named by its content so its URL tells nothing about whose it is, and deleted once no profile shows it. In the app, the Profile tab holds it as a form, for people with an identity.
|
||||
- **Deleting a person** goes through `deletePerson` (`lib/src/auth/account.dart`), in one transaction and in ADR 0001 rule 6's order: their sessions end first (a device with a valid session would re-upload everything), then their space goes with all its content through the foreign keys, then the person with their identities. `AccountEndpoint.delete` deletes the caller; the app also calls it when an anonymous account is given up (signed out of, or switched away from to GitHub), since nobody could ever sign into it again.
|
||||
- **Pruning**: `PruningFutureCall` deletes anonymous people unused for 90 days, daily, scheduled when the server starts. Scheduling a future call always adds an entry, so the server cancels the previous one by its identifier first.
|
||||
- **One person's data per device**: `PersonFolders` wipes every other person's folder when a person's data opens: their sessions ended on this device. Opening waits for the previous person's database to close.
|
||||
|
|
|
|||
Loading…
Reference in a new issue