From a60b612f6893178b01853ab25118fa9e3e67f8f4 Mon Sep 17 00:00:00 2001 From: tomdebone Date: Mon, 6 Jul 2026 21:34:10 +0200 Subject: [PATCH] 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. --- crates/pds-server/src/main.rs | 4 +++- crates/pds-server/src/state.rs | 13 +++++++++++++ lexicons/app/bsky/feed/like.json | 18 ++++++++++++++++++ lexicons/app/bsky/feed/repost.json | 18 ++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 lexicons/app/bsky/feed/like.json create mode 100644 lexicons/app/bsky/feed/repost.json diff --git a/crates/pds-server/src/main.rs b/crates/pds-server/src/main.rs index 3e156b0..bb86013 100644 --- a/crates/pds-server/src/main.rs +++ b/crates/pds-server/src/main.rs @@ -103,7 +103,9 @@ pub fn router(state: AppState) -> Router { "/xrpc/com.atproto.uploadBlob", post(routes::blob::upload_blob) .layer(routes::blob::upload_blob_body_limit()) - .layer(axum::middleware::from_fn(routes::blob::body_limit_fallback)), + .layer::<_, std::convert::Infallible>(axum::middleware::from_fn( + routes::blob::body_limit_fallback, + )), ) .route( "/xrpc/com.atproto.sync.getRepo", diff --git a/crates/pds-server/src/state.rs b/crates/pds-server/src/state.rs index 387a912..8d4ca1f 100644 --- a/crates/pds-server/src/state.rs +++ b/crates/pds-server/src/state.rs @@ -25,6 +25,19 @@ impl AppState { "app.twi.post".to_string(), Lex::from_json(include_str!("../../../lexicons/app/twi/post.json")).unwrap(), ); + // AT-Protocol standard collections: only the records the user + // might legitimately create server-side (feed.like + feed.repost). + // The full atproto collection library is out of scope — for + // anything else, callers pass `validate: false` in the + // createRecord body. + lex.lexicons.insert( + "app.bsky.feed.like".to_string(), + Lex::from_json(include_str!("../../../lexicons/app/bsky/feed/like.json")).unwrap(), + ); + lex.lexicons.insert( + "app.bsky.feed.repost".to_string(), + Lex::from_json(include_str!("../../../lexicons/app/bsky/feed/repost.json")).unwrap(), + ); let plc_url = cfg.plc_directory_url.clone(); // The PDS speaks to the AppView via the cluster-internal URL — // never the public one, because the ingest endpoint is unauth'd diff --git a/lexicons/app/bsky/feed/like.json b/lexicons/app/bsky/feed/like.json new file mode 100644 index 0000000..6808093 --- /dev/null +++ b/lexicons/app/bsky/feed/like.json @@ -0,0 +1,18 @@ +{ + "lexicon": 1, + "id": "app.bsky.feed.like", + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": ["subject", "createdAt"], + "properties": { + "subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, + "createdAt": { "type": "datetime" } + } + } + } + } +} diff --git a/lexicons/app/bsky/feed/repost.json b/lexicons/app/bsky/feed/repost.json new file mode 100644 index 0000000..e11434f --- /dev/null +++ b/lexicons/app/bsky/feed/repost.json @@ -0,0 +1,18 @@ +{ + "lexicon": 1, + "id": "app.bsky.feed.repost", + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": ["subject", "createdAt"], + "properties": { + "subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, + "createdAt": { "type": "datetime" } + } + } + } + } +}