Skip to content
Open
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: 15 additions & 2 deletions docker-compose/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ services:
- ../nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- frontend
- backend
- file-storage
restart: unless-stopped
rabbitmq:
image: rabbitmq:3.13-management
file-storage:
image: seber/maxit-file-storage:latest
ports:
- "8888:8888" # public server (for signed URL access)
- "8080:8080" # internal server (for backend/worker)
environment:
- APP_PORT=8888
- INTERNAL_PORT=8080
- ROOT_DIRECTORY=/data
volumes:
- file-storage-media:/data
backend:
image: maxit/backend:latest
depends_on:
Expand All @@ -36,7 +45,8 @@ services:
- DB_PASSWORD=password
- DB_NAME=maxit
- FILE_STORAGE_HOST=file-storage
- FILE_STORAGE_PORT=8888
- FILE_STORAGE_PORT=8080
- FILE_STORAGE_PUBLIC_URL=https://mini-maxit.pl/files
db:
image: postgres:17
environment:
Expand Down Expand Up @@ -68,13 +78,16 @@ services:
- JOBS_DATA_VOLUME=maxit_jobs-data # watch out for prefix "maxit_" the same as compose name
- RESPONSE_QUEUE_NAME=worker_response_queue
- WORKER_QUEUE_NAME=worker_queue
- STORAGE_HOST=file-storage
- STORAGE_PORT=8080
frontend:
image: maxit/frontend:latest
depends_on:
- backend
environment:
- BACKEND_URL=http://backend:8000/api/v1
- PUBLIC_BACKEND_API_URL=https://mini-maxit.pl/api/v1
- ORIGIN=https://mini-maxit.pl
- BODY_SIZE_LIMIT=20M
expose:
- "3000"
volumes:
Expand Down
38 changes: 37 additions & 1 deletion nginx/conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ http {
application/atom+xml
image/svg+xml;

# Rate limiting for frontend
# Rate limiting
limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;

proxy_buffer_size 32k;
proxy_buffers 8 32k;
Expand Down Expand Up @@ -72,6 +73,41 @@ http {
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Backend API
location /api/ {
limit_req zone=api burst=50 nodelay;

proxy_pass http://backend:8000;
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# File storage - signed URL downloads only, no write access from outside
# Frontend URLs are like: /files/buckets/maxit/task/1/file.pdf?expires=...&signature=...
# We strip the /files prefix so file-storage receives /buckets/... (matching the signed path)
location /files/ {
limit_except GET {
deny all;
}

rewrite ^/files(/.*)$ $1 break;
proxy_pass http://file-storage:8888;
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_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# Frontend serving
location / {
limit_req zone=frontend burst=50 nodelay;
Expand Down
120 changes: 120 additions & 0 deletions nginx/conf/nginx.local.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
events {
worker_connections 1024;
}

http {
client_max_body_size 25m;

include /etc/nginx/mime.types;
default_type application/octet-stream;

# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;

# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;

# Rate limiting
limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;

proxy_buffer_size 32k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
large_client_header_buffers 4 64k;

server {
listen 80;
server_name _;

# Backend API
location /api/ {
limit_req zone=api burst=50 nodelay;

proxy_pass http://backend:8000;
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# File storage - signed URL downloads only, no write access from outside
# Frontend URLs are like: /files/buckets/maxit/task/1/file.pdf?expires=...&signature=...
# We strip the /files prefix so file-storage receives /buckets/... (matching the signed path)
location /files/ {
limit_except GET {
deny all;
}

rewrite ^/files(/.*)$ $1 break;
proxy_pass http://file-storage:8888;
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_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# Frontend serving
location / {
limit_req zone=frontend burst=100 nodelay;

proxy_pass http://frontend:3000;
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# Nginx health check endpoint
location /health {
access_log off;
return 200 "nginx healthy\n";
add_header Content-Type text/plain;
}

# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
}