From ce45314c781d159ca495d0596e0fb0f58dc5d2dd Mon Sep 17 00:00:00 2001 From: Xor290 Date: Fri, 12 Jun 2026 17:51:17 +0200 Subject: [PATCH] chore: update --- backend/gestion/handlers/deleviry.go | 117 +++++++++++++++ backend/gestion/routes/routes.go | 1 + docker/.env.example | 2 + docker/backend/nginx.conf | 18 ++- docker/docker-compose-pre-prod.yml | 212 +++++++++++++++++++++++++++ docker/docker-compose-prod.yml | 9 +- docker/lbtelegram.env | 30 ---- 7 files changed, 356 insertions(+), 33 deletions(-) create mode 100644 docker/docker-compose-pre-prod.yml delete mode 100644 docker/lbtelegram.env diff --git a/backend/gestion/handlers/deleviry.go b/backend/gestion/handlers/deleviry.go index 8832dbd9..4c3dbcbd 100644 --- a/backend/gestion/handlers/deleviry.go +++ b/backend/gestion/handlers/deleviry.go @@ -9,6 +9,7 @@ import ( "net/http" "slices" "strconv" + "time" "github.com/gin-gonic/gin" ) @@ -497,3 +498,119 @@ func ReportDeliveryIssue(c *gin.Context) { log.Printf("📋 [ISSUE] Créé par %s pour commande #%d: %s", username, commandID, req.IssueType) c.JSON(http.StatusCreated, gin.H{"success": true, "issue": issue}) } + +// GET /api/v1/livreur/stats +func GetMyDeliveryStats(c *gin.Context) { + database := c.MustGet("database").(*db.Database) + + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) + return + } + if c.GetString("role") != "livreur" { + c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"}) + return + } + usernameStr := username.(string) + gdb := database.GDB + + type DayRow struct { + Day time.Time `gorm:"column:day"` + Count int `gorm:"column:count"` + Revenue float64 `gorm:"column:revenue"` + } + type WeekRow struct { + WeekNum int `gorm:"column:week_num"` + Year int `gorm:"column:year"` + Count int `gorm:"column:count"` + Revenue float64 `gorm:"column:revenue"` + } + type MonthRow struct { + MonthNum int `gorm:"column:month_num"` + Year int `gorm:"column:year"` + Count int `gorm:"column:count"` + Revenue float64 `gorm:"column:revenue"` + } + + var dayRows []DayRow + gdb.Raw(` + SELECT DATE(updated_at) AS day, + COUNT(*) AS count, + COALESCE(SUM(total_prix), 0) AS revenue + FROM commandes + WHERE livreur_assign = ? + AND status IN ('livre', 'approved') + AND updated_at >= NOW() - INTERVAL '30 days' + GROUP BY DATE(updated_at) + ORDER BY day + `, usernameStr).Scan(&dayRows) + + var weekRows []WeekRow + gdb.Raw(` + SELECT EXTRACT(WEEK FROM updated_at)::int AS week_num, + EXTRACT(YEAR FROM updated_at)::int AS year, + COUNT(*) AS count, + COALESCE(SUM(total_prix), 0) AS revenue + FROM commandes + WHERE livreur_assign = ? + AND status IN ('livre', 'approved') + AND updated_at >= NOW() - INTERVAL '12 weeks' + GROUP BY week_num, year + ORDER BY year, week_num + `, usernameStr).Scan(&weekRows) + + var monthRows []MonthRow + gdb.Raw(` + SELECT EXTRACT(MONTH FROM updated_at)::int AS month_num, + EXTRACT(YEAR FROM updated_at)::int AS year, + COUNT(*) AS count, + COALESCE(SUM(total_prix), 0) AS revenue + FROM commandes + WHERE livreur_assign = ? + AND status IN ('livre', 'approved') + AND updated_at >= NOW() - INTERVAL '12 months' + GROUP BY month_num, year + ORDER BY year, month_num + `, usernameStr).Scan(&monthRows) + + monthNames := [13]string{"", "Jan", "Fév", "Mar", "Avr", "Mai", "Jun", "Jul", "Aoû", "Sep", "Oct", "Nov", "Déc"} + + byDay := make([]gin.H, len(dayRows)) + for i, r := range dayRows { + byDay[i] = gin.H{ + "label": r.Day.Format("02/01"), + "count": r.Count, + "revenue": r.Revenue, + } + } + + byWeek := make([]gin.H, len(weekRows)) + for i, r := range weekRows { + byWeek[i] = gin.H{ + "label": fmt.Sprintf("S%d", r.WeekNum), + "count": r.Count, + "revenue": r.Revenue, + } + } + + byMonth := make([]gin.H, len(monthRows)) + for i, r := range monthRows { + label := "?" + if r.MonthNum >= 1 && r.MonthNum <= 12 { + label = monthNames[r.MonthNum] + } + byMonth[i] = gin.H{ + "label": label, + "count": r.Count, + "revenue": r.Revenue, + } + } + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "by_day": byDay, + "by_week": byWeek, + "by_month": byMonth, + }) +} diff --git a/backend/gestion/routes/routes.go b/backend/gestion/routes/routes.go index 68c6da1b..cd3121d4 100644 --- a/backend/gestion/routes/routes.go +++ b/backend/gestion/routes/routes.go @@ -379,6 +379,7 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services // QUEUE PERSONNELLE // ============================================ livreurGroupV1.GET("/queue", handlers.GetMyQueue) + livreurGroupV1.GET("/stats", handlers.GetMyDeliveryStats) // ============================================ // ALERTES POLICE diff --git a/docker/.env.example b/docker/.env.example index 2673b66b..389dfacb 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -19,6 +19,8 @@ TELEGRAM_WEBHOOK_URL=https://uber-demo.club/webhook/telegram BACKEND_LINK_SECRET=QxAEEGUGRMtWbNvC2REo27haN78Rl5c5EQ LBTELEGRAM_URL=http://lbtelegram:8081 LBTELEGRAM_BOT1_USERNAME=GetRezStealer_bot +LBTELEGRAM_BOT2_USERNAME=rezDJDFJSFUltraFast_bot +BACKEND_LINK_SECRET=change_me_internal_secret API_PORT=8080 FRONTEND_PORT=5173 GIN_MODE=release diff --git a/docker/backend/nginx.conf b/docker/backend/nginx.conf index 69c73f62..557db88f 100644 --- a/docker/backend/nginx.conf +++ b/docker/backend/nginx.conf @@ -126,7 +126,23 @@ server { } # --------------------------------------------------- - # Webhooks Telegram (LBTelegram — /webhook/bot1, /webhook/bot2…) + # Webhook Telegram principal → backend Go + # --------------------------------------------------- + location = /webhook/telegram { + limit_except POST { deny all; } + + set $upstream_backend http://backend:8080; + proxy_pass $upstream_backend; + proxy_http_version 1.1; + proxy_set_header Connection ""; + 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; + } + + # --------------------------------------------------- + # Webhooks LBTelegram (/webhook/bot1, /webhook/bot2…) # --------------------------------------------------- location /webhook/ { limit_except POST { deny all; } diff --git a/docker/docker-compose-pre-prod.yml b/docker/docker-compose-pre-prod.yml new file mode 100644 index 00000000..0a4c84d8 --- /dev/null +++ b/docker/docker-compose-pre-prod.yml @@ -0,0 +1,212 @@ +services: + # ========================================================= + # Backend Go + # ========================================================= + backend: + image: xor1234/backend-mln:pre-prod + container_name: gestion-backend + restart: unless-stopped + environment: + - DB_HOST=${DB_HOST:-postgres} + - DB_PORT=${DB_PORT:-5432} + - DB_USER=${DB_USER:-postgres} + - DB_PASSWORD=${DB_PASSWORD} + - DB_NAME=${DB_NAME:-gestion_db} + - DB_SSLMODE=${DB_SSLMODE:-disable} + - SESSION_SECRET=${SESSION_SECRET} + - USER_JWT_SECRET=${USER_JWT_SECRET} + - USER_JWT_SECRET_OLD=${USER_JWT_SECRET_OLD} + - ADMIN_JWT_SECRET=${ADMIN_JWT_SECRET} + - ADMIN_JWT_SECRET_OLD=${ADMIN_JWT_SECRET_OLD} + - REDIS_HOST=${REDIS_HOST:-redis} + - REDIS_PORT=${REDIS_PORT:-6379} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - TOMTOM_API_KEY=${TOMTOM_API_KEY} + - TOMTOM_API_KEY_1=${TOMTOM_API_KEY_1} + - TOMTOM_API_KEY_2=${TOMTOM_API_KEY_2} + - TOMTOM_API_KEY_3=${TOMTOM_API_KEY_3} + - API_PORT=${API_PORT:-8080} + - NOWPAYMENTS_IPN_SECRET=${NOWPAYMENTS_IPN_SECRET} + - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} + - TELEGRAM_BOT_USERNAME=${TELEGRAM_BOT_USERNAME} + - TELEGRAM_WEBHOOK_SECRET=${TELEGRAM_WEBHOOK_SECRET} + - TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL} + - LBTELEGRAM_URL=http://lbtelegram:8081 + - LBTELEGRAM_BOT1_USERNAME=${LBTELEGRAM_BOT1_USERNAME:-GetRezStealer_bot} + - LBTELEGRAM_BOT2_USERNAME=${LBTELEGRAM_BOT2_USERNAME:-rezDJDFJSFUltraFast_bot} + - BACKEND_LINK_SECRET=${BACKEND_LINK_SECRET:-change_me_internal_secret} + volumes: + - backend_uploads:/app/uploads + networks: + - gestion-network + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + # ========================================================= + # Frontend Web (React/Vite — servi en HTTP interne) + # ========================================================= + frontend: + image: xor1234/frontend-mln:pre-prod + container_name: gestion-frontend + restart: unless-stopped + networks: + - gestion-network + depends_on: + - backend + + waf: + image: xor1234/backend-mln:waf-pre-prod + container_name: gestion-waf + restart: unless-stopped + environment: + - DISABLE_MODSEC_ENV_SUBST=true + - PARANOIA=2 + - ANOMALY_INBOUND=5 + - ANOMALY_OUTBOUND=4 + - MODSEC_AUDIT_LOG=/var/log/modsec/modsec_audit.log + ports: + - "80:80" + - "443:443" + volumes: + - backend_uploads:/usr/share/nginx/html/uploads:ro + - ./certs:/etc/nginx/certs:ro + - ./backend/nginx.conf:/etc/nginx/conf.d/app.conf:ro + - ./backend/custom-rules.conf:/etc/nginx/modsec/custom-rules.conf:ro + - /var/log/waf/nginx:/var/log/nginx + - /var/log/waf/modsec:/var/log/modsec + networks: + - gestion-network + depends_on: + - backend + - frontend + + # ========================================================= + # PostgreSQL + # ========================================================= + postgres: + image: postgres:16-alpine + container_name: gestion-postgres + restart: unless-stopped + environment: + - POSTGRES_USER=${DB_USER:-postgres} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=${DB_NAME:-gestion_db} + - PGDATA=/var/lib/postgresql/data/pgdata + volumes: + - postgres_data:/var/lib/postgresql/data + networks: + - gestion-network + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-gestion_db}", + ] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + # ========================================================= + # Redis + # ========================================================= + redis: + image: redis:7-alpine + container_name: gestion-redis + restart: unless-stopped + command: > + redis-server + --requirepass ${REDIS_PASSWORD} + --appendonly yes + --appendfsync everysec + --maxmemory 256mb + --maxmemory-policy allkeys-lru + volumes: + - redis_data:/data + networks: + - gestion-network + healthcheck: + test: + [ + "CMD", + "redis-cli", + "--no-auth-warning", + "-a", + "${REDIS_PASSWORD}", + "ping", + ] + interval: 10s + timeout: 3s + retries: 5 + start_period: 10s + + # ========================================================= + # LBTelegram — Gateway Telegram load balancer + # ========================================================= + lbtelegram: + image: xor1234/load-balancer-tlg:latest + container_name: gestion-lbtelegram + restart: unless-stopped + env_file: ./.env.lbtelegram + environment: + - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 + - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}?sslmode=disable + networks: + - gestion-network + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + backend: + condition: service_started + + clamav: + deploy: + resources: + limits: + memory: 1g + cpus: 0.5 + image: clamav/clamav:latest + container_name: gestion-clamav + restart: unless-stopped + volumes: + - backend_uploads:/app/uploads:ro + - clamav_data:/var/lib/clamav + networks: + - gestion-network + healthcheck: + test: ["CMD", "clamdcheck.sh"] + interval: 60s + timeout: 10s + retries: 3 + start_period: 120s + + dozzle-agent: + image: amir20/dozzle:latest + command: agent + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + ports: + - "7007:7007" + restart: unless-stopped + +networks: + gestion-network: + driver: bridge + internal: false + driver_opts: + com.docker.network.bridge.name: gestion-br0 + +volumes: + postgres_data: + driver: local + redis_data: + driver: local + backend_uploads: + driver: local + clamav_data: + driver: local diff --git a/docker/docker-compose-prod.yml b/docker/docker-compose-prod.yml index 13c77dab..d62c40bd 100644 --- a/docker/docker-compose-prod.yml +++ b/docker/docker-compose-prod.yml @@ -27,8 +27,13 @@ services: - TOMTOM_API_KEY_3=${TOMTOM_API_KEY_3} - API_PORT=${API_PORT:-8080} - NOWPAYMENTS_IPN_SECRET=${NOWPAYMENTS_IPN_SECRET} + - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} + - TELEGRAM_BOT_USERNAME=${TELEGRAM_BOT_USERNAME} + - TELEGRAM_WEBHOOK_SECRET=${TELEGRAM_WEBHOOK_SECRET} + - TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL} - LBTELEGRAM_URL=http://lbtelegram:8081 - - LBTELEGRAM_BOT1_USERNAME=${LBTELEGRAM_BOT1_USERNAME:-rezsssnfdsjfdsfbot} + - LBTELEGRAM_BOT1_USERNAME=${LBTELEGRAM_BOT1_USERNAME:-GetRezStealer_bot} + - LBTELEGRAM_BOT2_USERNAME=${LBTELEGRAM_BOT2_USERNAME:-rezDJDFJSFUltraFast_bot} - BACKEND_LINK_SECRET=${BACKEND_LINK_SECRET:-change_me_internal_secret} volumes: - backend_uploads:/app/uploads @@ -145,7 +150,7 @@ services: image: xor1234/load-balancer-tlg:latest container_name: gestion-lbtelegram restart: unless-stopped - env_file: ./lbtelegram.env + env_file: ./.env.lbtelegram environment: - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}?sslmode=disable diff --git a/docker/lbtelegram.env b/docker/lbtelegram.env deleted file mode 100644 index b91d7cda..00000000 --- a/docker/lbtelegram.env +++ /dev/null @@ -1,30 +0,0 @@ -# Gateway -PORT=8081 -ENV=production -BOT_COUNT=2 - -# Bots Telegram -BOT1_TOKEN=change_me_bot1_token -BOT1_USERNAME=change_me_bot1_username -BOT1_WEBHOOK_SECRET=change_me_bot1_webhook_secret - -BOT2_TOKEN=change_me_bot2_token -BOT2_USERNAME=change_me_bot2_username -BOT2_WEBHOOK_SECRET=change_me_bot2_webhook_secret - -# URL publique de la gateway (pour setWebhook Telegram) -GATEWAY_URL=https://uber-demo.club - -# JWT -JWT_SECRET=change_me_jwt_secret -JWT_TTL_SECONDS=300 - -# Load balancer: roundrobin | leastconn | failover -LB_STRATEGY=failover - -# Health check interval en secondes -HEALTH_CHECK_INTERVAL=30 - -# URL interne du backend pour valider les tokens de liaison -BACKEND_LINK_URL=http://backend:8080/api/internal/telegram/link -BACKEND_LINK_SECRET=change_me_backend_link_secret