Commit Graph
4 Commits
Author SHA1 Message Date
tomdeboneandClaude Opus 5 6fbea4fe6f fix: Follows waren über den Client nicht anlegbar
Der Client legt Follows über createRecord mit app.bsky.graph.follow an. Das
Lexicon war in der PDS aber nicht registriert, und createRecord validiert
per Default — jede Anfrage kam mit

  400 lex validation failed: unknown lexicon: app.bsky.graph.follow

zurück. Der Follow-Button kann also nie funktioniert haben, auch wenn der
Commit, der ihn eingeführt hat, "end-to-end follow / unfollow" heißt. Beim
Gegenprüfen des Firehose-Pfads aufgefallen: der Testaufbau scheiterte schon
am Anlegen des Follows.

Das Lexicon ist jetzt da (subject als DID-String, nicht als strongRef —
genau das, was der Client schickt und was follow_subject_did in der AppView
liest) und registriert. Live geprüft: anlegen, in der AppView indiziert,
löschen, Zeile weg.

Dazu ein zweiter Grund, warum das nie auffiel: create_record_with nahm einen
Parameter `_validate` entgegen und verwarf ihn. Der eine Aufrufer, der
`false` übergab, bekam trotzdem Validierung. Der Parameter wird jetzt
tatsächlich mitgeschickt; der Repost-Pfad steht auf `true`, weil genau das
bisher schon passiert ist und funktioniert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HC9HLrUU1LNwkzp8nkDLX
2026-09-10 07:08:46 +02:00
tomdebone 3064d3d8b7 feat(pds-server): app.bsky.actor.profile get/set XRPC endpoints
Read / read-modify-write the authenticated user's profile record
through the standard atproto repo-write path. Auth is checked via
the existing bearer-token helper; the request body's
`display_name` / `description` / `avatar_blob_cid` /
`banner_blob_cid` overlay the existing record (None fields
preserve the old value).

Blob CID ownership: any supplied avatar/banner CID is looked up
in the `blobs` table with `WHERE cid = $1 AND did = $2`,
rejecting with 400 if the blob isn't owned by the authenticated
user. The resolved `mime_type` / `size` is written into the
record so consumers reading `size` for layout decisions get the
real value (previously hardcoded to "image/png" / 0).

Best-effort push to the AppView via `AppViewPushClient::push_profile`
so the `profiles` cache reflects the new avatar / display name
without waiting for the Jetstream replay path.

Wire shape:

  GET  /xrpc/app.bsky.actor.profile.get
       → { did, handle, profile: { displayName, description, ... } | null }

  POST /xrpc/app.bsky.actor.profile.set
       body: { displayName, description, avatarBlobCid, bannerBlobCid }
       → same shape as get

Includes `merge_profile_fields` testable helper (4 unit tests
locking the camelCase wire shape and the merge semantics).

The AppView-side indexer arm and the Tauri UI land in the
following two commits.
2026-07-18 17:56:28 +02:00
tomdebone a60b612f68 fix(pds): 413 body shape + register app.bsky.feed.like/repost
Three fixes for the integration test plan:

1. Layer order in tauri.conf.json: `body_limit_fallback` must
   wrap `upload_blob_body_limit` so the JSON override is in
   effect when the 413 fires. Swapped.

2. Type annotation on the `from_fn` middleware:
   `.layer::<_, std::convert::Infallible>(...)`. The function
   never errors, so the second type param is Infallible.

3. Register the standard `app.bsky.feed.like` and
   `app.bsky.feed.repost` lexicons so the like/repost
   endpoints (which create records of those collections) pass
   the lex validator. We only ship what the PDS actually lets
   users create server-side; anything else passes `validate: false`.

The 'unprocessable entity' style message and 'unknown lexicon'
errors that came up during manual testing are now gone.

Also dropped the stuck migration-2 row from `_sqlx_migrations`
on the dev DB so the new lex schemas apply.
2026-07-06 21:34:10 +02:00
tomdebone c586fd39c9 maarcadetweet: initial commit
AT Protocol PDS + AppView + Tauri Desktop Client, 160-char post limit.

- PDS (Rust + axum + sqlx)
  - Auth: createAccount, createSession, refreshSession
  - Records: createRecord, deleteRecord (race-safe via SELECT FOR UPDATE)
  - Feed: feed.like.create, feed.repost.create
  - Sync: getRepo, getBlocks, getLatestCommit, getRecord (with MST proof), listRepos
  - Identity: resolveHandle
  - MST: spec-conformant (at-mst crate, 27 tests)
  - Repo: signed commits, TID counter (monotonic, 4096 wrap safe)

- AppView (Rust + axum + sqlx)
  - Jetstream consumer (WebSocket, exponential backoff, 38k+ events indexed)
  - REST API: timeline/home (graph-aware), profile, search, post (with thread hydration)
  - Handle-sync worker (did:plc + did:web)
  - JSONB embed storage + thread columns (migration 0003)
  - Like/repost counter cache (migration 0004)

- Tauri 2 + Svelte 5 Desktop Client
  - System tray (Show/Compose/Quit menu)
  - OS notifications (tauri-plugin-notification)
  - Auto-update (tauri-plugin-updater, placeholder endpoint)
  - Window-state (tauri-plugin-window-state)
  - 160-char compose with live counter
  - Image/Link embed rendering
  - LocalStorage-persisted like state
  - Timeline with poll (prepend new posts)
  - Custom TitleBar (transparent, no decorations)
  - Orange/IBM Plex Mono maarcade design

Tests: 231 Rust + 9 vitest = 240 passed.
2026-07-05 20:01:31 +02:00