Ignore:
Timestamp:
07/30/2026 11:26:35 AM (6 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
6dd26e5
Parents:
a9da2c0
Message:

C2S Delete voor eigen notes en shaer:author op de eigen outbox

Twee dingen uit de app (Robins verzoek, 30-7). Een: long-press delete op
een eigen post. De C2S outbox accepteert nu Delete voor notes van dit
account: eigen posts (Tombstone naar volgers, dan cascade zoals de
web-route) en eigen outbound replies (via deliverOutboxDelete). Andermans
posts en onbekende notes krijgen een helder 403/404.

Twee: eigen posts hadden in de app geen header. De timeline-entries
dragen shaer:author, maar de eigen outbox-leg niet, dus elke kaart had
een byline behalve je eigen. De outbox zet nu op de friend-leg de eigen
display-info (titel, handle, avatar) als shaer:author op elke note.

Changed files:
src/services/ActivityPubService.js

  • case 'Delete' in ingestOutboxActivity: eigen post -> deliverDelete + cascade (comments, FTS, post); eigen outbox-reply -> tombstone; vreemd -> 403/404
  • selfAuthor(base, site): eigen byline in shaer:author-vorm

src/routes/activitypub.js

  • outbox friend-leg verrijkt elke note met shaer:author

test/c2s-compose.test.js

  • delete-test (eigen wel, andermans niet, onbekend 404) en selfAuthor-test

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    ra9da2c0 r67f7150  
    760760  if (user) out.preferredUsername = user;
    761761  if (d.icon) out.icon = { type: 'Image', url: d.icon };
     762  return out;
     763}
     764
     765// The site's OWN display info in the same shape as `shaer:author` on timeline
     766// entries. The owner's app reads its own posts from the outbox, which carried
     767// no author info, so every card but your own had a byline (Robins melding,
     768// 30-7: geen header van self op eigen posts).
     769export function selfAuthor(base, site) {
     770  const out = {
     771    name: site.title || site.slug,
     772    handle: `@${site.slug}@${String(base).replace(/^https?:\/\//, '')}`,
     773    url: `${base}/${site.slug === site.primary_slug ? '' : 'user/' + encodeURIComponent(site.slug)}`,
     774  };
     775  if (site.profile_photo) {
     776    out.icon = /^https?:/.test(site.profile_photo) ? site.profile_photo : `${base}${site.profile_photo.startsWith('/') ? '' : '/'}${site.profile_photo}`;
     777  }
    762778  return out;
    763779}
     
    23742390        return { status: 400, error: 'unsupported_undo' };
    23752391      }
    2376       // Delete/Update of arbitrary objects need the post-edit pipeline; tracked
     2392      // Delete your OWN note (Robins verzoek, 30-7: long-press delete in de
     2393      // app). Scope stays narrow: this account's posts and outbound replies,
     2394      // nothing else. The web delete route is the model: Tombstone to the
     2395      // followers first, then the cascade, so nobody keeps a live copy of a
     2396      // post the child took back.
     2397      case 'Delete': {
     2398        const targetUri = c2sIdOf(object);
     2399        if (!targetUri) return { status: 400, error: 'missing_object' };
     2400        const pid = postIdFromNoteUrl(targetUri, base);
     2401        if (pid) {
     2402          const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(pid);
     2403          if (post) {
     2404            if (post.site_id !== site.id) return { status: 403, error: 'not_your_note' };
     2405            if (post.status === 'published') deliverDelete(site, post).catch(() => { /* best-effort */ });
     2406            db.transaction(() => {
     2407              db.prepare('DELETE FROM comments WHERE post_id = ?').run(post.id);
     2408              try { db.prepare('DELETE FROM posts_fts WHERE post_id = ?').run(post.id); } catch { /* FTS optional */ }
     2409              db.prepare('DELETE FROM posts WHERE id = ?').run(post.id);
     2410            })();
     2411            return { status: 202, url: targetUri };
     2412          }
     2413          // Same /ap/notes/ namespace: one of our outbound replies/messages.
     2414          // deliverOutboxDelete checks the site itself and tombstones too.
     2415          if (await deliverOutboxDelete(site, pid)) return { status: 202, url: targetUri };
     2416        }
     2417        return { status: 404, error: 'not_your_note' };
     2418      }
     2419      // Update of arbitrary objects needs the post-edit pipeline; tracked
    23772420      // separately (klonkt-demo-c2s-del). Reject clearly rather than half-doing it.
    23782421      default:
     
    41144157  linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections,
    41154158  noteVisibility, belongsInTimeline, playerUrlFor, isRejectedObject, rejectInteraction, interactionReportTarget,
    4116   getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, actorDisplay, buildActorRef, prefersEnriched,
     4159  getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, actorDisplay, buildActorRef, prefersEnriched, selfAuthor,
    41174160};
Note: See TracChangeset for help on using the changeset viewer.