meta options
This commit is contained in:
parent
4697dab339
commit
8726c931fa
21 changed files with 3028 additions and 3 deletions
281
backend/app/routes_meta.py
Normal file
281
backend/app/routes_meta.py
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
from urllib.parse import quote
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import RedirectResponse
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .auth import current_user
|
||||
from .config import settings
|
||||
from .database import get_db
|
||||
from .meta_service import (
|
||||
MetaApiError,
|
||||
analytics,
|
||||
authorization_url,
|
||||
disconnect,
|
||||
finish_oauth,
|
||||
http_meta,
|
||||
list_ad_accounts,
|
||||
list_ads,
|
||||
list_businesses,
|
||||
list_campaigns,
|
||||
list_pages,
|
||||
overview,
|
||||
page_detail,
|
||||
public_connection,
|
||||
publish_campaign,
|
||||
load_connection,
|
||||
recent_audit,
|
||||
select_ad_account,
|
||||
select_page,
|
||||
set_ad_status,
|
||||
set_campaign_status,
|
||||
settings_view,
|
||||
update_ad,
|
||||
update_campaign,
|
||||
)
|
||||
from .models import UserAccount
|
||||
|
||||
router = APIRouter(prefix="/api/v1/meta", tags=["meta"])
|
||||
|
||||
|
||||
class Confirm(BaseModel):
|
||||
confirm: bool = False
|
||||
|
||||
|
||||
class IdBody(Confirm):
|
||||
id: str = Field(min_length=1, max_length=64)
|
||||
|
||||
|
||||
class StatusBody(Confirm):
|
||||
status: str
|
||||
|
||||
|
||||
class CampaignEdit(Confirm):
|
||||
name: str | None = None
|
||||
daily_budget: float | None = Field(default=None, gt=0, le=10_000_000)
|
||||
|
||||
|
||||
class CampaignPublish(Confirm):
|
||||
name: str = Field(min_length=1, max_length=120)
|
||||
objective: str
|
||||
destination: str
|
||||
page_id: str
|
||||
daily_budget: float = Field(gt=0, le=10_000_000)
|
||||
countries: list[str] = Field(min_length=1, max_length=5)
|
||||
age_min: int = Field(default=18, ge=18, le=65)
|
||||
age_max: int = Field(default=65, ge=18, le=65)
|
||||
message: str = Field(min_length=1, max_length=2000)
|
||||
headline: str = ""
|
||||
link: str = ""
|
||||
call_to_action: str = "LEARN_MORE"
|
||||
activate: bool = False
|
||||
|
||||
|
||||
class AdEdit(Confirm):
|
||||
name: str | None = None
|
||||
message: str | None = None
|
||||
headline: str | None = None
|
||||
call_to_action: str | None = None
|
||||
link: str | None = None
|
||||
|
||||
|
||||
def _confirmed(body: Confirm) -> None:
|
||||
if not body.confirm:
|
||||
raise http_meta(MetaApiError(422, "validation", "Confirm this action before Urmi sends it to Meta."))
|
||||
|
||||
|
||||
def _redirect(query: str) -> RedirectResponse:
|
||||
origin = settings.frontend_origin.rstrip("/")
|
||||
return RedirectResponse(f"{origin}/console/meta/connections?{query}", status_code=302)
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
def status(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
conn = load_connection(db, user)
|
||||
visible = conn if conn and conn.status != "disconnected" else None
|
||||
return public_connection(visible)
|
||||
|
||||
|
||||
@router.get("/oauth/start")
|
||||
def oauth_start(user: UserAccount = Depends(current_user)) -> dict:
|
||||
try:
|
||||
return {"authorization_url": authorization_url(user)}
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/oauth/callback")
|
||||
async def oauth_callback(code: str = "", state: str = "", error: str = "", db: Session = Depends(get_db)):
|
||||
if error or not code or not state:
|
||||
return _redirect("error=Meta%20Login%20was%20cancelled")
|
||||
try:
|
||||
await finish_oauth(db, code, state)
|
||||
except MetaApiError as exc:
|
||||
return _redirect(f"error={quote(exc.message)}")
|
||||
except Exception:
|
||||
return _redirect("error=Meta%20Login%20could%20not%20be%20completed")
|
||||
return _redirect("connected=1")
|
||||
|
||||
|
||||
@router.post("/disconnect")
|
||||
def disconnect_meta(body: Confirm, user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
disconnect(db, user)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
return {"status": "disconnected"}
|
||||
|
||||
|
||||
@router.get("/overview")
|
||||
async def meta_overview(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
return await overview(db, user)
|
||||
|
||||
|
||||
@router.get("/businesses")
|
||||
async def businesses(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
try:
|
||||
return await list_businesses(db, user)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/pages")
|
||||
async def pages(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
try:
|
||||
return await list_pages(db, user)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/pages/{page_id}")
|
||||
async def page(page_id: str, user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
try:
|
||||
return await page_detail(db, user, page_id)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/pages/select")
|
||||
async def choose_page(body: IdBody, user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await select_page(db, user, body.id)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/ad-accounts")
|
||||
async def ad_accounts(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
try:
|
||||
return await list_ad_accounts(db, user)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/ad-accounts/select")
|
||||
async def choose_ad_account(body: IdBody, user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await select_ad_account(db, user, body.id)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/campaigns")
|
||||
async def campaigns(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
try:
|
||||
return await list_campaigns(db, user)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/campaigns")
|
||||
async def create_campaign(body: CampaignPublish, user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await publish_campaign(db, user, body.model_dump())
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/campaigns/{campaign_id}")
|
||||
async def edit_campaign(
|
||||
campaign_id: str,
|
||||
body: CampaignEdit,
|
||||
user: UserAccount = Depends(current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await update_campaign(db, user, campaign_id, body.name, body.daily_budget)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/campaigns/{campaign_id}/status")
|
||||
async def campaign_status(
|
||||
campaign_id: str,
|
||||
body: StatusBody,
|
||||
user: UserAccount = Depends(current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await set_campaign_status(db, user, campaign_id, body.status)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/ads")
|
||||
async def ads(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
try:
|
||||
return await list_ads(db, user)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/ads/{ad_id}")
|
||||
async def edit_ad(ad_id: str, body: AdEdit, user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await update_ad(db, user, ad_id, body.model_dump())
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.post("/ads/{ad_id}/status")
|
||||
async def ad_status(
|
||||
ad_id: str,
|
||||
body: StatusBody,
|
||||
user: UserAccount = Depends(current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
_confirmed(body)
|
||||
try:
|
||||
return await set_ad_status(db, user, ad_id, body.status)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/analytics")
|
||||
async def meta_analytics(
|
||||
date_preset: str = "last_30d",
|
||||
user: UserAccount = Depends(current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
try:
|
||||
return await analytics(db, user, date_preset)
|
||||
except MetaApiError as exc:
|
||||
raise http_meta(exc) from exc
|
||||
|
||||
|
||||
@router.get("/audit")
|
||||
def audit(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
return {"events": recent_audit(db, user)}
|
||||
|
||||
|
||||
@router.get("/settings")
|
||||
def meta_settings(user: UserAccount = Depends(current_user), db: Session = Depends(get_db)) -> dict:
|
||||
return settings_view(db, user)
|
||||
Loading…
Add table
Add a link
Reference in a new issue