ReviewOS

also looking at this

stacks/postline

Edit queued and scheduled posts (#21)

#22
Merged glennmichael123 wants to merge feat/edit-queued-posts into main
6 files +359 -7
app/Actions/Postline/QueueUpdateAction.tsadded+60-0
Changes to app/Actions/Postline/QueueUpdateAction.ts
@@ -0,0 +1,60 @@
1import type { SocialProvider } from '../../Support/Social/types'
2import type { RequestInstance } from '@stacksjs/types'
3import { Action } from '@stacksjs/actions'
4import { response } from '@stacksjs/router'
5import { crosspostProviders } from '../../Services/Social/CrosspostService'
6import { postQueue } from '../../Services/Social/QueueService'
7import { readUploadedImage } from '../../Support/Social/uploads'
8
9export default new Action({
10 name: 'Postline Queue Update',
11 description: 'Edit a draft, scheduled, or failed post in place.',
12 method: 'POST',
13
14 async handle(request: RequestInstance) {
15 const id = Number(request.get('id') || 0)
16 if (!id)
17 return response.json({ ok: false, error: 'Post id is required.' }, { status: 422 })
18
19 const text = String(request.get('text') || request.get('body') || '').trim()
20 const available = new Set<string>(crosspostProviders())
21 const providers = String(request.get('providers') || '')
22 .split(',')
23 .map(value => value.trim().toLowerCase())
24 .filter(value => available.has(value)) as SocialProvider[]
25
26 const scheduledAt = String(request.get('scheduled_at') || '').trim() || null
27 const title = String(request.get('title') || '').trim() || null
28
29 const externalUri = String(request.get('external_uri') || '').trim()
30 const externalTitle = String(request.get('external_title') || '').trim()
31 const external = externalUri && externalTitle
32 ? { uri: externalUri, title: externalTitle, description: String(request.get('external_description') || '').trim() || undefined }
33 : null
34
35 // Image tri-state: a new upload/url replaces; `remove_image=1` clears;
36 // otherwise (undefined) the stored image is kept.
37 const imageAlt = String(request.get('image_alt') || '').trim() || undefined
38 const uploaded = await readUploadedImage(request.file?.('image'))
39 const imageUrl = String(request.get('image_url') || '').trim()
40 const removeImage = String(request.get('remove_image') || '') === '1'
41 const image = uploaded
42 ? { ...uploaded, altText: imageAlt }
43 : imageUrl
44 ? { url: imageUrl, altText: imageAlt }
45 : removeImage
46 ? null
47 : undefined
48
49 try {
50 const data = await postQueue.update(id, { text, providers, title, scheduledAt, external, image })
51 return response.json({ ok: true, data })
52 }
53 catch (error) {
54 return response.json({
55 ok: false,
56 error: error instanceof Error ? error.message : String(error),
57 }, { status: 422 })
58 }
59 },
60})