Docker compose con variabili d'ambiente
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
# ==========================================================================
|
||||||
|
# ScommettiApp - variabili d'ambiente per docker-compose (root)
|
||||||
|
#
|
||||||
|
# Copia questo file in ".env" nella stessa cartella e compila i valori.
|
||||||
|
# Il file ".env" reale NON va committato (è già in .gitignore).
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Rete / porte pubblicate sull'host
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# IP su cui pubblicare le porte dei container sull'host della VPS.
|
||||||
|
# Caddy gira in un CT Proxmox separato (non nella stessa rete Docker), quindi
|
||||||
|
# deve raggiungere questi servizi via IP dell'host + porta pubblicata.
|
||||||
|
# Imposta qui l'IP con cui la VPS è raggiungibile dal CT di Caddy (es. IP
|
||||||
|
# della rete interna/bridge Proxmox), NON 0.0.0.0: pubblicare su 0.0.0.0
|
||||||
|
# espone i servizi anche a internet, bypassando Caddy (e occhio: le regole
|
||||||
|
# iptables di Docker bypassano ufw se non configurato apposta - vedi note
|
||||||
|
# nel Caddyfile.example).
|
||||||
|
BIND_IP=10.0.0.10
|
||||||
|
|
||||||
|
BACKEND_PORT=3000
|
||||||
|
FRONTEND_PORT=8081
|
||||||
|
KEYCLOAK_PORT=8080
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Applicazione
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# PostgreSQL (dati app: users, groups, friendships, ...)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
POSTGRES_DB=amici_scommesse
|
||||||
|
POSTGRES_USER=admin
|
||||||
|
POSTGRES_PASSWORD=change-me-strong-password
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# MongoDB (bets)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
MONGO_INITDB_ROOT_USERNAME=root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD=change-me-strong-password
|
||||||
|
MONGO_INITDB_DATABASE=amici-scommesse
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Keycloak
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Dominio pubblico con cui Caddy espone l'app (usato da Keycloak per
|
||||||
|
# costruire URL assoluti corretti dietro il reverse proxy).
|
||||||
|
DOMAIN=scommetti.example.com
|
||||||
|
|
||||||
|
KEYCLOAK_ADMIN=admin
|
||||||
|
KEYCLOAK_ADMIN_PASSWORD=change-me-strong-password
|
||||||
|
|
||||||
|
KEYCLOAK_DB=keycloak
|
||||||
|
KEYCLOAK_DB_USER=keycloak
|
||||||
|
KEYCLOAK_DB_PASSWORD=change-me-strong-password
|
||||||
@@ -19,10 +19,6 @@ module.exports = {
|
|||||||
uri:
|
uri:
|
||||||
process.env.MONGO_URI ||
|
process.env.MONGO_URI ||
|
||||||
'mongodb://localhost:27017/amici-scommesse',
|
'mongodb://localhost:27017/amici-scommesse',
|
||||||
options: {
|
options: {},
|
||||||
user: 'root',
|
|
||||||
pass: 'root@servovio',
|
|
||||||
authSource: 'admin',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class UsersController {
|
|||||||
|
|
||||||
async getCurrentUser(req, res, next) {
|
async getCurrentUser(req, res, next) {
|
||||||
try {
|
try {
|
||||||
const user = await this.usersService.getUserById(req.userId);
|
const user = await this.usersService.getCurrentUserWithRank(req.userId);
|
||||||
return ResponseHandler.success(res, user, 'User retrieved successfully');
|
return ResponseHandler.success(res, user, 'User retrieved successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
|||||||
@@ -32,6 +32,27 @@ class UsersRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async count() {
|
||||||
|
try {
|
||||||
|
return await this.User.count();
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError('Failed to count users', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async countWithScoreAbove(score, excludeId) {
|
||||||
|
try {
|
||||||
|
return await this.User.count({
|
||||||
|
where: {
|
||||||
|
score: { [Op.gt]: score },
|
||||||
|
id: { [Op.ne]: excludeId },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError('Failed to count users above score', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async findById(id, options = {}) {
|
async findById(id, options = {}) {
|
||||||
try {
|
try {
|
||||||
const user = await this.User.findByPk(id, options);
|
const user = await this.User.findByPk(id, options);
|
||||||
|
|||||||
@@ -13,6 +13,17 @@ class UsersService {
|
|||||||
return await this.usersRepository.findById(id);
|
return await this.usersRepository.findById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getCurrentUserWithRank(id) {
|
||||||
|
const user = await this.usersRepository.findById(id);
|
||||||
|
const userJson = user.toJSON ? user.toJSON() : { ...user };
|
||||||
|
const totalUsers = await this.usersRepository.count();
|
||||||
|
const usersAbove = await this.usersRepository.countWithScoreAbove(userJson.score, id);
|
||||||
|
const rankPercentile = totalUsers > 1
|
||||||
|
? Math.max(1, Math.round((usersAbove / totalUsers) * 100))
|
||||||
|
: 1;
|
||||||
|
return { ...userJson, rankPercentile };
|
||||||
|
}
|
||||||
|
|
||||||
async searchUsers(query, excludeUserId) {
|
async searchUsers(query, excludeUserId) {
|
||||||
return await this.usersRepository.search(query, excludeUserId);
|
return await this.usersRepository.search(query, excludeUserId);
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-44
@@ -1,37 +1,45 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
|
# Stack di produzione per la VPS, pensato per stare dietro a Caddy che gira
|
||||||
|
# su un CT Proxmox separato (routing per path sullo stesso dominio: "/" al
|
||||||
|
# frontend, "/api" al backend, "/auth" a Keycloak - vedi Caddyfile.example).
|
||||||
|
#
|
||||||
|
# Le variabili sono lette da un file ".env" nella stessa cartella
|
||||||
|
# (copia .env.example -> .env e compila i valori prima di avviare lo stack).
|
||||||
|
#
|
||||||
|
# Solo backend, frontend e keycloak pubblicano una porta sull'host (quella
|
||||||
|
# che deve raggiungere Caddy): postgres, mongodb e keycloak-db restano
|
||||||
|
# raggiungibili solo dalla rete Docker interna.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# PostgreSQL Database
|
# PostgreSQL Database (dati app)
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
container_name: scommetti-postgres
|
container_name: scommetti-postgres
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: amici_scommesse
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
POSTGRES_USER: admin
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
POSTGRES_PASSWORD: admin
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
- ./backend-node/init.sql:/docker-entrypoint-initdb.d/init.sql
|
- ./backend-node/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
networks:
|
networks:
|
||||||
- scommetti-network
|
- scommetti-network
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U admin -d amici_scommesse"]
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
# MongoDB Database
|
# MongoDB Database (bets)
|
||||||
mongodb:
|
mongodb:
|
||||||
image: mongo:7
|
image: mongo:7
|
||||||
container_name: scommetti-mongodb
|
container_name: scommetti-mongodb
|
||||||
environment:
|
environment:
|
||||||
MONGO_INITDB_ROOT_USERNAME: root
|
MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
|
||||||
MONGO_INITDB_ROOT_PASSWORD: root@servovio
|
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
|
||||||
MONGO_INITDB_DATABASE: amici-scommesse
|
MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE}
|
||||||
ports:
|
|
||||||
- "27017:27017"
|
|
||||||
volumes:
|
volumes:
|
||||||
- mongodb_data:/data/db
|
- mongodb_data:/data/db
|
||||||
networks:
|
networks:
|
||||||
@@ -41,43 +49,53 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
# Keycloak Database
|
# Keycloak Database
|
||||||
keycloak-db:
|
keycloak-db:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
container_name: scommetti-keycloak-db
|
container_name: scommetti-keycloak-db
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: keycloak
|
POSTGRES_DB: ${KEYCLOAK_DB}
|
||||||
POSTGRES_USER: keycloak
|
POSTGRES_USER: ${KEYCLOAK_DB_USER}
|
||||||
POSTGRES_PASSWORD: keycloak
|
POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- keycloak_db_data:/var/lib/postgresql/data
|
- keycloak_db_data:/var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
- scommetti-network
|
- scommetti-network
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
|
test: ["CMD-SHELL", "pg_isready -U ${KEYCLOAK_DB_USER} -d ${KEYCLOAK_DB}"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
# Keycloak Auth Server
|
# Keycloak Auth Server
|
||||||
keycloak:
|
keycloak:
|
||||||
image: quay.io/keycloak/keycloak:23.0.0
|
image: quay.io/keycloak/keycloak:23.0.0
|
||||||
container_name: scommetti-keycloak
|
container_name: scommetti-keycloak
|
||||||
command: start-dev --import-realm
|
command: start --import-realm
|
||||||
environment:
|
environment:
|
||||||
KC_DB: postgres
|
KC_DB: postgres
|
||||||
KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
|
KC_DB_URL: jdbc:postgresql://keycloak-db:5432/${KEYCLOAK_DB}
|
||||||
KC_DB_USERNAME: keycloak
|
KC_DB_USERNAME: ${KEYCLOAK_DB_USER}
|
||||||
KC_DB_PASSWORD: keycloak
|
KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
|
||||||
KEYCLOAK_ADMIN: admin
|
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
|
||||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
|
||||||
KC_HEALTH_ENABLED: "true"
|
KC_HEALTH_ENABLED: "true"
|
||||||
KC_METRICS_ENABLED: "true"
|
KC_METRICS_ENABLED: "true"
|
||||||
KC_HOSTNAME_STRICT: "false"
|
|
||||||
KC_HTTP_ENABLED: "true"
|
KC_HTTP_ENABLED: "true"
|
||||||
|
# Keycloak è raggiunto tramite Caddy su https://${DOMAIN}/auth/*: gli
|
||||||
|
# diciamo di servire tutto sotto /auth e di fidarsi degli header
|
||||||
|
# X-Forwarded-* impostati dal proxy (TLS termina su Caddy, "edge").
|
||||||
|
KC_HTTP_RELATIVE_PATH: /auth
|
||||||
|
KC_HOSTNAME: ${DOMAIN}
|
||||||
|
KC_HOSTNAME_PATH: /auth
|
||||||
|
KC_HOSTNAME_STRICT: "true"
|
||||||
|
KC_HOSTNAME_STRICT_HTTPS: "true"
|
||||||
|
KC_PROXY: edge
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "${BIND_IP}:${KEYCLOAK_PORT}:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./keycloak/realm-export.json:/opt/keycloak/data/import/realm.json:ro
|
- ./keycloak/realm-export.json:/opt/keycloak/data/import/realm.json:ro
|
||||||
- ./keycloak/themes/scommettiapp:/opt/keycloak/themes/scommettiapp:ro
|
- ./keycloak/themes/scommettiapp:/opt/keycloak/themes/scommettiapp:ro
|
||||||
@@ -87,29 +105,30 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- scommetti-network
|
- scommetti-network
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && printf 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && timeout 5 cat <&3 | grep -q 'UP'"]
|
# Con KC_HTTP_RELATIVE_PATH=/auth l'health endpoint si sposta sotto
|
||||||
|
# /auth/health/ready (verificato: /health/ready da solo risponde 404).
|
||||||
|
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && printf 'GET /auth/health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && timeout 5 cat <&3 | grep -q 'UP'"]
|
||||||
interval: 15s
|
interval: 15s
|
||||||
timeout: 15s
|
timeout: 15s
|
||||||
retries: 10
|
retries: 10
|
||||||
start_period: 90s
|
start_period: 90s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
# Backend Node.js
|
# Backend Node.js
|
||||||
backend:
|
backend:
|
||||||
build:
|
build:
|
||||||
context: ./backend-node
|
context: ./backend-node
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
target: production
|
||||||
container_name: scommetti-backend
|
container_name: scommetti-backend
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: development
|
NODE_ENV: ${NODE_ENV}
|
||||||
PORT: 3000
|
PORT: 3000
|
||||||
POSTGRES_URI: postgres://admin:admin@postgres:5432/amici_scommesse
|
POSTGRES_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
|
||||||
MONGO_URI: mongodb://mongodb:27017/amici-scommesse
|
MONGO_URI: mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongodb:27017/${MONGO_INITDB_DATABASE}?authSource=admin
|
||||||
KEYCLOAK_JWKS_URI: http://keycloak:8080/realms/scommettiapp/protocol/openid-connect/certs
|
KEYCLOAK_JWKS_URI: http://keycloak:8080/auth/realms/scommettiapp/protocol/openid-connect/certs
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "${BIND_IP}:${BACKEND_PORT}:3000"
|
||||||
volumes:
|
|
||||||
- ./backend-node:/app
|
|
||||||
- /app/node_modules
|
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -119,23 +138,17 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
networks:
|
networks:
|
||||||
- scommetti-network
|
- scommetti-network
|
||||||
command: npm start
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# Frontend Angular
|
# Frontend Angular (build statica servita da nginx)
|
||||||
frontend:
|
frontend:
|
||||||
build:
|
build:
|
||||||
context: ./frontend-angular-new
|
context: ./frontend-angular
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
target: production
|
||||||
container_name: scommetti-frontend
|
container_name: scommetti-frontend
|
||||||
environment:
|
|
||||||
NODE_ENV: development
|
|
||||||
ports:
|
ports:
|
||||||
- "4200:4200"
|
- "${BIND_IP}:${FRONTEND_PORT}:80"
|
||||||
volumes:
|
|
||||||
- ./frontend-angular-new:/app
|
|
||||||
- /app/node_modules
|
|
||||||
- /app/.angular
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
- keycloak
|
- keycloak
|
||||||
|
|||||||
Reference in New Issue
Block a user