shreyas-joshi Cursor commited on
Commit
6497be7
·
1 Parent(s): f8a7e1d

Add Space homepage and runtime info endpoints

Browse files

Co-authored-by: Cursor <cursoragent@cursor.com>

Files changed (1) hide show
  1. backend/server.py +92 -1
backend/server.py CHANGED
@@ -1,6 +1,7 @@
1
  import uvicorn
2
- from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
3
  from fastapi.middleware.cors import CORSMiddleware
 
4
  import json
5
  import asyncio
6
  import logging
@@ -51,6 +52,96 @@ app.add_middleware(
51
  allow_headers=["*"],
52
  )
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  @app.get("/health")
55
  async def health():
56
  return {"ok": True, "tts_ready": app.state.tts is not None}
 
1
  import uvicorn
2
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.responses import HTMLResponse
5
  import json
6
  import asyncio
7
  import logging
 
52
  allow_headers=["*"],
53
  )
54
 
55
+ def _space_runtime_info(request: Request) -> dict:
56
+ tts = getattr(app.state, "tts", None)
57
+ voices = []
58
+ if tts:
59
+ try:
60
+ voices = tts.list_voices()
61
+ except Exception:
62
+ voices = []
63
+
64
+ scheme = request.headers.get("x-forwarded-proto", request.url.scheme) or "https"
65
+ host = request.headers.get("x-forwarded-host", request.headers.get("host", request.url.netloc))
66
+ ws_scheme = "wss" if scheme == "https" else "ws"
67
+
68
+ return {
69
+ "name": "CoreReader Backend",
70
+ "status": "running",
71
+ "tts_ready": tts is not None,
72
+ "voice_count": len(voices),
73
+ "sample_rate": getattr(tts, "sample_rate", None) if tts else None,
74
+ "model_path": getattr(tts, "model_path", None) if tts else None,
75
+ "voices_path": getattr(tts, "voices_path", None) if tts else None,
76
+ "endpoints": {
77
+ "health": "/health",
78
+ "voices": "/voices",
79
+ "novel_index": "/novel_index?url=<novel_url>",
80
+ "novel_meta": "/novel_meta?url=<novel_url>",
81
+ "novel_chapter": "/novel_chapter?url=<novel_url>&n=<chapter_number>",
82
+ "websocket": "/ws",
83
+ "openapi_docs": "/docs",
84
+ "openapi_json": "/openapi.json",
85
+ },
86
+ "frontend_base_url": f"{ws_scheme}://{host}",
87
+ "frontend_ws_url": f"{ws_scheme}://{host}/ws",
88
+ }
89
+
90
+
91
+ @app.get("/", response_class=HTMLResponse)
92
+ async def root(request: Request):
93
+ info = _space_runtime_info(request)
94
+ html = f"""
95
+ <!doctype html>
96
+ <html>
97
+ <head>
98
+ <meta charset="utf-8" />
99
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
100
+ <title>CoreReader Backend</title>
101
+ <style>
102
+ body {{ font-family: Arial, sans-serif; margin: 2rem; line-height: 1.45; }}
103
+ code {{ background: #f5f5f5; padding: 0.15rem 0.35rem; border-radius: 4px; }}
104
+ .ok {{ color: #0b7a2a; }}
105
+ .card {{ border: 1px solid #ddd; border-radius: 10px; padding: 1rem; max-width: 880px; }}
106
+ </style>
107
+ </head>
108
+ <body>
109
+ <div class="card">
110
+ <h1>CoreReader Backend</h1>
111
+ <p class="ok"><strong>Status:</strong> {info["status"]}</p>
112
+ <p><strong>TTS Ready:</strong> {info["tts_ready"]}</p>
113
+ <p><strong>Voices Loaded:</strong> {info["voice_count"]}</p>
114
+ <p><strong>Sample Rate:</strong> {info["sample_rate"]}</p>
115
+ <p><strong>Model:</strong> <code>{info["model_path"]}</code></p>
116
+ <p><strong>Voices File:</strong> <code>{info["voices_path"]}</code></p>
117
+
118
+ <h3>Use this in Frontend</h3>
119
+ <p><strong>WebSocket base URL:</strong> <code>{info["frontend_base_url"]}</code></p>
120
+ <p><strong>WebSocket endpoint:</strong> <code>{info["frontend_ws_url"]}</code></p>
121
+
122
+ <h3>API Endpoints</h3>
123
+ <ul>
124
+ <li><code>/health</code></li>
125
+ <li><code>/voices</code></li>
126
+ <li><code>/novel_index?url=&lt;novel_url&gt;</code></li>
127
+ <li><code>/novel_meta?url=&lt;novel_url&gt;</code></li>
128
+ <li><code>/novel_chapter?url=&lt;novel_url&gt;&amp;n=&lt;chapter_number&gt;</code></li>
129
+ <li><code>/ws</code> (WebSocket)</li>
130
+ <li><code>/docs</code> (interactive API docs)</li>
131
+ <li><code>/info</code> (JSON details)</li>
132
+ </ul>
133
+ </div>
134
+ </body>
135
+ </html>
136
+ """
137
+ return HTMLResponse(content=html)
138
+
139
+
140
+ @app.get("/info")
141
+ async def info(request: Request):
142
+ return _space_runtime_info(request)
143
+
144
+
145
  @app.get("/health")
146
  async def health():
147
  return {"ok": True, "tts_ready": app.state.tts is not None}