Spaces:
Running
Running
File size: 5,057 Bytes
5f3e9f5 | 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | # TextBro β Flask Backend
The Python/Flask service that powers TextβVideo, HTMLβVideo, and Image/PDFβVideo
generation. Originally lived in
[`shiv12345678901/yt-project`](https://github.com/shiv12345678901/yt-project);
now colocated with the React frontend in this repo.
## Running locally
```bash
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
playwright install chromium
cp config/config.example.py config/config.py # add your API_KEY/API_URL/MODEL
cp .env.example .env # tweak security knobs
python start.py
# β http://127.0.0.1:5000
```
By default the server binds to **127.0.0.1** only β there's no shared-secret
auth out of the box, so don't expose it on a LAN until you set `API_KEY`.
## Running in production
Use a real WSGI server. The repo ships `wsgi.py` and pins both `gunicorn`
(Linux/Mac) and `waitress` (Windows) in `requirements.txt`:
```bash
# Linux/Mac
gunicorn -w 4 -b 127.0.0.1:5000 wsgi:app
# Windows
waitress-serve --listen=127.0.0.1:5000 wsgi:app
```
`app.run(debug=True)` is **never** invoked unless `FLASK_DEBUG=1` is set, and
`start.py` will refuse to enable debug mode on a non-loopback host.
## Security configuration
All knobs live in `backend/.env` (see `.env.example`). Defaults are safe for
local dev:
| Variable | Default | Purpose |
| --- | --- | --- |
| `FLASK_DEBUG` | `0` | `1` enables Werkzeug debug + reloader. NEVER on in prod (PIN-protected RCE shell). |
| `FLASK_HOST` | `127.0.0.1` | Bind address. Override only behind an authenticating reverse proxy. |
| `PORT` | `5000` | Listen port. |
| `ALLOWED_ORIGINS` | `http://localhost:5173,http://127.0.0.1:5173,http://localhost:5000,http://127.0.0.1:5000` | CORS allowlist (comma-separated). Wildcard intentionally unsupported. |
| `API_KEY` | _(unset)_ | Shared secret. When set, every non-public request must carry `X-API-Key`. |
| `RATE_LIMIT_DEFAULT` | `200 per hour;30 per minute` | Per-IP global limit. |
| `RATE_LIMIT_HEAVY` | `20 per hour;5 per minute` | Tighter limit on `/generate*`, `/extract-from-image`, `/image-to-screenshots-sse`, `/regenerate`. |
| `RATE_LIMIT_STORAGE_URI` | `memory://` | Switch to e.g. `redis://localhost:6379` when running multiple workers. |
| `MAX_CONTENT_LENGTH_BYTES` | `33554432` | 32 MiB cap on uploaded files. |
### Public paths (always reachable)
- `GET /healthz` β liveness probe (200 if process is alive).
- `GET /` β index page.
- `GET /static/...` β static assets.
Every other route (including `/screenshots/*` and `/html/*`) requires the API
key when one is configured.
### Threat model
This is a single-tenant developer tool. The defaults assume:
- The process runs on a trusted machine on a trusted network.
- Only a small group of operators hits the AI / screenshot endpoints.
- Production deployment puts the app behind a reverse proxy that adds TLS
and (optionally) further auth. `API_KEY` is a coarse-grained shared secret,
not user-level auth.
If you need multi-tenant access, swap the `_enforce_api_key` `before_request`
hook for a real auth middleware (OIDC, JWT, etc.).
## Project layout
```
backend/
βββ app.py # Flask app factory + security middleware
βββ start.py # Dev launcher with preflight checks
βββ wsgi.py # Production WSGI entrypoint (gunicorn / waitress)
βββ requirements.txt
βββ .env.example
βββ config/
β βββ config.example.py # copy to config.py and fill in
βββ routes/ # generate / html / image / resources blueprints
βββ src/
β βββ core/ # AI + vision clients
β βββ screenshot_engines/ # Playwright wrapper
β βββ utils/ # cache, metrics, ETA, retry helpers
βββ static/
βββ templates/
```
## Backend endpoints used by the React frontend
| Path | Method | Purpose |
| ----------------------------- | ------ | -------------------------------- |
| `/healthz` | GET | Liveness probe |
| `/generate-sse` | POST | Text β HTML β screenshots (SSE) |
| `/generate-html` | POST | HTML β screenshots |
| `/image-to-screenshots-sse` | POST | Image/PDF β screenshots (SSE) |
| `/cancel/<operation_id>` | POST | Cancel an in-progress generation |
| `/beautify`, `/minify` | POST | HTML helpers |
| `/screenshots/<filename>` | GET | Serve screenshot PNG |
| `/html/<filename>` | GET | Serve HTML |
| `/download-zip` | POST | Bundle files into a ZIP |
| `/list` | GET | List generated files |
| `/delete/<type>/<filename>` | DELETE | Delete a file |
| `/history` | GET | Generation history |
| `/cache/stats`, `/cache/clear` | GET/POST | AI response cache |
|