Skip to content

feat(v2): delete artifacts (files + external links) from inspector#274

Open
samxu01 wants to merge 1 commit intomainfrom
feat/v2-artifact-delete
Open

feat(v2): delete artifacts (files + external links) from inspector#274
samxu01 wants to merge 1 commit intomainfrom
feat/v2-artifact-delete

Conversation

@samxu01
Copy link
Copy Markdown
Contributor

@samxu01 samxu01 commented May 3, 2026

Third of three "artifact polish" PRs. Will not auto-merge.

What this changes

Pod owner OR original creator can now delete an artifact from the inspector detail view.

Backend:

  • New `DELETE /api/pods/:podId/files/:fileId` — pod owner OR file uploader. Deletes bytes from the configured ObjectStore driver and the `File` row. Driver delete failures are logged but don't block the metadata delete; orphaned bytes are GC'd by the future ADR-002 Phase 4 sweep. Validates fileId shape against the ObjectId regex before any DB call (ReDoS guard).
  • `DELETE /api/pods/external-link/:id` relaxed from owner-only to owner OR `createdBy`. Pod members at large still can't nuke shared links — keeps a malicious pod-mate from clearing artifacts.

Frontend:

  • `V2PodInspector renderArtifactDetail` gains a Delete button next to Open when the viewer is permitted (pod owner OR creator).
  • ArtifactItems carry `sourceKind / sourceId / createdById` so the detail view knows which DELETE endpoint to hit and whether the current viewer can.
  • `window.confirm` before delete. On success we remove the item from local state and pop back to the overview.

What's deliberately not deletable

Announcements aren't deletable from the inspector. Those live in a separate admin flow (and the legacy `DELETE /api/pods/announcement/:id` route). If we want to surface a delete here later we add a 4th `sourceKind` and a permission check.

Tests

35/35 passing.

  • `pods.external-links.test.js`: 28 cases (was 27). New "allows the link creator to delete (even if not pod owner)" + updated 403 case to assert "neither owner nor creator".
  • `pods.files-delete.test.js`: 7 new cases. Owner deletes, uploader deletes, neither 403s, invalid fileId 400s, missing file 404s, wrong-pod 404s, ObjectStore failure doesn't block metadata delete.

Test plan

  • Deploy dev: `gh workflow run deploy-dev.yml --ref feat/v2-artifact-delete`
  • Sam's YC Sprint pod → upload a file → click it in artifacts → confirm Delete button shows. Delete it → returns to overview, gone from list.
  • As a non-owner non-uploader pod member, open someone else's file → confirm Delete button is hidden.
  • Add an external link as user A, switch to user B (not owner): Delete button hidden. Switch to pod owner: Delete shows.
  • Confirm announcements still don't show a Delete button.

🤖 Generated with Claude Code

Pod owner OR original creator can now delete an artifact from the
inspector detail view. Backend enforces independently; frontend gates
the button.

Backend:
- DELETE /api/pods/:podId/files/:fileId — owner | uploader. Deletes
  bytes from the configured ObjectStore driver and the File row.
  Driver delete failures are logged but don't block the metadata
  delete; orphaned bytes can be GC'd later (ADR-002 Phase 4 sweep).
  Validates fileId shape against the ObjectId regex before any DB
  call (ReDoS guard).
- DELETE /api/pods/external-link/:id relaxed from owner-only to
  owner | createdBy. Pod members at large still can't nuke shared
  links — keeps a malicious pod-mate from clearing artifacts.

Frontend:
- V2PodInspector renderArtifactDetail gains a Delete button next
  to Open when the viewer is permitted (pod owner OR creator).
- ArtifactItems carry sourceKind / sourceId / createdById so the
  detail view knows which DELETE endpoint to hit and whether the
  current viewer can.
- Confirm dialog (window.confirm) before delete; on success we
  remove the item from local state and pop back to the overview.
- Announcements stay non-deletable from this surface — those live
  in a separate admin flow.

Tests: 35/35 passing.
- pods.external-links.test.js: 28 cases (was 27). New "allows the
  link creator to delete (even if not pod owner)" + updated 403
  case to assert "neither owner nor creator".
- pods.files-delete.test.js: 7 new cases. Owner can delete,
  uploader can delete, neither 403, invalid fileId 400, missing
  file 404, wrong-pod 404, ObjectStore failure doesn't block
  metadata delete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread backend/routes/pods.ts
if (!fileDoc) return res.status(404).json({ message: 'File not found' });
if (fileDoc.podId?.toString() !== podId) return res.status(404).json({ message: 'File not in this pod' });
const pod = await Pod.findById(podId) as { createdBy?: { toString: () => string } } | null;
if (!pod) return res.status(404).json({ message: 'Pod not found' });
Comment thread backend/routes/pods.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants