Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Build context для Go-сервисов = корень репо (issue #63: общий модуль pkg/).
# Оставляем только исходники трёх Go-сервисов и pkg — остальное в контекст
# не нужно (.git, node_modules, .env и т.д. в образ не должны попадать).
.git
.env
data/
tmp/
docs/
deploy/
frontend/
scripts/
tests/
services/auth/
services/transcoder/
**/__pycache__/
**/node_modules/
**/.venv/
10 changes: 9 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,18 @@ RABBITMQ_MGMT_PORT=15672
MINIO_PUBLIC_ENDPOINT=http://localhost:9000
MINIO_API_CORS_ALLOW_ORIGIN=*

# Redis for auth rate-limit (backlog) — 5/min/IP on /auth/login
# Redis — rate-limit storage (auth slowapi + gateway fixed-window, issue #52)
REDIS_URL=redis://redis:6379/0
REDIS_PORT=6379

# Gateway (issue #52)
# CORS — явный allowlist origin через запятую; пусто = без CORS-заголовков (без wildcard)
CORS_ALLOWED_ORIGINS=http://localhost:3000
# CIDR прокси, которым gateway доверяет X-Forwarded-For. Пусто = никому
# (gateway — edge, LB перед ним нет). Для auth эта же переменная задаёт доверие
# X-Real-IP от gateway: compose по умолчанию подставляет 172.16.0.0/12.
#TRUSTED_PROXY_CIDRS=

# App ports
GATEWAY_PORT=8080
AUTH_PORT=8001
Expand Down
19 changes: 19 additions & 0 deletions .golangci.strict.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Строгий набор под gopls в Zed (см. lsp.gopls в settings.json):
# staticcheck с полным набором проверок (включая ST*) + govet shadow.
# Используется в `make lint-report`, чтобы его вывод совпадал с диагностиками Zed.
version: "2"
linters:
default: none
enable:
- staticcheck
- govet
- errcheck
- ineffassign
- unused
settings:
staticcheck:
checks:
- all
govet:
enable:
- shadow
45 changes: 34 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# .env — единственный в корне, compose явно указывает на него (--env-file), deploy/.env не нужен
COMPOSE=docker compose --env-file .env -f deploy/docker-compose.yml

.PHONY: up down logs ps build lint fmt test e2e e2e-file swagger swagger-install sync-py migrate-up migrate-down migrate-create migrate-alembic-up metrics loki-logs grafana prometheus
.PHONY: up down logs ps build lint lint-report fmt test e2e e2e-file swagger swagger-install sync-py migrate-up migrate-down migrate-create migrate-alembic-up metrics loki-logs grafana prometheus

up:
$(COMPOSE) up --build -d
Expand Down Expand Up @@ -41,27 +41,27 @@ build:
$(COMPOSE) build

# Go — локально если установлено, иначе через Docker (golang:1.27, golangci-lint)
# Каждый сервис — отдельный go.mod, поэтому линтим per-service
# Каждый модуль — отдельный go.mod (pkg + сервисы), поэтому линтим per-module
lint-go:
@for svc in metadata gateway upload; do \
echo "==> lint $$svc"; \
@for dir in pkg services/metadata services/gateway services/upload; do \
echo "==> lint $$dir"; \
if which golangci-lint >/dev/null 2>&1; then \
(cd services/$$svc && golangci-lint run ./...) || exit 1; \
(cd $$dir && golangci-lint run ./...) || exit 1; \
else \
docker run --rm -v $(PWD):/app -w /app/services/$$svc golangci/golangci-lint:latest golangci-lint run ./... || exit 1; \
docker run --rm -v $(PWD):/app -w /app/$$dir golangci/golangci-lint:latest golangci-lint run ./... || exit 1; \
fi; \
done

fmt-go:
@which gofmt >/dev/null 2>&1 && gofmt -w services/ || docker run --rm -v $(PWD):/app -w /app golang:1.27-alpine gofmt -w ./services
@which gofmt >/dev/null 2>&1 && gofmt -w services/ pkg/ || docker run --rm -v $(PWD):/app -w /app golang:1.27-alpine gofmt -w ./services ./pkg

test-go:
@for svc in metadata gateway upload; do \
echo "==> test $$svc"; \
@for dir in pkg services/metadata services/gateway services/upload; do \
echo "==> test $$dir"; \
if which go >/dev/null 2>&1; then \
(cd services/$$svc && go test ./...) || exit 1; \
(cd $$dir && go test ./...) || exit 1; \
else \
docker run --rm -v $(PWD):/app -w /app/services/$$svc golang:1.27-alpine go test ./... || exit 1; \
docker run --rm -v $(PWD):/app -w /app/$$dir golang:1.27-alpine go test ./... || exit 1; \
fi; \
done

Expand Down Expand Up @@ -119,6 +119,29 @@ dev-frontend:
lint-front:
cd frontend && npm run lint

# Ошибки и варнинги всех проектов одним списком (file:line: message) — удобно копировать целиком.
# Не падает на первой ошибке; полный отчёт дублируется в /tmp/flowix-diagnostics.txt
lint-report:
@: > /tmp/flowix-diagnostics.txt
@ROOT=$$(pwd); \
for dir in pkg services/metadata services/gateway services/upload; do \
echo "==> go: $$dir" | tee -a /tmp/flowix-diagnostics.txt; \
if which golangci-lint >/dev/null 2>&1; then \
(cd $$dir && golangci-lint run -c $$ROOT/.golangci.strict.yaml ./...) 2>&1 | tee -a /tmp/flowix-diagnostics.txt; \
else \
echo "golangci-lint not installed, skip go ($$dir)" | tee -a /tmp/flowix-diagnostics.txt; \
fi; \
done
@echo "==> py: auth (flake8 + mypy)" | tee -a /tmp/flowix-diagnostics.txt
@uv run --project services/auth flake8 services/auth/src 2>&1 | tee -a /tmp/flowix-diagnostics.txt
@uv run --project services/auth mypy services/auth/src 2>&1 | tee -a /tmp/flowix-diagnostics.txt
@echo "==> py: transcoder (flake8 + mypy)" | tee -a /tmp/flowix-diagnostics.txt
@uv run --project services/transcoder flake8 services/transcoder/app 2>&1 | tee -a /tmp/flowix-diagnostics.txt
@uv run --project services/transcoder mypy services/transcoder/app 2>&1 | tee -a /tmp/flowix-diagnostics.txt
@echo "==> frontend (next lint)" | tee -a /tmp/flowix-diagnostics.txt
@(cd frontend && npm run lint) 2>&1 | tee -a /tmp/flowix-diagnostics.txt
@echo "report saved: /tmp/flowix-diagnostics.txt"

fmt-front:
cd frontend && npm run format || npx prettier --write .

Expand Down
36 changes: 32 additions & 4 deletions deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,13 @@ services:
MINIO_ENDPOINT: minio:9000
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
# X-Real-IP от gateway (внутри docker-сети) — ключ auth-лимитера по
# реальному клиенту; см. services/auth/src/core/limiter.py
TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:-172.16.0.0/12}
ports:
- "${AUTH_PORT:-8001}:8001"
# ревью фазы 17 (H1): auth не для внешнего доступа — клиенты идут через
# gateway (rate-limit + CORS); порт только для localhost (e2e/dev)
- "127.0.0.1:${AUTH_PORT:-8001}:8001"
depends_on:
postgres:
condition: service_healthy
Expand All @@ -178,7 +183,10 @@ services:
start_period: 15s

metadata:
build: ../services/metadata
# issue #63: context = repo root — Dockerfile копирует общий модуль pkg/
build:
context: ..
dockerfile: services/metadata/Dockerfile
environment:
DATABASE_URL: postgres://flowix:flowix@postgres:5432/flowix?sslmode=disable
DATABASE_POOL_SIZE: ${DATABASE_POOL_SIZE:-5}
Expand Down Expand Up @@ -208,7 +216,11 @@ services:
start_period: 10s

upload:
build: ../services/upload
# issue #63: context = repo root; graceful shutdown ждёт in-flight загрузки
stop_grace_period: 5m
build:
context: ..
dockerfile: services/upload/Dockerfile
environment:
DATABASE_URL: postgres://flowix:flowix@postgres:5432/flowix?sslmode=disable
RABBITMQ_URL: amqp://flowix:flowix@rabbitmq:5672/
Expand All @@ -221,6 +233,8 @@ services:
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required — set it in .env (see .env.example)}
UPLOAD_MAX_BYTES: ${UPLOAD_MAX_BYTES:-5368709120}
MINIO_PUBLIC_ENDPOINT: ${MINIO_PUBLIC_ENDPOINT:-http://localhost:9000}
# grace для Shutdown меньше stop_grace_period (issue #63)
SHUTDOWN_GRACE: 4m
ports:
- "${UPLOAD_PORT:-8003}:8003"
depends_on:
Expand Down Expand Up @@ -275,7 +289,11 @@ services:
start_period: 20s

gateway:
build: ../services/gateway
# issue #63: context = repo root; graceful shutdown ждёт in-flight загрузки
stop_grace_period: 5m
build:
context: ..
dockerfile: services/gateway/Dockerfile
environment:
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required — set it in .env (see .env.example)}
INTERNAL_TOKEN: ${INTERNAL_TOKEN:?INTERNAL_TOKEN is required — set it in .env (see .env.example)}
Expand All @@ -284,6 +302,14 @@ services:
METADATA_URL: http://metadata:8002
UPLOAD_URL: http://upload:8003
VOD_URL: http://nginx-vod:80
# grace для Shutdown меньше stop_grace_period (issue #63)
SHUTDOWN_GRACE: 4m
# issue #52: CORS allowlist, Redis rate-limit. TRUSTED_PROXY_CIDRS пуст —
# gateway является edge (LB перед ним нет), клиентский XFF не доверяем.
# Если появится LB — указать его CIDR в .env.
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:3000}
TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:-}
REDIS_URL: redis://redis:6379/0
ports:
- "${GATEWAY_PORT:-8080}:8080"
depends_on:
Expand All @@ -295,6 +321,8 @@ services:
condition: service_healthy
nginx-vod:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
Expand Down
6 changes: 5 additions & 1 deletion docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ The backend is split into microservices written in **Go** and **Python** to leve

```
.
├── go.work # Go workspace: pkg + три Go-сервиса (issue #63)
├── pkg/ # Shared Go module (flowix/pkg): auth middleware, request logger,
│ # metrics, httputil.WriteJSON, httpserver (таймауты + graceful shutdown)
├── services/
│ ├── gateway/ # Go (API Gateway)
│ ├── metadata/ # Go (Metadata Service)
Expand All @@ -47,14 +50,15 @@ The backend is split into microservices written in **Go** and **Python** to leve
└── README.md
```

Each service has its own `Dockerfile` and can be developed independently.
Each service has its own `Dockerfile` and can be developed independently. Go-сервисы используют общий модуль `pkg/` (через `replace` в go.mod + `go.work` локально; build context в compose — корень репо). Все HTTP-серверы запускаются через `pkg/httpserver` — таймауты Read/Write/Idle и graceful shutdown по SIGTERM/SIGINT (issue #63).

---

## Microservices

### 1. API Gateway (Go)
- **Purpose**: Single entry point for client requests; routes to appropriate services; handles CORS, rate limiting, request aggregation.
- **Security (issue #52)**: CORS — явный allowlist origin (`CORS_ALLOWED_ORIGINS`, без wildcard с credentials); rate-limit — fixed-window в Redis (`REDIS_URL`), fail-open при недоступности Redis; `X-Forwarded-For`/`X-Real-IP` доверяются только от trusted прокси (`TRUSTED_PROXY_CIDRS`), downstream получает проверенный `X-Real-IP` (по нему ключует лимитер auth).
- **Framework**: Gin, Echo, or chi.
- **Communication**: REST or gRPC to internal services.

Expand Down
8 changes: 8 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
go 1.25.0

use (
./pkg
./services/gateway
./services/metadata
./services/upload
)
Loading
Loading