placeholder-api/README.md

105 lines
3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Placeholder API
Generate SVG and PNG placeholder images with configurable dimensions and font size.
## Prerequisites
Local development uses uv and Python 3.12 or newer. PNG rendering also needs
Cairo, which you install with your system package manager before running `uv sync`.
Run the appropriate command for your distribution as root:
| Distribution | Install Cairo |
| --- | --- |
| [Alpine](https://pkgs.alpinelinux.org/packages?name=cairo) | `apk add cairo` |
| [Arch](https://archlinux.org/packages/extra/x86_64/cairo/) | `pacman -S cairo` |
| [Debian](https://packages.debian.org/en/trixie/libcairo2) | `apt install libcairo2` |
| [Void](https://github.com/void-linux/void-packages/blob/master/srcpkgs/cairo/template) | `xbps-install -S cairo` |
For a container setup, you only need Podman on the host. The image includes
Python, Cairo, and fonts.
## Running the app
Run the development server:
```sh
uv sync
uv run flask --app app:create_app run --debug
```
Alternatively, `uv run app` starts the development server without debug mode.
- `GET /placeholder.svg` returns an SVG placeholder.
- `GET /placeholder.png` returns a PNG placeholder.
- `GET /healthz` returns `{"status": "ok"}`.
Image endpoints accept `width`, `height`, and `font_size` query parameters.
For example, `/placeholder.png?width=320&height=180&font_size=20` returns a
320 × 180 PNG with 20-pixel text.
Run the Gunicorn server locally:
```sh
uv run gunicorn --bind 0.0.0.0:8000 --workers 2 --access-logfile - --error-logfile - 'app:create_app()'
```
Build and run the container:
```sh
podman build -t placeholder-api -f Containerfile .
podman run --rm -p 8000:8000 placeholder-api
```
The container runs as an unprivileged user and serves HTTP on port 8000.
Check it with `curl http://localhost:8000/healthz`.
Additional Gunicorn options can be passed through `GUNICORN_CMD_ARGS`.
The application factory is in `src/app/__init__.py`; API routes live in
`src/app/routes/api.py`.
## Development checks
Install the Git hooks once after cloning and running `uv sync`:
```sh
uv run pre-commit install
```
Run all checks on tracked files:
```sh
uv run pre-commit run --all-files
```
The hooks run Ruff, Pyright, and pytest before commits, and validate commit
messages with Commitizen. To run just the tests:
```sh
uv run pytest -q
```
## Caching and rate limits
Successful image responses allow public caching for one day. Rendered images are
also cached in memory on the server with a one-day expiration.
Rate limits apply per client IP, per Gunicorn worker:
| Format | Requests per minute | Uncached renders per minute |
| --- | ---: | ---: |
| SVG | 900 | 300 |
| PNG | 900 | 30 |
Exceeding a limit returns HTTP 429. Server-cached images remain available after
the render limit is reached, subject to the request limit. Responses served
directly from a browser or CDN cache do not reach the app or consume its quotas.
Each Gunicorn worker maintains its own cache and rate-limit counters. Defaults
are configured in `src/app/config.py`.
## License
Licensed under the [BSD 3-Clause License](LICENSE).