""" Content Management Module Handles articles, SEO metadata, and content publishing """ from fastapi import APIRouter, Depends, HTTPException, status, Query from sqlalchemy.orm import Session from typing import List, Optional from datetime import datetime from app.database import get_db from app.models import Article, SEOMetadata, ArticleStatus, User from app.core.security import get_current_user router = APIRouter(prefix="/content", tags=["content"]) # ─── ARTICLES ───────────────────────────────────────────────────────────────── @router.get("/articles") async def list_articles( status: Optional[ArticleStatus] = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """List all articles with optional filtering.""" query = db.query(Article) if status: query = query.filter(Article.status == status) articles = query.offset(skip).limit(limit).all() return { "articles": [ { "id": a.id, "title": a.title, "slug": a.slug, "status": a.status.value, "author": a.author.full_name if a.author else None, "published_at": a.published_at.isoformat() if a.published_at else None, "created_at": a.created_at.isoformat() if a.created_at else None, } for a in articles ], "total": query.count(), "skip": skip, "limit": limit, } @router.post("/articles") async def create_article( title: str, slug: str, content: str, excerpt: Optional[str] = None, seo_title: Optional[str] = None, seo_description: Optional[str] = None, keywords: Optional[List[str]] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create a new article.""" article = Article( title=title, slug=slug, content=content, excerpt=excerpt, author_id=current_user.id, seo_title=seo_title, seo_description=seo_description, keywords=keywords, ) db.add(article) db.commit() db.refresh(article) return {"status": "created", "article_id": article.id, "slug": slug} @router.get("/articles/{article_id}") async def get_article( article_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get a specific article.""" article = db.query(Article).filter(Article.id == article_id).first() if not article: raise HTTPException(status_code=404, detail="Article not found") return { "id": article.id, "title": article.title, "slug": article.slug, "content": article.content, "excerpt": article.excerpt, "status": article.status.value, "author": article.author.full_name if article.author else None, "seo_title": article.seo_title, "seo_description": article.seo_description, "keywords": article.keywords, "published_at": article.published_at.isoformat() if article.published_at else None, "created_at": article.created_at.isoformat() if article.created_at else None, "updated_at": article.updated_at.isoformat() if article.updated_at else None, } @router.put("/articles/{article_id}") async def update_article( article_id: int, title: Optional[str] = None, content: Optional[str] = None, status: Optional[ArticleStatus] = None, seo_title: Optional[str] = None, seo_description: Optional[str] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Update an article.""" article = db.query(Article).filter(Article.id == article_id).first() if not article: raise HTTPException(status_code=404, detail="Article not found") if title: article.title = title if content: article.content = content if status: article.status = status if status == ArticleStatus.PUBLISHED and not article.published_at: article.published_at = datetime.utcnow() if seo_title: article.seo_title = seo_title if seo_description: article.seo_description = seo_description db.commit() db.refresh(article) return {"status": "updated", "article_id": article.id} @router.delete("/articles/{article_id}") async def delete_article( article_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Delete an article.""" article = db.query(Article).filter(Article.id == article_id).first() if not article: raise HTTPException(status_code=404, detail="Article not found") db.delete(article) db.commit() return {"status": "deleted", "article_id": article_id} # ─── SEO METADATA ───────────────────────────────────────────────────────────── @router.get("/seo") async def list_seo_metadata( db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """List all SEO metadata entries.""" seo_entries = db.query(SEOMetadata).all() return { "entries": [ { "id": s.id, "page_path": s.page_path, "title": s.title, "description": s.description, "audit_score": s.audit_score, "last_audit": s.last_audit.isoformat() if s.last_audit else None, } for s in seo_entries ] } @router.post("/seo") async def create_seo_metadata( page_path: str, title: Optional[str] = None, description: Optional[str] = None, canonical_url: Optional[str] = None, og_image: Optional[str] = None, schema_markup: Optional[dict] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create SEO metadata for a page.""" seo = SEOMetadata( page_path=page_path, title=title, description=description, canonical_url=canonical_url, og_image=og_image, schema_markup=schema_markup, ) db.add(seo) db.commit() db.refresh(seo) return {"status": "created", "seo_id": seo.id} @router.put("/seo/{seo_id}") async def update_seo_metadata( seo_id: int, title: Optional[str] = None, description: Optional[str] = None, audit_score: Optional[int] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Update SEO metadata.""" seo = db.query(SEOMetadata).filter(SEOMetadata.id == seo_id).first() if not seo: raise HTTPException(status_code=404, detail="SEO metadata not found") if title: seo.title = title if description: seo.description = description if audit_score is not None: seo.audit_score = audit_score seo.last_audit = datetime.utcnow() db.commit() db.refresh(seo) return {"status": "updated", "seo_id": seo.id}