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
94 changes: 94 additions & 0 deletions .env.backend.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Decoded Backend — unified env file for Docker stack (api + ai + meilisearch + redis + searxng)
# Usage: cp .env.backend.example .env.backend.prod (or .env.backend.dev / .env.backend.staging)
#
# Docker Compose overrides internal service URLs automatically (MEILISEARCH_URL, REDIS_HOST, etc.)
# You only need to fill in external service credentials below.

# ─── Shared ───────────────────────────────────────────────
API_SERVER_GRPC_PORT=50053

# ─── API Server (Rust/Axum) ──────────────────────────────
ENV=production
HOST=0.0.0.0
PORT=8000
RUST_LOG=info
LOG_FORMAT=text
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173

# Database (Supabase PostgreSQL)
DATABASE_URL=postgresql://postgres:[PASSWORD]@[PROJECT-REF].supabase.co:5432/postgres
DB_MAX_CONNECTIONS=100
DB_MIN_CONNECTIONS=5
DB_CONNECT_TIMEOUT=30
DB_IDLE_TIMEOUT=600

# Supabase Auth
SUPABASE_URL=https://[PROJECT-REF].supabase.co
SUPABASE_ANON_KEY=eyJhbGc...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGc...
SUPABASE_JWT_SECRET=your-jwt-secret

# Meilisearch
MEILISEARCH_URL=http://localhost:7700
MEILISEARCH_MASTER_KEY=your-master-key

# Cloudflare R2
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
R2_BUCKET_NAME=decoded-images
R2_PUBLIC_URL=https://pub-xxxxx.r2.dev

# Affiliate
RAKUTEN_API_KEY=your-rakuten-key
RAKUTEN_PUBLISHER_ID=your-publisher-id

# AI Queue gRPC (API -> ai-server)
AI_SERVER_GRPC_URL=http://localhost:50052

# Vector Search (OpenAI Embeddings)
OPENAI_API_KEY=sk-...
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
OPENAI_EMBEDDING_DIMENSIONS=256

# ─── AI Server (Python/gRPC) ─────────────────────────────
APP_ENV=prod

JWT_SECRET_KEY=your-jwt-secret-change-me
JWT_ALGORITHM=HS256

LOG_LEVEL=INFO
# Note: LOG_FORMAT is set per-service in docker-compose (api=text, ai=json)

REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password
REDIS_DB=0

QUEUE_BATCH_SIZE=10

API_SERVER_HTTP_URL=http://localhost:8000
API_SERVER_ACCESS_TOKEN=your_backend_token

SELENIUM_URL=http://localhost:4444

API_SERVER_GRPC_HOST=localhost

PERPLEXITY_API_KEY=
PERPLEXITY_API_URL=https://api.perplexity.ai
PERPLEXITY_MODEL=sonar
PERPLEXITY_MAX_RETRIES=3
PERPLEXITY_REQUEST_TIMEOUT=30

SEARXNG_API_URL=http://localhost:4000
SEARXNG_MAX_RETRIES=3
SEARXNG_REQUEST_TIMEOUT=10

TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
TELEGRAM_ENABLED=false

BATCH_SIZE=10
MAX_CONCURRENT_REQUESTS=5
REQUEST_TIMEOUT=30
MAX_RETRIES=3
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ node_modules/
.env
.env.local
.env.*.local
.env.backend.dev
.env.backend.staging
.env.backend.prod

# Next.js
.next/
Expand Down
6 changes: 3 additions & 3 deletions packages/api-server/docker/stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Build / run (모노레포 루트)

1. `packages/api-server/.env.dev` + `packages/ai-server/.dev.env` 준비 (staging/prod는 각각 `.env.staging` / `.staging.env`, `.env.prod` / `.prod.env`).
1. 모노레포 루트에 `.env.backend.dev` 준비 (staging: `.env.backend.staging`, prod: `.env.backend.prod`). 템플릿: `.env.backend.example`.
2. 배포 스크립트:

```bash
Expand Down Expand Up @@ -35,13 +35,13 @@ Compose `environment`로 덮어쓰는 값: `MEILISEARCH_URL`, `AI_SERVER_GRPC_UR
## 수동 compose

```bash
docker compose --env-file packages/api-server/.env.dev \
docker compose --env-file .env.backend.dev \
-f packages/api-server/docker/stack/docker-compose.yml up --build
```

## Meilisearch 키

`deploy-backend.sh`는 API env를 `--env-file`로 넘겨 `${MEILISEARCH_MASTER_KEY}` 보간에 사용합니다. prod는 `packages/api-server/.env.prod`에 키가 있어야 합니다.
`deploy-backend.sh`는 통합 env를 `--env-file`로 넘겨 `${MEILISEARCH_MASTER_KEY}` 보간에 사용합니다. prod는 `.env.backend.prod`에 키가 있어야 합니다.

## 환경 변수 이름

Expand Down
12 changes: 7 additions & 5 deletions packages/api-server/docker/stack/docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Production-style: api + ai + Meilisearch + Redis + SearXNG (Meili/Redis/SearXNG not published to host).
# Env: packages/api-server/.env.prod + packages/ai-server/.prod.env
# Env: .env.backend.prod (repo root, unified)
# From repo root: bash scripts/deploy-backend.sh prod up
# Set MEILISEARCH_MASTER_KEY in packages/api-server/.env.prod (used with deploy-backend.sh --env-file).
# Set MEILISEARCH_MASTER_KEY in .env.backend.prod (used with deploy-backend.sh --env-file).

name: decoded-backend-prod

Expand All @@ -15,10 +15,11 @@ services:
ports:
- "8080:8080"
env_file:
- ../../.env.prod
- ../../../../.env.backend.prod
environment:
PORT: "8080"
HOST: "0.0.0.0"
LOG_FORMAT: text
MEILISEARCH_URL: http://meilisearch:7700
AI_SERVER_GRPC_URL: http://ai:50051
API_SERVER_GRPC_PORT: "50052"
Expand All @@ -44,8 +45,9 @@ services:
expose:
- "50051"
env_file:
- ../../../ai-server/.prod.env
- ../../../../.env.backend.prod
environment:
LOG_FORMAT: json
REDIS_HOST: redis
REDIS_PORT: "6379"
SEARXNG_API_URL: http://searxng:8080
Expand Down Expand Up @@ -95,7 +97,7 @@ services:
networks:
- decoded-backend-prod
env_file:
- ../../../ai-server/.prod.env

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale env file path in Meilisearch error message

Medium Severity

The MEILISEARCH_MASTER_KEY required-variable error message on line 86 still references the old path packages/api-server/.env.prod, but this PR moved the env file to .env.backend.prod at the repo root. The comment at line 4 of the same file was correctly updated to .env.backend.prod, so this is an inconsistent omission. When MEILISEARCH_MASTER_KEY is missing in prod, the error will direct the operator to a file that no longer exists.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6edc54c. Configure here.

- ../../../../.env.backend.prod
command:
- /bin/sh
- -c
Expand Down
12 changes: 7 additions & 5 deletions packages/api-server/docker/stack/docker-compose.staging.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Staging: api + ai + Meilisearch + Redis + SearXNG
# Env: packages/api-server/.env.staging + packages/ai-server/.staging.env
# Env: .env.backend.staging (repo root, unified)
# From repo root: bash scripts/deploy-backend.sh staging up

name: decoded-backend-staging
Expand All @@ -14,10 +14,11 @@ services:
ports:
- "8081:8080"
env_file:
- ../../.env.staging
- ../../../../.env.backend.staging
environment:
PORT: "8080"
HOST: "0.0.0.0"
LOG_FORMAT: text
MEILISEARCH_URL: http://meilisearch:7700
AI_SERVER_GRPC_URL: http://ai:50051
API_SERVER_GRPC_PORT: "50052"
Expand All @@ -43,16 +44,17 @@ services:
expose:
- "50051"
env_file:
- ../../../ai-server/.staging.env
- ../../../../.env.backend.staging
environment:
LOG_FORMAT: json
REDIS_HOST: redis
REDIS_PORT: "6379"
SEARXNG_API_URL: http://searxng:8080
API_SERVER_HTTP_URL: http://api:8080
API_SERVER_GRPC_HOST: api
API_SERVER_GRPC_PORT: "50052"
AI_GRPC_LISTEN_PORT: "50051"
APP_ENV: prod
APP_ENV: staging
ENV: staging
depends_on:
api:
Expand Down Expand Up @@ -98,7 +100,7 @@ services:
networks:
- decoded-backend-staging
env_file:
- ../../../ai-server/.staging.env
- ../../../../.env.backend.staging
command:
- /bin/sh
- -c
Expand Down
12 changes: 7 additions & 5 deletions packages/api-server/docker/stack/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Decoded backend: api + ai + Meilisearch + Redis + SearXNG (multi-container).
# From repo root: bash scripts/deploy-backend.sh dev up --build
# Requires packages/api-server/.env.dev + packages/ai-server/.dev.env
# Requires .env.backend.dev (repo root, unified)

name: decoded-backend

Expand All @@ -14,10 +14,11 @@ services:
ports:
- "8080:8080"
env_file:
- ../../.env.dev
- ../../../../.env.backend.dev
environment:
PORT: "8080"
HOST: "0.0.0.0"
LOG_FORMAT: text

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dev compose missing ENV override for api service

Medium Severity

The dev compose api service environment block doesn't override ENV, unlike staging (ENV: staging) and prod (ENV: production). Previously this was safe because the dev-specific env file had the correct value. Now the unified env file template sets ENV=production, so a dev api container created from a copy of .env.backend.example would run with ENV=production. The Rust config uses ENV to determine behavior like default log format and the reported environment mode.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6edc54c. Configure here.

MEILISEARCH_URL: http://meilisearch:7700
AI_SERVER_GRPC_URL: http://ai:50051
API_SERVER_GRPC_PORT: "50052"
Expand All @@ -44,16 +45,17 @@ services:
expose:
- "50051"
env_file:
- ../../../ai-server/.dev.env
- ../../../../.env.backend.dev
environment:
LOG_FORMAT: json
REDIS_HOST: redis
REDIS_PORT: "6379"
SEARXNG_API_URL: http://searxng:8080
API_SERVER_HTTP_URL: http://api:8080
API_SERVER_GRPC_HOST: api
API_SERVER_GRPC_PORT: "50052"
AI_GRPC_LISTEN_PORT: "50051"
APP_ENV: prod
APP_ENV: dev
depends_on:
api:
condition: service_healthy
Expand Down Expand Up @@ -98,7 +100,7 @@ services:
networks:
- decoded-backend
env_file:
- ../../../ai-server/.dev.env
- ../../../../.env.backend.dev
command:
- /bin/sh
- -c
Expand Down
37 changes: 13 additions & 24 deletions scripts/deploy-backend.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
# 액션: up | down | build | pull | ps | logs | restart | config
# 기본 액션: up -d
#
# 필수 env 파일:
# dev: packages/api-server/.env.dev + packages/ai-server/.dev.env
# staging: packages/api-server/.env.staging + packages/ai-server/.staging.env
# prod: packages/api-server/.env.prod + packages/ai-server/.prod.env
# 필수 env 파일 (모노레포 루트):
# dev: .env.backend.dev
# staging: .env.backend.staging
# prod: .env.backend.prod
#
# Meilisearch: compose의 ${MEILISEARCH_MASTER_KEY}는 API env 파일로 보간됨 (--env-file).
# prod는 .env.prod에 MEILISEARCH_MASTER_KEY 필수(:?).
# Meilisearch: compose의 ${MEILISEARCH_MASTER_KEY}는 env 파일로 보간됨 (--env-file).
# prod는 .env.backend.prod에 MEILISEARCH_MASTER_KEY 필수(:?).

set -euo pipefail

Expand All @@ -44,35 +44,24 @@ EXTRA=("$@")
case "$ENV" in
dev)
COMPOSE="$STACK/docker-compose.yml"
API_ENV="$ROOT/packages/api-server/.env.dev"
AI_ENV="$ROOT/packages/ai-server/.dev.env"
ENV_FILE="$ROOT/.env.backend.dev"
;;
staging)
COMPOSE="$STACK/docker-compose.staging.yml"
API_ENV="$ROOT/packages/api-server/.env.staging"
AI_ENV="$ROOT/packages/ai-server/.staging.env"
ENV_FILE="$ROOT/.env.backend.staging"
;;
prod)
COMPOSE="$STACK/docker-compose.prod.yml"
API_ENV="$ROOT/packages/api-server/.env.prod"
AI_ENV="$ROOT/packages/ai-server/.prod.env"
ENV_FILE="$ROOT/.env.backend.prod"
;;
*)
usage
;;
esac

require_env_files() {
local missing=0
if [[ ! -f "$API_ENV" ]]; then
echo "Missing: $API_ENV (copy from packages/api-server/.env.dev.example or sibling)" >&2
missing=1
fi
if [[ ! -f "$AI_ENV" ]]; then
echo "Missing: $AI_ENV (copy from packages/ai-server/.dev.env.example or sibling)" >&2
missing=1
fi
if [[ "$missing" -ne 0 ]]; then
if [[ ! -f "$ENV_FILE" ]]; then
echo "Missing: $ENV_FILE (copy from .env.backend.example)" >&2
exit 1
fi
}
Expand All @@ -81,8 +70,8 @@ compose() {
# --env-file: ${MEILISEARCH_MASTER_KEY} 등 compose 파일 내 보간용 (컨테이너 전체에 노출되지 않음)
# set -u + 빈 EXTRA[@]는 일부 bash(예: macOS 3.2)에서 실패하므로 배열로 합쳐서 실행
local -a cmd
if [[ -f "$API_ENV" ]]; then
cmd=(docker compose --env-file "$API_ENV" -f "$COMPOSE" "$@")
if [[ -f "$ENV_FILE" ]]; then
cmd=(docker compose --env-file "$ENV_FILE" -f "$COMPOSE" "$@")
else
cmd=(docker compose -f "$COMPOSE" "$@")
fi
Expand Down
Loading