commit 4a6249dfaeeb93f8861d9282704326511591d6e7 Author: Yuri Lima Date: Tue Jul 29 10:09:46 2025 +0200 first commit diff --git a/.env b/.env new file mode 100755 index 0000000..74c7c83 --- /dev/null +++ b/.env @@ -0,0 +1,33 @@ +# ===== Enviroment Stage ====== +NODE_ENV=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=0SGVvbf42a4g69T +PGADMIN_DEFAULT_PASSWORD=mpXdSohHCKuY9FR +DB_NAME="phoenix" +DB_HOST="phoenixDB" +DB_PORT=5432 +DB_USERNAME="postgres" +PHX_SYSTEM_CONNECTION_POOL_MAX=5 +PHX_WORKER_CONNECTION_POOL_MAX=2 +PGADMIN_DEFAULT_EMAIL="info@phx-erp.de" +MAIL_SERVER="mail.phx-erp.de" +MAIL_PORT=465 +MAIL_USERNAME="yuri.lima@phx-erp.de" +MAIL_PASSWORD="0rB0@et68" +SECURITY_EMAIL_SENDER="'No Reply PHX '" +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD=betarc +# ====== Redis Configuration ====== +REDIS_PASSWORD=dTvShdBatwFtF9P +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ===== Metris Configuration ====== +# Loki API URL -> The IP 5.75.153.161 is the Grafana Server where it has a firewall rule to allow the connection. Please, if you change here, need to be change in NGINX too. +LOKI_URL=http://grafana.phx-erp.de:3100/loki/api/v1/push +LOKI_RETRIES=5 +LOKI_BATCH_SIZE=500 +# ===== HTTPS-PORTAL Configuration ====== +HTTPS_PORTAL_DOMAINS='beta-rc.phx-erp.de -> http://phoenix-app:80' +# ====== PHX-SYSTEM Configuration ====== +PHOENIX_SYSTEM_REPLICAS=1 +PHX_HOST_NAME='beta-rc.phx-erp.de' \ No newline at end of file diff --git a/.env.10600.2024-02-22@14:53:32~ b/.env.10600.2024-02-22@14:53:32~ new file mode 100755 index 0000000..ea9e639 --- /dev/null +++ b/.env.10600.2024-02-22@14:53:32~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD= +PGADMIN_DEFAULT_PASSWORD= +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD= +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.10625.2024-02-22@14:53:32~ b/.env.10625.2024-02-22@14:53:32~ new file mode 100755 index 0000000..b030c73 --- /dev/null +++ b/.env.10625.2024-02-22@14:53:32~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=0SGVvbf42a4g69T +PGADMIN_DEFAULT_PASSWORD= +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD= +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.10650.2024-02-22@14:53:33~ b/.env.10650.2024-02-22@14:53:33~ new file mode 100755 index 0000000..6633926 --- /dev/null +++ b/.env.10650.2024-02-22@14:53:33~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=0SGVvbf42a4g69T +PGADMIN_DEFAULT_PASSWORD=mpXdSohHCKuY9FR +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD= +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.10675.2024-02-22@14:53:33~ b/.env.10675.2024-02-22@14:53:33~ new file mode 100755 index 0000000..6db5090 --- /dev/null +++ b/.env.10675.2024-02-22@14:53:33~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=0SGVvbf42a4g69T +PGADMIN_DEFAULT_PASSWORD=mpXdSohHCKuY9FR +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD=dTvShdBatwFtF9P +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.6892.2024-02-22@13:09:04~ b/.env.6892.2024-02-22@13:09:04~ new file mode 100755 index 0000000..ea9e639 --- /dev/null +++ b/.env.6892.2024-02-22@13:09:04~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD= +PGADMIN_DEFAULT_PASSWORD= +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD= +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.6917.2024-02-22@13:09:04~ b/.env.6917.2024-02-22@13:09:04~ new file mode 100755 index 0000000..9eae0cb --- /dev/null +++ b/.env.6917.2024-02-22@13:09:04~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=LzJO0zfwQkRkh4x +PGADMIN_DEFAULT_PASSWORD= +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD= +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.6942.2024-02-22@13:09:05~ b/.env.6942.2024-02-22@13:09:05~ new file mode 100755 index 0000000..dc689ba --- /dev/null +++ b/.env.6942.2024-02-22@13:09:05~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=LzJO0zfwQkRkh4x +PGADMIN_DEFAULT_PASSWORD=uAMfCnBzGSsEmOL +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD= +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.env.6967.2024-02-22@13:09:05~ b/.env.6967.2024-02-22@13:09:05~ new file mode 100755 index 0000000..9e76ddc --- /dev/null +++ b/.env.6967.2024-02-22@13:09:05~ @@ -0,0 +1,24 @@ +# ===== Enviroment Stage ====== +ENV_MODE=production +# ====== Database Configuration ====== +POSTGRES_PASSWORD=LzJO0zfwQkRkh4x +PGADMIN_DEFAULT_PASSWORD=uAMfCnBzGSsEmOL +# ====== Phoenix Super Admin Configuration ====== +SUPER_ADMIN_USER_PASSWORD= +# ====== Redis Configuration ====== +REDIS_PASSWORD=DQHae9U8WbJEwAi +# ====== Worker Configuration ====== +RUN_JOB_QUEUE=1 +# ====== Email Configuration ====== +SMTP_FROM="info@phx-erp.de" +SMTP_TYPE='smtp' +SMTP_NAME='mail.phx-erp.de' +SMTP_HOST='mail.phx-erp.de' +SMTP_PORT=465 +SMTP_SECURE='true' +SMTP_USER='yuri.lima@phx-erp.de' +SMTP_PASS='0rB0@et68' +SMTP_LOGGING='true' +SMTP_DEBUG='true' +SMTP_TLS_REJECT_UNAUTHORIZED='false' # If true will reject self-signed certificates. It will show: Hostname/IP does not match certificate's altnames: Host: mail.phx-erp.de. is not in the cert's altnames: DNS:*.netcup.net, DNS:netcup.net trace +SMTP_SECURE_CONNECTION='true' # Not in use diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..559ad0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +assets +database +fail2ban +logs +https_portal +pgadmin +redis diff --git a/app_custom/xcustom-style.css b/app_custom/xcustom-style.css new file mode 100755 index 0000000..a70e160 --- /dev/null +++ b/app_custom/xcustom-style.css @@ -0,0 +1,3 @@ +.login-logo-img { + background-image: url("/assets/custom/loginscreen-logo.png")!Important; +} diff --git a/app_custom/xloginscreen-logo.png b/app_custom/xloginscreen-logo.png new file mode 100755 index 0000000..282e9d9 Binary files /dev/null and b/app_custom/xloginscreen-logo.png differ diff --git a/crash_diagnose.sh b/crash_diagnose.sh new file mode 100644 index 0000000..8417ef6 --- /dev/null +++ b/crash_diagnose.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -euo pipefail + +YELLOW='\033[1;33m' +RED='\033[1;31m' +NC='\033[0m' # No color + +echo -e "๐Ÿ“ฆ ${YELLOW}PHX Crash Diagnostic Tool (Docker + Linux Server)${NC}" +echo "๐Ÿ” Boot Timeline:" +journalctl --list-boots | head -3 + +echo -e "\nโš ๏ธ OOM Kills:" +journalctl -b -1 | grep -i 'killed process' || echo "None found." + +echo -e "\nโš ๏ธ Out of Memory Events:" +journalctl -b -1 | grep -i 'out of memory' || echo "None found." + +echo -e "\nโš ๏ธ systemd-oomd Events:" +journalctl -b -1 | grep systemd-oomd || echo "None found." + +echo -e "\n๐Ÿ”ฅ CPU/Load Pressure (dmesg/syslog):" +journalctl -b -1 | grep -Ei 'cpu|load average|soft lockup|hung task' || echo "None found." + +echo -e "\n๐Ÿšจ System Errors (priority 0โ€“3):" +journalctl -b -1 -p 3..0 || echo "None found." + +if command -v docker &> /dev/null && docker info >/dev/null 2>&1; then + echo -e "\n๐Ÿณ Docker detected and running." + + CONTAINERS=$(docker ps -aq) + if [[ -z "$CONTAINERS" ]]; then + echo -e "\nโš ๏ธ No containers found. Skipping container-specific diagnostics." + else + echo -e "\n๐Ÿณ Docker OOM-Killed Containers:" + docker inspect $CONTAINERS 2>/dev/null | grep -B10 '"OOMKilled": true' || echo "No containers were OOMKilled." + + echo -e "\n๐Ÿ” Recently Restarted Containers:" + docker ps -a --format '{{.Names}}\t{{.Status}}' | grep -i 'restarted' || echo "No recent restarts." + + echo -e "\n๐Ÿ“‰ Top 5 Containers by Memory Usage (now):" + docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}" | sort -k2 -hr | head -n 6 + + echo -e "\n๐Ÿ“ˆ Top 5 Containers by CPU Usage (now):" + docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}" | sort -k2 -hr | head -n 6 + + echo -e "\n๐Ÿ“‹ Docker Container Memory Limits:" + docker inspect $CONTAINERS --format '{{ .Name }}: {{ .HostConfig.Memory }} bytes' | grep -v ': 0' || echo "None set" + + echo -e "\n๐Ÿ“‹ Containers With No Memory Limit:" + docker inspect $CONTAINERS --format '{{ .Name }}: {{ .HostConfig.Memory }}' | awk '$2 == 0 {print $1}' + + echo -e "\n๐Ÿ“ Last 100 Log Lines from PHX Containers:" + for name in $(docker ps -a --format '{{.Names}}' | grep -i 'phoenix\|pgadmin\|postgres'); do + echo -e "\n--- Logs for $name ---" + docker logs --tail=100 "$name" 2>/dev/null || echo "No logs for $name" + done + fi +else + echo -e "\n๐Ÿณ ${RED}Docker is not installed or not running.${NC}" +fi + +# Historical CPU/memory usage with 'sar' +if command -v sar &> /dev/null; then + echo -e "\n๐Ÿ“Š Analyzing Memory and CPU Usage via sar (last 60 mins if possible)..." + + echo -e "\n๐Ÿ” Memory Usage (High Usage if >90%):" + sar -r | awk ' + BEGIN { OFS="\t"; print "Time", "%memused", "%commit", "Status" } + /^[0-9]/ { + memused = $4; commit = $8; + status = (memused+0 > 90 || commit+0 > 95) ? "โš ๏ธ HIGH" : "OK"; + printf "%s\t%s%%\t%s%%\t%s\n", $1, memused, commit, status; + }' + + echo -e "\n๐Ÿ” CPU Usage (High if %idle < 10 or %system > 90):" + sar -u | awk ' + BEGIN { OFS="\t"; print "Time", "%user", "%system", "%idle", "Status" } + /^[0-9]/ { + user = $3; sys = $5; idle = $8; + status = (idle+0 < 10 || sys+0 > 90) ? "โš ๏ธ HIGH" : "OK"; + printf "%s\t%s%%\t%s%%\t%s%%\t%s\n", $1, user, sys, idle, status; + }' +else + echo -e "\nโ„น๏ธ 'sar' (sysstat) is not installed. Skipping historical CPU/memory analysis." +fi + +echo -e "\nโœ… ${YELLOW}Done. Use this script after crashes or schedule it in cron for proactive monitoring.${NC}" \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100755 index 0000000..30ab3a2 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,413 @@ +--- +services: + postgres: + restart: always + image: "postgres:15.1-alpine" + container_name: phoenixDB # Hostname + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-postgres,env=prod" + networks: + - backend + environment: + DEBUG: true + POSTGRES_DB: ${DB_NAME} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - "./database:/var/lib/postgresql/data" + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres" ] + interval: 5s # Time between each health check + timeout: 2s # Number of failures before marking as unhealthy + retries: 5 # Grace period before health checks start + pgadmin: + restart: always + image: dpage/pgadmin4 + container_name: pgAdmin4-ui + user: "5050:5050" + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-pgadmin,env=prod" + networks: + - backend + - frontend + environment: + PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL} + PGADMIN_DEFAULT_PASSWORD: ${SUPER_ADMIN_USER_PASSWORD} + PGADMIN_CONFIG_SERVER_MODE: 'True' + PGADMIN_CONFIG_WSGI_SCRIPT_NAME: "'/pgadmin4'" + PGADMIN_CONFIG_PROXY_X_PROTO_COUNT: 1 + PGADMIN_SERVER_JSON_FILE: '/var/lib/pgadmin/servers.json' + PGADMIN_REPLACE_SERVERS_ON_STARTUP: 'True' + PGADMIN_CONFIG_DATA_DIR: "'/var/lib/pgadmin'" + PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False' + + # pgpass dynamic vars + PGPASSFILE: /var/lib/pgadmin/pgpass + PGPASS_HOST: ${DB_HOST} + PGPASS_PORT: ${DB_PORT} + PGPASS_DB: ${DB_NAME} + PGPASS_USER: ${DB_USERNAME} + PGPASS_PASSWORD: ${POSTGRES_PASSWORD} + + # Other config + ALLOW_SAVE_PASSWORD: 'False' + MFA_ENABLED: 'True' + MFA_FORCE_REGISTRATION: 'False' + MFA_SUPPORTED_METHODS: 'email' + MFA_EMAIL_SUBJECT: 'Your MFA code by PHX-ERP' + MAX_LOGIN_ATTEMPTS: 5 + ENHANCED_COOKIE_PROTECTION: 'True' + SHOW_GRAVATAR_IMAGE: 'True' + SECURITY_EMAIL_SENDER: ${SECURITY_EMAIL_SENDER} + MAIL_SERVER: ${MAIL_SERVER} + MAIL_PORT: ${MAIL_PORT} + MAIL_USE_SSL: 'False' + MAIL_USE_TLS: 'False' + MAIL_USERNAME: ${MAIL_USERNAME} + MAIL_PASSWORD: ${MAIL_PASSWORD} + MAIL_DEBUG: 'False' + volumes: + - ./pgadmin/data:/var/lib/pgadmin + - ./pgadmin/pgadmin-entrypoint.sh:/docker-entrypoint.sh:ro + entrypoint: ["/bin/sh", "/docker-entrypoint.sh"] + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-O", "-", "http://localhost:80/misc/ping"] + interval: 15s + timeout: 10s + retries: 5 + start_period: 60s + phoenix-app: + restart: always + image: "phxerp/phoenix-app:beta" + container_name: phoenix-app + ports: + - "3000:3000" # Restrict to only allow access from Grafana Server IP + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-app,env=prod,project=phoenix" + volumes: + - ./app_custom:/usr/share/nginx/html/assets/custom + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - ./nginx/includes:/etc/nginx/includes:ro + networks: + - backend + - frontend + depends_on: + pgadmin: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://phoenix-app/login"] # localhost checks that the NGINX server inside the container is serving something at the root + interval: 10s # check every 10 seconds + timeout: 5s # allow 5 seconds per check + retries: 5 # mark as unhealthy after 5 failures + start_period: 15s # wait 15 seconds after container start before checking + phoenix-system: + restart: always + image: "phxerp/phoenix-system:beta" + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phoenix-system,env=prod" + environment: + - "DB_HOST=phoenixDB" + - "DB_NAME=${DB_NAME}" + - "DB_PASSWORD=${POSTGRES_PASSWORD}" + - "DB_USERNAME=postgres" + - "SUPER_ADMIN_USER_PASSWORD=${SUPER_ADMIN_USER_PASSWORD}" + - "REDIS_PASSWORD=${REDIS_PASSWORD}" + - RUN_JOB_QUEUE=${RUN_JOB_QUEUE} + - NODE_ENV=${NODE_ENV} + - PHX_HOST_NAME=${PHX_HOST_NAME} + command: ["npm", "run", "start:server"] + deploy: + replicas: ${PHOENIX_SYSTEM_REPLICAS} #change here if u want to have more replicas. Cant find a way to set via variable right now + networks: + backend: + aliases: + - phoenix-system + depends_on: + postgres: + condition: service_healthy + phoenix-redis: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -s http://phoenix-system:3000/health | grep -q '\"admin-api\":{\"status\":\"up\"}' && curl -s http://phoenix-system:3000/health | grep -q '\"database\":{\"status\":\"up\"}'"] # Checks both admin-api and database status + interval: 10s # Time between each health check + timeout: 6s # Max time to wait for each check + retries: 10 # Number of failures before marking as unhealthy + start_period: 40s # Grace period before health checks start + volumes: + - "./assets:/usr/src/app/packages/dev-server/assets" + - "./server_custom:/usr/src/app/packages/dev-server/custom" + # - "./logs:/usr/src/app/packages/dev-server/logs" + phoenix-worker: + restart: always + image: "phxerp/phoenix-system:beta" + container_name: "phoenix-worker" + ports: + - "3001:3001" # Restrict to only allow access from Grafana Server IP + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-worker,env=prod" + networks: + - backend + environment: + - DB_HOST=phoenixDB + - "DB_NAME=${DB_NAME}" + - "DB_PASSWORD=${POSTGRES_PASSWORD}" + - DB_USERNAME=postgres + - "SUPER_ADMIN_USER_PASSWORD=${SUPER_ADMIN_USER_PASSWORD}" + - REDIS_PASSWORD=${REDIS_PASSWORD} + - NODE_ENV=${NODE_ENV} + - PHX_HOST_NAME=${PHX_HOST_NAME} + command: ['npm', 'run', 'start:worker'] + depends_on: + phoenix-system: + condition: service_healthy + postgres: + condition: service_healthy + healthcheck: + test: [ "CMD-SHELL", "curl -s http://phoenix-worker:3001/health | grep -q '\"status\":\"ok\"'" ] # Check if worker responds with status ok + interval: 10s # Time between each health check + timeout: 6s # Max time to wait for each check + retries: 20 # Grace period before health checks start + start_period: 30s # Grace period before health checks start + volumes: + - "./assets:/usr/src/app/packages/dev-server/assets" + - "./server_custom:/usr/src/app/packages/dev-server/custom" + # - "./logs:/usr/src/app/packages/dev-server/logs" + phoenix-redis: + image: 'bitnami/redis:latest' + container_name: redis + command: /opt/bitnami/scripts/redis/run.sh --maxmemory 100mb + user: root + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-redis,env=prod" + networks: + - backend + restart: always + environment: + ALLOW_EMPTY_PASSWORD: "no" + REDIS_PASSWORD: ${REDIS_PASSWORD} + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + interval: 5s + retries: 10 # Increase retries if Redis takes a while to start + timeout: 5s # Increase timeout if needed + depends_on: + postgres: + condition: service_healthy + volumes: + - "./redis/data:/bitnami/redis/data" + phoenix-health-exporter: + image: phxerp/phoenix-health-exporter:alpha + container_name: health_exporter + restart: unless-stopped + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-health-exporter,env=prod" + ports: + - "9800:9800" + environment: + DB_HOST: ${DB_HOST} + DB_NAME: ${DB_NAME} + DB_PASSWORD: ${POSTGRES_PASSWORD} + DB_USERNAME: ${DB_USERNAME} + networks: + - frontend + - backend + volumes: + - /etc/hostname:/etc/host_hostname:ro # This ensures the container always uses the real machine hostname, even if restarted or recreated. + security_opt: + - no-new-privileges:true + deploy: + resources: + limits: + cpus: '0.25' + memory: 128M + depends_on: + phoenix-system: + condition: service_healthy + phoenix-worker: + condition: service_healthy + postgres: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:9800/healthz || exit 1"] + interval: 1m + timeout: 5s + retries: 3 + start_period: 15s + node-exporter: + image: quay.io/prometheus/node-exporter:latest + container_name: node_exporter + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-node-exporter,env=prod" + networks: + - metrics + - frontend + restart: unless-stopped + ports: + - "9100:9100" # Restrict to only allow access from Grafana Server IP + command: + - "--path.procfs=/host/proc" + - "--path.sysfs=/host/sys" + - "--path.rootfs=/host" + - "--collector.filesystem.ignored-mount-points=^/(sys|proc|dev)($$|/)" + volumes: + - "/proc:/host/proc:ro" + - "/sys:/host/sys:ro" + - "/:/host:ro,rslave" + security_opt: + - no-new-privileges:true + deploy: + resources: + limits: + cpus: '0.25' + memory: 128M + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:9100/metrics"] + interval: 15s + timeout: 5s + retries: 3 + start_period: 20s + # nginx-exporter: + # image: nginx/nginx-prometheus-exporter:1.4.2 + # container_name: nginx_exporter + # restart: unless-stopped + # # logging: + # # driver: loki + # # options: + # # loki-url: "${LOKI_URL}" + # # loki-retries: "${LOKI_RETRIES}" + # # loki-batch-size: "${LOKI_BATCH_SIZE}" + # # loki-external-labels: "service=phx-nginx-exporter,env=prod" + # ports: + # - "9113:9113" # Restrict to only allow access from Grafana Server IP + # command: + # - '--nginx.scrape-uri=http://phoenix-app/stub_status' + # security_opt: + # - no-new-privileges:true + # deploy: + # resources: + # limits: + # cpus: '0.25' + # memory: 128M + # depends_on: + # phoenix-app: + # condition: service_healthy + # networks: + # - frontend + # - metrics + # healthcheck: + # test: ["CMD", "wget", "-qO-", "http://localhost:9113/metrics"] # Not working as expected + # interval: 15s + # timeout: 5s + # retries: 3 + # start_period: 10s + https_portal: + container_name: https_portal + image: "steveltn/https-portal:1.21" + restart: unless-stopped + # logging: + # driver: loki + # options: + # loki-url: "${LOKI_URL}" + # loki-retries: "${LOKI_RETRIES}" + # loki-batch-size: "${LOKI_BATCH_SIZE}" + # loki-external-labels: "service=phx-https-portal,env=prod" + networks: + - frontend # [ PgAdmin, Phoenix-App ] + ports: + - "80:80" + - "443:443" + # - host:container + environment: + STAGE: "production" # Use Let's Encrypt production server + WEBSOCKET: "true" # Enable websocket support + DEBUG: "true" + RENEW_MARGIN_DAYS: 30 + CLIENT_MAX_BODY_SIZE: 0 + SERVER_NAMES_HASH_BUCKET_SIZE: 128 # Increase hash bucket size for server names - good for bigger domains names, if not set correctly, it will throw an error, break the container. + # FORCE_RENEW: 'true' + DOMAINS: "${HTTPS_PORTAL_DOMAINS}" + volumes: + - ./https_portal/data:/var/lib/https-portal # ssl_certs, vhost.d, htdocs + - ./https_portal/log:/var/log/nginx # nginx logs + # - ./https_portal/config/custom_nginx.conf:/opt/custom_nginx.conf:ro # โœ… Mount file in a safe path + depends_on: + pgadmin: + condition: service_healthy + postgres: + condition: service_healthy + fail2ban: + image: crazymax/fail2ban:latest + container_name: fail2ban + network_mode: 'host' + cap_add: + - NET_ADMIN + - NET_RAW + volumes: + - ./fail2ban/data:/data + - ./fail2ban/jail.d:/etc/fail2ban/jail.d + - /var/log:/var/log:ro + restart: always + +networks: + backend: + driver: bridge + external: false + ipam: + config: + - subnet: 172.19.0.0/16 + + frontend: + driver: bridge + external: false + ipam: + config: + - subnet: 172.20.0.0/16 + + metrics: + driver: bridge + external: false + ipam: + config: + - subnet: 172.22.0.0/16 \ No newline at end of file diff --git a/docker-compose.yaml.10700.2024-02-22@14:53:34~ b/docker-compose.yaml.10700.2024-02-22@14:53:34~ new file mode 100755 index 0000000..194e6e7 --- /dev/null +++ b/docker-compose.yaml.10700.2024-02-22@14:53:34~ @@ -0,0 +1,162 @@ +version: "2.1" +services: + postgres: + restart: always + image: "postgres:15.1-alpine" + container_name: phoenixDB + networks: + - backend + ports: + - "5432:5432" + environment: + - DEBUG=false + - POSTGRES_DB=phoenix + - "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" + volumes: + - "./database:/var/lib/postgresql/data" + healthcheck: + test: + - CMD-SHELL + - pg_isready -U postgres + interval: 5s + timeout: 2s + retries: 5 + pgadmin: + image: dpage/pgadmin4 + container_name: pgadmin_container + networks: + - backend + - frontend + environment: + PGADMIN_DEFAULT_EMAIL: "pgadmin4@pgadmin.org" + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD} + PGADMIN_CONFIG_SERVER_MODE: 'False' + ports: + - "${PGADMIN_PORT:-5050}:80" + volumes: + - "pgadmin:/var/lib/pgadmin" + phoenix-app: + restart: always + image: "dennx/phoenix-app:beta-rc" + container_name: phoenixApp + volumes: + - "./app_custom:/usr/share/nginx/html/assets/custom" + networks: + - backend + - frontend + depends_on: + - phoenix-system + phoenix-system: + restart: always + image: "dennx/phoenix-system:beta-rc" + environment: + - "DB_HOST=phoenixDB" + - "DB_PASSWORD=${POSTGRES_PASSWORD}" + - "DB_USERNAME=postgres" + - "SUPER_ADMIN_USER_PASSWORD=${SUPER_ADMIN_USER_PASSWORD}" + - "REDIS_PASSWORD=${REDIS_PASSWORD}" + - RUN_JOB_QUEUE=${RUN_JOB_QUEUE} + - SMTP_FROM=${SMTP_FROM} + - SMTP_TYPE=${SMTP_TYPE} + - SMTP_NAME=${SMTP_NAME} + - SMTP_HOST=${SMTP_HOST} + - SMTP_PORT=${SMTP_PORT} + - SMTP_SECURE=${SMTP_SECURE} + - SMTP_USER=${SMTP_USER} + - SMTP_PASS=${SMTP_PASS} + - SMTP_LOGGING=${SMTP_LOGGING} + - SMTP_DEBUG=${SMTP_DEBUG} + - SMTP_TLS_REJECT_UNAUTHORIZED=${SMTP_TLS_REJECT_UNAUTHORIZED} + - SMTP_SECURE_CONNECTION=${SMTP_SECURE_CONNECTION} + - ENV_MODE=${ENV_MODE} + command: ["npm", "run", "start:server"] + deploy: + replicas: 1 #change here if u want to have more replicas. Cant find a way to set via variable right now + networks: + - backend + depends_on: + postgres: + condition: service_healthy + phoenix-redis: + condition: service_healthy + volumes: + - "./assets:/usr/src/app/packages/dev-server/assets" + - "./server_custom:/usr/src/app/packages/dev-server/custom" + phoenix-worker: + restart: always + image: "dennx/phoenix-system:beta-rc" + networks: + - backend + environment: + - DB_HOST=phoenixDB + - "DB_PASSWORD=${POSTGRES_PASSWORD}" + - DB_USERNAME=postgres + - "SUPER_ADMIN_USER_PASSWORD=${SUPER_ADMIN_USER_PASSWORD}" + - REDIS_PASSWORD=${REDIS_PASSWORD} + command: ["npm", "run", "start:worker"] + depends_on: + postgres: + condition: service_healthy + volumes: + - "./assets:/usr/src/app/packages/dev-server/assets" + - "./server_custom:/usr/src/app/packages/dev-server/custom" + phoenix-redis: + image: 'bitnami/redis:latest' + container_name: redis + command: /opt/bitnami/scripts/redis/run.sh --maxmemory 100mb + user: root + networks: + - backend + restart: always + environment: + ALLOW_EMPTY_PASSWORD: "no" + REDIS_PASSWORD: ${REDIS_PASSWORD} + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + volumes: + - "./redis/data:/bitnami/redis/data" + https_portal: + container_name: https_portal + image: "steveltn/https-portal:1.21" + restart: unless-stopped + user: "root" + networks: + - frontend # [ PgAdmin, Phoenix-App ] + - external # [ Outside of the World] + ports: + - "80:80" + - "443:443" + # - host:container + environment: + STAGE: "production" # Use Let's Encrypt production server + WEBSOCKET: "true" # Enable websocket support + DEBUG: "true" + RENEW_MARGIN_DAYS: 30 + CLIENT_MAX_BODY_SIZE: 0 + # FORCE_RENEW: 'true' + DOMAINS: + volumes: + - ./https_portal/data:/var/lib/https-portal # ssl_certs, vhost.d, htdocs + - ./https_portal/log:/var/log/nginx # nginx logs + depends_on: + - phoenix-app + - phoenix-system + - pgadmin + - phoenix-redis + - postgres + +networks: + backend: + driver: bridge + external: false + + frontend: + driver: bridge + external: false + + external: + driver: bridge + external: true + +volumes: + pgadmin: null diff --git a/docker-compose.yaml.6992.2024-02-22@13:09:06~ b/docker-compose.yaml.6992.2024-02-22@13:09:06~ new file mode 100755 index 0000000..194e6e7 --- /dev/null +++ b/docker-compose.yaml.6992.2024-02-22@13:09:06~ @@ -0,0 +1,162 @@ +version: "2.1" +services: + postgres: + restart: always + image: "postgres:15.1-alpine" + container_name: phoenixDB + networks: + - backend + ports: + - "5432:5432" + environment: + - DEBUG=false + - POSTGRES_DB=phoenix + - "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" + volumes: + - "./database:/var/lib/postgresql/data" + healthcheck: + test: + - CMD-SHELL + - pg_isready -U postgres + interval: 5s + timeout: 2s + retries: 5 + pgadmin: + image: dpage/pgadmin4 + container_name: pgadmin_container + networks: + - backend + - frontend + environment: + PGADMIN_DEFAULT_EMAIL: "pgadmin4@pgadmin.org" + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD} + PGADMIN_CONFIG_SERVER_MODE: 'False' + ports: + - "${PGADMIN_PORT:-5050}:80" + volumes: + - "pgadmin:/var/lib/pgadmin" + phoenix-app: + restart: always + image: "dennx/phoenix-app:beta-rc" + container_name: phoenixApp + volumes: + - "./app_custom:/usr/share/nginx/html/assets/custom" + networks: + - backend + - frontend + depends_on: + - phoenix-system + phoenix-system: + restart: always + image: "dennx/phoenix-system:beta-rc" + environment: + - "DB_HOST=phoenixDB" + - "DB_PASSWORD=${POSTGRES_PASSWORD}" + - "DB_USERNAME=postgres" + - "SUPER_ADMIN_USER_PASSWORD=${SUPER_ADMIN_USER_PASSWORD}" + - "REDIS_PASSWORD=${REDIS_PASSWORD}" + - RUN_JOB_QUEUE=${RUN_JOB_QUEUE} + - SMTP_FROM=${SMTP_FROM} + - SMTP_TYPE=${SMTP_TYPE} + - SMTP_NAME=${SMTP_NAME} + - SMTP_HOST=${SMTP_HOST} + - SMTP_PORT=${SMTP_PORT} + - SMTP_SECURE=${SMTP_SECURE} + - SMTP_USER=${SMTP_USER} + - SMTP_PASS=${SMTP_PASS} + - SMTP_LOGGING=${SMTP_LOGGING} + - SMTP_DEBUG=${SMTP_DEBUG} + - SMTP_TLS_REJECT_UNAUTHORIZED=${SMTP_TLS_REJECT_UNAUTHORIZED} + - SMTP_SECURE_CONNECTION=${SMTP_SECURE_CONNECTION} + - ENV_MODE=${ENV_MODE} + command: ["npm", "run", "start:server"] + deploy: + replicas: 1 #change here if u want to have more replicas. Cant find a way to set via variable right now + networks: + - backend + depends_on: + postgres: + condition: service_healthy + phoenix-redis: + condition: service_healthy + volumes: + - "./assets:/usr/src/app/packages/dev-server/assets" + - "./server_custom:/usr/src/app/packages/dev-server/custom" + phoenix-worker: + restart: always + image: "dennx/phoenix-system:beta-rc" + networks: + - backend + environment: + - DB_HOST=phoenixDB + - "DB_PASSWORD=${POSTGRES_PASSWORD}" + - DB_USERNAME=postgres + - "SUPER_ADMIN_USER_PASSWORD=${SUPER_ADMIN_USER_PASSWORD}" + - REDIS_PASSWORD=${REDIS_PASSWORD} + command: ["npm", "run", "start:worker"] + depends_on: + postgres: + condition: service_healthy + volumes: + - "./assets:/usr/src/app/packages/dev-server/assets" + - "./server_custom:/usr/src/app/packages/dev-server/custom" + phoenix-redis: + image: 'bitnami/redis:latest' + container_name: redis + command: /opt/bitnami/scripts/redis/run.sh --maxmemory 100mb + user: root + networks: + - backend + restart: always + environment: + ALLOW_EMPTY_PASSWORD: "no" + REDIS_PASSWORD: ${REDIS_PASSWORD} + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + volumes: + - "./redis/data:/bitnami/redis/data" + https_portal: + container_name: https_portal + image: "steveltn/https-portal:1.21" + restart: unless-stopped + user: "root" + networks: + - frontend # [ PgAdmin, Phoenix-App ] + - external # [ Outside of the World] + ports: + - "80:80" + - "443:443" + # - host:container + environment: + STAGE: "production" # Use Let's Encrypt production server + WEBSOCKET: "true" # Enable websocket support + DEBUG: "true" + RENEW_MARGIN_DAYS: 30 + CLIENT_MAX_BODY_SIZE: 0 + # FORCE_RENEW: 'true' + DOMAINS: + volumes: + - ./https_portal/data:/var/lib/https-portal # ssl_certs, vhost.d, htdocs + - ./https_portal/log:/var/log/nginx # nginx logs + depends_on: + - phoenix-app + - phoenix-system + - pgadmin + - phoenix-redis + - postgres + +networks: + backend: + driver: bridge + external: false + + frontend: + driver: bridge + external: false + + external: + driver: bridge + external: true + +volumes: + pgadmin: null diff --git a/nginx/includes/pgadmin.conf b/nginx/includes/pgadmin.conf new file mode 100755 index 0000000..9a73011 --- /dev/null +++ b/nginx/includes/pgadmin.conf @@ -0,0 +1,18 @@ +# pgAdmin reverse proxy (under subpath) +location /pgadmin4 { + proxy_pass http://pgadmin4-ui/; + proxy_set_header X-Script-Name /pgadmin4; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + proxy_redirect off; + + # โš ๏ธ Rewrite required to remove /pgadmin4 from the path + rewrite ^/pgadmin4(/.*)$ $1 break; +} \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100755 index 0000000..d2b45a6 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,402 @@ +# Main process configuration +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + geo $frontend_whitelist { + default 1; + 127.0.0.1 1; + 172.20.0.0/16 1; # Frontend Docker subnet + 5.75.153.161 1; # Grafana or monitoring + 167.235.254.4 1; # Ansible server IP + } + + geo $backend_whitelist { + default 1; + 127.0.0.1 1; + 172.19.0.0/16 1; # Backend Docker subnet + 5.75.153.161 1; # Grafana or monitoring + 167.235.254.4 1; # Ansible server IP + } + + # These settings ensure that $remote_addr reflects the real client IP forwarded by https-portal, which is needed for your allow rules to work correctly + # Recommended for resolving client IP behind proxy + # Docker networks where both frontend and backend containers communicate through NGINX. + # To avoid potential misclassification of real client IPs from backend routes. + # The set_real_ip_from directive doesnโ€™t allow access โ€” it just instructs NGINX to trust the X-Forwarded-For header from those IPs. + set_real_ip_from 172.20.0.0/16; # Replace with your Docker network subnet (matches your `frontend` network) + set_real_ip_from 172.19.0.0/16; # Replace with your Docker network subnet (matches your `backend` network) + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + resolver 127.0.0.11 valid=10s; + resolver_timeout 5s; + + upstream phoenix_system_cluster { + zone phoenix_system_cluster 64k; + least_conn; + server phoenix-system:3000 resolve fail_timeout=1s max_fails=0; + # ADD_SYSTEM_SERVERS_HERE + } + + upstream phoenix_worker_cluster { + zone phoenix_worker_cluster 64k; + least_conn; + server phoenix-worker:3001 resolve fail_timeout=1s max_fails=0; + # ADD_WORKER_SERVERS_HERE + } + + server_tokens off; # Disable NGINX version tokens to avoid leaking NGINX version. + + # File handling & upload limits + sendfile on; + client_max_body_size 64m; + + # Prevent warning when setting many proxy headers, like we do + proxy_headers_hash_max_size 1024; + proxy_headers_hash_bucket_size 128; + + # Gzip compression (for better bandwidth efficiency) + gzip on; + gzip_min_length 1000; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + # Trust the protocol from upstream proxy/load balancer + map $http_x_forwarded_proto $forwarded_proto { + default $scheme; + https https; + http http; + } + + # File types and default mime type + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # ๐Ÿงฉ Logs + map $request_uri $loggable { + default 1; + ~^/stub_status 0; + ~^/health/system 0; + ~^/health/worker 0; + } + + log_format main_with_realip '$remote_addr - $realip_remote_addr [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent"'; + + log_format json_compatible escape=json '{' + '"time":"$time_iso8601",' + '"remote_addr":"$remote_addr",' + '"proxy_addr":"$proxy_protocol_addr",' + '"x_forwarded_for":"$http_x_forwarded_for",' + '"request_method":"$request_method",' + '"request_uri":"$request_uri",' + '"status":$status,' + '"body_bytes_sent":$body_bytes_sent,' + '"request_time":$request_time,' + '"upstream_response_time":"$upstream_response_time",' + '"http_referer":"$http_referer",' + '"http_user_agent":"$http_user_agent",' + '"host":"$host",' + '"realip":"$realip_remote_addr"' + '}'; + + access_log /var/log/nginx/access_json.log json_compatible if=$loggable; # JSON format for Loki + access_log /var/log/nginx/access.log main_with_realip if=$loggable; + # End of logs + + ################################################################## + # ๐Ÿงฉ HTTP Server Block + ################################################################## + server { + listen 80; + server_name _; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + root /usr/share/nginx/html; + index index.html index.htm; + + # Frontend SPA fallback + location / { + try_files $uri $uri/ /index.html; + } + + # Backend API routes + location /backend-api/ { + proxy_pass http://phoenix_system_cluster/; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + # Increase timeout settings for file uploads + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 600; + } + + location /admin-api { + proxy_pass http://phoenix_system_cluster/admin-api; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + location /remote-assets { + proxy_pass http://phoenix_system_cluster/remote-assets; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + location /sti { + proxy_pass http://phoenix_system_cluster/sti; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + # WebSocket support + location /ws { + proxy_pass http://phoenix_system_cluster/graphql; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + # Reverse proxy for pgAdmin (subpath support) + include /etc/nginx/includes/*.conf; + + # Health check endpoints -> used by the health check exporter + location /health/system { + proxy_pass http://phoenix_system_cluster/health; + # Secure the health check endpoint + if ($backend_whitelist = 0) { + return 403; + } + # End of security + # Include headers for proxying + 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 $forwarded_proto; + # End of headers + } + + # location /health/system/metrics { + # proxy_pass http://phoenix_system_cluster/health/metrics; + # # Secure the health check endpoint + # # if ($backend_whitelist = 0) { + # # return 403; + # # } + # # End of security + # # Include headers for proxying + # 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 $forwarded_proto; + # # End of headers + # } + + location /health/worker { + proxy_pass http://phoenix_worker_cluster/health; + # Secure the health check endpoint + if ($backend_whitelist = 0) { + return 403; + } + # End of security + # Include headers for proxying + 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 $forwarded_proto; + # End of headers + } + + # location /health/worker/metrics { + # proxy_pass http://phoenix_worker_cluster/health/metrics; + # # Secure the health check endpoint + # # if ($backend_whitelist = 0) { + # # return 403; + # # } + # # End of security + # # Include headers for proxying + # 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 $forwarded_proto; + # # End of headers + # } + + location /stub_status { + stub_status; + # Secure the stub status endpoint + if ($frontend_whitelist = 0) { + return 403; + } + # End of security + } + } + + ################################################################## + # ๐Ÿ” HTTPS Server Block + ################################################################## + server { + listen 443 ssl; + http2 on; + server_name _; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + ssl_certificate /etc/nginx/external-certificate/certificate.crt; + ssl_certificate_key /etc/nginx/external-certificate/certificate.key; + + root /usr/share/nginx/html; + index index.html index.htm; + + location / { + try_files $uri $uri/ /index.html; + } + + # Secure API routes + location /backend-api/ { + proxy_pass http://phoenix_system_cluster/; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + # Increase timeout settings for file uploads + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 600; + } + + location /admin-api { + proxy_pass http://phoenix_system_cluster/admin-api; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + location /remote-assets { + proxy_pass http://phoenix_system_cluster/remote-assets; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + location /sti { + proxy_pass http://phoenix_system_cluster/sti; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + location /ws { + proxy_pass http://phoenix_system_cluster/graphql; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + # Include headers for proxying + 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-Host $host; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + # End of headers + } + + # Reverse proxy for pgAdmin (subpath support) + include /etc/nginx/includes/*.conf; + + location /health/system { + proxy_pass http://phoenix_system_cluster/health; + # Secure the health check endpoint + if ($backend_whitelist = 0) { + return 403; + } + # End of security + # Include headers for proxying + 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 $forwarded_proto; + # End of headers + } + + location /health/worker { + proxy_pass http://phoenix_worker_cluster/health; + # Secure the health check endpoint + if ($backend_whitelist = 0) { + return 403; + } + # End of security + # Include headers for proxying + 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 $forwarded_proto; + # End of headers + } + + location /stub_status { + stub_status; + # Secure the stub status endpoint + if ($frontend_whitelist = 0) { + return 403; + } + # End of security + } + } +} \ No newline at end of file diff --git a/server_custom/config.ts b/server_custom/config.ts new file mode 100755 index 0000000..0210844 --- /dev/null +++ b/server_custom/config.ts @@ -0,0 +1,253 @@ +/* tslint:disable:no-console */ +import { AssetServerPlugin } from '@phoenix/asset-server-plugin'; +import { ADMIN_API_PATH, API_PORT, SHOP_API_PATH, SUPER_ADMIN_USER_IDENTIFIER } from '@phoenix/common'; +import { DefaultJobQueuePlugin, WinstonLogger, LogLevel, RedisSessionCachePlugin, SystemConfig, TypeOrmLogger } from '@phoenix/core'; +import { EmailPlugin, EmailPluginOptions, FileBasedTemplateLoader, defaultEmailHandlers } from '@phoenix/email-plugin'; +import path from 'path'; +import { ConnectionOptions } from 'typeorm'; +// Import EmailSettingsService + +//DEV for now +// import { BonnEmailEventHandler } from './plugins/bonn-api-plugin/handler/bonn-email-handler'; + +/** + * Config settings used during development + */ +export const devConfig: SystemConfig = { + apiOptions: { + port: API_PORT, + // sslPort: API_SSL_PORT, + //sslCertPath: path.join(__dirname, './secrets/certificate.crt'), + //sslKeyPath: path.join(__dirname, './secrets/certificate.key'), + adminApiPath: ADMIN_API_PATH, + shopApiPath: SHOP_API_PATH, + cors: { + origin: true, + credentials: true, + }, + adminApiPlayground: true + }, + authOptions: { + disableAuth: true, + sessionSecret: 'some-secret', + requireVerification: false, + tokenMethod: "bearer", + superadminCredentials: { + identifier: SUPER_ADMIN_USER_IDENTIFIER, + password: process.env.SUPER_ADMIN_USER_PASSWORD || 'superadmin' + } + }, + dbConnectionOptions: { + // synchronize: true, + // logging: true, + logger: new TypeOrmLogger(), + migrations: [path.join(__dirname, 'migrations/*.ts')], + ...getDbConfig(), + // migrationsRun: true, + // migrations: ["migration/*.js"], + // cli: { + // migrationsDir: "migration" + // } + // logging: ["error"] + }, + // dbConnectionOptionsEx: [{ + // name: "sl", + // synchronize: false, + // host: 'localhost', + // username: 'sa', + // password: 'sa', + // database: 'SL_MWAWI', + // options: { encrypt: false, instanceName: "" }, + // extra: { trustedConnection: false }, + // logger: new TypeOrmLogger(), + // type: 'mssql' + // } as any], + // paymentOptions: { + // // paymentMethodHandlers: [examplePaymentHandler], + // }, + customFields: { + + Product: [ + // { + // name: 'customFieldx', + // type: 'string', + // } + + ], + DocumentLineItem: [ + + ], + }, + searchableFields: { + processResource: [ + "scanId" + ] + }, + logger: new WinstonLogger({ level: LogLevel.Debug }), + workerLogger: new WinstonLogger({ level: LogLevel.Info }), + importExportOptions: { + importProductAssetsDir: path.join(__dirname, 'import', 'product-assets'), + }, + defaults: { + defaultTakeNumber: 100, + }, + plugins: [ + // not needed for local dev + RedisSessionCachePlugin.init({ + namespace: 'phx-session', + redisOptions: { + host: process.env.REDIS_HOST || 'redis', + port: process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : 6379, + db: process.env.REDIS_DB ? parseInt(process.env.REDIS_DB) : 0, + password: process.env.REDIS_PASSWORD || 'admin' + } + }), + AssetServerPlugin.init({ + route: 'remote-assets', + assetUploadDir: path.join(__dirname, 'assets'), + port: 5002, + assetUrlPrefix: "\\remote-assets\\" // to make it relative for client + }), + // only 4 dev + // BonnAPIPlugin.init({ + // callerID: 'b64f845c-e4ed-43e9-b1f8-2e0b274afde0', + // apikey: 'ab9748dd-ac5f-40d8-954c-5c6d01092d80', + // XAccessToken: '48jerefi21r9itwp7ax88fxv2v20blhh', + // lotInfoUrl: 'https://api.zf.com/ZFMessTraceAuxSvc/v2.0/bptrace/lot-info', + // emailReceiver: 'ds@cts-schmid.de', + // autoBelegReportId: "9d77ddff-afec-4412-97dd-9272b497e0c3", + // printerHost: 'DESKTOP-OEEV0PG', + // MESAssignLotUrl: "" + // }), + // BonnAPIPlugin.init({ + // callerID: '18265a9e-7792-4671-88b2-8fa2ac4af5d4', + // apikey: '83997009-248c-472d-8579-5ec681c29daa', + // XAccessToken: 'gjyntdym13u7hsb8wxnk0bfwnxko52xo', + // lotInfoUrl: 'https://apidev.zf.com/ZFMessTraceAuxSvc/v1.0/bptrace/lot-info' + // }), + // ReinerSCTPlugin.init( + // { + // hostname: 'https://timecard.bonn-unternehmensgruppe.de', + // username: 'ctsapi', + // password: 'Tje6tiuEsY' + // } + // ) + // , + //just for dev for now + // EdiTransusPlugin.init({ + // url: "https://webconnect.transus.com/exchange.asmx", + // clientId: "10904548", + // clientKey: "R304WGXHKBZG" + // }), + DefaultJobQueuePlugin.init({}), + // DefaultStoragePlaceRankPlugin.init({}) + // new DefaultSearchPlugin(), + // new ElasticsearchPlugin({ + // host: 'http://192.168.99.100', + // port: 9200, + // }), + // DocusignPlugin.init({ + // devMode:true, + // handlers: defaultDocusignHandlers, + // assetDownloadDir: path.join(__dirname, 'docusign'), + // assetUploadDir: path.join(__dirname, 'docusign'), + // port: API_PORT, + // route: "docusign" + // }), + EmailPlugin.init({ + route: 'mailbox', + handlers: [...defaultEmailHandlers], + // Dynamic Email Templates + templateLoader: new FileBasedTemplateLoader(path.join(__dirname, '../email-plugin/templates')), + outputPath: path.join(__dirname, 'test-emails'), + globalTemplateVars: { + verifyEmailAddressUrl: 'http://localhost:4201/verify', + passwordResetUrl: 'http://localhost:4201/reset-password', + changeEmailAddressUrl: 'http://localhost:4201/change-email-address', + }, + // transport: { + // type: 'smtp', + // host: '', + // port: null, + // secure: false, + // auth: { + // user: '', + // pass: '', + // }, + // tls: { + // rejectUnauthorized: false, + // }, + // } + } as EmailPluginOptions), + ], +}; + +function getDbConfig(): ConnectionOptions { + const dbType = process.env.DB || 'postgres'; + const dbHost = process.env.DB_HOST || 'localhost'; + const dbPort = +process.env.DB_PORT || 5432; + + const connectionPoolMax = process.env.CONNECTION_POOL_MAX ?? 20; + + const dbUsername = process.env.DB_USERNAME || 'postgres'; + const password = process.env.DB_PASSWORD || 'admin'; + const database = process.env.DB_NAME || 'phoenix' + + if (password == "admin") + console.warn("default postgres password is used!"); + + if (process.env.DB_HOST) + console.log(`using DB Host ${dbHost} from env`); + + console.log(`using Database ${database}`); + console.log(`using User ${dbUsername}`); + + switch (dbType) { + case 'postgres': + console.log('Using postgres connection at ' + dbHost); + return { + synchronize: true, + type: 'postgres', + //host: '127.0.0.1', + host: dbHost, + port: dbPort, + username: dbUsername, + password: password, + database: database, + // logging: "all", + extra: { + max: connectionPoolMax + }, + cache: { + alwaysEnabled: false, + duration: 10000 + } + }; + case 'sqlite': + console.log('Using sqlite connection'); + return { + type: 'sqlite', + database: path.join(__dirname, 'phoenix.sqlite'), + }; + case 'sqljs': + console.log('Using sql.js connection'); + return { + type: 'sqljs', + autoSave: true, + database: new Uint8Array([]), + location: path.join(__dirname, 'phoenix.sqlite'), + }; + case 'mysql': + default: + console.log('Using mysql connection'); + return { + synchronize: true, + type: 'mysql', + host: '192.168.99.100', + port: 3306, + username: 'root', + password: '', + database: 'phoenix-dev', + }; + } +}