diff --git a/.github/workflows/backend-build.yml b/.github/workflows/backend-build.yml index 34d2395b..c0682d10 100644 --- a/.github/workflows/backend-build.yml +++ b/.github/workflows/backend-build.yml @@ -2,13 +2,13 @@ name: Backend - Build & Lint on: push: - branches: [main, pre-prod] + branches: [main] paths: - - "backend/**" + - "backend/**/**" pull_request: - branches: [main, pre-prod] + branches: [main] paths: - - "backend/**" + - "backend/**/**" jobs: lint: @@ -54,26 +54,31 @@ jobs: working-directory: backend/gestion run: go build -v ./... + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: backend-binary + path: backend/gestion/gestion + retention-days: 7 + docker: name: Docker Build & Push needs: build runs-on: ubuntu-latest - if: > - (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/pre-prod')) || - (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'pre-prod')) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build & push backend (runtime) uses: docker/build-push-action@v6 with: @@ -81,9 +86,7 @@ jobs: file: docker/backend/Dockerfile target: runtime push: true - tags: xor1234/backend-mln:${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && 'latest' || 'pre-prod' }} - cache-from: type=gha,scope=backend-runtime - cache-to: type=gha,mode=max,scope=backend-runtime + tags: xor1234/backend-mln:latest - name: Build & push WAF uses: docker/build-push-action@v6 @@ -92,17 +95,12 @@ jobs: file: docker/backend/Dockerfile target: waf push: true - tags: xor1234/backend-mln:${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && 'waf' || 'waf-pre-prod' }} - cache-from: type=gha,scope=backend-waf - cache-to: type=gha,mode=max,scope=backend-waf + tags: xor1234/backend-mln:waf deploy: - name: Deploy to server + name: SSH Deploy needs: docker runs-on: ubuntu-latest - if: > - (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/pre-prod')) || - (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'pre-prod')) steps: - name: SSH deploy diff --git a/.github/workflows/frontend-admin-build.yml b/.github/workflows/frontend-admin-build.yml index c1ac7c1d..498c1a3a 100644 --- a/.github/workflows/frontend-admin-build.yml +++ b/.github/workflows/frontend-admin-build.yml @@ -32,10 +32,9 @@ jobs: working-directory: frontend-admin run: npx tsc --noEmit - build-apk-pre-prod: + build-apk: needs: typecheck runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/pre-prod' steps: - uses: actions/checkout@v4 @@ -63,13 +62,13 @@ jobs: jq '.expo.extra.eas.projectId = "${{ secrets.EXPO_PROJECT_ID }}"' app.json > app.tmp.json mv app.tmp.json app.json - - name: Build production APK + - name: Build APK working-directory: frontend-admin env: EAS_BUILD_NO_EXPO_GO_WARNING: true - run: eas build --platform android --profile pre-production --non-interactive + run: eas build --platform android ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && '--profile production' || '--profile preview' }} --non-interactive - - name: Download production APK + - name: Download APK working-directory: frontend-admin run: | APK_URL=$(eas build:list --platform android --status finished --limit 1 --json --non-interactive | jq -r '.[0].artifacts.buildUrl') diff --git a/.github/workflows/frontend-client-build.yml b/.github/workflows/frontend-client-build.yml index f35e983a..3bdeed9d 100644 --- a/.github/workflows/frontend-client-build.yml +++ b/.github/workflows/frontend-client-build.yml @@ -32,10 +32,9 @@ jobs: working-directory: mobile run: npx tsc --noEmit - build-apk-prod: + build-apk: needs: typecheck runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/pre-prod' steps: - uses: actions/checkout@v4 @@ -67,11 +66,11 @@ jobs: working-directory: mobile run: cat app.json - - name: Build production APK + - name: Build APK working-directory: mobile env: EAS_BUILD_NO_EXPO_GO_WARNING: true - run: eas build --platform android --profile production --non-interactive + run: eas build --platform android ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && '--profile production' || '--profile preview' }} --non-interactive - name: Download production APK working-directory: mobile diff --git a/.github/workflows/frontend-web-build.yml b/.github/workflows/frontend-web-build.yml index 36e7aaad..5538fef5 100644 --- a/.github/workflows/frontend-web-build.yml +++ b/.github/workflows/frontend-web-build.yml @@ -98,9 +98,6 @@ jobs: name: Deploy to server needs: docker runs-on: ubuntu-latest - if: > - (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/pre-prod')) || - (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'pre-prod')) steps: - name: SSH deploy diff --git a/backend/gestion/db/db_clients.go b/backend/gestion/db/db_clients.go index cefd85e6..e928afef 100644 --- a/backend/gestion/db/db_clients.go +++ b/backend/gestion/db/db_clients.go @@ -374,10 +374,12 @@ func (d *Database) GetClientByUsername(username string) (*models.Client, error) MustChangePassword bool `gorm:"column:must_change_password"` PointsExtraJSON []byte `gorm:"column:points_extra"` CreatedAt time.Time `gorm:"column:created_at"` + TwoFAEnabled bool `gorm:"column:two_fa_enabled"` } err := d.GDB.Raw(` SELECT id, username, password, nom, prenom, telephone, command, amende, - must_change_password, COALESCE(points_extra, '{}'::jsonb) as points_extra, created_at + must_change_password, COALESCE(points_extra, '{}'::jsonb) as points_extra, created_at, + two_fa_enabled FROM clients WHERE username = ?`, username).Scan(&row).Error if err != nil { return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err) @@ -397,6 +399,7 @@ func (d *Database) GetClientByUsername(username string) (*models.Client, error) Amende: row.Amende, MustChangePassword: row.MustChangePassword, CreatedAt: row.CreatedAt, + TwoFAEnabled: row.TwoFAEnabled, } client.PointsExtra = map[string]int{} if len(row.PointsExtraJSON) > 0 { @@ -406,6 +409,10 @@ func (d *Database) GetClientByUsername(username string) (*models.Client, error) return client, nil } +func (d *Database) SetClientTwoFAEnabled(clientID int, enabled bool) error { + return d.GDB.Model(&models.Client{}).Where("id = ?", clientID).Update("two_fa_enabled", enabled).Error +} + func (d *Database) GetClientPenaltiesInfo(username string) (map[string]interface{}, error) { amende, err := d.GetClientAmende(username) if err != nil { diff --git a/backend/gestion/db/db_settings.go b/backend/gestion/db/db_settings.go index 508df618..54bbb3db 100644 --- a/backend/gestion/db/db_settings.go +++ b/backend/gestion/db/db_settings.go @@ -66,6 +66,7 @@ func DefaultSettings() models.AppSettings { }, }, }, + ShopName: "Milieu-Nantais", DeliveryMode: models.DeliveryModeConfig{ Mode: "single", CategoryRoutes: []models.CategoryRoute{}, @@ -81,6 +82,7 @@ func DefaultSettings() models.AppSettings { "44860", "44220", "44118", "44710", "44690", "44119", }}, }, + Telegram2FAEnabled: false, } } @@ -149,6 +151,10 @@ func (d *Database) GetSettings() (models.AppSettings, error) { if err := json.Unmarshal([]byte(row.Value), &mode); err == nil { settings.DeliveryMode = mode } + case "telegram_2fa_enabled": + settings.Telegram2FAEnabled = row.Value == "true" + case "shop_name": + settings.ShopName = row.Value } } return settings, nil @@ -226,7 +232,9 @@ func (d *Database) UpdateSettings(s models.AppSettings) error { {"telegram_bot_token", s.TelegramBotToken}, {"telegram_bot_username", s.TelegramBotUsername}, {"telegram_notifications_enabled", boolStr(s.TelegramNotificationsEnabled)}, + {"telegram_2fa_enabled", boolStr(s.Telegram2FAEnabled)}, {"delivery_mode", string(deliveryModeJSON)}, + {"shop_name", s.ShopName}, } upsert := `INSERT INTO app_settings (key, value) VALUES (?, ?) diff --git a/backend/gestion/db/db_telegram.go b/backend/gestion/db/db_telegram.go index c8ecef2b..37c5863f 100644 --- a/backend/gestion/db/db_telegram.go +++ b/backend/gestion/db/db_telegram.go @@ -15,6 +15,7 @@ func (d *Database) MigrateAddTelegramColumns() { migrations := []string{ `ALTER TABLE clients ADD COLUMN IF NOT EXISTS telegram_chat_id BIGINT`, `ALTER TABLE users ADD COLUMN IF NOT EXISTS telegram_chat_id BIGINT`, + `ALTER TABLE clients ADD COLUMN IF NOT EXISTS two_fa_enabled BOOLEAN NOT NULL DEFAULT FALSE`, } for _, q := range migrations { if err := d.GDB.Exec(q).Error; err != nil { @@ -127,3 +128,36 @@ func (d *Database) GetUserByTelegramChatID(chatID int64) (username, role string, return "", "", fmt.Errorf("aucun compte lié à ce chat_id") } + +// ── 2FA sessions ───────────────────────────────────────────────────────────── + +const twoFASessionTTL = 5 * time.Minute + +type twoFASessionData struct { + Username string `json:"username"` + Code string `json:"code"` +} + +func Store2FASession(sessionToken, username, code string) error { + data, err := json.Marshal(twoFASessionData{Username: username, Code: code}) + if err != nil { + return err + } + return Redis.Set(RedisCtx, "2fa:session:"+sessionToken, data, twoFASessionTTL).Err() +} + +// Verify2FASession valide le code et retourne le username. GETDEL = atomique (anti-replay). +func Verify2FASession(sessionToken, code string) (string, error) { + val, err := Redis.GetDel(RedisCtx, "2fa:session:"+sessionToken).Bytes() + if err != nil { + return "", fmt.Errorf("session invalide ou expirée") + } + var d twoFASessionData + if err := json.Unmarshal(val, &d); err != nil { + return "", fmt.Errorf("données corrompues") + } + if d.Code != code { + return "", fmt.Errorf("code incorrect") + } + return d.Username, nil +} diff --git a/backend/gestion/handlers/auth.go b/backend/gestion/handlers/auth.go index e22c49c4..6b351778 100644 --- a/backend/gestion/handlers/auth.go +++ b/backend/gestion/handlers/auth.go @@ -1,8 +1,11 @@ package handlers import ( + "crypto/rand" + "fmt" "gestion/db" "gestion/models" + "gestion/services" "gestion/utils" "log" "net/http" @@ -76,7 +79,7 @@ func RegisterClient(c *gin.Context) { if err := c.ShouldBindJSON(&req); err != nil { log.Printf("❌ [REGISTER_CLIENT] Erreur binding: %v", err) c.JSON(http.StatusBadRequest, gin.H{ - "error": "Données invalides", + "error": "Données invalides", }) return } @@ -242,8 +245,16 @@ func AdminCreateClient(c *gin.Context) { }) } +func cryptoRandInt() int { + b := make([]byte, 4) + rand.Read(b) + return int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3]) +} + // LoginClient authentifie un client func LoginClient(c *gin.Context) { + database := c.MustGet("database").(*db.Database) + var req models.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { log.Printf("❌ [LOGIN_CLIENT] Erreur binding: %v", err) @@ -251,8 +262,6 @@ func LoginClient(c *gin.Context) { return } - database := c.MustGet("database").(*db.Database) - client, err := database.GetClientByUsername(req.Username) if err != nil || client == nil { log.Printf("❌ [LOGIN_CLIENT] Client non trouvé: %s", req.Username) @@ -266,6 +275,30 @@ func LoginClient(c *gin.Context) { return } + settings, err := database.GetSettings() + if err != nil { + log.Printf("❌ [LOGIN_CLIENT] Erreur récupération des paramètres: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur interne"}) + return + } + + if settings.Telegram2FAEnabled && client.TwoFAEnabled { + chatID, linked, _ := database.GetClientTelegramChatID(client.Username) + if linked { + code := fmt.Sprintf("%06d", cryptoRandInt()%1000000) + sessionToken := uuid.New().String() + if err := db.Store2FASession(sessionToken, client.Username, code); err == nil { + msg := fmt.Sprintf("🔐 Code de vérification : %s\n\nValable 5 minutes.", code) + services.TelegramBot.SendMessage(chatID, msg) + c.JSON(http.StatusOK, gin.H{ + "requires_2fa": true, + "session_token": sessionToken, + }) + return + } + } + } + token, err := generateClientToken(client) if err != nil { log.Printf("❌ [LOGIN_CLIENT] Erreur génération token: %v", err) @@ -280,7 +313,6 @@ func LoginClient(c *gin.Context) { return } - // Créer la session Redis sessionID := uuid.New().String() if err := database.CreateClientSession(client.ID, client.Username, sessionID); err != nil { log.Printf("⚠️ [LOGIN_CLIENT] Erreur session Redis: %v", err) @@ -303,6 +335,125 @@ func LoginClient(c *gin.Context) { }) } +func Verify2FAClient(c *gin.Context) { + database := c.MustGet("database").(*db.Database) + + var req struct { + SessionToken string `json:"session_token" binding:"required"` + Code string `json:"code" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"}) + return + } + + username, err := db.Verify2FASession(req.SessionToken, req.Code) + if err != nil { + log.Printf("❌ [2FA] Échec vérification: %v", err) + c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) + return + } + + client, err := database.GetClientByUsername(username) + if err != nil || client == nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur interne"}) + return + } + + token, err := generateClientToken(client) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur génération token"}) + return + } + + expiresAt := time.Now().Add(clientTokenDuration) + if err := database.SaveToken(client.ID, "client", token, expiresAt); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur enregistrement token"}) + return + } + + sessionID := uuid.New().String() + if err := database.CreateClientSession(client.ID, client.Username, sessionID); err != nil { + log.Printf("⚠️ [2FA] Erreur session Redis: %v", err) + } + + c.JSON(http.StatusOK, models.LoginResponse{ + AccessToken: token, + TokenType: "Bearer", + ExpiresIn: int(clientTokenDuration.Seconds()), + User: gin.H{ + "id": client.ID, + "username": client.Username, + "nom": client.Nom, + "prenom": client.Prenom, + "telephone": client.Telephone, + "role": "client", + "session_id": sessionID, + "must_change_password": client.MustChangePassword, + }, + }) +} + +func GetClient2FAStatus(c *gin.Context) { + clientID := c.GetInt("client_id") + database := c.MustGet("database").(*db.Database) + + client, err := database.GetClientByID(clientID) + if err != nil || client == nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"}) + return + } + + _, tgLinked, _ := database.GetClientTelegramChatID(client.Username) + + settings, _ := database.GetSettings() + + c.JSON(http.StatusOK, gin.H{ + "two_fa_enabled": client.TwoFAEnabled, + "telegram_linked": tgLinked, + "admin_2fa_enabled": settings.Telegram2FAEnabled, + }) +} + +func ToggleClient2FA(c *gin.Context) { + clientID := c.GetInt("client_id") + database := c.MustGet("database").(*db.Database) + + var req struct { + Enabled bool `json:"enabled"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"}) + return + } + + client, err := database.GetClientByID(clientID) + if err != nil || client == nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"}) + return + } + + if req.Enabled { + _, linked, _ := database.GetClientTelegramChatID(client.Username) + if !linked { + c.JSON(http.StatusBadRequest, gin.H{"error": "Telegram non lié — impossible d'activer la 2FA"}) + return + } + settings, _ := database.GetSettings() + if !settings.Telegram2FAEnabled { + c.JSON(http.StatusBadRequest, gin.H{"error": "La 2FA n'est pas activée par l'administrateur"}) + return + } + } + + if err := database.SetClientTwoFAEnabled(clientID, req.Enabled); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur mise à jour"}) + return + } + + c.JSON(http.StatusOK, gin.H{"success": true, "two_fa_enabled": req.Enabled}) +} + // ChangePassword permet à un client de changer son mot de passe func ChangePassword(c *gin.Context) { var req struct { diff --git a/backend/gestion/handlers/settings.go b/backend/gestion/handlers/settings.go index c78a1aaf..bd7c50d6 100644 --- a/backend/gestion/handlers/settings.go +++ b/backend/gestion/handlers/settings.go @@ -30,20 +30,22 @@ func GetPublicSettings(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{ - "success": true, - "penalties_enabled": settings.PenaltiesEnabled, - "show_amende_score": settings.ShowAmendeScore, - "points_enabled": settings.PointsEnabled, - "points_separated": len(settings.PointsPools) > 1, - "pool_names": poolNames, - "pool_keys": poolKeys, - "referral_enabled": settings.ReferralEnabled, - "referral_amount": settings.ReferralAmount, - "delivery_schedule": settings.DeliverySchedule, - "crypto_payment_enabled": settings.CryptoPaymentEnabled, - "crypto_only": settings.CryptoOnly, - "nowpayments_currencies": settings.NowPaymentsCurrencies, - "telegram_notifications_enabled": settings.TelegramNotificationsEnabled, + "success": true, + "penalties_enabled": settings.PenaltiesEnabled, + "show_amende_score": settings.ShowAmendeScore, + "points_enabled": settings.PointsEnabled, + "points_separated": len(settings.PointsPools) > 1, + "pool_names": poolNames, + "pool_keys": poolKeys, + "referral_enabled": settings.ReferralEnabled, + "referral_amount": settings.ReferralAmount, + "delivery_schedule": settings.DeliverySchedule, + "crypto_payment_enabled": settings.CryptoPaymentEnabled, + "crypto_only": settings.CryptoOnly, + "nowpayments_currencies": settings.NowPaymentsCurrencies, + "telegram_notifications_enabled": settings.TelegramNotificationsEnabled, + "shop_name": settings.ShopName, + "two_fa_enabled": settings.Telegram2FAEnabled, }) } diff --git a/backend/gestion/handlers/telegram.go b/backend/gestion/handlers/telegram.go index fcf94917..5a1062d9 100644 --- a/backend/gestion/handlers/telegram.go +++ b/backend/gestion/handlers/telegram.go @@ -242,13 +242,20 @@ func UnlinkClientTelegram(c *gin.Context) { return } + clientID := c.GetInt("client_id") database := c.MustGet("database").(*db.Database) + if err := database.DeleteClientTelegramChatID(username); err != nil { log.Printf("❌ [TELEGRAM_UNLINK] Erreur pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur déliaison"}) return } + // Désactiver la 2FA si Telegram est délié + if clientID > 0 { + _ = database.SetClientTwoFAEnabled(clientID, false) + } + log.Printf("✅ [TELEGRAM_UNLINK] Compte client %s délié", username) c.JSON(http.StatusOK, gin.H{"success": true}) } diff --git a/backend/gestion/models/client.go b/backend/gestion/models/client.go index 6f302fc2..e2d2f1b1 100644 --- a/backend/gestion/models/client.go +++ b/backend/gestion/models/client.go @@ -21,6 +21,7 @@ type Client struct { Parrain string `gorm:"column:parrain" json:"parrain"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` + TwoFAEnabled bool `gorm:"column:two_fa_enabled;default:false" json:"two_fa_enabled"` } func (Client) TableName() string { return "clients" } diff --git a/backend/gestion/models/settings.go b/backend/gestion/models/settings.go index 933a601d..48cc3b7a 100644 --- a/backend/gestion/models/settings.go +++ b/backend/gestion/models/settings.go @@ -61,22 +61,24 @@ type DeliveryModeConfig struct { // AppSettings contient les paramètres globaux de l'application type AppSettings struct { - PenaltiesEnabled bool `json:"penalties_enabled"` - ShowAmendeScore bool `json:"show_amende_score"` // afficher le score d'amendes aux clients/cabine - PenaltyTiers []PenaltyTier `json:"penalty_tiers"` // barème des amendes (liste configurable) - PointsEnabled bool `json:"points_enabled"` // afficher/activer le système de points - PointsPools []PointsPool `json:"points_pools"` // types de points personnalisés - ReferralEnabled bool `json:"referral_enabled"` // activer/désactiver le système de parrainage - ReferralAmount float64 `json:"referral_amount"` // montant crédité par parrainage - CryptoPaymentEnabled bool `json:"crypto_payment_enabled"` // activer/désactiver le paiement crypto - CryptoOnly bool `json:"crypto_only"` // forcer le paiement crypto uniquement (pas d'espèces) - NowPaymentsAPIKey string `json:"nowpayments_api_key"` // clé API NowPayments - NowPaymentsIPNSecret string `json:"nowpayments_ipn_secret"` // secret IPN NowPayments - NowPaymentsCurrencies []string `json:"nowpayments_currencies"` // cryptos acceptées (ex: ["btc","eth","ltc"]) - DeliverySchedule DeliverySchedule `json:"delivery_schedule"` // horaires de livraison par jour - PostalZones []PostalZone `json:"postal_zones"` // zones de livraison avec minimum de commande - TelegramBotToken string `json:"telegram_bot_token"` // token du bot Telegram (BotFather) - TelegramBotUsername string `json:"telegram_bot_username"` // username du bot (sans @) - TelegramNotificationsEnabled bool `json:"telegram_notifications_enabled"` // activer/désactiver les notifications Telegram - DeliveryMode DeliveryModeConfig `json:"delivery_mode"` // mode d'assignation des livreurs + PenaltiesEnabled bool `json:"penalties_enabled"` + ShowAmendeScore bool `json:"show_amende_score"` // afficher le score d'amendes aux clients/cabine + PenaltyTiers []PenaltyTier `json:"penalty_tiers"` // barème des amendes (liste configurable) + PointsEnabled bool `json:"points_enabled"` // afficher/activer le système de points + PointsPools []PointsPool `json:"points_pools"` // types de points personnalisés + ReferralEnabled bool `json:"referral_enabled"` // activer/désactiver le système de parrainage + ReferralAmount float64 `json:"referral_amount"` // montant crédité par parrainage + CryptoPaymentEnabled bool `json:"crypto_payment_enabled"` // activer/désactiver le paiement crypto + CryptoOnly bool `json:"crypto_only"` // forcer le paiement crypto uniquement (pas d'espèces) + NowPaymentsAPIKey string `json:"nowpayments_api_key"` // clé API NowPayments + NowPaymentsIPNSecret string `json:"nowpayments_ipn_secret"` // secret IPN NowPayments + NowPaymentsCurrencies []string `json:"nowpayments_currencies"` // cryptos acceptées (ex: ["btc","eth","ltc"]) + DeliverySchedule DeliverySchedule `json:"delivery_schedule"` // horaires de livraison par jour + PostalZones []PostalZone `json:"postal_zones"` // zones de livraison avec minimum de commande + TelegramBotToken string `json:"telegram_bot_token"` // token du bot Telegram (BotFather) + TelegramBotUsername string `json:"telegram_bot_username"` // username du bot (sans @) + TelegramNotificationsEnabled bool `json:"telegram_notifications_enabled"` // activer/désactiver les notifications Telegram + DeliveryMode DeliveryModeConfig `json:"delivery_mode"` // mode d'assignation des livreurs + ShopName string `json:"shop_name"` // nom affiché dans la sidebar du site client + Telegram2FAEnabled bool `json:"telegram_2fa_enabled"` // activer/désactiver l'authentification à deux facteurs } diff --git a/backend/gestion/routes/routes.go b/backend/gestion/routes/routes.go index 3a06b49f..2507b66b 100644 --- a/backend/gestion/routes/routes.go +++ b/backend/gestion/routes/routes.go @@ -34,6 +34,7 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services { authGroupV1.POST("/login", middleware.LoginRateLimitMiddleware, handlers.LoginClient) authGroupV1.POST("/logout", handlers.LogoutClient) + authGroupV1.POST("/2fa/verify", middleware.LoginRateLimitMiddleware, handlers.Verify2FAClient) } // Route change-password (auth client requise) @@ -107,6 +108,10 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services cartGroupV1.GET("/profile", handlers.GetMyProfile) // ✅ Récupérer mon profil cartGroupV1.PUT("/profile/update", handlers.UpdateMyProfile) // ✅ Modifier mon profil + // 🔐 2FA CLIENT + cartGroupV1.GET("/two-fa/status", handlers.GetClient2FAStatus) + cartGroupV1.POST("/two-fa/toggle", handlers.ToggleClient2FA) + // 🎁 PARRAINAGE CLIENT cartGroupV1.GET("/referral/balance", handlers.GetMyReferralBalance) cartGroupV1.GET("/parrain", handlers.GetMyParrainInfo) diff --git a/backend/gestion/services/geo_services.go b/backend/gestion/services/geo_services.go index 6817f3f2..29d85f86 100644 --- a/backend/gestion/services/geo_services.go +++ b/backend/gestion/services/geo_services.go @@ -16,10 +16,6 @@ import ( "github.com/redis/go-redis/v9" ) -// ============================================ -// CONSTANTES -// ============================================ - const ( NominatimBaseURL = "https://nominatim.openstreetmap.org/search" EarthRadiusKm = 6371.0 diff --git a/docker/docker-compose-prod.yml b/docker/docker-compose-prod.yml index cc560b93..0e02e72f 100644 --- a/docker/docker-compose-prod.yml +++ b/docker/docker-compose-prod.yml @@ -138,6 +138,27 @@ services: - "7007:7007" restart: unless-stopped + 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 + networks: gestion-network: driver: bridge @@ -152,3 +173,5 @@ volumes: driver: local backend_uploads: driver: local + clamav_data: + driver: local diff --git a/frontend-admin/eas.json b/frontend-admin/eas.json index f08b415b..4fba5714 100644 --- a/frontend-admin/eas.json +++ b/frontend-admin/eas.json @@ -15,7 +15,7 @@ "simulator": true }, "env": { - "API_URL": "https://5.181.0.112.nip.io" + "API_URL": "https://mln-uber.club" } }, "preview": { @@ -24,7 +24,7 @@ "buildType": "apk" }, "env": { - "API_URL": "https://5.181.0.112.nip.io" + "API_URL": "https://mln-uber.club" } }, "production": { @@ -34,7 +34,7 @@ "buildType": "apk" }, "env": { - "API_URL": "https://5.181.0.112.nip.io" + "API_URL": "https://mln-uber.club" } } } diff --git a/frontend-admin/package-lock.json b/frontend-admin/package-lock.json index 25546185..8c8a9900 100644 --- a/frontend-admin/package-lock.json +++ b/frontend-admin/package-lock.json @@ -17,11 +17,9 @@ "axios": "^1.13.4", "expo": "~54.0.34", "expo-build-properties": "~1.0.10", - "expo-device": "~8.0.10", "expo-font": "~14.0.11", "expo-image-picker": "~17.0.11", "expo-location": "~19.0.8", - "expo-notifications": "~0.32.17", "expo-status-bar": "~3.0.9", "jwt-decode": "^4.0.0", "react": "19.1.0", @@ -81,7 +79,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -1369,7 +1366,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -2049,11 +2045,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@ide/backoff": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz", - "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==" - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -2567,7 +2558,6 @@ "version": "7.1.28", "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.28.tgz", "integrity": "sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==", - "peer": true, "dependencies": { "@react-navigation/core": "^7.14.0", "escape-string-regexp": "^4.0.0", @@ -2767,7 +2757,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", "devOptional": true, - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2982,18 +2971,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, "node_modules/async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -3004,20 +2981,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/axios": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", @@ -3224,11 +3187,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/badgin": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz", - "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3367,7 +3325,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3454,23 +3411,6 @@ "node": ">=8" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -3483,21 +3423,6 @@ "node": ">= 0.4" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -3936,22 +3861,6 @@ "node": ">=10" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -3961,22 +3870,6 @@ "node": ">=8" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4077,6 +3970,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -4086,6 +3980,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -4217,7 +4112,6 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.34.tgz", "integrity": "sha512-XkVHguZZDC8BcTQxHAd14/TQFbDp1Wt0Z/KApO9t68Ll5A127hLCPzU+a9gytfCIiyL/V1IpF1vIcOLKEVAoNQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.24", @@ -4265,14 +4159,6 @@ } } }, - "node_modules/expo-application": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-7.0.8.tgz", - "integrity": "sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q==", - "peerDependencies": { - "expo": "*" - } - }, "node_modules/expo-asset": { "version": "12.0.13", "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.13.tgz", @@ -4324,42 +4210,6 @@ "react-native": "*" } }, - "node_modules/expo-device": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-8.0.10.tgz", - "integrity": "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA==", - "dependencies": { - "ua-parser-js": "^0.7.33" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-device/node_modules/ua-parser-js": { - "version": "0.7.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", - "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/expo-file-system": { "version": "19.0.22", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.22.tgz", @@ -4374,7 +4224,6 @@ "version": "14.0.11", "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", - "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -4450,26 +4299,6 @@ "react-native": "*" } }, - "node_modules/expo-notifications": { - "version": "0.32.17", - "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.32.17.tgz", - "integrity": "sha512-lwwzn7tImuzTzn9PAglZlS2VfZEvsfFGJTK9Eb8I4cqkGh2DI23YJFJH+WPEIu4QhDvk5JeBjklenJ8IZbmA4A==", - "license": "MIT", - "dependencies": { - "@expo/image-utils": "^0.8.8", - "@ide/backoff": "^1.0.0", - "abort-controller": "^3.0.0", - "assert": "^2.0.0", - "badgin": "^1.1.5", - "expo-application": "~7.0.8", - "expo-constants": "~18.0.13" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, "node_modules/expo-server": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.6.tgz", @@ -4817,20 +4646,6 @@ "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==" }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -4889,14 +4704,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5055,17 +4862,6 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -5295,37 +5091,11 @@ "loose-envify": "^1.0.0" } }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-arrayish": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==" }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -5362,39 +5132,6 @@ "node": ">=8" } }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5411,37 +5148,6 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -6760,48 +6466,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -7138,14 +6802,6 @@ "node": ">=4.0.0" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/postcss": { "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", @@ -7354,7 +7010,6 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7372,7 +7027,6 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -7400,7 +7054,6 @@ "version": "0.81.5", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", - "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", @@ -7527,7 +7180,6 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", - "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -7537,7 +7189,6 @@ "version": "4.16.0", "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", - "peer": true, "dependencies": { "react-freeze": "^1.0.0", "react-native-is-edge-to-edge": "^1.2.1", @@ -7552,7 +7203,6 @@ "version": "0.21.2", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", @@ -7582,7 +7232,6 @@ "version": "13.15.0", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.15.0.tgz", "integrity": "sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ==", - "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -7596,7 +7245,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz", "integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==", - "peer": true, "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", @@ -7720,7 +7368,6 @@ "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7973,27 +7620,12 @@ ], "license": "MIT" }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true + "optional": true, + "peer": true }, "node_modules/sax": { "version": "1.4.4", @@ -8109,22 +7741,6 @@ "node": ">= 0.8" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -8615,7 +8231,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "peer": true, "engines": { "node": ">=12" }, @@ -8825,18 +8440,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -8954,26 +8557,6 @@ "node": ">= 8" } }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/wonka": { "version": "6.3.6", "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.6.tgz", diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts index bd9d77ed..63877bad 100644 --- a/frontend-admin/src/api/api_admin.ts +++ b/frontend-admin/src/api/api_admin.ts @@ -9,9 +9,9 @@ import type { Alert, } from "./types"; -const V2 = "https://5.181.0.112.nip.io/api/v2"; -const CABINE_URL = "https://5.181.0.112.nip.io/api/v1/cabine"; -const V1_PUBLIC = "https://5.181.0.112.nip.io/api/v1"; +const V2 = "https://mln-uber.club/api/v2"; +const CABINE_URL = "https://mln-uber.club/api/v1/cabine"; +const V1_PUBLIC = "https://mln-uber.club/api/v1"; export const loginAdmin = async ( username: string, @@ -52,10 +52,6 @@ export const logoutAdmin = async (): Promise => { } }; -// ============================================ -// CLIENTS -// ============================================ - export const getAllClients = async (): Promise => { const { data } = await apiClient.get(`${V2}/admin/protected/all/clients`); return data.clients || []; @@ -90,7 +86,6 @@ export const updateUserByAdmin = async ( // ============================================ // COMMANDES -// ============================================ export const getAllCommands = async (status?: string, username?: string) => { let url = `${V2}/admin/protected/orders`; @@ -963,7 +958,9 @@ export interface AppSettings { telegram_bot_token: string; telegram_bot_username: string; telegram_notifications_enabled: boolean; + telegram_2fa_enabled: boolean; delivery_mode: DeliveryModeConfig; + shop_name: string; } export const getSettings = async (): Promise<{ diff --git a/frontend-admin/src/api/api_cabine.ts b/frontend-admin/src/api/api_cabine.ts index c5193b94..953f2073 100644 --- a/frontend-admin/src/api/api_cabine.ts +++ b/frontend-admin/src/api/api_cabine.ts @@ -6,12 +6,8 @@ import type { Alert, } from "./types"; -const API = "https://5.181.0.112.nip.io/api/v1/cabine"; -const V2 = "https://5.181.0.112.nip.io/api/v2"; - -// ============================================ -// ITEMS -// ============================================ +const API = "https://mln-uber.club/api/v1/cabine"; +const V2 = "https://mln-uber.club/api/v2"; export const getCommandItems = async (commandId: number) => { const { data } = await apiClient.get(`${API}/commands/${commandId}/items`); diff --git a/frontend-admin/src/api/api_delivery.ts b/frontend-admin/src/api/api_delivery.ts index 6ba79bd3..e6bf0183 100644 --- a/frontend-admin/src/api/api_delivery.ts +++ b/frontend-admin/src/api/api_delivery.ts @@ -7,7 +7,7 @@ import type { Alert, } from "./types"; -const API = "https://5.181.0.112.nip.io/api/v1/livreur"; +const API = "https://mln-uber.club/api/v1/livreur"; // ============================================ // STATUT diff --git a/frontend-admin/src/api/client.ts b/frontend-admin/src/api/client.ts index 83438956..16b4521e 100644 --- a/frontend-admin/src/api/client.ts +++ b/frontend-admin/src/api/client.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { getToken, getAdminToken } from "../auth/tokenStorage"; // Change this to your server IP/domain -export const API_BASE_URL = "https://5.181.0.112.nip.io"; +export const API_BASE_URL = "https://mln-uber.club"; const apiClient = axios.create({ baseURL: API_BASE_URL, diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx index 0e792e32..7d266512 100644 --- a/frontend-admin/src/screens/admin/SettingsScreen.tsx +++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx @@ -677,7 +677,9 @@ export default function SettingsScreen() { telegram_bot_token: "", telegram_bot_username: "", telegram_notifications_enabled: false, + telegram_2fa_enabled: false, delivery_mode: { mode: "single" as const, category_routes: [] }, + shop_name: "Milieu-Nantais", }); const [showApiKey, setShowApiKey] = useState(false); const [showIpnSecret, setShowIpnSecret] = useState(false); @@ -939,6 +941,27 @@ export default function SettingsScreen() { return ( + {/* Personnalisation */} + + Personnalisation + + + Nom du shop + Affiché dans la sidebar du site client + + + + setSettings((p) => ({ ...p, shop_name: v }))} + placeholder="Ex: Milieu-Nantais" + placeholderTextColor={colors.textMuted} + autoCorrect={false} + /> + + + {/* Amendes */} Amendes @@ -1412,6 +1435,18 @@ export default function SettingsScreen() { thumbColor="#fff" /> + + + Authentification 2FA + Permettre aux clients d'activer la double authentification via Telegram lors de la connexion + + setSettings((prev) => ({ ...prev, telegram_2fa_enabled: v }))} + trackColor={{ false: colors.border, true: colors.accent }} + thumbColor="#fff" + /> + Configurez le bot Telegram pour envoyer des notifications aux utilisateurs qui ont lié leur compte. diff --git a/frontend-prep/index.html b/frontend-prep/index.html index b243debe..5c9b2d49 100644 --- a/frontend-prep/index.html +++ b/frontend-prep/index.html @@ -3,8 +3,7 @@ - - + diff --git a/frontend-prep/src/api/api.ts b/frontend-prep/src/api/api.ts index 91b2692b..62a557c0 100644 --- a/frontend-prep/src/api/api.ts +++ b/frontend-prep/src/api/api.ts @@ -41,6 +41,8 @@ export interface AuthResponse { access_token?: string; // ✅ CRITICAL! token_type?: string; expires_in?: number; + requires_2fa?: boolean; + session_token?: string; user?: { id: number; username: string; @@ -210,6 +212,15 @@ export const loginUser = async ( const data = await safeJson(response); console.log("📋 [LOGIN] Réponse:", data); + // 2FA requis — retourner sans token + if (data.requires_2fa) { + return { + success: true, + requires_2fa: true, + session_token: data.session_token, + }; + } + // ✅ Vérifier access_token if (!data.access_token) { console.error("❌ [LOGIN] Pas de access_token"); @@ -1219,11 +1230,14 @@ export const calculateOrderTotal = (order: Record): number => { // Fallback: calculer depuis les items si présents if (Array.isArray(order.items) && order.items.length > 0) { - return (order.items as Record[]).reduce((sum: number, item) => { - const price = Number(item.prix || item.price || 0); - const quantity = Number(item.quantite || item.quantity || 1); - return sum + price * quantity; - }, 0); + return (order.items as Record[]).reduce( + (sum: number, item) => { + const price = Number(item.prix || item.price || 0); + const quantity = Number(item.quantite || item.quantity || 1); + return sum + price * quantity; + }, + 0, + ); } return 0; @@ -1863,6 +1877,8 @@ export interface PublicSettings { crypto_payment_enabled: boolean; crypto_only: boolean; nowpayments_currencies: string[]; + shop_name: string; + two_fa_enabled: boolean; } export const getPublicSettings = async (): Promise => { @@ -1877,6 +1893,8 @@ export const getPublicSettings = async (): Promise => { crypto_payment_enabled: false, crypto_only: false, nowpayments_currencies: [], + shop_name: "Milieu-Nantais", + two_fa_enabled: false, }; try { const response = await fetch(`${API_URL}/app-settings`); @@ -1898,12 +1916,71 @@ export const getPublicSettings = async (): Promise => { nowpayments_currencies: Array.isArray(data.nowpayments_currencies) ? data.nowpayments_currencies : [], + shop_name: data.shop_name || "Milieu-Nantais", + two_fa_enabled: data.two_fa_enabled ?? false, }; } catch { return defaults; } }; +export const verify2FA = async ( + sessionToken: string, + code: string, +): Promise => { + try { + const response = await fetch(`${API_URL}/auth/2fa/verify`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ session_token: sessionToken, code }), + }); + const data = await safeJson(response); + if (!response.ok) { + return { success: false, message: data.error || "Code invalide" }; + } + sessionStorage.setItem("token", data.access_token); + syncUsernameFromJWT(); + return { + success: true, + access_token: data.access_token, + token_type: data.token_type, + expires_in: data.expires_in, + user: data.user, + }; + } catch (error) { + return { success: false, message: error instanceof Error ? error.message : "Erreur" }; + } +}; + +export const get2FAStatus = async (): Promise<{ two_fa_enabled: boolean; telegram_linked: boolean; admin_2fa_enabled: boolean }> => { + const token = sessionStorage.getItem("token"); + try { + const response = await fetch(`${API_URL}/two-fa/status`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!response.ok) return { two_fa_enabled: false, telegram_linked: false, admin_2fa_enabled: false }; + return await safeJson(response); + } catch { + return { two_fa_enabled: false, telegram_linked: false, admin_2fa_enabled: false }; + } +}; + +export const toggle2FA = async (enabled: boolean): Promise<{ success: boolean; error?: string }> => { + const token = sessionStorage.getItem("token"); + try { + const response = await fetch(`${API_URL}/two-fa/toggle`, { + method: "POST", + headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, + body: JSON.stringify({ enabled }), + }); + const data = await safeJson(response); + if (!response.ok) return { success: false, error: data.error }; + return { success: true }; + } catch { + return { success: false, error: "Erreur réseau" }; + } +}; + export interface CryptoPaymentStatus { command_id: number; client_order_number?: number; diff --git a/frontend-prep/src/api/api_types.ts b/frontend-prep/src/api/api_types.ts index ff8a1728..fac2599f 100644 --- a/frontend-prep/src/api/api_types.ts +++ b/frontend-prep/src/api/api_types.ts @@ -58,6 +58,8 @@ export interface LoginResponse { token_type?: string; expires_in?: number; user?: UserResponse; + requires_2fa?: boolean; + session_token?: string; } /** diff --git a/frontend-prep/src/components/Navbar.tsx b/frontend-prep/src/components/Navbar.tsx index c7b39a04..ca30253e 100644 --- a/frontend-prep/src/components/Navbar.tsx +++ b/frontend-prep/src/components/Navbar.tsx @@ -39,6 +39,7 @@ function Navbar() { const [unreadCount, setUnreadCount] = useState(0); const [showNotifPanel, setShowNotifPanel] = useState(false); const [referralEnabled, setReferralEnabled] = useState(true); + const [shopName, setShopName] = useState("Milieu-Nantais"); const seenKeysRef = useRef>(new Set()); const isFirstLoadRef = useRef(true); const notifPanelRef = useRef(null); @@ -74,7 +75,10 @@ function Navbar() { }, [fetchNotifications]); useEffect(() => { - getPublicSettings().then((s) => setReferralEnabled(s.referral_enabled)); + getPublicSettings().then((s) => { + setReferralEnabled(s.referral_enabled); + if (s.shop_name) setShopName(s.shop_name); + }); }, []); useEffect(() => { @@ -235,7 +239,7 @@ function Navbar() {
-

Milieu-Nantais

+

{shopName}

Mon espace

diff --git a/frontend-prep/src/components/ProductCard.css b/frontend-prep/src/components/ProductCard.css index d0f543ea..4dbe3ae0 100644 --- a/frontend-prep/src/components/ProductCard.css +++ b/frontend-prep/src/components/ProductCard.css @@ -382,11 +382,14 @@ div[data-category="gros&semi"] .success-message { background: rgba(0, 0, 0, 0.75); backdrop-filter: blur(8px); border-radius: 50%; - width: 40px; - height: 40px; + width: 48px; + height: 48px; + min-width: 48px; + min-height: 48px; display: flex; align-items: center; justify-content: center; + overflow: visible; z-index: 3; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); transition: all 0.3s ease; @@ -406,8 +409,11 @@ div[data-category="gros&semi"] .success-message { .media-indicator svg { color: #ffffff; - width: 18px; - height: 18px; + width: 24px; + height: 24px; + min-width: 24px; + min-height: 24px; + flex-shrink: 0; animation: cameraPulse 2s ease-in-out infinite; } @@ -601,12 +607,15 @@ div[data-category="gros&semi"] .details-button:hover { backdrop-filter: blur(10px); border: 2px solid rgba(255, 255, 255, 0.3); color: white; - width: 40px; - height: 40px; + width: 48px; + height: 48px; + min-width: 48px; + min-height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; + overflow: visible; cursor: pointer; z-index: 10; transition: all 0.3s ease; @@ -623,8 +632,11 @@ div[data-category="gros&semi"] .details-button:hover { } .video-close-btn svg { - width: 18px; - height: 18px; + width: 24px; + height: 24px; + min-width: 24px; + min-height: 24px; + flex-shrink: 0; } .video-player { @@ -668,13 +680,17 @@ div[data-category="gros&semi"] .details-button:hover { .video-close-btn { top: 10px; right: 10px; - width: 36px; - height: 36px; + width: 44px; + height: 44px; + min-width: 44px; + min-height: 44px; } .video-close-btn svg { - width: 16px; - height: 16px; + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; } .video-player { @@ -699,15 +715,19 @@ div[data-category="gros&semi"] .details-button:hover { } .media-indicator { - width: 36px; - height: 49px; + width: 44px; + height: 44px; + min-width: 44px; + min-height: 44px; top: 10px; right: 10px; } .media-indicator svg { - width: 16px; - height: 16px; + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; } } diff --git a/frontend-prep/src/components/ProductCard.tsx b/frontend-prep/src/components/ProductCard.tsx index 4fe92e53..1b233c45 100644 --- a/frontend-prep/src/components/ProductCard.tsx +++ b/frontend-prep/src/components/ProductCard.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { createPortal } from "react-dom"; import { useNavigate } from "react-router-dom"; import { useCart } from "../context/useCart"; import { Camera, Info, X } from "lucide-react"; @@ -218,8 +219,8 @@ function ProductCard({ )} - {/* ✨ Modal vidéo */} - {showVideo && videoUrl && ( + {/* ✨ Modal vidéo — rendu via Portal pour éviter le clipping du transform:scale sur .product-card */} + {showVideo && videoUrl && createPortal(
-
+ , + document.body )} ); diff --git a/frontend-prep/src/pages/LoginClient/Login.tsx b/frontend-prep/src/pages/LoginClient/Login.tsx index bad91717..f01d9f63 100644 --- a/frontend-prep/src/pages/LoginClient/Login.tsx +++ b/frontend-prep/src/pages/LoginClient/Login.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; -import { Lock, Mail, Eye, EyeOff, User } from "lucide-react"; +import { Lock, Mail, Eye, EyeOff, User, Shield } from "lucide-react"; import "./Login.css"; -import { loginUser, syncUsernameFromJWT } from "../../api/api"; +import { loginUser, verify2FA, syncUsernameFromJWT } from "../../api/api"; import type { LoginRequest } from "../../api/api_types"; import { useNavigate } from "react-router-dom"; @@ -21,6 +21,10 @@ const LoginClient = () => { const [isLoading, setIsLoading] = useState(false); const [apiError, setApiError] = useState(""); + const [twoFAStep, setTwoFAStep] = useState(false); + const [sessionToken, setSessionToken] = useState(""); + const [twoFACode, setTwoFACode] = useState(""); + /** * ✅ Valider le formulaire */ @@ -79,30 +83,19 @@ const LoginClient = () => { hasToken: !!result.access_token, }); - if (result.success && result.access_token) { - console.log("✅ [LOGIN] Connexion réussie!"); - - // ✅ La synchronisation JWT est AUTOMATIQUE dans loginUser() - // Pas besoin de le faire ici - console.log("✅ [LOGIN] Token et username synchronisés"); - - // ✅ Vérifier la synchronisation + if (result.success && result.requires_2fa) { + setSessionToken(result.session_token || ""); + setTwoFAStep(true); + } else if (result.success && result.access_token) { const syncedUsername = syncUsernameFromJWT(); console.log("✅ [LOGIN] Username synchronisé:", syncedUsername); - - // ✅ Redirection if (result.user?.must_change_password) { - console.log("✅ [LOGIN] Première connexion - changement de mot de passe requis"); navigate("/user/change-password"); } else { - console.log("✅ [LOGIN] Redirection vers /user/accueil"); navigate("/user/accueil"); } } else { - // ❌ Erreur API - const errorMessage = - result.message || "Identifiants incorrects"; - console.error("❌ [LOGIN] Erreur API:", errorMessage); + const errorMessage = result.message || "Identifiants incorrects"; setApiError(errorMessage); setErrors({ username: errorMessage }); } @@ -117,6 +110,30 @@ const LoginClient = () => { } }; + const handle2FASubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!twoFACode.trim()) return; + setIsLoading(true); + setApiError(""); + try { + const result = await verify2FA(sessionToken, twoFACode.trim()); + if (result.success && result.access_token) { + syncUsernameFromJWT(); + if (result.user?.must_change_password) { + navigate("/user/change-password"); + } else { + navigate("/user/accueil"); + } + } else { + setApiError(result.message || "Code invalide"); + } + } catch { + setApiError("Erreur de vérification"); + } finally { + setIsLoading(false); + } + }; + /** * ✅ Gérer les changements d'input */ @@ -141,6 +158,66 @@ const LoginClient = () => { } }; + if (twoFAStep) { + return ( +
+
+
+
+ +
+

Vérification 2FA

+

+ Entrez le code envoyé sur votre Telegram +

+
+
+
+ {apiError && ( +
⚠️ {apiError}
+ )} +
+ +
+ setTwoFACode(e.target.value)} + className="form-input" + placeholder="000000" + maxLength={6} + disabled={isLoading} + autoComplete="one-time-code" + style={{ letterSpacing: "0.3em", textAlign: "center", fontSize: "1.5rem" }} + /> +
+
+ + +
+
+
+
+ ); + } + return (
diff --git a/frontend-prep/src/pages/User/ProfilePage.tsx b/frontend-prep/src/pages/User/ProfilePage.tsx index ef3fd2ee..2e4710fb 100644 --- a/frontend-prep/src/pages/User/ProfilePage.tsx +++ b/frontend-prep/src/pages/User/ProfilePage.tsx @@ -1,11 +1,11 @@ import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import Navbar from '../../components/Navbar'; -import { isUserAuthenticated, extractUsernameFromToken, getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram } from '../../api/api'; +import { isUserAuthenticated, extractUsernameFromToken, getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram, get2FAStatus, toggle2FA, getPublicSettings } from '../../api/api'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUser, faMapMarkerAlt, faPhone, faCommentDots, - faSave, faCheckCircle, faExclamationTriangle, faPaperPlane, faUnlink, faTimes, faLock, + faSave, faCheckCircle, faExclamationTriangle, faPaperPlane, faUnlink, faTimes, faLock, faShieldAlt, } from '@fortawesome/free-solid-svg-icons'; import './ProfilePage.css'; @@ -37,6 +37,11 @@ export default function ProfilePage() { const [tgEnabled, setTgEnabled] = useState(false); const [tgLoading, setTgLoading] = useState(false); + // 2FA + const [twoFAEnabled, setTwoFAEnabled] = useState(false); + const [twoFAAdminEnabled, setTwoFAAdminEnabled] = useState(false); + const [twoFALoading, setTwoFALoading] = useState(false); + // Modal confirmation infos par défaut const [showSaveModal, setShowSaveModal] = useState(false); @@ -50,6 +55,12 @@ export default function ProfilePage() { // Statut Telegram getTelegramStatus().then((s) => { setTgLinked(s.linked); setTgEnabled(s.enabled); }); + // Statut 2FA + Promise.all([get2FAStatus(), getPublicSettings()]).then(([status, pub]) => { + setTwoFAEnabled(status.two_fa_enabled); + setTwoFAAdminEnabled(pub.two_fa_enabled); + }); + // Charger depuis backend getMyProfile().then((res) => { if (res.success && res.client) { @@ -99,9 +110,23 @@ export default function ProfilePage() { if (!window.confirm('Délier votre compte Telegram ? Vous ne recevrez plus de notifications.')) return; await unlinkTelegram(); setTgLinked(false); + setTwoFAEnabled(false); showSuccess('Compte Telegram délié'); }; + const handleToggle2FA = async () => { + const newVal = !twoFAEnabled; + setTwoFALoading(true); + const res = await toggle2FA(newVal); + setTwoFALoading(false); + if (res.success) { + setTwoFAEnabled(newVal); + showSuccess(newVal ? 'Double authentification activée' : 'Double authentification désactivée'); + } else { + showError(res.error || 'Erreur lors de la modification'); + } + }; + const saveContact = async () => { setSavingContact(true); const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() }); @@ -276,6 +301,34 @@ export default function ProfilePage() { )}
)} + + {/* Section 2FA — visible uniquement si l'admin l'a activé ET Telegram est lié */} + {twoFAAdminEnabled && tgLinked && ( +
+

+ + Double authentification (2FA) +

+

+ À chaque connexion, un code vous sera envoyé sur Telegram avant d'accéder à votre compte. +

+
+ + {twoFAEnabled && ( + + Activée + + )} +
+
+ )}
{showSaveModal && ( diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 4f8b1c78..32fc401a 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -41,8 +41,6 @@ "devDependencies": { "@expo/ngrok": "^4.1.3", "@types/react": "~19.1.0", - "javascript-obfuscator": "^4.1.1", - "react-native-obfuscating-transformer": "^1.0.0", "typescript": "~5.9.2" } }, @@ -2578,31 +2576,6 @@ "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz", "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==" }, - "node_modules/@inversifyjs/common": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@inversifyjs/common/-/common-1.3.3.tgz", - "integrity": "sha512-ZH0wrgaJwIo3s9gMCDM2wZoxqrJ6gB97jWXncROfYdqZJv8f3EkqT57faZqN5OTeHWgtziQ6F6g3L8rCvGceCw==", - "dev": true - }, - "node_modules/@inversifyjs/core": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@inversifyjs/core/-/core-1.3.4.tgz", - "integrity": "sha512-gCCmA4BdbHEFwvVZ2elWgHuXZWk6AOu/1frxsS+2fWhjEk2c/IhtypLo5ytSUie1BCiT6i9qnEo4bruBomQsAA==", - "dev": true, - "dependencies": { - "@inversifyjs/common": "1.3.3", - "@inversifyjs/reflect-metadata-utils": "0.2.3" - } - }, - "node_modules/@inversifyjs/reflect-metadata-utils": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@inversifyjs/reflect-metadata-utils/-/reflect-metadata-utils-0.2.3.tgz", - "integrity": "sha512-d3D0o9TeSlvaGM2I24wcNw/Aj3rc4OYvHXOKDC09YEph5fMMiKd6fq1VTQd9tOkDNWvVbw+cnt45Wy9P/t5Lvw==", - "dev": true, - "peerDependencies": { - "reflect-metadata": "0.2.2" - } - }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -2672,43 +2645,6 @@ "node": ">=8" } }, - "node_modules/@javascript-obfuscator/escodegen": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@javascript-obfuscator/escodegen/-/escodegen-2.3.1.tgz", - "integrity": "sha512-Z0HEAVwwafOume+6LFXirAVZeuEMKWuPzpFbQhCEU9++BMz0IwEa9bmedJ+rMn/IlXRBID9j3gQ0XYAa6jM10g==", - "dev": true, - "dependencies": { - "@javascript-obfuscator/estraverse": "^5.3.0", - "esprima": "^4.0.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/@javascript-obfuscator/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@javascript-obfuscator/estraverse": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@javascript-obfuscator/estraverse/-/estraverse-5.4.0.tgz", - "integrity": "sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/@jest/create-cache-key-function": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", @@ -3574,12 +3510,6 @@ "node": ">=10" } }, - "node_modules/@types/app-root-path": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@types/app-root-path/-/app-root-path-1.2.8.tgz", - "integrity": "sha512-l12miuN6JXAi3yuADZNhRKbyN7IIyaUP9hFVZ/BbHhWYpBkHLbOaX2WkQoXGJyAgMcP9iZ0S9+tz/FN40VrwWQ==", - "dev": true - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3617,62 +3547,6 @@ "@babel/types": "^7.28.2" } }, - "node_modules/@types/babel-core": { - "version": "6.25.10", - "resolved": "https://registry.npmjs.org/@types/babel-core/-/babel-core-6.25.10.tgz", - "integrity": "sha512-VyqZTf+n8wj+8Powi4k2PwQiHT6ESJb6BEX5IETfmPzdZ9G2sOtFIopdpC2TI3T4Tmf1Shys2QeRv4ZBi4U2EA==", - "dev": true, - "dependencies": { - "@types/babel-generator": "*", - "@types/babel-template": "*", - "@types/babel-traverse": "*", - "@types/babel-types": "*", - "@types/babylon": "*" - } - }, - "node_modules/@types/babel-generator": { - "version": "6.25.8", - "resolved": "https://registry.npmjs.org/@types/babel-generator/-/babel-generator-6.25.8.tgz", - "integrity": "sha512-f5l89J0UpYhTE6TFCxy3X+8pJVru1eig1fcvF9qHmOk9h1VxZimd+++tu5GShntCOdhE/MoZZ0SlpGTyh4XrKg==", - "dev": true, - "dependencies": { - "@types/babel-types": "*" - } - }, - "node_modules/@types/babel-template": { - "version": "6.25.5", - "resolved": "https://registry.npmjs.org/@types/babel-template/-/babel-template-6.25.5.tgz", - "integrity": "sha512-1f+n65xfg9ukqz+BaHYrCWe3Fymv4Ho0i9fU/MHri8MQKU+S4n+vexR5hXs+94d/fsaMLMP45q0hX6FRspKLiQ==", - "dev": true, - "dependencies": { - "@types/babel-types": "*", - "@types/babylon": "*" - } - }, - "node_modules/@types/babel-traverse": { - "version": "6.25.10", - "resolved": "https://registry.npmjs.org/@types/babel-traverse/-/babel-traverse-6.25.10.tgz", - "integrity": "sha512-B3XitTFG8YeXb5lr7Nj62t1DikCDuAJ/4BDeK6GCuWREEmdunI9DWnv+81oHl2yQBQPWY/C1PmV3vaRZB4LQmw==", - "dev": true, - "dependencies": { - "@types/babel-types": "*" - } - }, - "node_modules/@types/babel-types": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.16.tgz", - "integrity": "sha512-5QXs9GBFTNTmilLlWBhnsprqpjfrotyrnzUdwDrywEL/DA4LuCWQT300BTOXA3Y9ngT9F2uvmCoIxI6z8DlJEA==", - "dev": true - }, - "node_modules/@types/babylon": { - "version": "6.16.9", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.9.tgz", - "integrity": "sha512-sEKyxMVEowhcr8WLfN0jJYe4gS4Z9KC2DGz0vqfC7+MXFbmvOF7jSjALC77thvAO2TLgFUPa9vDeOak+AcUrZA==", - "dev": true, - "dependencies": { - "@types/babel-types": "*" - } - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -3739,12 +3613,6 @@ "@types/node": "*" } }, - "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, "node_modules/@types/node": { "version": "25.1.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", @@ -3771,23 +3639,11 @@ "@types/node": "*" } }, - "node_modules/@types/semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", - "dev": true - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, - "node_modules/@types/validator": { - "version": "13.15.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", - "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", - "dev": true - }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -3892,33 +3748,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.4.2" - } - }, "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", @@ -3985,15 +3814,6 @@ "node": ">= 8" } }, - "node_modules/app-root-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", - "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4018,33 +3838,6 @@ "node": ">=10" } }, - "node_modules/array-differ": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", - "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -4072,16 +3865,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/atomically": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz", - "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==", - "dev": true, - "dependencies": { - "stubborn-fs": "^2.0.0", - "when-exit": "^2.1.4" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4106,31 +3889,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "dependencies": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "node_modules/babel-generator/node_modules/jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4215,15 +3973,6 @@ "node": ">=8" } }, - "node_modules/babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", - "dev": true, - "dependencies": { - "babel-runtime": "^6.22.0" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -4339,23 +4088,6 @@ "@babel/plugin-syntax-flow": "^7.12.1" } }, - "node_modules/babel-polyfill": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", - "integrity": "sha512-0l7mVU+LrQ2X/ZTUq63T5i3VyR2aTgcRTFmBcD6djQ/Fek6q1A9t5u0F4jZVYHzp78jwWAzGfLpAY1b4/I3lfg==", - "dev": true, - "dependencies": { - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" - } - }, - "node_modules/babel-polyfill/node_modules/regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==", - "dev": true - }, "node_modules/babel-preset-current-node-syntax": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", @@ -4396,34 +4128,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "dev": true, - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "node_modules/babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", - "dev": true, - "dependencies": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, "node_modules/badgin": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz", @@ -4724,36 +4428,6 @@ "node": ">=4" } }, - "node_modules/chance": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.13.tgz", - "integrity": "sha512-V6lQCljcLznE7tUYUM9EOAnnKXbctE6j/rdQkYOHIWbfGQbrzTsAXNW9CdU5XCo4ArXQCj/rb6HgxPlmGJcaUg==", - "dev": true - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", - "dev": true - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -4819,17 +4493,6 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, - "node_modules/class-validator": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", - "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", - "dev": true, - "dependencies": { - "@types/validator": "^13.15.3", - "libphonenumber-js": "^1.11.1", - "validator": "^13.15.20" - } - }, "node_modules/cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -4852,12 +4515,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -5019,29 +4676,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/conf": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/conf/-/conf-15.0.2.tgz", - "integrity": "sha512-JBSrutapCafTrddF9dH3lc7+T2tBycGF4uPkI4Js+g4vLLEhG6RZcFi3aJd5zntdf5tQxAejJt8dihkoQ/eSJw==", - "dev": true, - "dependencies": { - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "atomically": "^2.0.3", - "debounce-fn": "^6.0.0", - "dot-prop": "^10.0.0", - "env-paths": "^3.0.0", - "json-schema-typed": "^8.0.1", - "semver": "^7.7.2", - "uint8array-extras": "^1.5.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -5074,14 +4708,6 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, - "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true - }, "node_modules/core-js-compat": { "version": "3.48.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", @@ -5115,15 +4741,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -5146,21 +4763,6 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "devOptional": true }, - "node_modules/debounce-fn": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-6.0.0.tgz", - "integrity": "sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==", - "dev": true, - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -5220,12 +4822,6 @@ "node": ">=4.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -5319,18 +4915,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", - "dev": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -5344,36 +4928,6 @@ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, - "node_modules/dot-prop": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz", - "integrity": "sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==", - "dev": true, - "dependencies": { - "type-fest": "^5.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/type-fest": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz", - "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==", - "dev": true, - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -5439,7 +4993,8 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -5448,7 +5003,8 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -5473,18 +5029,6 @@ "node": ">=8" } }, - "node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/error-stack-parser": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", @@ -5555,91 +5099,6 @@ "node": ">=0.8.0" } }, - "node_modules/escodegen-wallaby": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/escodegen-wallaby/-/escodegen-wallaby-1.6.15.tgz", - "integrity": "sha512-tGP81y7GbgaW/IBHL4D/lauhWT/eYwVVxnTJ0eHVIw/jI5zLJb0raupZIyFwr2381OVr2MVrE7swZkugn0Dc/A==", - "dev": true, - "dependencies": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.10.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, - "node_modules/escodegen-wallaby/node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/escodegen-wallaby/node_modules/estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/escodegen-wallaby/node_modules/source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -5652,36 +5111,6 @@ "node": ">=4" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -6665,20 +6094,6 @@ "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==" }, - "node_modules/external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "dependencies": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6689,12 +6104,6 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -6745,18 +6154,6 @@ "asap": "~2.0.3" } }, - "node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -7098,27 +6495,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -7279,18 +6655,6 @@ "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==" }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -7368,134 +6732,6 @@ "css-in-js-utils": "^3.1.0" } }, - "node_modules/inquirer": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz", - "integrity": "sha512-thluxTGBXUGb8DuQcvH9/CM/CrcGyB5xUpWc9x6Slqcq1z/hRr2a6KxUpX4ddRfmbe0hg3E4jTvo5833aWz3BA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^1.1.0", - "chalk": "^1.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.1", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx": "^4.1.0", - "string-width": "^2.0.0", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - } - }, - "node_modules/inquirer/node_modules/ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inquirer/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/inquirer/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/inquirer/node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/inquirer/node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -7504,16 +6740,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/inversify": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/inversify/-/inversify-6.1.4.tgz", - "integrity": "sha512-PbxrZH/gTa1fpPEEGAjJQzK8tKMIp5gRg6EFNJlCtzUcycuNdmhv3uk5P8Itm/RIjgHJO16oQRLo9IHzQN51bA==", - "dev": true, - "dependencies": { - "@inversifyjs/common": "1.3.3", - "@inversifyjs/core": "1.3.4" - } - }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -7534,12 +6760,6 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==" }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -7579,18 +6799,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -7665,15 +6873,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", @@ -7735,137 +6934,6 @@ "semver": "bin/semver.js" } }, - "node_modules/javascript-obfuscator": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/javascript-obfuscator/-/javascript-obfuscator-4.2.2.tgz", - "integrity": "sha512-+7oXAUnFCA6vS0omIGHcWpSr67dUBIF7FKGYSXyzxShSLqM6LBgdugWKFl0XrYtGWyJMGfQR5F4LL85iCefkRA==", - "dev": true, - "dependencies": { - "@javascript-obfuscator/escodegen": "2.3.1", - "@javascript-obfuscator/estraverse": "5.4.0", - "acorn": "8.15.0", - "assert": "2.1.0", - "chalk": "4.1.2", - "chance": "1.1.13", - "class-validator": "0.14.3", - "commander": "12.1.0", - "conf": "15.0.2", - "eslint-scope": "8.4.0", - "eslint-visitor-keys": "4.2.1", - "fast-deep-equal": "3.1.3", - "inversify": "6.1.4", - "js-string-escape": "1.0.1", - "md5": "2.3.0", - "mkdirp": "3.0.1", - "multimatch": "5.0.0", - "process": "0.11.10", - "reflect-metadata": "0.2.2", - "source-map-support": "0.5.21", - "string-template": "1.0.0", - "stringz": "2.1.0", - "tslib": "2.8.1" - }, - "bin": { - "javascript-obfuscator": "bin/javascript-obfuscator" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/javascript-obfuscator/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/javascript-obfuscator/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/javascript-obfuscator/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/javascript-obfuscator/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/javascript-obfuscator/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/javascript-obfuscator/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/javascript-obfuscator/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/javascript-obfuscator/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", @@ -8246,21 +7314,6 @@ "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==" }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true - }, - "node_modules/js-string-escape": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", - "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8305,12 +7358,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "dev": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -8363,25 +7410,6 @@ "node": ">=6" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/libphonenumber-js": { - "version": "1.12.40", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.40.tgz", - "integrity": "sha512-HKGs7GowShNls3Zh+7DTr6wYpPk5jC78l508yQQY3e8ZgJChM3A9JZghmMJZuK+5bogSfuTafpjksGSR3aMIEg==", - "dev": true - }, "node_modules/lighthouse-logger": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", @@ -8657,12 +7685,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -8733,17 +7755,6 @@ "node": ">= 0.4" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dev": true, - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", @@ -9147,18 +8158,6 @@ "node": ">=4" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -9225,53 +8224,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/multimatch": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", - "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", - "dev": true, - "dependencies": { - "@types/minimatch": "^3.0.3", - "array-differ": "^3.0.0", - "array-union": "^2.1.0", - "arrify": "^2.0.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/multimatch/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/multimatch/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", - "dev": true - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -9502,125 +8454,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/opencollective": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz", - "integrity": "sha512-YBRI0Qa8+Ui0/STV1qYuPrJm889PT3oCPHMVoL+8Y3nwCffj7PSrB2NlGgrhgBKDujxTjxknHWJ/FiqOsYcIDw==", - "dev": true, - "dependencies": { - "babel-polyfill": "6.23.0", - "chalk": "1.1.3", - "inquirer": "3.0.6", - "minimist": "1.2.0", - "node-fetch": "1.6.3", - "opn": "4.0.2" - }, - "bin": { - "oc": "dist/bin/opencollective.js", - "opencollective": "dist/bin/opencollective.js" - } - }, - "node_modules/opencollective/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/opencollective/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/opencollective/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/opencollective/node_modules/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha512-7Wl+Jz+IGWuSdgsQEJ4JunV0si/iMhg42MnQQG6h1R6TNeVenp4U9x5CC5v/gYqz/fENLQITAWXidNtVL0NNbw==", - "dev": true - }, - "node_modules/opencollective/node_modules/node-fetch": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", - "integrity": "sha512-BDxbhLHXFFFvilHjh9xihcDyPkXQ+kjblxnl82zAX41xUYSNvuRpFRznmldR9+OKu+p+ULZ7hNoyunlLB5ecUA==", - "dev": true, - "dependencies": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node_modules/opencollective/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/opencollective/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/opn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", - "integrity": "sha512-iPBWbPP4OEOzR1xfhpGLDh+ypKBOygunZhM9jBtA7FS5sKjEiMZw0EFb82hnDOmTZX90ZWLoZKUza4cVt8MexA==", - "dev": true, - "dependencies": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", @@ -9656,15 +8489,6 @@ "node": ">=6" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -9808,27 +8632,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -9837,12 +8640,6 @@ "node": ">= 6" } }, - "node_modules/pjson": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/pjson/-/pjson-1.0.9.tgz", - "integrity": "sha512-4hRJH3YzkUpOlShRzhyxAmThSNnAaIlWZCAb27hd0pVUAXNUAHAO7XZbsPPvsCYwBFEScTmCCL6DGE8NyZ8BdQ==", - "dev": true - }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -9904,15 +8701,6 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -9956,15 +8744,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -10232,227 +9011,6 @@ } } }, - "node_modules/react-native-obfuscating-transformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/react-native-obfuscating-transformer/-/react-native-obfuscating-transformer-1.0.0.tgz", - "integrity": "sha512-DrKt9ZxhxZfwDUM7M6OnTThc5lQd80sVMyVqV2fFfVRbZGYt8lnXGGd/i5HAlejkieJ9SQsffoYa6rfWA33rYw==", - "dev": true, - "dependencies": { - "@types/app-root-path": "^1.2.4", - "@types/babel-core": "^6.25.3", - "@types/babel-generator": "^6.25.1", - "@types/node": "^9.3.0", - "@types/semver": "^5.4.0", - "app-root-path": "^2.0.1", - "babel-generator": "^6.26.1", - "javascript-obfuscator": "^0.13.0", - "jju": "^1.3.0", - "semver": "^5.4.1", - "source-map": "^0.6.1" - }, - "peerDependencies": { - "react-native": ">=0.45.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/@types/node": { - "version": "9.6.61", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.61.tgz", - "integrity": "sha512-/aKAdg5c8n468cYLy2eQrcR5k6chlbNwZNGUj3TboyPa2hcO2QAJcfymlqPzMiRj8B6nYKXjzQz36minFE0RwQ==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/chance": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/chance/-/chance-1.0.12.tgz", - "integrity": "sha512-W99uMuboG5CT1iToDmizEH6yQYqICzZnrSRbbXPuJErzFWLPaoiEDvwnKbESjDo/8st1n3pyh70VBMmfqPmf+Q==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/class-validator": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.7.3.tgz", - "integrity": "sha512-aRRlS1WlQ+52aHlmDCDX5dLwtpbg9is7i9yYhzQosTAVs86nX0Um8hb7ChTwMn7jfpyxxjAZpBrlrAc2tqNpYA==", - "dev": true, - "dependencies": { - "validator": "^7.0.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha512-VHvyaGnJy+FuGfcfaM7W7OZw4mQiKW73jPHwQXx2VnMSUBajYmytOT5sKEfsBvNPtGX6YDwcrGDz2eocoHg0JA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/inversify": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/inversify/-/inversify-4.8.0.tgz", - "integrity": "sha512-d3Dqjq9yFB+2a680OeN8STC5QVRvIemFbicuwsK7scE+YnFrIPBfKPEWePUz/e8+mf0hyXNgiZSGtMCIkY6ZjQ==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/javascript-obfuscator": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/javascript-obfuscator/-/javascript-obfuscator-0.13.0.tgz", - "integrity": "sha512-81vHJ3i1A+reeD5CcIVK1n0EGGRYBlT7dWx8vMDJRAb0kGnaS48WQEKJ056ybHEMLHtPvS2IuRanISmL7b9SdQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "chalk": "2.3.0", - "chance": "1.0.12", - "class-validator": "0.7.3", - "commander": "2.12.2", - "escodegen-wallaby": "1.6.15", - "esprima": "4.0.0", - "estraverse": "4.2.0", - "inversify": "4.8.0", - "md5": "2.2.1", - "mkdirp": "0.5.1", - "opencollective": "1.0.3", - "pjson": "^1.0.9", - "reflect-metadata": "0.1.10", - "source-map-support": "0.5.0", - "string-template": "1.0.0", - "tslib": "1.8.1" - }, - "bin": { - "javascript-obfuscator": "bin/javascript-obfuscator.js" - }, - "engines": { - "node": ">=4.8.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==", - "dev": true, - "dependencies": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/reflect-metadata": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", - "integrity": "sha512-oYJQNaTL+aECnOmLtlsD+eyeNXfXm8VSg1MOENAUnYsRKNCkk4uq5zohyZsUwG2CDHa4dZ/qExt5Xzw+9pGpww==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/source-map-support": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.0.tgz", - "integrity": "sha512-vUoN3I7fHQe0R/SJLKRdKYuEdRGogsviXFkHHo17AWaTGv17VLnxw+CFXvqy+y4ORZ3doWLQcxRYfwKrsd/H7Q==", - "dev": true, - "dependencies": { - "source-map": "^0.6.0" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==", - "dev": true, - "dependencies": { - "has-flag": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/react-native-obfuscating-transformer/node_modules/tslib": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.1.tgz", - "integrity": "sha512-13/fEL8ro991mjA1w35XI/FSHF4dI2L+zNxkJJnGUcjFG4Ht+3ztBusOmg/DFiOks5NKJX3wXSG+q/nKUmhe5g==", - "dev": true - }, - "node_modules/react-native-obfuscating-transformer/node_modules/validator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-7.2.0.tgz", - "integrity": "sha512-c8NGTUYeBEcUIGeMppmNVKHE7wwfm3mYbNZxV+c5mlv9fDHI7Ad3p07qfNrn/CvpdkK2k61fOLRO2sTEhgQXmg==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/react-native-reanimated": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.6.tgz", @@ -10717,12 +9275,6 @@ } } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "dev": true - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -10776,18 +9328,6 @@ "regjsparser": "bin/parser" } }, - "node_modules/repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", - "dev": true, - "dependencies": { - "is-finite": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10961,21 +9501,6 @@ "node": "*" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -11015,7 +9540,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/sax": { "version": "1.4.4", @@ -11357,12 +9883,6 @@ "node": ">=4" } }, - "node_modules/string-template": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz", - "integrity": "sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg==", - "dev": true - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -11376,15 +9896,6 @@ "node": ">=8" } }, - "node_modules/stringz": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/stringz/-/stringz-2.1.0.tgz", - "integrity": "sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11409,21 +9920,6 @@ "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==" }, - "node_modules/stubborn-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz", - "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==", - "dev": true, - "dependencies": { - "stubborn-utils": "^1.0.1" - } - }, - "node_modules/stubborn-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz", - "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==", - "dev": true - }, "node_modules/styleq": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.1.3.tgz", @@ -11511,18 +10007,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tagged-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", - "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", - "dev": true, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tar": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz", @@ -11668,12 +10152,6 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -11716,32 +10194,11 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" }, - "node_modules/to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -11766,15 +10223,6 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -11785,18 +10233,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -11851,18 +10287,6 @@ "node": "*" } }, - "node_modules/uint8array-extras": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/undici": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", @@ -12053,15 +10477,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/validator": { - "version": "13.15.26", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", - "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -12318,12 +10733,6 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "node_modules/when-exit": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz", - "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==", - "dev": true - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -12363,15 +10772,6 @@ "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==" }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/mobile/src/api/api.ts b/mobile/src/api/api.ts index 8f279981..7f9de019 100644 --- a/mobile/src/api/api.ts +++ b/mobile/src/api/api.ts @@ -12,7 +12,7 @@ import type { import { getToken } from "../auth/tokenStorage"; import { extractUsernameFromToken } from "../auth/jwtUtils"; -const V1 = "https://5.181.0.112.nip.io/api/v1"; +const V1 = "https://mln-uber.club/api/v1"; export const getJwtUsername = async (): Promise => { const token = await getToken(); @@ -787,6 +787,7 @@ export interface PublicSettings { crypto_only: boolean; nowpayments_currencies: string[]; telegram_notifications_enabled: boolean; + two_fa_enabled: boolean; } export const getPublicSettings = async (): Promise => { @@ -801,6 +802,7 @@ export const getPublicSettings = async (): Promise => { crypto_only: false, nowpayments_currencies: [], telegram_notifications_enabled: false, + two_fa_enabled: false, }; try { const { data } = await apiClient.get(`${V1}/app-settings`); @@ -821,6 +823,7 @@ export const getPublicSettings = async (): Promise => { : [], telegram_notifications_enabled: data.telegram_notifications_enabled ?? false, + two_fa_enabled: data.two_fa_enabled ?? false, }; } catch { return defaults; @@ -889,6 +892,30 @@ export const unlinkTelegram = async (): Promise => { } }; +export const get2FAStatus = async (): Promise<{ + two_fa_enabled: boolean; + telegram_linked: boolean; + admin_2fa_enabled: boolean; +}> => { + try { + const { data } = await apiClient.get(`${V1}/two-fa/status`); + return data; + } catch { + return { two_fa_enabled: false, telegram_linked: false, admin_2fa_enabled: false }; + } +}; + +export const toggle2FA = async ( + enabled: boolean, +): Promise<{ success: boolean; error?: string }> => { + try { + const { data } = await apiClient.post(`${V1}/two-fa/toggle`, { enabled }); + return { success: data.success ?? true }; + } catch (e: any) { + return { success: false, error: e?.response?.data?.error || "Erreur" }; + } +}; + export const calculateOrderTotal = (order: any): number => { if (typeof order.total === "number" && order.total > 0) return order.total; if (typeof order.total_prix === "number" && order.total_prix > 0) diff --git a/mobile/src/api/client.ts b/mobile/src/api/client.ts index e257b745..9b25dc01 100644 --- a/mobile/src/api/client.ts +++ b/mobile/src/api/client.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { getToken, getAdminToken } from "../auth/tokenStorage"; // Change this to your server IP/domain -export const API_BASE_URL = "https://5.181.0.112.nip.io"; +export const API_BASE_URL = "https://mln-uber.club"; const apiClient = axios.create({ baseURL: API_BASE_URL, @@ -12,9 +12,7 @@ const apiClient = axios.create({ }, }); -// Request interceptor: attach JWT token apiClient.interceptors.request.use(async (config) => { - // Determine which token to use based on URL const isAdminRoute = config.url?.includes("/api/v2/") || config.url?.includes("/api/v1/cabine/") || diff --git a/mobile/src/screens/client/OrderHistoryScreen.tsx b/mobile/src/screens/client/OrderHistoryScreen.tsx index fc1db9fd..6f8614f5 100644 --- a/mobile/src/screens/client/OrderHistoryScreen.tsx +++ b/mobile/src/screens/client/OrderHistoryScreen.tsx @@ -45,7 +45,7 @@ export default function OrderHistoryScreen() { const [orders, setOrders] = useState([]); const [stats, setStats] = useState(null); const [penalties, setPenalties] = useState(null); - const [appSettings, setAppSettings] = useState({ penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: false, pool_names: ['Pool 1', 'Pool 2'], crypto_payment_enabled: false, nowpayments_currencies: [], crypto_only: false, telegram_notifications_enabled: false }); + const [appSettings, setAppSettings] = useState({ penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: false, pool_names: ['Pool 1', 'Pool 2'], crypto_payment_enabled: false, nowpayments_currencies: [], crypto_only: false, telegram_notifications_enabled: false, two_fa_enabled: false }); const [referralBalance, setReferralBalance] = useState(0); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); diff --git a/mobile/src/screens/client/ProfileScreen.tsx b/mobile/src/screens/client/ProfileScreen.tsx index 00d3184c..49c84997 100644 --- a/mobile/src/screens/client/ProfileScreen.tsx +++ b/mobile/src/screens/client/ProfileScreen.tsx @@ -6,6 +6,7 @@ import { ScrollView, StyleSheet, TouchableOpacity, + Switch, Alert, Modal, KeyboardAvoidingView, @@ -17,7 +18,7 @@ import { Feather } from "@expo/vector-icons"; import { useFocusEffect } from "@react-navigation/native"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { Ionicons } from "@expo/vector-icons"; -import { getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram, changePassword } from "../../api/api"; +import { getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram, changePassword, get2FAStatus, toggle2FA, getPublicSettings } from "../../api/api"; import TextInput from "../../components/ui/TextInput"; import Button from "../../components/ui/Button"; import { useTheme } from "../../context/ThemeContext"; @@ -49,6 +50,11 @@ export default function ProfileScreen() { const [telegramEnabled, setTelegramEnabled] = useState(false); const [telegramLoading, setTelegramLoading] = useState(false); + // 2FA + const [twoFAEnabled, setTwoFAEnabled] = useState(false); + const [twoFAAdminEnabled, setTwoFAAdminEnabled] = useState(false); + const [twoFALoading, setTwoFALoading] = useState(false); + // Modal confirmation infos par défaut const [showSaveModal, setShowSaveModal] = useState(false); @@ -64,15 +70,19 @@ export default function ProfileScreen() { const loadData = useCallback(async () => { setLoadingProfile(true); - const [savedAddress, savedPhone, savedSignal, profileRes, tgStatus] = await Promise.all([ + const [savedAddress, savedPhone, savedSignal, profileRes, tgStatus, twoFAStatus, pubSettings] = await Promise.all([ AsyncStorage.getItem(STORAGE_ADDRESS), AsyncStorage.getItem(STORAGE_PHONE), AsyncStorage.getItem(STORAGE_SIGNAL), getMyProfile(), getTelegramStatus(), + get2FAStatus(), + getPublicSettings(), ]); setTelegramLinked(tgStatus.linked); setTelegramEnabled(tgStatus.enabled); + setTwoFAEnabled(twoFAStatus.two_fa_enabled); + setTwoFAAdminEnabled(pubSettings.two_fa_enabled); if (savedAddress !== null) setDefaultAddress(savedAddress); if (savedPhone !== null) setDefaultPhone(savedPhone); @@ -136,6 +146,7 @@ export default function ProfileScreen() { onPress: async () => { await unlinkTelegram(); setTelegramLinked(false); + setTwoFAEnabled(false); }, }, ], @@ -170,6 +181,17 @@ export default function ProfileScreen() { } }; + const handleToggle2FA = async (value: boolean) => { + setTwoFALoading(true); + const res = await toggle2FA(value); + setTwoFALoading(false); + if (res.success) { + setTwoFAEnabled(value); + } else { + Alert.alert("Erreur", res.error || "Impossible de modifier la 2FA"); + } + }; + const saveContact = async () => { setSavingContact(true); const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() }); @@ -327,6 +349,17 @@ export default function ProfileScreen() { }, pwdInputIcon: { marginRight: spacing.s }, pwdInput: { flex: 1, fontSize: fontSize.sm }, + twoFARow: { + flexDirection: "row" as const, + alignItems: "center" as const, + justifyContent: "space-between" as const, + paddingTop: spacing.xs, + }, + twoFALabel: { + color: colors.textPrimary, + fontSize: fontSize.sm, + fontWeight: fontWeight.semibold, + }, }), [colors]); if (loadingProfile) { @@ -505,6 +538,42 @@ export default function ProfileScreen() {
)} + {/* Carte 2FA — visible uniquement si l'admin l'a activé ET Telegram est lié */} + {twoFAAdminEnabled && telegramLinked && ( + + + + Double authentification (2FA) + + + À chaque connexion, un code à 6 chiffres vous sera envoyé sur Telegram avant d'accéder à votre compte. + + + + + {twoFAEnabled ? "Activée" : "Désactivée"} + + {twoFAEnabled && ( + + + Protection activée + + )} + + {twoFALoading ? ( + + ) : ( + + )} + + + )} +