This commit is contained in:
2026-06-06 13:08:05 +03:00
commit a1213ae256
8 changed files with 341 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
# ======================================================================
# mycoolmusicapp — combined env for docker compose (dev).
# Copy to .env: cp .env.example .env (or: make env)
# Compose injects this into api, worker and webui. Never commit real .env.
# Per-repo .env.example files still exist for running a service standalone.
# ======================================================================
# ---- Postgres (db service) -------------------------------------------
POSTGRES_USER=mcma
POSTGRES_PASSWORD=mcma
POSTGRES_DB=mcma
POSTGRES_PORT=5432
REDIS_PORT=6379
# ---- Backend (api + worker) ------------------------------------------
ENVIRONMENT=dev # dev | test | prod
LOG_LEVEL=INFO
LOG_JSON=false # true in prod
# DATABASE_URL / REDIS_URL are overridden in compose to point at the db/redis
# services. These localhost values are the fallback for host-run processes.
DATABASE_URL=postgresql+asyncpg://mcma:mcma@localhost:5432/mcma
DB_ECHO=false
REDIS_URL=redis://localhost:6379/0
# auth — GENERATE a strong secret for prod: `openssl rand -hex 32`
JWT_SECRET=change-me-in-prod
ACCESS_TOKEN_TTL_SECONDS=900
REFRESH_TOKEN_TTL_SECONDS=2592000
# media / storage (paths inside the container)
MEDIA_PATH=/data/media
TRANSCODE_CACHE_PATH=/data/transcode-cache
MAX_PARALLEL_DOWNLOADS=2
# external services (all optional — backend degrades gracefully if unset)
# ML_SERVICE_URL=http://ml:9000
# ACOUSTID_API_KEY=
MUSICBRAINZ_USER_AGENT=mcma-backend/0.1.0 ( https://github.com/your/repo )
# YOUTUBE_COOKIES_PATH=/data/cookies.txt
# ---- Frontend (webui) ------------------------------------------------
# Served same-origin behind nginx, so the default '/api/v1' just works.
PUBLIC_API_BASE_URL=/api/v1
+4
View File
@@ -0,0 +1,4 @@
.env
*.zip
plans/
modern-sk/
+6
View File
@@ -0,0 +1,6 @@
[submodule "mcma-backend"]
path = mcma-backend
url = ssh://git@git.ollyhearn.ru:49239/olly/mcma-backend.git
[submodule "mcma-webui"]
path = mcma-webui
url = ssh://git@git.ollyhearn.ru:49239/olly/mcma-webui.git
+105
View File
@@ -0,0 +1,105 @@
# mycoolmusicapp — dev workflow shortcuts.
# `make` or `make help` lists targets.
COMPOSE := docker compose
.DEFAULT_GOAL := help
.PHONY: help env up build rebuild down stop restart ps logs logs-api logs-webui \
sh-api sh-webui db-shell redis-cli migrate makemigration downgrade \
test test-api test-webui lint fmt clean prune \
prod-build prod-build-api prod-build-webui
help: ## Show this help
@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}'
env: ## Create .env from .env.example (no overwrite)
@test -f .env || cp .env.example .env && echo ".env ready"
## ---- lifecycle -------------------------------------------------------
up: env ## Build (if needed) + start the full dev stack
$(COMPOSE) up --build
build: ## Build all images
$(COMPOSE) build
rebuild: ## Rebuild images with no cache
$(COMPOSE) build --no-cache
down: ## Stop and remove containers
$(COMPOSE) down
stop: ## Stop containers (keep them)
$(COMPOSE) stop
restart: ## Restart all services
$(COMPOSE) restart
ps: ## Show container status
$(COMPOSE) ps
## ---- logs ------------------------------------------------------------
logs: ## Tail logs (all services)
$(COMPOSE) logs -f --tail=100
logs-api: ## Tail api logs
$(COMPOSE) logs -f --tail=100 api
logs-webui: ## Tail webui logs
$(COMPOSE) logs -f --tail=100 webui
## ---- shells ----------------------------------------------------------
sh-api: ## Shell into the api container
$(COMPOSE) exec api bash
sh-webui: ## Shell into the webui container
$(COMPOSE) exec webui sh
db-shell: ## psql into the database
$(COMPOSE) exec db psql -U $${POSTGRES_USER:-mcma} -d $${POSTGRES_DB:-mcma}
redis-cli: ## redis-cli into redis
$(COMPOSE) exec redis redis-cli
## ---- database migrations (alembic) -----------------------------------
migrate: ## Apply latest migrations
$(COMPOSE) exec api alembic upgrade head
makemigration: ## Autogenerate a migration: make makemigration m="msg"
$(COMPOSE) exec api alembic revision --autogenerate -m "$(m)"
downgrade: ## Roll back one migration
$(COMPOSE) exec api alembic downgrade -1
## ---- quality ---------------------------------------------------------
test: test-api test-webui ## Run all tests
test-api: ## Run backend tests
$(COMPOSE) exec api pytest
test-webui: ## Run frontend tests
$(COMPOSE) exec webui npm test
lint: ## Lint backend + frontend
$(COMPOSE) exec api ruff check .
$(COMPOSE) exec webui npm run lint
fmt: ## Format backend + frontend
$(COMPOSE) exec api ruff format .
$(COMPOSE) exec webui npm run format
## ---- cleanup ---------------------------------------------------------
clean: ## Stop + remove containers AND volumes (DESTROYS db data)
$(COMPOSE) down -v
prune: ## Docker system prune (dangling images/build cache)
docker system prune -f
## ---- production image builds (no compose yet) ------------------------
prod-build: prod-build-api prod-build-webui ## Build both prod images
prod-build-api: ## Build the backend prod image
docker build -f mcma-backend/dockerfiles/Dockerfile.prod -t mcma-backend:prod ./mcma-backend
prod-build-webui: ## Build the webui prod image
docker build -f mcma-webui/dockerfiles/Dockerfile.prod -t mcma-webui:prod ./mcma-webui
+120
View File
@@ -0,0 +1,120 @@
# DEV stack for mycoolmusicapp — one entrypoint at http://localhost:80.
#
# make up # build + start everything (db, redis, api, worker, webui, nginx)
# make logs # tail logs
# make down # stop
#
# Source is bind-mounted into api/worker/webui, so edits hot-reload without a
# rebuild. PROD has NO compose yet — services there are built straight from
# their dockerfiles/Dockerfile.prod (a separate prod compose lands later).
#
# Env: copy .env.example -> .env at the repo root (single combined file).
services:
# -- backing services -----------------------------------------------------
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-mcma}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mcma}
POSTGRES_DB: ${POSTGRES_DB:-mcma}
ports:
- "${POSTGRES_PORT:-5432}:5432" # exposed so host-run tests can reach it
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mcma}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --save 60 1 --loglevel warning
ports:
- "${REDIS_PORT:-6379}:6379"
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# -- application services -------------------------------------------------
api:
build:
context: ./mcma-backend
dockerfile: dockerfiles/Dockerfile.dev
env_file: .env
environment:
# Service-name URLs override the localhost values from .env.
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-mcma}:${POSTGRES_PASSWORD:-mcma}@db:5432/${POSTGRES_DB:-mcma}
REDIS_URL: redis://redis:6379/0
volumes:
- ./mcma-backend:/app # live source (hot reload)
- /app/.venv # keep the image's venv, don't shadow it
- media:/data/media
- transcode_cache:/data/transcode-cache
ports:
- "8000:8000" # direct access for debugging / docs
depends_on:
db: { condition: service_healthy }
redis: { condition: service_healthy }
restart: unless-stopped
worker:
build:
context: ./mcma-backend
dockerfile: dockerfiles/Dockerfile.dev
command: arq app.workers.arq_worker.WorkerSettings
env_file: .env
environment:
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-mcma}:${POSTGRES_PASSWORD:-mcma}@db:5432/${POSTGRES_DB:-mcma}
REDIS_URL: redis://redis:6379/0
volumes:
- ./mcma-backend:/app
- /app/.venv
- media:/data/media
- transcode_cache:/data/transcode-cache
depends_on:
db: { condition: service_healthy }
redis: { condition: service_healthy }
restart: unless-stopped
webui:
build:
context: ./mcma-webui
dockerfile: dockerfiles/Dockerfile.dev
env_file: .env
environment:
RSBUILD_HOST: 0.0.0.0
RSBUILD_PORT: "3000"
# Browser reaches HMR through nginx on :80, not the container's :3000.
RSBUILD_HMR_CLIENT_PORT: "80"
volumes:
- ./mcma-webui:/app
- /app/node_modules # keep the image's install, don't shadow it
expose:
- "3000"
restart: unless-stopped
# -- reverse proxy: single entrypoint at localhost:80 ---------------------
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- webui
restart: unless-stopped
volumes:
pgdata:
redisdata:
media:
transcode_cache:
Submodule
+1
Submodule mcma-backend added at 87b48e941e
Submodule
+1
Submodule mcma-webui added at 37c1a5944a
+60
View File
@@ -0,0 +1,60 @@
# DEV reverse proxy — single entrypoint at http://localhost:80.
# / -> webui rsbuild dev server (with HMR websocket)
# /rsbuild-hmr -> webui HMR websocket channel
# /api/... -> backend API
# /health... -> backend health/readiness probes
#
# This is the DEV topology only. Production gets its own proxy in the future
# prod compose (the webui PROD image already self-serves static assets).
# WebSocket upgrade plumbing.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream webui_dev { server webui:3000; }
upstream api_dev { server api:8000; }
server {
listen 80;
server_name localhost;
# Large media uploads — don't choke on big files.
client_max_body_size 0;
# ---- backend -------------------------------------------------------
location /api/ {
proxy_pass http://api_dev;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_buffering off;
}
location /health {
proxy_pass http://api_dev;
proxy_set_header Host $host;
}
# ---- rsbuild HMR websocket ----------------------------------------
location /rsbuild-hmr {
proxy_pass http://webui_dev;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
# ---- webui dev server (catch-all) ---------------------------------
location / {
proxy_pass http://webui_dev;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}