SpaceTune is a music streaming backend platform, built as a sequential, production-grade FastAPI service. It allows users to upload, stream, grade, and manage tracks
- 🔐 JWT-based authentication (access + refresh tokens)
- 🌐 Google OAuth 2.0 login
- 🎶 Track upload, retrieval, and grading
- ☁️ AWS S3 integration for audio/image storage
- 🩺 Health-check endpoint (
GET /health) with DB connectivity probe - 📝 Structured JSON logging with automatic sensitive-field redaction
- 🔗 Correlation-ID propagation across requests (
X-Request-ID) - 🐳 Multi-stage, non-root Docker image + Docker Compose orchestration
(Telegram bot and frontend are planned for later stages — see Roadmap below)
Backend
- Python 3.13
- FastAPI
- PostgreSQL + SQLAlchemy (async) + Alembic
- Redis (planned)
- AWS S3 (boto3)
- JWT (PyJWT), bcrypt
- structlog (structured JSON logging)
Infrastructure
- Docker / Docker Compose (multi-stage build, non-root
appuser)
Package management
Make sure you have git installed.
git clone {repository url}
cd SpaceTuneThe compose.yml files live at the repository root — all commands below are run from SpaceTune/, not from SpaceTune/backend/.
A single .env file at the repo root is required — it holds the database connection components and the app secrets:
cp .env.example .env.env — DB_USER, DB_PASSWORD, DB_NAME (Docker Compose provisions the db service from them, and the backend builds its connection string out of them), plus the app secrets: JWT_SECRET_KEY, GOOGLE_CLIENT_ID, GOOGLE_REDIRECT_URI, GOOGLE_CLIENT_SECRET, GOOGLE_USERINFO_URL, AWS_SECRET, AWS_ACCESS, AWS_REGION, BUCKET_NAME.
DB_HOST, DB_PORT and TEST_DB_PORT are optional and default to localhost, 5432 and 5433 — override them only if PostgreSQL lives elsewhere. Docker Compose overrides DB_HOST with db for the container.
Make sure you have Docker and Docker Compose installed.
docker compose -f compose.yml -f compose.dev.yml up --buildThis starts the backend (hot-reload enabled, port 8000) and a PostgreSQL 16 instance together. The backend waits for the database to report healthy (depends_on: condition: service_healthy) before starting. Database migrations still need to be applied — see step 5.
Install uv and a Python interpreter.
cd backend
uv venvActivate the environment:
- Windows:
.venv\Scripts\activate- Linux/macOS:
source .venv/bin/activateInstall dependencies:
uv sync --frozenThis requires a PostgreSQL instance reachable at the DB_HOST/DB_PORT from the root .env (default localhost:5432, e.g. run only the db service via docker compose up db).
From backend/, apply all migrations to create the schema:
uv run alembic upgrade headIf running inside the Docker container, exec into it instead:
docker compose exec backend alembic upgrade headuv run uvicorn src.api:api.app --reloadThe API will be available at http://localhost:8000. Interactive docs at http://localhost:8000/docs.
Verify the service and database connection are up:
curl http://localhost:8000/healthReturns 200 when the database is reachable, or 503 if it isn't.
Unit tests mock the database and run without any external dependency:
cd backend
uv run pytest tests/unitIntegration tests need a real PostgreSQL connection. This project uses an isolated,
ephemeral test database — it lives in a separate db-test service (RAM-backed via
tmpfs, no persistent volume) so each run starts clean and nothing needs manual
cleanup between runs:
docker compose -f compose.yml -f compose.dev.yml up -d db-test
docker compose -f compose.yml -f compose.dev.yml exec db-test pg_isready -U ${DB_USER:-postgres}
cd backend
uv run pytest
docker compose -f compose.yml -f compose.dev.yml rm -fsv db-testThe pg_isready check may need a few retries right after up -d — Postgres takes
a couple of seconds to accept connections. Run it again if it reports "no response".
If db-test is left running between sessions, that's harmless: its data lives in
tmpfs and disappears the moment the container stops, so there's nothing to reset
manually the way the persistent db service sometimes needs (see the note on
docker compose down -v below).
If docker compose up fails with a PermissionError during migrations (or any
other file-access error) shortly after pulling a change to backend/docker/Dockerfile
or the compose*.yml files, try this before digging further:
docker compose -f compose.yml -f compose.dev.yml down -v
docker compose -f compose.yml -f compose.dev.yml up --builddown -v removes anonymous volumes (e.g. the .venv volume used for hot-reload),
which can retain stale ownership from before the Dockerfile change. This resolves
the issue in most cases without needing a deeper investigation.