ihtesham0345's picture
feat: Add LinkedIn API posting + enhanced analyzer + schema
639b959
Raw
History Blame Contribute Delete
7.61 kB
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from fastapi.exceptions import ResponseValidationError
from starlette.requests import Request
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from models.schemas import (
SEORequest, SEOResponse,
YouTubeResponse, InstagramResponse, LinkedInResponse,
FacebookResponse, TwitterResponse, TikTokResponse, PinterestResponse,
GrammarRequest, GrammarResponse, BatchRequest
)
from services.batch_analyzer import analyze_batch
from services.analyzer import analyze_seo_content
from services.youtube_analyzer import analyze_youtube
from services.instagram_analyzer import analyze_instagram
from services.linkedin_analyzer import analyze_linkedin
from services.linkedin_api import get_oauth_url, exchange_code, get_user_info, create_post
from services.facebook_analyzer import analyze_facebook
from services.twitter_analyzer import analyze_twitter
from services.tiktok_analyzer import analyze_tiktok
from services.pinterest_analyzer import analyze_pinterest
from services.grammar_analyzer import analyze_grammar
import uvicorn
import traceback
app = FastAPI(
title="Social Media SEO & Grammar API",
description="Professional social media content analysis + grammar correction using Generative AI. Covers YouTube, Instagram, LinkedIn, Facebook, X/Twitter, TikTok, Pinterest, and grammar correction.",
version="2.1.0"
)
ERROR_MASK = "An internal error occurred. Please try again later."
ERROR_MASK_VALIDATION = "The model returned an incomplete response. Please try again."
@app.exception_handler(ResponseValidationError)
async def validation_exception_handler(request: Request, exc: ResponseValidationError):
print(f"[ValidationError] {exc}")
return JSONResponse(
status_code=200,
content={"error": ERROR_MASK_VALIDATION},
)
def safe_analyze(analyze_fn, content: str):
try:
result = analyze_fn(content)
if isinstance(result, dict) and result.get("error"):
print(f"[Warning] Analysis completed with error: {result['error']}")
return result
except HTTPException:
raise
except Exception as e:
print(f"CRITICAL SERVER ERROR: {e}\n{traceback.format_exc()}")
raise HTTPException(status_code=500, detail=ERROR_MASK)
# ---------------------------------------------------------------------------
# Root - Serve static frontend
# ---------------------------------------------------------------------------
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
async def read_root():
from fastapi.responses import FileResponse
return FileResponse("static/index.html")
# ---------------------------------------------------------------------------
# Generic SEO
# ---------------------------------------------------------------------------
@app.post("/analyze-seo", response_model=SEOResponse)
def analyze_seo(request: SEORequest):
return safe_analyze(analyze_seo_content, request.content)
# ---------------------------------------------------------------------------
# YouTube
# ---------------------------------------------------------------------------
@app.post("/analyze/youtube", response_model=YouTubeResponse)
def analyze_youtube_route(request: SEORequest):
return safe_analyze(analyze_youtube, request.content)
# ---------------------------------------------------------------------------
# Instagram
# ---------------------------------------------------------------------------
@app.post("/analyze/instagram", response_model=InstagramResponse)
def analyze_instagram_route(request: SEORequest):
return safe_analyze(analyze_instagram, request.content)
# ---------------------------------------------------------------------------
# LinkedIn
# ---------------------------------------------------------------------------
@app.post("/analyze/linkedin", response_model=LinkedInResponse)
def analyze_linkedin_route(request: SEORequest):
return safe_analyze(analyze_linkedin, request.content)
@app.get("/api/linkedin/auth-url")
def linkedin_auth_url(state: str = ""):
url = get_oauth_url(state)
return {"url": url}
class TokenExchangeRequest(BaseModel):
code: str
@app.post("/api/linkedin/exchange-token")
def linkedin_exchange_token(req: TokenExchangeRequest):
token_data, err = exchange_code(req.code)
if err:
raise HTTPException(status_code=400, detail=err)
user_info, uerr = get_user_info(token_data["access_token"])
if uerr:
raise HTTPException(status_code=400, detail=uerr)
return {
"access_token": token_data["access_token"],
"expires_at": token_data["expires_at"],
"user": user_info,
}
class PostToLinkedInRequest(BaseModel):
access_token: str
author_urn: str
text: str
hashtags: list[str] = []
@app.post("/api/linkedin/post")
def linkedin_post(req: PostToLinkedInRequest):
result, err = create_post(req.access_token, req.author_urn, req.text, req.hashtags)
if err:
raise HTTPException(status_code=400, detail=err)
return {"success": True, **result}
# ---------------------------------------------------------------------------
# Facebook
# ---------------------------------------------------------------------------
@app.post("/analyze/facebook", response_model=FacebookResponse)
def analyze_facebook_route(request: SEORequest):
return safe_analyze(analyze_facebook, request.content)
# ---------------------------------------------------------------------------
# X / Twitter
# ---------------------------------------------------------------------------
@app.post("/analyze/twitter", response_model=TwitterResponse)
def analyze_twitter_route(request: SEORequest):
return safe_analyze(analyze_twitter, request.content)
# ---------------------------------------------------------------------------
# TikTok
# ---------------------------------------------------------------------------
@app.post("/analyze/tiktok", response_model=TikTokResponse)
def analyze_tiktok_route(request: SEORequest):
return safe_analyze(analyze_tiktok, request.content)
# ---------------------------------------------------------------------------
# Pinterest
# ---------------------------------------------------------------------------
@app.post("/analyze/pinterest", response_model=PinterestResponse)
def analyze_pinterest_route(request: SEORequest):
return safe_analyze(analyze_pinterest, request.content)
# ---------------------------------------------------------------------------
# Grammar Correction
# ---------------------------------------------------------------------------
@app.post("/analyze/grammar", response_model=GrammarResponse)
def grammar_correction_route(request: GrammarRequest):
return safe_analyze(analyze_grammar, request.text)
# ---------------------------------------------------------------------------
# Batch - Multi-Platform
# ---------------------------------------------------------------------------
class BatchResponse(BaseModel):
results: dict
@app.post("/analyze/batch", response_model=BatchResponse)
def batch_analyze_route(request: BatchRequest):
results = safe_analyze(lambda c: analyze_batch(c, request.platforms), request.content)
return {"results": results}
# ---------------------------------------------------------------------------
# Entrypoint
# ---------------------------------------------------------------------------
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)