initial
This commit is contained in:
@@ -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
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.env
|
||||||
|
*.zip
|
||||||
|
plans/
|
||||||
|
modern-sk/
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user