ReviewOS

also looking at this

stacks/postline

Actionable Bluesky publish errors + refresh-failure handling (#17)

#24
Merged glennmichael123 wants to merge feat/bluesky-publish-hardening into main
3 files +89 -4
app/Services/Social/BlueskyService.tsmodified+17-4
Changes to app/Services/Social/BlueskyService.ts
@@ -1,6 +1,7 @@
11import type { BlueskySession, CrosspostTargetResult, PublishContent, PublishedPost, TimelineResult } from '../../Support/Social/types'
22import { db } from '@stacksjs/database'
33import { env } from '@stacksjs/env'
4import { describeBlueskyError } from '../../Support/Social/bluesky-errors'
45import { BlueskyApiError, BlueskyDriver } from './Drivers/BlueskyDriver'
56
67const database = db as any
@@ -261,7 +262,7 @@ export class BlueskyService {
261262 }
262263 }
263264 catch (error) {
264 const message = error instanceof Error ? error.message : String(error)
265 const message = describeBlueskyError(error)
265266 const failedAt = now()
266267 await database.updateTable('post_targets').set({
267268 status: 'failed',
@@ -388,9 +389,21 @@ export class BlueskyService {
388389 throw error
389390 }
390391
391 const session = await this.driver.refreshSession(identity.refresh_token)
392 const driver = await this.ensureDriver()
393 const refreshed = await this.saveSession(session, driver)
392 // Access JWT expired — refresh once and retry. If the refresh token is
393 // also dead, mark the identity expired so /accounts prompts a reconnect.
394 let refreshed: SocialIdentityRow
395 try {
396 const session = await this.driver.refreshSession(identity.refresh_token)
397 const driver = await this.ensureDriver()
398 refreshed = await this.saveSession(session, driver)
399 }
400 catch {
401 await database.updateTable('social_identities')
402 .set({ auth_status: 'expired', updated_at: now() })
403 .where('id', '=', identity.id)
404 .execute()
405 throw new Error('Bluesky session expired — reconnect your account on the Accounts page.')
406 }
394407 return await callback(refreshed)
395408 }
396409 }