forked from sagnik/Project_Velocity
feat: Ipad app production readiness, Colony orchestration, Social posting (#44)
#38 Ipad app production readiness, Colony orchestration, Social posting Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local> Reviewed-on: sagnik/Project_Velocity#44
This commit is contained in:
@@ -10,6 +10,8 @@ Routes:
|
||||
POST /api/catalyst/auth/meta — OAuth token acquisition
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import hashlib
|
||||
@@ -17,9 +19,11 @@ import logging
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query, Request, status
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from backend.auth.dependencies import UserPrincipal, get_current_user
|
||||
from backend.services.ad_network_service import (
|
||||
AdInsight,
|
||||
BidStrategyUpdate,
|
||||
@@ -27,6 +31,17 @@ from backend.services.ad_network_service import (
|
||||
Platform,
|
||||
ad_network_service,
|
||||
)
|
||||
from backend.services.social_posting import (
|
||||
PostRequest,
|
||||
PostStatus,
|
||||
SocialPlatform,
|
||||
SocialPostingConfigurationError,
|
||||
SocialPostingError,
|
||||
get_post,
|
||||
list_posts,
|
||||
publish_content,
|
||||
publish_due_scheduled,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -91,6 +106,13 @@ def _ok(data: Any, meta: dict | None = None) -> dict:
|
||||
return {"status": "ok", "data": data, "meta": meta or {}}
|
||||
|
||||
|
||||
def _get_db_pool(request: Request) -> Any:
|
||||
pool = getattr(request.app.state, "db_pool", None)
|
||||
if pool is None:
|
||||
raise HTTPException(status_code=503, detail="Database unavailable.")
|
||||
return pool
|
||||
|
||||
|
||||
def _sha256_hash(value: str) -> str:
|
||||
"""SHA-256 hash an email for Meta's hashed audience upload."""
|
||||
return hashlib.sha256(value.strip().lower().encode()).hexdigest()
|
||||
@@ -510,3 +532,91 @@ async def meta_oauth(payload: MetaAuthRequest) -> dict:
|
||||
"token_type": token_data.get("token_type", "bearer"),
|
||||
"expires_in": token_data.get("expires_in"),
|
||||
})
|
||||
|
||||
|
||||
# ── 6. Social publishing ─────────────────────────────────────────────────────
|
||||
|
||||
@router.post("/publish", status_code=status.HTTP_201_CREATED, summary="Publish or schedule content to social channels")
|
||||
async def api_publish_content(
|
||||
payload: PostRequest,
|
||||
request: Request,
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
try:
|
||||
result = await publish_content(
|
||||
pool=_get_db_pool(request),
|
||||
tenant_id=user.tenant_id,
|
||||
actor_id=user.user_id,
|
||||
payload=payload,
|
||||
)
|
||||
except SocialPostingConfigurationError as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=422, detail=f"Invalid schedule_time: {exc}") from exc
|
||||
except (SocialPostingError, httpx.HTTPError) as exc:
|
||||
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
||||
return _ok(result)
|
||||
|
||||
|
||||
@router.get("/posts", summary="List tenant-scoped social posts")
|
||||
async def api_list_social_posts(
|
||||
request: Request,
|
||||
platform: SocialPlatform | None = Query(default=None),
|
||||
post_status: PostStatus | None = Query(default=None, alias="status"),
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
posts = await list_posts(
|
||||
pool=_get_db_pool(request),
|
||||
tenant_id=user.tenant_id,
|
||||
platform=platform,
|
||||
status=post_status,
|
||||
limit=limit,
|
||||
)
|
||||
return _ok(posts, meta={"count": len(posts)})
|
||||
|
||||
|
||||
@router.get("/posts/{post_id}", summary="Get a tenant-scoped social post")
|
||||
async def api_get_social_post(
|
||||
post_id: str,
|
||||
request: Request,
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
post = await get_post(pool=_get_db_pool(request), tenant_id=user.tenant_id, post_id=post_id)
|
||||
if post is None:
|
||||
raise HTTPException(status_code=404, detail=f"Social post '{post_id}' not found.")
|
||||
return _ok(post)
|
||||
|
||||
|
||||
@router.get("/scheduled", summary="List scheduled social posts for the authenticated tenant")
|
||||
async def api_scheduled_posts(
|
||||
request: Request,
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
posts = await list_posts(
|
||||
pool=_get_db_pool(request),
|
||||
tenant_id=user.tenant_id,
|
||||
status=PostStatus.SCHEDULED,
|
||||
limit=limit,
|
||||
)
|
||||
return _ok(posts, meta={"count": len(posts)})
|
||||
|
||||
|
||||
@router.post("/scheduled/publish-due", summary="Publish due scheduled social posts for the authenticated tenant")
|
||||
async def api_publish_due_scheduled(
|
||||
request: Request,
|
||||
limit: int = Query(default=20, ge=1, le=100),
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
try:
|
||||
result = await publish_due_scheduled(
|
||||
pool=_get_db_pool(request),
|
||||
tenant_id=user.tenant_id,
|
||||
limit=limit,
|
||||
)
|
||||
except SocialPostingConfigurationError as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
except (SocialPostingError, httpx.HTTPError) as exc:
|
||||
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
||||
return _ok(result)
|
||||
|
||||
Reference in New Issue
Block a user