diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 4fa3d2bc0..2b8b5a5e2 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -77,7 +77,7 @@ jobs: fi - name: Log in to Docker Hub - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: docker.io username: ${{ secrets.DOCKER_HUB_USERNAME }} @@ -149,7 +149,7 @@ jobs: fi - name: Log in to Docker Hub - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: docker.io username: ${{ secrets.DOCKER_HUB_USERNAME }} @@ -204,7 +204,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Log in to Docker Hub - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: docker.io username: ${{ secrets.DOCKER_HUB_USERNAME }} @@ -407,7 +407,7 @@ jobs: shell: bash - name: Login to GitHub Container Registry (for cosign) - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index cf574dd3c..ba60e6337 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '24' diff --git a/.github/workflows/mirror.yaml b/.github/workflows/mirror.yaml index f60922d21..022b4bbb9 100644 --- a/.github/workflows/mirror.yaml +++ b/.github/workflows/mirror.yaml @@ -23,7 +23,7 @@ jobs: skopeo --version - name: Install cosign - uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - name: Input check run: | diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index 2db8632e9..e7a03a98d 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -14,7 +14,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: days-before-stale: 14 days-before-close: 14 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 30567f0f7..970cd0dc9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Node - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '24' diff --git a/install/config/config.yml b/install/config/config.yml index 90d1bf5d5..1f50057e8 100644 --- a/install/config/config.yml +++ b/install/config/config.yml @@ -22,7 +22,8 @@ server: methods: ["GET", "POST", "PUT", "DELETE", "PATCH"] allowed_headers: ["X-CSRF-Token", "Content-Type"] credentials: false - {{if .EnableGeoblocking}}maxmind_db_path: "./config/GeoLite2-Country.mmdb"{{end}} + {{if .EnableMaxMind}}maxmind_db_path: "./config/GeoLite2-Country.mmdb"{{end}} + {{if .EnableMaxMind}}maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"{{end}} {{if .EnableEmail}} email: smtp_host: "{{.EmailSMTPHost}}" diff --git a/install/main.go b/install/main.go index b3979ace9..f12a0c769 100644 --- a/install/main.go +++ b/install/main.go @@ -54,7 +54,7 @@ type Config struct { InstallGerbil bool TraefikBouncerKey string DoCrowdsecInstall bool - EnableGeoblocking bool + EnableMaxMind bool Secret string IsEnterprise bool } @@ -123,11 +123,11 @@ func main() { fmt.Println("\nConfiguration files created successfully!") - // Download MaxMind database if requested - if config.EnableGeoblocking { - fmt.Println("\n=== Downloading MaxMind Database ===") + // Download MaxMind Country / ASN database if requested + if config.EnableMaxMind { + fmt.Println("\n=== Downloading MaxMind Country and ASN Databases ===") if err := downloadMaxMindDatabase(); err != nil { - fmt.Printf("Error downloading MaxMind database: %v\n", err) + fmt.Printf("Error downloading MaxMind databases: %v\n", err) fmt.Println("You can download it manually later if needed.") } } @@ -188,15 +188,15 @@ func main() { fmt.Println("\n=== MaxMind Database Update ===") if _, err := os.Stat("config/GeoLite2-Country.mmdb"); err == nil { fmt.Println("MaxMind GeoLite2 Country database found.") - if readBool("Would you like to update the MaxMind database to the latest version?", false) { + if readBool("Would you like to update the MaxMind databases (Country and ASN) to the latest version?", false) { if err := downloadMaxMindDatabase(); err != nil { fmt.Printf("Error updating MaxMind database: %v\n", err) fmt.Println("You can try updating it manually later if needed.") } } } else { - fmt.Println("MaxMind GeoLite2 Country database not found.") - if readBool("Would you like to download the MaxMind GeoLite2 database for geoblocking functionality?", false) { + fmt.Println("MaxMind GeoLite2 Country and ASN databases not found.") + if readBool("Would you like to download the MaxMind GeoLite2 databases for blocking functionality?", false) { if err := downloadMaxMindDatabase(); err != nil { fmt.Printf("Error downloading MaxMind database: %v\n", err) fmt.Println("You can try downloading it manually later if needed.") @@ -204,8 +204,10 @@ func main() { // Now you need to update your config file accordingly to enable geoblocking fmt.Print("Please remember to update your config/config.yml file to enable geoblocking! \n\n") // add maxmind_db_path: "./config/GeoLite2-Country.mmdb" under server - fmt.Println("Add the following line under the 'server' section:") + // add maxmind_asn_path: "./config/GeoLite2-ASN.mmdb" under server + fmt.Println("Add the following lines under the 'server' section:") fmt.Println(" maxmind_db_path: \"./config/GeoLite2-Country.mmdb\"") + fmt.Println(" maxmind_asn_path: \"./config/GeoLite2-ASN.mmdb\"") } } } @@ -527,7 +529,7 @@ func collectUserInput() Config { fmt.Println("\n=== Advanced Configuration ===") config.EnableIPv6 = readBool("Is your server IPv6 capable?", true) - config.EnableGeoblocking = readBool("Do you want to download the MaxMind GeoLite2 database for geoblocking functionality?", true) + config.EnableMaxMind = readBool("Do you want to download the MaxMind GeoLite2 Country and ADN databases for blocking functionality?", true) if config.DashboardDomain == "" { fmt.Println("Error: Dashboard Domain name is required") @@ -780,29 +782,42 @@ func checkPortsAvailable(port int) error { } func downloadMaxMindDatabase() error { - fmt.Println("Downloading MaxMind GeoLite2 Country database...") + fmt.Println("Downloading MaxMind GeoLite2 Country and ASN databases...") - // Download the GeoLite2 Country database + // Download the GeoLite2 Country databases if err := run("curl", "-L", "-o", "GeoLite2-Country.tar.gz", "https://github.com/GitSquared/node-geolite2-redist/raw/refs/heads/master/redist/GeoLite2-Country.tar.gz"); err != nil { - return fmt.Errorf("failed to download GeoLite2 database: %v", err) + return fmt.Errorf("failed to download GeoLite2 Country database: %v", err) + } + if err := run("curl", "-L", "-o", "GeoLite2-ASN.tar.gz", + "https://github.com/GitSquared/node-geolite2-redist/raw/refs/heads/master/redist/GeoLite2-ASN.tar.gz"); err != nil { + return fmt.Errorf("failed to download GeoLite2 ASN database: %v", err) } - // Extract the database + // Extract the Country database if err := run("tar", "-xzf", "GeoLite2-Country.tar.gz"); err != nil { - return fmt.Errorf("failed to extract GeoLite2 database: %v", err) + return fmt.Errorf("failed to extract GeoLite2 Country database: %v", err) + } + if err := run("tar", "-xzf", "GeoLite2-ASN.tar.gz"); err != nil { + return fmt.Errorf("failed to extract GeoLite2 ASN database: %v", err) } // Find the .mmdb file and move it to the config directory if err := run("bash", "-c", "mv GeoLite2-Country_*/GeoLite2-Country.mmdb config/"); err != nil { - return fmt.Errorf("failed to move GeoLite2 database to config directory: %v", err) + return fmt.Errorf("failed to move GeoLite2 Country database to config directory: %v", err) + } + if err := run("bash", "-c", "mv GeoLite2-ASN_*/GeoLite2-ASN.mmdb config/"); err != nil { + return fmt.Errorf("failed to move GeoLite2 ASN database to config directory: %v", err) } // Clean up the downloaded files - if err := run("rm", "-rf", "GeoLite2-Country.tar.gz", "GeoLite2-Country_*"); err != nil { - fmt.Printf("Warning: failed to clean up temporary files: %v\n", err) + if err := run("sh", "-c", "rm -rf GeoLite2-Country.tar.gz GeoLite2-Country_*"); err != nil { + fmt.Printf("Warning: failed to clean up temporary country files: %v\n", err) + } + if err := run("sh", "-c", "rm -rf GeoLite2-ASN.tar.gz GeoLite2-ASN_*"); err != nil { + fmt.Printf("Warning: failed to clean up temporary ASN files: %v\n", err) } - fmt.Println("MaxMind GeoLite2 Country database downloaded successfully!") + fmt.Println("MaxMind GeoLite2 Country and ASN database downloaded successfully!") return nil } diff --git a/messages/en-US.json b/messages/en-US.json index 027d9fc38..63929b544 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1987,7 +1987,7 @@ "sshSudoModeCommandsDescription": "User can run only the specified commands with sudo.", "sshSudo": "Allow sudo", "sshSudoCommands": "Sudo Commands", - "sshSudoCommandsDescription": "Comma separated list of commands the user is allowed to run with sudo.", + "sshSudoCommandsDescription": "Comma separated list of commands the user is allowed to run with sudo. Absolute paths must be used.", "sshCreateHomeDir": "Create Home Directory", "sshUnixGroups": "Unix Groups", "sshUnixGroupsDescription": "Comma separated Unix groups to add the user to on the target host.", diff --git a/package-lock.json b/package-lock.json index 69ad91121..fb0e39ff9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,9 +41,9 @@ "@react-email/render": "2.0.8", "@react-email/tailwind": "2.0.7", "@simplewebauthn/browser": "13.3.0", - "@simplewebauthn/server": "13.3.0", + "@simplewebauthn/server": "13.3.1", "@tailwindcss/forms": "0.5.11", - "@tanstack/react-query": "5.100.10", + "@tanstack/react-query": "5.100.14", "@tanstack/react-table": "8.21.3", "arctic": "3.7.0", "axios": "1.16.1", @@ -75,7 +75,7 @@ "next-themes": "0.4.6", "nextjs-toploader": "3.9.17", "node-cache": "5.1.2", - "nodemailer": "8.0.7", + "nodemailer": "8.0.9", "oslo": "1.2.1", "pg": "8.20.0", "posthog-node": "5.34.1", @@ -88,8 +88,7 @@ "react-icons": "5.6.0", "recharts": "3.8.1", "reodotdev": "1.1.0", - "resend": "6.12.3", - "semver": "7.8.0", + "semver": "7.8.1", "sshpk": "1.18.0", "stripe": "20.4.1", "swagger-ui-express": "5.0.1", @@ -109,9 +108,9 @@ "zod-validation-error": "5.0.0" }, "devDependencies": { - "@dotenvx/dotenvx": "1.66.0", + "@dotenvx/dotenvx": "1.69.1", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/ui": "^6.1.4", + "@react-email/ui": "^6.5.0", "@tailwindcss/postcss": "4.3.0", "@tanstack/react-query-devtools": "5.100.10", "@types/better-sqlite3": "7.6.13", @@ -124,7 +123,7 @@ "@types/jmespath": "0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "9.0.10", - "@types/node": "25.8.0", + "@types/node": "25.9.1", "@types/nodemailer": "8.0.0", "@types/nprogress": "0.2.3", "@types/pg": "8.20.0", @@ -140,16 +139,16 @@ "drizzle-kit": "0.31.10", "esbuild": "0.28.0", "esbuild-node-externals": "1.22.0", - "eslint": "10.3.0", + "eslint": "10.4.0", "eslint-config-next": "16.2.6", "postcss": "8.5.14", "prettier": "3.8.3", - "react-email": "6.1.4", + "react-email": "6.5.0", "tailwindcss": "4.3.0", "tsc-alias": "1.8.17", "tsx": "4.22.0", "typescript": "6.0.3", - "typescript-eslint": "8.59.3" + "typescript-eslint": "8.60.0" } }, "node_modules/@alloc/quick-lru": { @@ -1260,15 +1259,16 @@ "license": "MIT" }, "node_modules/@dotenvx/dotenvx": { - "version": "1.66.0", - "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.66.0.tgz", - "integrity": "sha512-qlQFhHUjhRDybrinqLAD0MClVZDOrsq80O8eD5iSjz3Qa/4f3Jg7SQrOaSobrRyP1QaWIYLGtGpj2c7H0D8NUw==", + "version": "1.69.1", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.69.1.tgz", + "integrity": "sha512-kwQB5KcAegxw/+NGUgXAo5ovyOSjlMhoXSSnSEpDhoHJwzMcMO0HE1U0VCYZ7jbAeCMGamed9XdWzOA5ixtTNg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", + "enquirer": "^2.4.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", @@ -1877,9 +1877,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2222,6 +2222,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2238,6 +2241,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2254,6 +2260,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2270,6 +2279,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2286,6 +2298,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2318,6 +2333,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2350,6 +2368,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2372,6 +2393,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2394,6 +2418,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2416,6 +2443,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2438,6 +2468,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2482,6 +2515,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2774,6 +2810,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2790,6 +2829,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3045,6 +3087,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3061,6 +3106,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3298,6 +3346,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3314,6 +3365,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3668,6 +3722,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3688,6 +3745,9 @@ "cpu": [ "arm" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3708,6 +3768,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3728,6 +3791,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -6590,9 +6656,9 @@ } }, "node_modules/@react-email/ui": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@react-email/ui/-/ui-6.1.4.tgz", - "integrity": "sha512-FyMCx7uNV0ugZY9ZWNAnWFTrXnYOhRQm97ybbZapJCQLu6uhZHmluXv2HcsSZ0RaMAzuLJgw0ziLQjq4uYEiNg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@react-email/ui/-/ui-6.5.0.tgz", + "integrity": "sha512-WWPwCW5B0ouZ0GFtpKJLF2DBptJannXUO1VOncOIg5WsTJQ/4tEnbf300i4vWFnzI83L4n3ObfEods75cOsp3w==", "dev": true, "license": "MIT", "dependencies": { @@ -6685,9 +6751,9 @@ "license": "MIT" }, "node_modules/@simplewebauthn/server": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.0.tgz", - "integrity": "sha512-MLHYFrYG8/wK2i+86XMhiecK72nMaHKKt4bo+7Q1TbuG9iGjlSdfkPWKO5ZFE/BX+ygCJ7pr8H/AJeyAj1EaTQ==", + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.1.tgz", + "integrity": "sha512-GV/oM/qeycWn8p42JZIMJBsXWQcNFg+nJFzeQTnMA4gN8mXg0+HZFWJerHg8ZN/zlveMS3iV1wzuFpOVWS/46w==", "license": "MIT", "dependencies": { "@hexagon/base64": "^1.1.27", @@ -6840,12 +6906,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@stablelib/base64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", - "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", - "license": "MIT" - }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -6953,6 +7013,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6969,6 +7032,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6985,6 +7051,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -7001,6 +7070,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -7268,6 +7340,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -7285,6 +7360,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -7358,72 +7436,6 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", @@ -7473,9 +7485,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.100.10", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.10.tgz", - "integrity": "sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w==", + "version": "5.100.14", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.14.tgz", + "integrity": "sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==", "license": "MIT", "funding": { "type": "github", @@ -7494,12 +7506,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.100.10", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.10.tgz", - "integrity": "sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q==", + "version": "5.100.14", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.14.tgz", + "integrity": "sha512-oOr6aRdSFEwWhzxEkD/9ZcItM3+LjBSkeVmadWKwUssAHTsqd/7bOjWrX4AbvEkoEhgAxzN0Xk6H/aYzXiYBAw==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.100.10" + "@tanstack/query-core": "5.100.14" }, "funding": { "type": "github", @@ -8050,9 +8062,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.8.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz", - "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -8241,17 +8253,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", - "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz", + "integrity": "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/type-utils": "8.59.3", - "@typescript-eslint/utils": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/type-utils": "8.60.0", + "@typescript-eslint/utils": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -8264,7 +8276,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.3", + "@typescript-eslint/parser": "^8.60.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -8280,16 +8292,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", - "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.0.tgz", + "integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3" }, "engines": { @@ -8305,14 +8317,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", - "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", + "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.3", - "@typescript-eslint/types": "^8.59.3", + "@typescript-eslint/tsconfig-utils": "^8.60.0", + "@typescript-eslint/types": "^8.60.0", "debug": "^4.4.3" }, "engines": { @@ -8327,14 +8339,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", - "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", + "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3" + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8345,9 +8357,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", - "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", + "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", "dev": true, "license": "MIT", "engines": { @@ -8362,15 +8374,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", - "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz", + "integrity": "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -8387,9 +8399,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", - "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", + "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", "dev": true, "license": "MIT", "engines": { @@ -8401,16 +8413,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", - "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", + "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.3", - "@typescript-eslint/tsconfig-utils": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/project-service": "8.60.0", + "@typescript-eslint/tsconfig-utils": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -8429,16 +8441,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", - "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", + "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3" + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8453,13 +8465,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", - "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", + "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/types": "8.60.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -8576,6 +8588,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8590,6 +8605,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8604,6 +8622,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8618,6 +8639,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8632,6 +8656,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8646,6 +8673,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8846,6 +8876,16 @@ "dev": true, "license": "MIT" }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -11091,6 +11131,43 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/enquirer/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/enquirer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -11386,16 +11463,16 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", + "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", @@ -11999,12 +12076,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-sha256": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", - "license": "Unlicense" - }, "node_modules/fast-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", @@ -13811,6 +13882,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -13832,6 +13906,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -14624,9 +14701,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.7.tgz", - "integrity": "sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.9.tgz", + "integrity": "sha512-5ofa7BUN8+C+Hckh5V2GjeeOGRQBx0CJQA6KxrvuZfC8iU4/q7sLn8XrtEEhJkjV6HdyIiQs7Bba6bTao8JhkA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -15051,6 +15128,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -15067,6 +15147,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -15497,12 +15580,6 @@ "node": ">= 0.4" } }, - "node_modules/postal-mime": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/postal-mime/-/postal-mime-2.7.4.tgz", - "integrity": "sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==", - "license": "MIT-0" - }, "node_modules/postcss": { "version": "8.5.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", @@ -15752,9 +15829,9 @@ } }, "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -15942,9 +16019,9 @@ } }, "node_modules/react-email": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-6.1.4.tgz", - "integrity": "sha512-UKCfry4W7zkAWoJX1ngaWgPrUazOebxI8IYrO8TBEqgFmmz97VqZ84ell2x36Fdvtzd/UI5e4ZOywlsXeydwgQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-6.5.0.tgz", + "integrity": "sha512-WrJ+XPW87O1dabF4RJNGnTr7VTGsNa+BlMiinAZdH5fg8Kepwk++ZzX+LEieTlk+a3r13TaTJ4DfI9gv++y02g==", "dev": true, "license": "MIT", "dependencies": { @@ -16337,27 +16414,6 @@ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", "license": "MIT" }, - "node_modules/resend": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/resend/-/resend-6.12.3.tgz", - "integrity": "sha512-FkEi6YPnVL96/LvH8+QP7NaeaBy5brYXwlRqUCqZZeNL0/iyKij18IPmyPXYauT/2ODn1JG04qKz+qlJfzqzTw==", - "license": "MIT", - "dependencies": { - "postal-mime": "2.7.4", - "svix": "1.92.2" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@react-email/render": "*" - }, - "peerDependenciesMeta": { - "@react-email/render": { - "optional": true - } - } - }, "node_modules/resolve": { "version": "2.0.0-next.6", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", @@ -16564,9 +16620,9 @@ } }, "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -17083,16 +17139,6 @@ "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", "license": "MIT" }, - "node_modules/standardwebhooks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", - "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", - "license": "MIT", - "dependencies": { - "@stablelib/base64": "^1.0.0", - "fast-sha256": "^1.3.0" - } - }, "node_modules/state-local": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", @@ -17397,15 +17443,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svix": { - "version": "1.92.2", - "resolved": "https://registry.npmjs.org/svix/-/svix-1.92.2.tgz", - "integrity": "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ==", - "license": "MIT", - "dependencies": { - "standardwebhooks": "1.0.0" - } - }, "node_modules/swagger-ui-dist": { "version": "5.32.6", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.6.tgz", @@ -17964,16 +18001,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", - "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.0.tgz", + "integrity": "sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.3", - "@typescript-eslint/parser": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/utils": "8.59.3" + "@typescript-eslint/eslint-plugin": "8.60.0", + "@typescript-eslint/parser": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 950402793..e7c99dfc1 100644 --- a/package.json +++ b/package.json @@ -64,9 +64,9 @@ "@react-email/render": "2.0.8", "@react-email/tailwind": "2.0.7", "@simplewebauthn/browser": "13.3.0", - "@simplewebauthn/server": "13.3.0", + "@simplewebauthn/server": "13.3.1", "@tailwindcss/forms": "0.5.11", - "@tanstack/react-query": "5.100.10", + "@tanstack/react-query": "5.100.14", "@tanstack/react-table": "8.21.3", "arctic": "3.7.0", "axios": "1.16.1", @@ -98,7 +98,7 @@ "next-themes": "0.4.6", "nextjs-toploader": "3.9.17", "node-cache": "5.1.2", - "nodemailer": "8.0.7", + "nodemailer": "8.0.9", "oslo": "1.2.1", "pg": "8.20.0", "posthog-node": "5.34.1", @@ -111,8 +111,7 @@ "react-icons": "5.6.0", "recharts": "3.8.1", "reodotdev": "1.1.0", - "resend": "6.12.3", - "semver": "7.8.0", + "semver": "7.8.1", "sshpk": "1.18.0", "stripe": "20.4.1", "swagger-ui-express": "5.0.1", @@ -132,9 +131,9 @@ "zod-validation-error": "5.0.0" }, "devDependencies": { - "@dotenvx/dotenvx": "1.66.0", + "@dotenvx/dotenvx": "1.69.1", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@react-email/ui": "^6.1.4", + "@react-email/ui": "^6.5.0", "@tailwindcss/postcss": "4.3.0", "@tanstack/react-query-devtools": "5.100.10", "@types/better-sqlite3": "7.6.13", @@ -147,7 +146,7 @@ "@types/jmespath": "0.15.2", "@types/js-yaml": "4.0.9", "@types/jsonwebtoken": "9.0.10", - "@types/node": "25.8.0", + "@types/node": "25.9.1", "@types/nodemailer": "8.0.0", "@types/nprogress": "0.2.3", "@types/pg": "8.20.0", @@ -163,16 +162,16 @@ "drizzle-kit": "0.31.10", "esbuild": "0.28.0", "esbuild-node-externals": "1.22.0", - "eslint": "10.3.0", + "eslint": "10.4.0", "eslint-config-next": "16.2.6", "postcss": "8.5.14", "prettier": "3.8.3", - "react-email": "6.1.4", + "react-email": "6.5.0", "tailwindcss": "4.3.0", "tsc-alias": "1.8.17", "tsx": "4.22.0", "typescript": "6.0.3", - "typescript-eslint": "8.59.3" + "typescript-eslint": "8.60.0" }, "overrides": { "esbuild": "0.28.0", diff --git a/server/lib/alerts/events/healthCheckEvents.ts b/server/lib/alerts/events/healthCheckEvents.ts index 1b9ff40ae..429cf73b0 100644 --- a/server/lib/alerts/events/healthCheckEvents.ts +++ b/server/lib/alerts/events/healthCheckEvents.ts @@ -221,10 +221,18 @@ async function handleResource( ) .where(eq(targets.resourceId, resource.resourceId)); + const monitoredTargets = otherTargets.filter( + (t) => t.hcHealth !== "unknown" + ); + let health = "healthy"; - const allUnknown = otherTargets.every((t) => t.hcHealth === "unknown"); - const allHealthy = otherTargets.every((t) => t.hcHealth === "healthy"); - const allUnhealthy = otherTargets.every((t) => t.hcHealth === "unhealthy"); + const allUnknown = monitoredTargets.length === 0; + const allHealthy = monitoredTargets.every( + (t) => t.hcHealth === "healthy" + ); + const allUnhealthy = monitoredTargets.every( + (t) => t.hcHealth === "unhealthy" + ); if (allUnknown) { logger.debug( diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index 13f4caa8f..221b5e586 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -82,7 +82,7 @@ export const RuleSchema = z .object({ action: z.enum(["allow", "deny", "pass"]), match: z.enum(["cidr", "path", "ip", "country", "asn", "region"]), - value: z.string(), + value: z.coerce.string(), priority: z.int().optional() }) .refine( @@ -340,7 +340,8 @@ export const ResourceSchema = z if (parts.includes("*", 1)) return false; // no further wildcards if (parts.length < 3) return false; // need at least *.label.tld - const labelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/; + const labelRegex = + /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/; return parts.slice(1).every((label) => labelRegex.test(label)); }, { diff --git a/server/lib/rebuildClientAssociations.ts b/server/lib/rebuildClientAssociations.ts index a9a2759c4..e5543d5ef 100644 --- a/server/lib/rebuildClientAssociations.ts +++ b/server/lib/rebuildClientAssociations.ts @@ -1826,3 +1826,77 @@ export async function verifyClientAssociationsCache( extraSiteIds: extraSiteIds.sort((a, b) => a - b) }; } + +// cleanupSiteAssociations efficiently removes all client associations for a +// site that is being deleted. Instead of calling +// rebuildClientAssociationsFromSiteResource once per site resource (which is +// O(resources) in DB round-trips and message fan-out), this function performs +// a single bulk lookup of affected clients and site resources, deletes all +// cache rows at once, and fires all peer/proxy removal messages in parallel. +// +// The caller is responsible for deleting the site row itself (and for sending +// the newt/wg/terminate signal to the newt process). +export async function cleanupSiteAssociations( + site: Site, + trx: Transaction | typeof db = db +): Promise { + const siteId = site.siteId; + + logger.debug(`cleanupSiteAssociations: START siteId=${siteId}`); + + // 1. Find every client currently cached against this site. + const cachedSiteClientRows = await trx + .select({ clientId: clientSitesAssociationsCache.clientId }) + .from(clientSitesAssociationsCache) + .where(eq(clientSitesAssociationsCache.siteId, siteId)); + + const cachedClientIds = cachedSiteClientRows.map((r) => r.clientId); + + // 2. Load full client details (needed for WireGuard public-key references). + const allClients = + cachedClientIds.length > 0 + ? await trx + .select({ + clientId: clients.clientId, + pubKey: clients.pubKey, + subnet: clients.subnet + }) + .from(clients) + .where(inArray(clients.clientId, cachedClientIds)) + : []; + + // 6. Bulk-delete all cache entries for this site. Do this before sending + // destination-update messages so updateClientSiteDestinations computes + // the correct (post-deletion) set of destinations. + await trx + .delete(clientSitesAssociationsCache) + .where(eq(clientSitesAssociationsCache.siteId, siteId)); + + logger.debug( + `cleanupSiteAssociations: siteId=${siteId} cache cleared. clients=${allClients.length}` + ); + + // 7. Fire all removal messages in parallel. + const jobs: Promise[] = []; + + for (const client of allClients) { + // Tell each olm to drop the site's WireGuard peer. + if (site.publicKey) { + jobs.push(olmDeletePeer(client.clientId, siteId, site.publicKey)); + } + + // Recompute and push updated relay destinations (now excluding this site). + if (client.pubKey && client.subnet) { + jobs.push(updateClientSiteDestinations(client, trx)); + } + } + + await Promise.all(jobs).catch((error) => { + logger.error( + `cleanupSiteAssociations: error sending cleanup messages for siteId=${siteId}:`, + error + ); + }); + + logger.debug(`cleanupSiteAssociations: DONE siteId=${siteId}`); +} diff --git a/server/routers/auditLogs/queryRequestAnalytics.ts b/server/routers/auditLogs/queryRequestAnalytics.ts index 1e0f1f401..fba5bb9a2 100644 --- a/server/routers/auditLogs/queryRequestAnalytics.ts +++ b/server/routers/auditLogs/queryRequestAnalytics.ts @@ -1,4 +1,4 @@ -import { logsDb, requestAuditLog, driver, primaryLogsDb } from "@server/db"; +import { logsDb, requestAuditLog, driver } from "@server/db"; import { registry } from "@server/openApi"; import { NextFunction } from "express"; import { Request, Response } from "express"; @@ -74,12 +74,12 @@ async function query(query: Q) { ); } - const [all] = await primaryLogsDb + const [all] = await logsDb .select({ total: count() }) .from(requestAuditLog) .where(baseConditions); - const [blocked] = await primaryLogsDb + const [blocked] = await logsDb .select({ total: count() }) .from(requestAuditLog) .where(and(baseConditions, eq(requestAuditLog.action, false))); @@ -90,7 +90,7 @@ async function query(query: Q) { const DISTINCT_LIMIT = 500; - const requestsPerCountry = await primaryLogsDb + const requestsPerCountry = await logsDb .selectDistinct({ code: requestAuditLog.location, count: totalQ @@ -118,7 +118,7 @@ async function query(query: Q) { const booleanTrue = driver === "pg" ? sql`true` : sql`1`; const booleanFalse = driver === "pg" ? sql`false` : sql`0`; - const requestsPerDay = await primaryLogsDb + const requestsPerDay = await logsDb .select({ day: groupByDayFunction.as("day"), allowedCount: diff --git a/server/routers/auditLogs/queryRequestAuditLog.ts b/server/routers/auditLogs/queryRequestAuditLog.ts index 000ec9815..635683fe8 100644 --- a/server/routers/auditLogs/queryRequestAuditLog.ts +++ b/server/routers/auditLogs/queryRequestAuditLog.ts @@ -1,4 +1,4 @@ -import { logsDb, primaryLogsDb, requestAuditLog, resources, siteResources, db, primaryDb } from "@server/db"; +import { logsDb, requestAuditLog, resources, siteResources, db, primaryDb } from "@server/db"; import { registry } from "@server/openApi"; import { NextFunction } from "express"; import { Request, Response } from "express"; @@ -110,7 +110,7 @@ function getWhere(data: Q) { } export function queryRequest(data: Q) { - return primaryLogsDb + return logsDb .select({ id: requestAuditLog.id, timestamp: requestAuditLog.timestamp, @@ -211,7 +211,7 @@ async function enrichWithResourceDetails(logs: Awaited `authRouterGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`, @@ -1252,7 +1252,7 @@ authRouter.post( windowMs: 15 * 60 * 1000, max: 900, keyGenerator: (req) => - `olmGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`, + `olmGetToken:${req.body.olmId || ipKeyGenerator(req.ip || "")}`, handler: (req, res, next) => { const message = `You can only request an Olm token ${900} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index 10ecbbf1e..a3542f970 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -1,8 +1,8 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, Site, siteNetworks, siteResources } from "@server/db"; -import { newts, newtSessions, sites } from "@server/db"; -import { eq, inArray } from "drizzle-orm"; +import { db } from "@server/db"; +import { newts, sites } from "@server/db"; +import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -11,7 +11,7 @@ import { deletePeer } from "../gerbil/peers"; import { fromError } from "zod-validation-error"; import { sendToClient } from "#dynamic/routers/ws"; import { OpenAPITags, registry } from "@server/openApi"; -import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations"; +import { cleanupSiteAssociations } from "@server/lib/rebuildClientAssociations"; import { usageService } from "@server/lib/billing/usageService"; import { FeatureId } from "@server/lib/billing"; @@ -63,7 +63,11 @@ export async function deleteSite( ); } - let deletedNewtId: string | null = null; + const [deletedNewt] = await db + .select() + .from(newts) + .where(eq(newts.siteId, siteId)) + .limit(1); await db.transaction(async (trx) => { if (site.type == "wireguard") { @@ -71,56 +75,24 @@ export async function deleteSite( await deletePeer(site.exitNodeId!, site.pubKey); } } else if (site.type == "newt") { - const networks = await trx - .select({ networkId: siteNetworks.networkId }) - .from(siteNetworks) - .where(eq(siteNetworks.siteId, siteId)); + // Clean up all client associations and send peer/proxy removal + // messages in a single efficient pass before deleting the row. + await cleanupSiteAssociations(site, trx); - // loop through them - const updatedSiteResources = await trx - .select() - .from(siteResources) - .where( - inArray( - siteResources.networkId, - networks.map((n) => n.networkId) - ) - ); - for (const siteResource of updatedSiteResources) { - await rebuildClientAssociationsFromSiteResource( - siteResource, - trx - ); - } - - // get the newt on the site by querying the newt table for siteId - const [deletedNewt] = await trx - .delete(newts) - .where(eq(newts.siteId, siteId)) - .returning(); - if (deletedNewt) { - deletedNewtId = deletedNewt.newtId; - - // delete all of the sessions for the newt - await trx - .delete(newtSessions) - .where(eq(newtSessions.newtId, deletedNewt.newtId)); - } + await trx.delete(sites).where(eq(sites.siteId, siteId)); } - await trx.delete(sites).where(eq(sites.siteId, siteId)); - await usageService.add(site.orgId, FeatureId.SITES, -1, trx); }); // Send termination message outside of transaction to prevent blocking - if (deletedNewtId) { + if (deletedNewt) { const payload = { type: `newt/wg/terminate`, data: {} }; // Don't await this to prevent blocking the response - sendToClient(deletedNewtId, payload).catch((error) => { + sendToClient(deletedNewt.newtId, payload).catch((error) => { logger.error( "Failed to send termination message to newt:", error diff --git a/server/routers/siteResource/batchAddClientToSiteResources.ts b/server/routers/siteResource/batchAddClientToSiteResources.ts index 34c7b58fe..cb424988a 100644 --- a/server/routers/siteResource/batchAddClientToSiteResources.ts +++ b/server/routers/siteResource/batchAddClientToSiteResources.ts @@ -15,10 +15,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { eq, and, inArray } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; -import { - rebuildClientAssociationsFromClient, - rebuildClientAssociationsFromSiteResource -} from "@server/lib/rebuildClientAssociations"; +import { rebuildClientAssociationsFromClient } from "@server/lib/rebuildClientAssociations"; const batchAddClientToSiteResourcesParamsSchema = z .object({ diff --git a/src/app/[orgId]/settings/logs/access/page.tsx b/src/app/[orgId]/settings/logs/access/page.tsx index 267ad8556..6b59581fc 100644 --- a/src/app/[orgId]/settings/logs/access/page.tsx +++ b/src/app/[orgId]/settings/logs/access/page.tsx @@ -182,6 +182,115 @@ export default function GeneralPage() { router.replace(`?${params.toString()}`, { scroll: false }); }; + const queryDateTime = async ( + startDate: DateTimeValue, + endDate: DateTimeValue, + page: number = currentPage, + size: number = pageSize, + filtersParam?: { + action?: string; + type?: string; + resourceId?: string; + location?: string; + actor?: string; + } + ) => { + console.log("Date range changed:", { startDate, endDate, page, size }); + if (!isPaidUser(tierMatrix.accessLogs) || build === "oss") { + console.log( + "Access denied: subscription inactive or license locked" + ); + return; + } + + setIsLoading(true); + + try { + // Use the provided filters or fall back to current state + const activeFilters = filtersParam || filters; + + // Convert the date/time values to API parameters + const params: any = { + limit: size, + offset: page * size, + ...activeFilters + }; + + if (startDate?.date) { + const startDateTime = new Date(startDate.date); + if (startDate.time) { + const [hours, minutes, seconds] = startDate.time + .split(":") + .map(Number); + startDateTime.setHours(hours, minutes, seconds || 0); + } + params.timeStart = startDateTime.toISOString(); + } + + if (endDate?.date) { + const endDateTime = new Date(endDate.date); + if (endDate.time) { + const [hours, minutes, seconds] = endDate.time + .split(":") + .map(Number); + endDateTime.setHours(hours, minutes, seconds || 0); + } else { + // If no time is specified, set to NOW + const now = new Date(); + endDateTime.setHours( + now.getHours(), + now.getMinutes(), + now.getSeconds(), + now.getMilliseconds() + ); + } + params.timeEnd = endDateTime.toISOString(); + } + + const res = await api.get(`/org/${orgId}/logs/access`, { params }); + if (res.status === 200) { + setRows(res.data.data.log || []); + setTotalCount(res.data.data.pagination?.total || 0); + setFilterAttributes(res.data.data.filterAttributes); + console.log("Fetched logs:", res.data); + } + } catch (error) { + toast({ + title: t("error"), + description: t("Failed to filter logs"), + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + const endDate = searchParams.get("end") + ? dateRange.endDate + : { date: new Date() }; + setDateRange((current) => ({ ...current, endDate })); + // Refresh data with current date range and pagination + await queryDateTime( + dateRange.startDate, + endDate, + currentPage, + pageSize + ); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + const exportData = async () => { try { const params: any = { diff --git a/src/app/[orgId]/settings/logs/action/page.tsx b/src/app/[orgId]/settings/logs/action/page.tsx index 7ccce8877..0c2410e49 100644 --- a/src/app/[orgId]/settings/logs/action/page.tsx +++ b/src/app/[orgId]/settings/logs/action/page.tsx @@ -169,6 +169,114 @@ export default function GeneralPage() { router.replace(`?${params.toString()}`, { scroll: false }); }; +<<<<<<< HEAD +======= + const queryDateTime = async ( + startDate: DateTimeValue, + endDate: DateTimeValue, + page: number = currentPage, + size: number = pageSize, + filtersParam?: { + action?: string; + actor?: string; + } + ) => { + console.log("Date range changed:", { startDate, endDate, page, size }); + if (!isPaidUser(tierMatrix.actionLogs)) { + console.log( + "Access denied: subscription inactive or license locked" + ); + return; + } + setIsLoading(true); + + try { + // Use the provided filters or fall back to current state + const activeFilters = filtersParam || filters; + + // Convert the date/time values to API parameters + const params: any = { + limit: size, + offset: page * size, + ...activeFilters + }; + + if (startDate?.date) { + const startDateTime = new Date(startDate.date); + if (startDate.time) { + const [hours, minutes, seconds] = startDate.time + .split(":") + .map(Number); + startDateTime.setHours(hours, minutes, seconds || 0); + } + params.timeStart = startDateTime.toISOString(); + } + + if (endDate?.date) { + const endDateTime = new Date(endDate.date); + if (endDate.time) { + const [hours, minutes, seconds] = endDate.time + .split(":") + .map(Number); + endDateTime.setHours(hours, minutes, seconds || 0); + } else { + // If no time is specified, set to NOW + const now = new Date(); + endDateTime.setHours( + now.getHours(), + now.getMinutes(), + now.getSeconds(), + now.getMilliseconds() + ); + } + params.timeEnd = endDateTime.toISOString(); + } + + const res = await api.get(`/org/${orgId}/logs/action`, { params }); + if (res.status === 200) { + setRows(res.data.data.log || []); + setTotalCount(res.data.data.pagination?.total || 0); + setFilterAttributes(res.data.data.filterAttributes); + console.log("Fetched logs:", res.data); + } + } catch (error) { + toast({ + title: t("error"), + description: t("Failed to filter logs"), + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + const endDate = searchParams.get("end") + ? dateRange.endDate + : { date: new Date() }; + setDateRange((current) => ({ ...current, endDate })); + // Refresh data with current date range and pagination + await queryDateTime( + dateRange.startDate, + endDate, + currentPage, + pageSize + ); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + +>>>>>>> main const exportData = async () => { try { const params: any = { diff --git a/src/app/[orgId]/settings/logs/connection/page.tsx b/src/app/[orgId]/settings/logs/connection/page.tsx index b21db7465..d1b9c4cae 100644 --- a/src/app/[orgId]/settings/logs/connection/page.tsx +++ b/src/app/[orgId]/settings/logs/connection/page.tsx @@ -205,6 +205,113 @@ export default function ConnectionLogsPage() { router.replace(`?${params.toString()}`, { scroll: false }); }; +<<<<<<< HEAD +======= + const queryDateTime = async ( + startDate: DateTimeValue, + endDate: DateTimeValue, + page: number = currentPage, + size: number = pageSize, + filtersParam?: typeof filters + ) => { + console.log("Date range changed:", { startDate, endDate, page, size }); + if (!isPaidUser(tierMatrix.connectionLogs)) { + console.log( + "Access denied: subscription inactive or license locked" + ); + return; + } + setIsLoading(true); + + try { + // Use the provided filters or fall back to current state + const activeFilters = filtersParam || filters; + + // Convert the date/time values to API parameters + const params: any = { + limit: size, + offset: page * size, + ...activeFilters + }; + + if (startDate?.date) { + const startDateTime = new Date(startDate.date); + if (startDate.time) { + const [hours, minutes, seconds] = startDate.time + .split(":") + .map(Number); + startDateTime.setHours(hours, minutes, seconds || 0); + } + params.timeStart = startDateTime.toISOString(); + } + + if (endDate?.date) { + const endDateTime = new Date(endDate.date); + if (endDate.time) { + const [hours, minutes, seconds] = endDate.time + .split(":") + .map(Number); + endDateTime.setHours(hours, minutes, seconds || 0); + } else { + // If no time is specified, set to NOW + const now = new Date(); + endDateTime.setHours( + now.getHours(), + now.getMinutes(), + now.getSeconds(), + now.getMilliseconds() + ); + } + params.timeEnd = endDateTime.toISOString(); + } + + const res = await api.get(`/org/${orgId}/logs/connection`, { + params + }); + if (res.status === 200) { + setRows(res.data.data.log || []); + setTotalCount(res.data.data.pagination?.total || 0); + setFilterAttributes(res.data.data.filterAttributes); + console.log("Fetched connection logs:", res.data); + } + } catch (error) { + toast({ + title: t("error"), + description: t("Failed to filter logs"), + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + const endDate = searchParams.get("end") + ? dateRange.endDate + : { date: new Date() }; + setDateRange((current) => ({ ...current, endDate })); + // Refresh data with current date range and pagination + await queryDateTime( + dateRange.startDate, + endDate, + currentPage, + pageSize + ); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + +>>>>>>> main const exportData = async () => { try { const params: any = { diff --git a/src/app/[orgId]/settings/logs/request/page.tsx b/src/app/[orgId]/settings/logs/request/page.tsx index 2196a0342..40d0797e5 100644 --- a/src/app/[orgId]/settings/logs/request/page.tsx +++ b/src/app/[orgId]/settings/logs/request/page.tsx @@ -185,6 +185,105 @@ export default function GeneralPage() { router.replace(`?${params.toString()}`, { scroll: false }); }; + const queryDateTime = async ( + startDate: DateTimeValue, + endDate: DateTimeValue, + page: number = currentPage, + size: number = pageSize, + filtersParam?: { + action?: string; + type?: string; + } + ) => { + console.log("Date range changed:", { startDate, endDate, page, size }); + setIsLoading(true); + + try { + // Use the provided filters or fall back to current state + const activeFilters = filtersParam || filters; + + // Convert the date/time values to API parameters + const params: any = { + limit: size, + offset: page * size, + ...activeFilters + }; + + if (startDate?.date) { + const startDateTime = new Date(startDate.date); + if (startDate.time) { + const [hours, minutes, seconds] = startDate.time + .split(":") + .map(Number); + startDateTime.setHours(hours, minutes, seconds || 0); + } + params.timeStart = startDateTime.toISOString(); + } + + if (endDate?.date) { + const endDateTime = new Date(endDate.date); + if (endDate.time) { + const [hours, minutes, seconds] = endDate.time + .split(":") + .map(Number); + endDateTime.setHours(hours, minutes, seconds || 0); + } else { + // If no time is specified, set to NOW + const now = new Date(); + endDateTime.setHours( + now.getHours(), + now.getMinutes(), + now.getSeconds(), + now.getMilliseconds() + ); + } + params.timeEnd = endDateTime.toISOString(); + } + + const res = await api.get(`/org/${orgId}/logs/request`, { params }); + if (res.status === 200) { + setRows(res.data.data.log || []); + setTotalCount(res.data.data.pagination?.total || 0); + setFilterAttributes(res.data.data.filterAttributes); + console.log("Fetched logs:", res.data); + } + } catch (error) { + toast({ + title: t("error"), + description: t("Failed to filter logs"), + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; + + const refreshData = async () => { + console.log("Data refreshed"); + setIsRefreshing(true); + try { + const endDate = searchParams.get("end") + ? dateRange.endDate + : { date: new Date() }; + setDateRange((current) => ({ ...current, endDate })); + // Refresh data with current date range and pagination + await queryDateTime( + dateRange.startDate, + endDate, + currentPage, + pageSize + ); + } catch (error) { + toast({ + title: t("error"), + description: t("refreshError"), + variant: "destructive" + }); + } finally { + setIsRefreshing(false); + } + }; + const exportData = async () => { try { // Prepare query params for export