Saltar al contenido principal

Programación de publicaciones — Referencia técnica

← Volver a Programación de publicaciones

La programación de publicaciones (Fase D.6 / roadmap 4.14) agrega un scheduledAt nullable a las publicaciones más un worker cron que libera las publicaciones que llegan a su hora. Antes de esto, las publicaciones solo tenían isPublished: Boolean (por defecto true) y no existía ningún concepto de publicación futura.

Dónde vive esto

Backend

Frontend

Checklist de implementación técnica

  • scheduledAt en Post / PostCreateInput; createPost mantiene sin publicar las publicaciones futuras
  • Query myScheduledPostsScheduledPostsPage.tsx
  • Mutation cancelScheduledPost — botón de cancelar en la misma página
  • Mutation updateScheduledPost (ScheduledPostUpdateInput: text/visibility/scheduledAt) — botón de editar en la misma página
  • Worker cron scheduled-post-publisher.service.js — publica las publicaciones vencidas cada minuto
  • getByUser oculta las publicaciones programadas a quienes visitan sin ser el dueño; el dueño ve sus propias publicaciones programadas en su cuadrícula de perfil, con una insignia con la hora de liberación
  • Programación de historias — StoryCreateInput.scheduledAt (CreateStoryModal.tsx); la ventana de 24h expiresAt de la historia empieza cuando el barrido la publica, no al crearla
  • Programación de clips — un clip es simplemente una publicación cuyo único medio es un video (type: 'clip', establecido automáticamente en createPost), así que ya queda cubierto por la programación normal de publicaciones; no hay una interfaz de programación de clips separada

API de GraphQL

# Programar una publicación: pasa un timestamp ISO futuro. Omítelo (o pasa
# una hora pasada) para publicar inmediatamente.
mutation CreateScheduledPost($input: PostCreateInput!) {
createPost(input: $input) { id scheduledAt isPublished }
}
# input: { text: "...", visibility: public, scheduledAt: "2026-08-01T15:00:00Z" }

# Las publicaciones programadas (aún no publicadas) propias de quien llama, la más próxima primero.
query MyScheduledPosts($limit: Int, $offset: Int) {
myScheduledPosts(limit: $limit, offset: $offset) {
id text scheduledAt visibility
media { mediaUrl thumbnailUrl mediaType }
}
}

# Cancelar antes de que se publique (solo el dueño, solo mientras siga programada).
mutation CancelScheduledPost($postId: ID!) {
cancelScheduledPost(postId: $postId)
}

# Editar el texto, la visibilidad y/o la hora de una publicación aún programada
# (solo el dueño, solo mientras siga programada; scheduledAt debe estar en el futuro).
mutation UpdateScheduledPost($postId: ID!, $input: ScheduledPostUpdateInput!) {
updateScheduledPost(postId: $postId, input: $input) { id text scheduledAt }
}

# Programar una historia de la misma manera; su ventana de 24h empieza cuando se publica.
mutation CreateScheduledStory($input: StoryCreateInput!) {
createStory(input: $input) { id visibility }
}

Cómo funciona la liberación

createPost/createStory guardan una publicación (o historia) programada a futuro con isPublished: false y el scheduledAt elegido. Las queries de feed y las cuadrículas de perfil de otros usuarios filtran todas por isPublished: true, así que nadie más que el dueño la ve antes de su hora. Cada minuto, scheduled-post-publisher.service.js llama a post.manager.js#publishDueScheduledPosts, que encuentra las publicaciones donde isPublished = false AND scheduledAt <= now (getDueScheduled, respaldado por el índice scheduled_at) y actualiza cada una a isPublished: true, scheduledAt: null — y, para una historia programada (type === 'story'), también establece un nuevo expiresAt a 24h desde el momento real de publicación, ya que una historia programada no inicia su ventana de 24h hasta que realmente se libera. A partir de ese momento la publicación/historia es indistinguible de una publicada normalmente. La latencia en el peor caso es de ~60 segundos después del minuto programado. Cada publicación se actualiza de forma independiente, así que un fallo no bloquea el resto del lote.