From 29520a00f6b12e4b4d091e84a44f562e566b0276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Consultor=C3=ADa=20AS?= Date: Tue, 3 Feb 2026 23:01:28 +0000 Subject: [PATCH] feat: Add publish and mark-published endpoints with validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add /api/posts/{id}/publish endpoint for API-based publishing - Add /api/posts/{id}/mark-published endpoint for manual workflow - Add content length validation before publishing - Update modal with "Ya lo publiqué" and "Publicar (API)" buttons - Fix retry_count handling for None values Co-Authored-By: Claude Opus 4.5 --- app/api/routes/posts.py | 56 ++++++++++++++++++++++++++++++++++ app/worker/tasks.py | 32 +++++++++++++++++-- dashboard/templates/posts.html | 28 ++++++++++++++--- worker/tasks/publish_post.py | 14 ++++++++- 4 files changed, 122 insertions(+), 8 deletions(-) diff --git a/app/api/routes/posts.py b/app/api/routes/posts.py index 6703aa6..1d51f1e 100644 --- a/app/api/routes/posts.py +++ b/app/api/routes/posts.py @@ -220,6 +220,62 @@ async def reject_post(post_id: int, db: Session = Depends(get_db)): return {"message": "Post rechazado", "post_id": post_id} +@router.post("/{post_id}/publish") +async def publish_post_now(post_id: int, db: Session = Depends(get_db)): + """Publicar un post inmediatamente.""" + from worker.tasks.publish_post import publish_to_platform + + post = db.query(Post).filter(Post.id == post_id).first() + if not post: + raise HTTPException(status_code=404, detail="Post no encontrado") + + if post.status not in ["draft", "pending_approval", "scheduled", "failed"]: + raise HTTPException( + status_code=400, + detail=f"No se puede publicar un post con status '{post.status}'" + ) + + # Cambiar estado a publishing + post.status = "publishing" + db.commit() + + # Encolar tarea de publicación para cada plataforma + for platform in post.platforms: + publish_to_platform.delay(post.id, platform) + + return { + "success": True, + "message": "Post enviado a publicación", + "post_id": post_id, + "platforms": post.platforms + } + + +@router.post("/{post_id}/mark-published") +async def mark_post_as_published(post_id: int, db: Session = Depends(get_db)): + """Marcar un post como publicado manualmente (sin usar API).""" + post = db.query(Post).filter(Post.id == post_id).first() + if not post: + raise HTTPException(status_code=404, detail="Post no encontrado") + + if post.status not in ["draft", "pending_approval", "scheduled", "failed"]: + raise HTTPException( + status_code=400, + detail=f"No se puede marcar un post con status '{post.status}'" + ) + + post.status = "published" + post.published_at = datetime.utcnow() + post.error_message = None + db.commit() + + return { + "success": True, + "message": "Post marcado como publicado", + "post_id": post_id + } + + @router.post("/{post_id}/regenerate") async def regenerate_post(post_id: int, db: Session = Depends(get_db)): """Regenerar contenido de un post con IA.""" diff --git a/app/worker/tasks.py b/app/worker/tasks.py index c3f45f9..060ba2a 100644 --- a/app/worker/tasks.py +++ b/app/worker/tasks.py @@ -60,10 +60,32 @@ def publish_post(self, post_id: int): for platform_name in post.platforms: try: platform = Platform(platform_name) + + # Get platform-specific content + platform_content = post.get_content_for_platform(platform_name) + + # Pre-validate content length with descriptive error + publisher = publisher_manager.get_publisher(platform) + if publisher and hasattr(publisher, 'char_limit'): + content_length = len(platform_content) + if content_length > publisher.char_limit: + error_msg = ( + f"Contenido excede límite: {content_length}/{publisher.char_limit} " + f"caracteres (sobra {content_length - publisher.char_limit})" + ) + logger.error(f"Validation failed for {platform_name}: {error_msg}") + results[platform_name] = { + "success": False, + "post_id": None, + "url": None, + "error": error_msg + } + continue + result = run_async( publisher_manager.publish( platform=platform, - content=post.content, + content=platform_content, image_path=post.image_url ) ) @@ -90,10 +112,14 @@ def publish_post(self, post_id: int): if any_success: post.status = "published" post.published_at = datetime.utcnow() - post.publish_results = results + post.platform_post_ids = { + k: v.get("post_id") for k, v in results.items() if v.get("success") + } else: post.status = "failed" - post.publish_results = results + # Collect all error messages + errors = [f"{k}: {v.get('error')}" for k, v in results.items() if v.get("error")] + post.error_message = "\n".join(errors) if errors else "Unknown error" db.commit() diff --git a/dashboard/templates/posts.html b/dashboard/templates/posts.html index 59a4617..506faef 100644 --- a/dashboard/templates/posts.html +++ b/dashboard/templates/posts.html @@ -184,10 +184,13 @@ ${platformContents} -
- ${post.status === 'draft' || post.status === 'pending_approval' ? - ` + ` : ''}