also looking at this
feat: crossposting — media (LinkedIn/IG/Threads), OAuth token refresh, and X/Twitter
#30
28 files
+1810
-109
| @@ -3,7 +3,7 @@ import type { RequestInstance } from '@stacksjs/types' | ||
| 3 | 3 | import { Action } from '@stacksjs/actions' |
| 4 | 4 | import { response } from '@stacksjs/router' |
| 5 | 5 | import { crosspost, crosspostProviders } from '../../Services/Social/CrosspostService' |
| 6 | import { readUploadedImage } from '../../Support/Social/uploads' | |
| 6 | import { persistTempMedia, readUploadedImage, removeTempMedia } from '../../Support/Social/uploads' | |
| 7 | 7 | |
| 8 | 8 | export default new Action({ |
| 9 | 9 | name: 'Postline Crosspost Publish', |
| @@ -35,9 +35,17 @@ export default new Action({ | ||
| 35 | 35 | const imageAlt = String(request.get('image_alt') || '').trim() |
| 36 | 36 | |
| 37 | 37 | const media: NonNullable<Parameters<typeof crosspost.publish>[2]>['media'] = [] |
| 38 | let tempMediaFile: string | null = null | |
| 38 | 39 | const uploadedImage = await readUploadedImage(request.file?.('image')) |
| 39 | if (uploadedImage) | |
| 40 | media.push({ ...uploadedImage, altText: imageAlt || undefined }) | |
| 40 | if (uploadedImage) { | |
| 41 | // Persist briefly so URL-only providers (Instagram/Threads) can fetch the | |
| 42 | // upload server-side; byte-upload providers use the bytes. Removed once | |
| 43 | // publishing finishes (Meta downloads during container creation, so the | |
| 44 | // file is safe to delete by then). | |
| 45 | const temp = await persistTempMedia(uploadedImage.bytes, uploadedImage.mimeType) | |
| 46 | tempMediaFile = temp?.filename ?? null | |
| 47 | media.push({ ...uploadedImage, url: temp?.url, altText: imageAlt || undefined }) | |
| 48 | } | |
| 41 | 49 | if (imageUrl) |
| 42 | 50 | media.push({ url: imageUrl, altText: imageAlt || undefined }) |
| 43 | 51 | |
| @@ -66,5 +74,8 @@ export default new Action({ | ||
| 66 | 74 | error: error instanceof Error ? error.message : String(error), |
| 67 | 75 | }, { status: 422 }) |
| 68 | 76 | } |
| 77 | finally { | |
| 78 | if (tempMediaFile) await removeTempMedia(tempMediaFile) | |
| 79 | } | |
| 69 | 80 | }, |
| 70 | 81 | }) |