PostQueen v3.3.0
Autopost and Webhooks hardening, launch polish, and the three subscription gates that were left open before launch. 18 commits since v3.2.0.
Fixed
Autopost never actually published. Every rule produced drafts. Turning that on exposed the rest of the path:
- The same feed item could be published more than once — the de-dupe cursor was written after the posts were scheduled, so a Temporal retry re-posted. The item is now claimed before scheduling.
- A
PUTthat omittedlastUrlwiped the cursor, andautoPublishswitched itself off the same way. Defaults now apply on create only. - Feed items vanished during a token outage. Skipping channels that cannot publish is right for auto-publish and wrong for drafts, so the filter is conditional on the mode.
- One failed post sent up to fifteen notifications and fifteen emails, two of them internal sentinels, one on a post that had actually succeeded.
- The failure reason shown in the calendar was either empty or a raw stack trace.
Webhooks were never delivered to any endpoint that redirects. redirect: 'manual' was not the SSRF protection — the dispatcher pins DNS on every hop, so redirects can be followed safely. Webhook deliveries now also carry real post data where they previously carried an empty array; if you consume PostQueen webhooks, check your receiver handles a populated payload.
A notification could abort the Stripe webhook, so a mail hiccup meant the customer was charged and the subscription row was never written.
The throttler answered 500 to anyone who asked. Route matching ran against req.url, which carries the query string, so POST /auth/login?x=/public/v1/posts entered the rate limiter and died on an undefined organisation — unauthenticated, repeatable, on any POST route. Separately, every upload and every public-API post threw when REDIS_URL was unset.
Auto Post was gated by the webhook quota rather than by its own pricing flag: an organisation that had used its webhook allowance could not create an autopost for an unrelated reason. Creator now includes Auto Post.
/copilot/chat was the only AI route with no subscription check.
The released version never reached the app. The container build passed the tag in under a name nothing read, so v3.1.4 through v3.2.0 all displayed "v3.0" and every Sentry event was filed under release 3.0.2.
Changed
- Loading and empty states consolidated: one skeleton, one spinner, ghosts that mirror the markup they replace, and no indefinite animation that ignores
prefers-reduced-motion. - Sign-up no longer waits on an email round-trip; the emailed code is a way to sign in, not a way to sign up.
- ESLint runs again — and CI runs at all, for the first time in this repository.
.env.exampledocuments 30 variables that the code read and it did not mention.
Upgrading
Deploy the orchestrator before the backend, then backend and frontend together. processCron now starts autoPostWorkflowV2 with TERMINATE_EXISTING; if the backend goes first, saving a rule terminates its running v1 execution and replaces it with a workflow type the old worker does not know.
Create the new index by hand first, in a low-traffic window:
CREATE INDEX CONCURRENTLY IF NOT EXISTS "Post_releaseId_idx" ON "Post"("releaseId");The migration creates it without CONCURRENTLY — Prisma runs migrations in a transaction — which takes a write lock on Post for the duration of the build.
If you run with PRISMA_MIGRATE=true, confirm _prisma_migrations contains 0_init before deploying. A database originally created with db push has no migration history, and migrate deploy will fail replaying the initial migration against a populated schema. Baseline it once with prisma migrate resolve --applied 0_init.
Check REDIS_URL is set. Rate limiting now falls back to a per-process counter without it, so N backend instances give N times the intended limit.