Summary
Closes the last queue gap: scheduled and drafted posts previously published with text only — the link preview, attached image, and explicit blog title were dropped at save time.
- New
posts.contentcolumn persists{ title?, external?, media? }as JSON - Uploaded images are stored under
storage/app/postline-media/(gitignored) until publish; publish hydrates them back to bytes for the Bluesky blob upload, then deletes the file — deletes also clean up publish-nowand the scheduled sweep pass the hydratedPublishContentthrough, so a scheduled blog post keeps its explicit title + full body, and a scheduled Bluesky post keeps its link card and image- Queue items show
image/link cardbadges
Bug found along the way
request.file('image') returns a raw Web File (the router's multipart parser stores File objects), not the typed FileInfo shape (buffer/mimetype) — so the publish action's image upload was silently dropping files too. A shared readUploadedImage helper now accepts both shapes; both actions use it.
Verification (live, authenticated)
- Save with title + link + image file → content JSON persisted, file on disk
- Publish-now → blog post has the explicit title with unstripped body; media file removed after success
- Delete a draft with an image → file removed from disk
- Queue list exposes
hasImage/hasLinkcorrectly bun test21 pass,pickierclean,tsc --noEmitexit 0