Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
14 changes: 6 additions & 8 deletions apps/api_v2/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
FROM golang:1.22-alpine AS builder
FROM golang:1.25-alpine AS builder

WORKDIR /app

ENV GO111MODULE=off \
GOPATH=/go \
CGO_ENABLED=0
ENV CGO_ENABLED=0

COPY go.mod ./
RUN go mod download

COPY . .

# Fetch runtime dependency in GOPATH mode because this project currently uses relative imports.
RUN go get github.com/gofiber/fiber/v2
RUN go test ./...
RUN go build -o /out/api ./cmd/app

FROM alpine:3.20
Expand All @@ -21,7 +19,7 @@ RUN adduser -D -u 10001 appuser

COPY --from=builder /out/api /usr/local/bin/api

EXPOSE 8080
EXPOSE 4000

USER appuser

Expand Down
2 changes: 1 addition & 1 deletion apps/web/Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ RUN npm install

COPY . .

ARG VITE_API_URL=http://localhost:3000
ARG VITE_API_URL=/api
ENV VITE_API_URL=$VITE_API_URL

# Build the Vite application
Expand Down
2 changes: 1 addition & 1 deletion apps/web/nginx.prod.conf
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ server {
# Reverse proxy to internal API container.
# Browser calls /api/* on the same public origin; nginx forwards inside Docker.
location /api/ {
proxy_pass http://api:3000/;
proxy_pass http://apiv2:4000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/api/client.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';

const baseURL = import.meta.env.VITE_API_URL || 'http://localhost:3000';
const baseURL = import.meta.env.VITE_API_URL || '/api';

const client = axios.create({
baseURL,
Expand Down
199 changes: 47 additions & 152 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,179 +1,74 @@
# PRODUCTION ENVIRONMENT
# Use: docker-compose -f docker-compose.prod.yml up -d
# Use: docker compose -f docker-compose.yml up -d --build
version: "3.9"

services:
api:
apiv2:
build:
context: ./apps/api
context: ./apps/api_v2
dockerfile: Dockerfile.prod
command: node dist/main.js
restart: always
expose:
- "${API_PORT:-3000}"
depends_on:
- postgres
- redis
- "${APIV2_PORT:-4000}"
env_file:
- .env.production
environment:
- NODE_ENV=${NODE_ENV}
- PORT=${PORT}
- CORS_ORIGIN=${CORS_ORIGIN}
- POSTGRES_HOST=${POSTGRES_HOST}
- POSTGRES_PORT=${POSTGRES_PORT}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- REDIS_HOST=${REDIS_HOST}
- REDIS_PORT=${REDIS_PORT}
- REDIS_PASSWORD=${REDIS_PASSWORD}
- GEMINI_API_KEY=${GEMINI_API_KEY}
networks:
- judge_net
healthcheck:
test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:3000/health', (r) => {if (r.statusCode !== 200) process.exit(1)})\""]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: '1'
memory: 1G

web:
build:
context: ./apps/web
dockerfile: Dockerfile.prod
args:
- VITE_API_URL=${VITE_API_URL}
restart: always
ports:
- "${WEB_PORT:-5173}:5173"
- APIV2_PORT=${APIV2_PORT:-4000}
- RABBITMQ_URL=${RABBITMQ_URL:-amqp://guest:guest@rabbitmq:5672/}
depends_on:
rabbitmq:
condition: service_healthy
networks:
- judge_net
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:5173/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M

postgres:
image: postgres:16-alpine
rabbitmq:
image: rabbitmq:3.13-management
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pgdata_prod:/var/lib/postgresql/data
- ./apps/api/migrations:/docker-entrypoint-initdb.d:ro
networks:
- judge_net
expose:
- "5672"
- "15672"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB || exit 1"]
interval: 10s
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 5s
timeout: 5s
retries: 15
start_period: 40s
deploy:
resources:
limits:
cpus: '1'
memory: 1G

redis:
image: redis:7-alpine
container_name: juez_redis_prod
restart: always
volumes:
- redis_data:/data
retries: 20
networks:
- judge_net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 15
start_period: 30s

runner-python:
profiles: ["judge"]
build:
context: ./apps/api/src/runners/python
image: juez_runner_python:prod
restart: "no"

runner-node:
profiles: ["judge"]
worker-python:
build:
context: ./apps/api/src/runners/node
image: juez_runner_node:prod
restart: "no"

runner-cpp:
profiles: ["judge"]
build:
context: ./apps/api/src/runners/cpp
image: juez_runner_cpp:prod
restart: "no"

runner-java:
profiles: ["judge"]
build:
context: ./apps/api/src/runners/java
image: juez_runner_java:prod
restart: "no"

worker:
profiles: ["judge"]
build:
context: ./apps/api
dockerfile: Dockerfile.worker
args:
- NODE_ENV=production
command: npm run worker
context: ./apps/workers/python
dockerfile: Dockerfile
working_dir: /app
command: python main.py
restart: always
env_file:
- .env.production
environment:
- API_BASE_URL=http://apiv2:${APIV2_PORT:-4000}
- RABBITMQ_URL=${RABBITMQ_URL:-amqp://guest:guest@rabbitmq:5672/}
- QUEUE_NAME=${PYTHON_QUEUE:-python.queue}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- submissions_data:/usr/src/app/data
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- runner-python
- runner-node
- runner-cpp
- runner-java
- postgres
- redis
environment:
- NODE_ENV=${NODE_ENV}
- POSTGRES_HOST=${POSTGRES_HOST}
- POSTGRES_PORT=${POSTGRES_PORT}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- REDIS_HOST=${REDIS_HOST}
- REDIS_PORT=${REDIS_PORT}
- REDIS_PASSWORD=${REDIS_PASSWORD}
- HOST_SUBMISSIONS_DIR=${HOST_SUBMISSIONS_DIR}
apiv2:
condition: service_started
rabbitmq:
condition: service_healthy
networks:
- judge_net
deploy:
resources:
limits:
cpus: '2'
memory: 2G

volumes:
pgdata_prod:
redis_data:
submissions_data:

web:
build:
context: ./apps/web
dockerfile: Dockerfile.prod
ports:
- "${WEB_PORT:-5173}:5173"
env_file:
- .env.production
networks:
- judge_net
depends_on:
- apiv2
networks:
judge_net:
driver: bridge
59 changes: 59 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#------------------------| Frontend |----------------------------#

# Vite Configuration
VITE_API_URL=http://localhost:4000
WEB_PORT=5137

# Node Environment
NODE_ENV=development/production


#--------------------------| API V2 |------------------------------#

# API Configuration
APIV2_PORT=4000
APIV2_BASE_URL=http://apiv2:4000

# Roble Configuration - Dev
ROBLE_PROJECT=
ROBLE_BASE_URL=https://roble-api.openlab.uninorte.edu.co

# Test user configuration
INTERNAL_USER_EMAIL=
INTERNAL_USER_PASSWORD=

# Worker Environment Variables
WORKER_KEY=
PYTHON_QUEUE=python.queue
JAVA_QUEUE=java.queue
CPP_QUEUE=cpp.queue

# RabbitMQ Configuration
RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/

# AI API keys
GEMINI_API_KEY=


#------------------------------------------------------------------#
# DEPRECATED #
#------------------------------------------------------------------#

#--------------------------| API V1 |------------------------------#
# PostgreSQL Configuration
POSTGRES_USER=juez_prod
POSTGRES_PASSWORD=
POSTGRES_DB=juez_db_prod
POSTGRES_HOST=postgres
POSTGRES_PORT=5432

# Redis Configuration (optional, leave empty for no password)
REDIS_PASSWORD=
REDIS_HOST=redis
REDIS_PORT=6379

# Worker Configuration
HOST_SUBMISSIONS_DIR=/data/submissions

# API Configuration
API_PORT=4000
Loading