Build and include the `RAHasher` binary in the Docker image, to
calculate hashes for RetroAchievements. Also, add a service to
run `RAHasher` from Python.
Example usage:
```python
from adapters.services.rahasher import RAHasherError, RAHasherService
rahasher = RAHasherService()
try:
hash = await rahasher.calculate_hash("nes", Path("path/to/rom.nes"))
except RAHasherError:
# Handle error
hash = None
```
This change replaces the bundled Redis server with Valkey. No breaking
changes are introduced, as considered environment variables still
maintain the `REDIS_` prefix.
Fixes#925.
This change installs and configures the `mod_zip` nginx module [1],
which allows nginx to stream ZIP files directly.
It includes a workaround needed to correctly calculate CRC-32 values for
included files, by including a new `server` section listening at port
8081, only used for the file requests to be upstream subrequests that
correctly trigger the CRC-32 calculation logic.
Also, to be able to provide a `m3u` file generated on the fly, we add a
`/decode` endpoint fully implemented in nginx using NJS, which receives
a `value` URL param, and decodes it using base64. The decoded value is
returned as the response.
That way, the contents of the `m3u` file is base64-encoded, and set as
part of the response, for `mod_zip` to include it in the ZIP file.
[1] https://github.com/evanmiller/mod_zip
Instead of making FastAPI handle file download, which has serious
performance issues on big files [1], this change uses nginx's `X-Accel`
feature to delegate single-file downloads to nginx.
Partial fix for #1079, as it solves the CPU usage issue for single-file
downloads.
[1] https://github.com/fastapi/fastapi/discussions/6050
Currently, the `request.url_for` and `URLPath.make_absolute_url` methods
always build URLs with "http" scheme, even when the original requested
URL is using "https".
The reason for this is that Gunicorn does not allow IPs other than
127.0.0.1 to set secure headers by default. As regular RomM
installations don't know which frontend IPs will try to set security
headers in advance, we can disable this validation, and fix URL
building.
A simple way to test this change is to access any of the `feed` endpoints,
which generate URLs using the mentioned methods. Accessing the endpoint
using "https" scheme must generate "https" URLs.
Reference:
* https://github.com/encode/starlette/issues/538#issuecomment-2054013679
* https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips
Fix FastAPI and nginx configuration, to make the application correctly
redirect URLs. This is specially useful when URLs ended with forward
slash are redirected to their stripped version.
Included changes:
* Stop removing the `/api` prefix in nginx rewrite rules, so FastAPI
knows what's the original URL path being requested.
* Use `$http_host` in nginx, so FastAPI receives both the original host
and port, to build the redirect URL (as `$host` does not include the
port, if present).
* Make all FastAPI included routers know their prefix, to correctly
route incoming requests.
This fix was found based on a report that redirects from URLs ended with
forward slash were not working [1].
[1] https://github.com/rommapp/romm/issues/1051#issuecomment-2269049762
This change moves the virtualenv creation in the `Dockerfile` to a
separate stage, to simplify isolating the process and reduce the need
for uninstalling build dependencies.
The approach is similar to the one explained in [1]. It relies on
building a virtualenv folder, and copying it in the final stage.
Changing the `PATH` environment variable makes the virtualenv usable by
default, without affecting the default Python installation.
Also, added Dockerfile arguments for Alpine, nginx, and Python versions,
as some of them are reused, and also simplifies testing new versions.
An extra side effect is that the image size for the final stage is
reduced from 315MB to 262MB.
[1] https://scribe.rip/@albertazzir/blazing-fast-python-docker-builds-with-poetry-a78a66f5aed0