File size: 3,365 Bytes
ede7942
 
 
 
11463f1
 
ede7942
 
 
 
bced5a1
ede7942
 
53f85e0
ede7942
 
 
 
 
 
 
 
 
 
 
 
 
 
53f85e0
ede7942
3a6f2bc
ede7942
11463f1
 
ede7942
 
 
11463f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ede7942
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bced5a1
ede7942
 
 
 
 
d78f4d3
 
 
 
 
 
 
 
3a6f2bc
d78f4d3
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.docs import get_redoc_html
from fastapi.responses import HTMLResponse

from config import settings
from database import create_tables
from routers import (
    admin, chat, cowriter, document, grammar, humanize,
    paraphrase, plagiarism, summarize, tone, translate,
)
from routers import auth


@asynccontextmanager
async def lifespan(app: FastAPI):
    create_tables()
    yield


app = FastAPI(
    title="Anovo API",
    description=(
        "AI-Powered Writing Tool API — Free & Open Source\n\n"
        "Endpoints for paraphrasing, grammar checking, summarization, "
        "translation, AI text humanization, plagiarism detection, "
        "tone analysis, co-writing, AI chat, and user accounts."
    ),
    version="2.1.0",
    docs_url="/docs",
    # Served by the custom route below so the ReDoc bundle URL can be pinned.
    redoc_url=None,
    lifespan=lifespan,
)

# FastAPI 0.111 (the version pinned in requirements.txt) points ReDoc at the
# floating "redoc@next" dist-tag, which now resolves to a pre-release whose
# standalone bundle fails to boot — the page loads but renders nothing. Pin an
# exact, known-good release so this cannot drift with either the CDN tag or the
# installed FastAPI version.
REDOC_JS_URL = "https://cdn.jsdelivr.net/npm/redoc@2.1.5/bundles/redoc.standalone.js"


@app.get("/redoc", include_in_schema=False)
def redoc_html() -> HTMLResponse:
    return get_redoc_html(
        openapi_url=app.openapi_url or "/openapi.json",
        title=f"{app.title} - ReDoc",
        redoc_js_url=REDOC_JS_URL,
    )


# ── CORS ─────────────────────────────────────────────────────────────────────
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origins,
    allow_origin_regex=r"https://.*\.(vercel\.app|hf\.space)$",
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ── Routers ───────────────────────────────────────────────────────────────────
app.include_router(paraphrase.router)
app.include_router(grammar.router)
app.include_router(summarize.router)
app.include_router(translate.router)
app.include_router(humanize.router)
app.include_router(plagiarism.router)
app.include_router(tone.router)
app.include_router(cowriter.router)
app.include_router(chat.router)
app.include_router(document.router)
app.include_router(admin.router)
app.include_router(auth.router)


@app.get("/", tags=["health"])
def health_check() -> dict:
    """Health check endpoint with provider diagnostics."""
    return {
        "status": "ok",
        "service": "Anovo API",
        "version": app.version,
        "providers": {
            "groq": bool(settings.groq_api_keys or settings.groq_api_key),
            "hf": bool(settings.hf_api_token),
            "premium_models": bool(settings.groq_api_keys or settings.groq_api_key),
        },
        "db": settings.database_url.split("///")[-1] if "sqlite" in settings.database_url else "postgres",
    }