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.
26 lines
656 B
JSON
26 lines
656 B
JSON
{
|
|
"lexicon": 1,
|
|
"id": "app.bsky.actor.profile",
|
|
"defs": {
|
|
"main": {
|
|
"type": "record",
|
|
"key": "tid",
|
|
"record": {
|
|
"type": "object",
|
|
"properties": {
|
|
"displayName": { "type": "string", "maxLength": 64, "maxGraphemes": 64 },
|
|
"description": { "type": "string", "maxLength": 300, "maxGraphemes": 300 },
|
|
"avatar": {
|
|
"type": "blob",
|
|
"accept": ["image/png", "image/jpeg", "image/webp", "image/gif"]
|
|
},
|
|
"banner": {
|
|
"type": "blob",
|
|
"accept": ["image/png", "image/jpeg", "image/webp"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|