MediaRouter / docs /unified-publishing.md
basyx's picture
Upload 794 files
1b2323a verified
|
Raw
History Blame Contribute Delete
7.78 kB
# Unified Publishing Center
## Architecture
The Publishing Center is a composition layer over `app/social`. It does not
own provider connections, tokens, media files, publishing jobs, retries, or
provider payload translation.
The authoritative flow is:
1. A canonical `SocialPost` references a workspace project and Social media
asset.
2. Each exact connected account creates one `SocialPostTarget`.
3. `PublishingService` validates account state, authorization scopes, provider
capabilities, canonical asset ownership, project provenance, and adapter
media constraints.
4. Publish-now creates idempotent `SocialJob` records. Scheduling creates one
durable `SocialSchedule` stored in UTC with its IANA display timezone.
5. Existing Social workers claim jobs atomically and call the registered
provider adapter.
6. Provider results and reconciliation update each target independently.
Post reconciliation reports `published`, `failed`, or `partial_success`.
Provider adapters remain the only components that translate canonical post
data into provider API payloads. An adapter that is registered but does not
advertise publishing remains unavailable and must raise
`SOCIAL_PROVIDER_NOT_IMPLEMENTED` or `SOCIAL_CAPABILITY_UNSUPPORTED`; neither
condition is converted to success.
## Canonical post and targets
`SocialPost` now records optional immutable project provenance, a bounded
canonical caption, and bounded canonical hashtags. Platform overrides remain
on targets and never mutate the canonical copy.
Target input is strict. YouTube, TikTok, X, and LinkedIn use their existing
typed provider metadata models. Providers without implemented publishing
contracts are capability-gated instead of accepting arbitrary payloads.
No binary media or credentials are stored in Social records. A Social media
asset binds an existing canonical MediaRouter asset to a validated immutable
output locator.
## Validation
`POST /v1/social/posts/{post_id}/validate` returns a structured result for
every target. Validation reuses:
- connected-account and OAuth scope checks;
- provider capability metadata;
- canonical Social media resolution;
- existing FFprobe/media validation;
- each provider adapter's `validate_media` implementation.
The publish and schedule paths revalidate server-side. Frontend validation is
advisory and cannot authorize publishing.
## Scheduling and jobs
Scheduling uses `SocialSchedule`; timestamps must be offset-aware and are
stored as UTC. The supplied IANA timezone is retained for display. The
existing scheduler claims due records using row locks and `SKIP LOCKED`.
Publishing jobs use deterministic per-target idempotency keys and bounded
worker concurrency.
Provider-native scheduling is used only by an adapter when explicitly
supported. The Publishing Center otherwise relies on MediaRouter's durable
scheduler. The separate Calendar interface is outside this phase.
## Retries, reconciliation, and partial success
The existing worker retry classifier applies exponential backoff and jitter
to transient errors. Invalid credentials, invalid media, permission denial,
unsupported operations, cancellation, and non-retryable provider responses
are not retried indefinitely. OAuth 401 handling refreshes once through
`OAuthService`.
The Publishing Center also exposes a target-scoped retry endpoint. It only
accepts a failed target with no external provider ID, rejects permanent
configuration/auth/media failures, and derives a deterministic retry job key.
It never requeues successful or uncertain provider operations.
Async provider acceptance is reconciled through the adapter's status or
recovery contract. MediaRouter does not claim exactly-once external delivery
where the provider cannot support lookup or idempotency; local idempotency
prevents duplicate internal operations.
Targets reconcile independently. A mixture of published and failed targets
produces `partial_success`, never `published`.
## Cancellation
Draft, scheduled, queued, and retrying jobs can transition to `cancelled`.
Preparing, processing, uploading, and publishing jobs instead record
`cancellation_requested_at`. Their target remains in its actual state until
the worker/provider result is reconciled. This prevents MediaRouter from
claiming cancellation after a provider may have accepted a post.
## Security, permissions, and RLS
The API uses existing Social scopes:
- `social:accounts:read` / `social:accounts:write`
- `social:posts:read` / `social:posts:write`
- `social:posts:publish`
- `social:schedules:write`
Project and asset ownership is checked server-side. The additive migration
adds a project foreign key, a same-workspace integrity trigger, indexes, and
retains forced RLS on posts, targets, and jobs. Social accounts, media
references, targets, schedules, and jobs continue using the existing
workspace policies. Runtime PostgreSQL RLS certification remains deferred.
Tokens are available only through `TokenService`. REST, SDK, MCP, n8n, logs,
audits, and job payloads never expose access tokens, refresh tokens, provider
secrets, authorization headers, or signed upload/session URLs.
## Audit and retention
The Publishing Center uses existing bounded Social audit records, including
post creation, validation, scheduling, publish start, completion/failure, and
cancellation. Audit and application logs contain opaque resource identifiers,
provider, status, duration, and error categories only.
Raw provider payloads are not retained by the composer. Provider recovery
state that must survive restarts remains encrypted in the existing dedicated
job field. Public result metadata is filtered before transport.
## Frontend
Routes:
- `/publish`
- `/projects/[projectId]/publish`
TanStack Query owns providers, accounts, assets, posts, jobs, and validation
responses. The composer keeps only draft selections in local UI state. It
requires exact account selection, capability-gates unavailable adapters,
registers an existing project output as a Social media reference when needed,
and requires an accessible confirmation dialog before external publishing.
Status uses the existing Social job polling rather than a second realtime
system.
Project actions link to the project-aware route. Content Studio, AI Studio,
and template-created projects use the same project/asset provenance and
publishing APIs; there is no specialized publishing path.
## Copilot
The shared Copilot action registry contains:
- `publishing.validate`
- `publishing.create_post`
- `publishing.schedule`
- `publishing.publish`
- `publishing.cancel`
Publish, schedule, and cancellation are registered as external side effects
and always require confirmation. Copilot uses exact post/account identifiers
and the canonical Social services; it cannot invoke an adapter or arbitrary
function directly.
## SDK, MCP, and n8n
TypeScript and Python SDKs extend the existing Social resource with structured
validation and the new canonical fields. Existing methods remain compatible.
MCP exposes typed validation and job listing alongside existing create,
publish, schedule, cancel, and status tools. Authorization remains enforced by
the MCP registry.
The existing n8n Social node adds List Posts, Validate Post, and List
Publishing Jobs. Operations use the official SDK, preserve idempotency and
Continue On Fail, and return real per-target backend state.
## Provider certification boundary
Unit tests may mock provider boundaries. Live provider certification, live
OAuth credentials, Docker, PostgreSQL runtime/RLS verification, FFmpeg runtime
verification, and final production hardening remain deferred. An
`implementation_status` of `registered` or a false publishing capability is
shown as unavailable and is never simulated.