Compare commits

..

38 Commits

Author SHA1 Message Date
dependabot[bot]
abfab56082 Bump node in the docker-dependencies group across 1 directory
Bumps the docker-dependencies group with 1 update in the / directory: node.


Updates `node` from 24-alpine to 26-alpine

---
updated-dependencies:
- dependency-name: node
  dependency-version: 26-alpine
  dependency-type: direct:production
  dependency-group: docker-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 01:32:53 +00:00
Owen Schwartz
963e9da7dd Merge pull request #3213 from ivenos/rename-to-compose-yaml
Rename docker-compose.yml to compose.yaml
2026-06-23 12:03:56 -07:00
Owen Schwartz
2cbc88fa05 Merge pull request #3326 from fosrl/dependabot/npm_and_yarn/js-yaml-4.2.0
Bump js-yaml from 4.1.1 to 4.2.0
2026-06-23 12:02:49 -07:00
Owen Schwartz
7ae29612d4 Merge pull request #3302 from RitwijParmar/codex/resource-update-inline-policy-response
Fix inline policy fields in resource update response
2026-06-23 08:30:31 -07:00
Owen Schwartz
1bff7bbc2f Merge pull request #3315 from fosrl/dependabot/npm_and_yarn/nodemailer-9.0.1
Bump nodemailer from 8.0.9 to 9.0.1
2026-06-23 07:58:58 -07:00
Owen Schwartz
50db5695fc Merge pull request #3264 from fosrl/dependabot/npm_and_yarn/esbuild-0.28.1
Bump esbuild from 0.28.0 to 0.28.1
2026-06-23 07:57:47 -07:00
dependabot[bot]
cfb63f9742 Bump js-yaml from 4.1.1 to 4.2.0
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.2.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.1...4.2.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.2.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 21:04:39 +00:00
Owen Schwartz
c76b4555e1 Merge pull request #3316 from fosrl/dependabot/npm_and_yarn/form-data-4.0.6
Bump form-data from 4.0.5 to 4.0.6
2026-06-22 14:03:06 -07:00
Owen Schwartz
c25bfbad27 Merge pull request #3317 from fosrl/dependabot/github_actions/actions/checkout-7.0.0
Bump actions/checkout from 6.0.2 to 7.0.0
2026-06-22 14:02:51 -07:00
Owen Schwartz
44782f8963 Merge pull request #3323 from fosrl/dependabot/go_modules/install/go-install-dependencies-4dfeb96e78
Bump golang.org/x/term from 0.43.0 to 0.44.0 in /install in the go-install-dependencies group
2026-06-22 14:02:31 -07:00
Owen Schwartz
e6f7cd6da9 Merge pull request #3206 from gmpinder/fix-idp-delete
fix: Add DELETE /idp/{idpId} to integration API
2026-06-22 14:01:47 -07:00
Owen Schwartz
62c7e0a13e Merge pull request #3251 from kshitijshresth/fix-path-rule-regex-escaping
Fix unescaped regex metacharacters in PATH rule matching causing request failures
2026-06-22 07:40:00 -07:00
dependabot[bot]
753358a17d Bump golang.org/x/term in /install in the go-install-dependencies group
Bumps the go-install-dependencies group in /install with 1 update: [golang.org/x/term](https://github.com/golang/term).


Updates `golang.org/x/term` from 0.43.0 to 0.44.0
- [Commits](https://github.com/golang/term/compare/v0.43.0...v0.44.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-version: 0.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-install-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 14:32:24 +00:00
Owen Schwartz
c859393418 Merge pull request #3225 from fosrl/chore/dependabot-single-pr-groups
chore(dependabot): group dependency updates into single PRs per ecosystem
2026-06-22 07:31:25 -07:00
dependabot[bot]
d747b45f0b Bump actions/checkout from 6.0.2 to 7.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](de0fac2e45...9c091bb21b)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 01:33:16 +00:00
dependabot[bot]
a24091257a Bump form-data from 4.0.5 to 4.0.6
Bumps [form-data](https://github.com/form-data/form-data) from 4.0.5 to 4.0.6.
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.5...v4.0.6)

---
updated-dependencies:
- dependency-name: form-data
  dependency-version: 4.0.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-21 19:40:11 +00:00
dependabot[bot]
1c60041390 Bump nodemailer from 8.0.9 to 9.0.1
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.9 to 9.0.1.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v8.0.9...v9.0.1)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 9.0.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-21 09:46:17 +00:00
Ritwij Aryan Parmar
95c3f74a33 Fix inline policy fields in resource update response 2026-06-17 14:36:34 -04:00
Owen Schwartz
16c0f4eef4 Merge pull request #3277 from fosrl/dev
Fix middleware and suppoter footer
2026-06-14 14:44:33 -07:00
Owen
a08c6d70fe Comment out 2026-06-14 14:44:08 -07:00
miloschwartz
a6568692b7 force set supporter status to true in server info endpoint 2026-06-14 14:40:37 -07:00
Owen
a1196d3da6 Remove supporter warning 2026-06-14 14:34:39 -07:00
Owen
70bc4c0b30 Remove the path rewrite from the next route 2026-06-14 14:30:16 -07:00
Owen Schwartz
a0fef89031 Merge pull request #3276 from fosrl/dev
Rewrite headers
2026-06-14 14:13:54 -07:00
Owen
ea1badf4e0 Add middleware for rewriting host headers 2026-06-14 12:04:02 -07:00
Owen Schwartz
f15654ed11 Merge pull request #3275 from fosrl/dev
Fill in missing ui urls from the passed params
2026-06-14 11:36:01 -07:00
Owen
4435a669a6 Fill in missing ui urls from the passed params 2026-06-14 11:35:27 -07:00
Owen Schwartz
0b41fe3d49 Merge pull request #3268 from fosrl/dev
Send browser gateway rsources to remote nodes
2026-06-14 11:11:06 -07:00
Owen
90eceb457a Clean up url passing 2026-06-14 11:10:05 -07:00
Owen
f39cbc9bf4 Add same signature to oss 2026-06-14 11:03:14 -07:00
Owen
50da863bb7 Add maintence page support for remote nodes 2026-06-13 21:45:52 -07:00
Owen
c6ddd5c402 Open up holepunch requirements 2026-06-13 14:14:34 -07:00
Owen
0fb5ace9c7 Support the browser gateways on the remote nodes 2026-06-13 14:08:03 -07:00
dependabot[bot]
cedccd8cdb Bump esbuild from 0.28.0 to 0.28.1
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.28.0 to 0.28.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.0...v0.28.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-13 01:23:46 +00:00
kshitijshresth
b136bd2246 Escape regex metacharacters in PATH rule wildcard matching
isValidUrlGlobPattern accepts characters like ( ) [ ] { } | . + ^ $ in PATH rule values, but isPathAllowed converted wildcard segments to regex without escaping them. A rule value such as /(api* produced an invalid regex and threw on every request to the resource, surfacing as a 500 from verifySession. Literal characters like . and + also changed matching semantics. isPathAllowed is extracted to server/lib/pathMatch.ts as a pure module, metacharacters are escaped before wildcard substitution, compiled segment regexes are cached, and the test suite now imports the real implementation instead of a stale copy, with added coverage for special characters.
2026-06-12 11:21:21 +03:00
Marc Schäfer
92d611df9a chore(dependabot): group dependency updates into single PRs per ecosystem 2026-06-07 11:10:53 +02:00
ivenos
89f3f3c8cd Rename docker-compose.yml to compose.yaml 2026-06-04 10:01:35 +02:00
Gerald Pinder
44c16d69af fix: Add DELETE /idp/{idpId} to integration API 2026-06-03 12:48:34 -04:00
37 changed files with 801 additions and 712 deletions

View File

@@ -1,52 +1,42 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: "npm" - package-ecosystem: "npm"
directory: "/" directory: "/"
schedule: schedule:
interval: "daily" interval: "daily"
open-pull-requests-limit: 1
groups: groups:
dev-patch-updates: npm-dependencies:
dependency-type: "development" patterns:
update-types: - "*"
- "patch"
dev-minor-updates:
dependency-type: "development"
update-types:
- "minor"
prod-patch-updates:
dependency-type: "production"
update-types:
- "patch"
prod-minor-updates:
dependency-type: "production"
update-types:
- "minor"
- package-ecosystem: "docker" - package-ecosystem: "docker"
directory: "/" directory: "/"
schedule: schedule:
interval: "daily" interval: "daily"
open-pull-requests-limit: 1
groups: groups:
patch-updates: docker-dependencies:
update-types: patterns:
- "patch" - "*"
minor-updates:
update-types:
- "minor"
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
open-pull-requests-limit: 1
groups:
github-actions-dependencies:
patterns:
- "*"
- package-ecosystem: "gomod" - package-ecosystem: "gomod"
directory: "/install" directory: "/install"
schedule: schedule:
interval: "daily" interval: "daily"
open-pull-requests-limit: 1
groups: groups:
patch-updates: go-install-dependencies:
update-types: patterns:
- "patch" - "*"
minor-updates:
update-types:
- "minor"

View File

@@ -62,7 +62,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Monitor storage space - name: Monitor storage space
run: | run: |
@@ -134,7 +134,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Monitor storage space - name: Monitor storage space
run: | run: |
@@ -201,7 +201,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
@@ -256,7 +256,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Extract tag name - name: Extract tag name
id: get-tag id: get-tag

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install Node - name: Install Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Build Docker image sqlite - name: Build Docker image sqlite
run: make dev-build-sqlite run: make dev-build-sqlite
@@ -71,7 +71,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Build Docker image pg - name: Build Docker image pg
run: make dev-build-pg run: make dev-build-pg

View File

@@ -1,4 +1,4 @@
FROM node:24-alpine FROM node:26-alpine
WORKDIR /app WORKDIR /app

View File

@@ -5,7 +5,7 @@ go 1.25.0
require ( require (
github.com/charmbracelet/huh v1.0.0 github.com/charmbracelet/huh v1.0.0
github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/lipgloss v1.1.0
golang.org/x/term v0.43.0 golang.org/x/term v0.44.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@@ -33,6 +33,6 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.15.0 // indirect golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.44.0 // indirect golang.org/x/sys v0.46.0 // indirect
golang.org/x/text v0.23.0 // indirect golang.org/x/text v0.23.0 // indirect
) )

View File

@@ -69,10 +69,10 @@ golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

438
package-lock.json generated
View File

@@ -70,7 +70,7 @@
"input-otp": "1.4.2", "input-otp": "1.4.2",
"ioredis": "5.11.0", "ioredis": "5.11.0",
"jmespath": "0.16.0", "jmespath": "0.16.0",
"js-yaml": "4.1.1", "js-yaml": "4.2.0",
"jsonwebtoken": "9.0.3", "jsonwebtoken": "9.0.3",
"lucide-react": "1.17.0", "lucide-react": "1.17.0",
"maxmind": "5.0.6", "maxmind": "5.0.6",
@@ -80,7 +80,7 @@
"next-themes": "0.4.6", "next-themes": "0.4.6",
"nextjs-toploader": "3.9.17", "nextjs-toploader": "3.9.17",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "8.0.9", "nodemailer": "9.0.1",
"oslo": "1.2.1", "oslo": "1.2.1",
"pg": "8.21.0", "pg": "8.21.0",
"posthog-node": "5.35.6", "posthog-node": "5.35.6",
@@ -142,7 +142,7 @@
"@types/yargs": "17.0.35", "@types/yargs": "17.0.35",
"babel-plugin-react-compiler": "1.0.0", "babel-plugin-react-compiler": "1.0.0",
"drizzle-kit": "0.31.10", "drizzle-kit": "0.31.10",
"esbuild": "0.28.0", "esbuild": "0.28.1",
"esbuild-node-externals": "1.22.0", "esbuild-node-externals": "1.22.0",
"eslint": "10.4.0", "eslint": "10.4.0",
"eslint-config-next": "16.2.6", "eslint-config-next": "16.2.6",
@@ -1248,9 +1248,9 @@
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -1265,9 +1265,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1282,9 +1282,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1299,9 +1299,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1316,9 +1316,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1333,9 +1333,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1350,9 +1350,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1367,9 +1367,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1384,9 +1384,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1401,9 +1401,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1418,9 +1418,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -1435,9 +1435,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -1452,9 +1452,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@@ -1469,9 +1469,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -1486,9 +1486,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -1503,9 +1503,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -1520,9 +1520,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1537,9 +1537,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1554,9 +1554,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1571,9 +1571,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1588,9 +1588,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1605,9 +1605,9 @@
} }
}, },
"node_modules/@esbuild/openharmony-arm64": { "node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1622,9 +1622,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1639,9 +1639,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1656,9 +1656,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -1673,9 +1673,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2076,9 +2076,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2095,9 +2092,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2114,9 +2108,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2133,9 +2124,6 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2152,9 +2140,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2187,9 +2172,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2222,9 +2204,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2247,9 +2226,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2272,9 +2248,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2297,9 +2270,6 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2322,9 +2292,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2369,9 +2336,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2664,9 +2628,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2683,9 +2644,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2941,9 +2899,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2960,9 +2915,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3200,9 +3152,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3219,9 +3168,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3582,9 +3528,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3605,9 +3548,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3628,9 +3568,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3651,9 +3588,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6873,9 +6807,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0 AND MIT", "license": "Apache-2.0 AND MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6892,9 +6823,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "Apache-2.0 AND MIT", "license": "Apache-2.0 AND MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6911,9 +6839,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0 AND MIT", "license": "Apache-2.0 AND MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6930,9 +6855,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "Apache-2.0 AND MIT", "license": "Apache-2.0 AND MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -7200,9 +7122,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -7220,9 +7139,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -7296,6 +7212,72 @@
"node": ">=14.0.0" "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": { "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
@@ -8448,9 +8430,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -8465,9 +8444,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -8482,9 +8458,6 @@
"ppc64" "ppc64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -8499,9 +8472,6 @@
"riscv64" "riscv64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -8516,9 +8486,6 @@
"riscv64" "riscv64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -8533,9 +8500,6 @@
"s390x" "s390x"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -11261,9 +11225,9 @@
] ]
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.28.0", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@@ -11274,32 +11238,32 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.0", "@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.28.0", "@esbuild/android-arm": "0.28.1",
"@esbuild/android-arm64": "0.28.0", "@esbuild/android-arm64": "0.28.1",
"@esbuild/android-x64": "0.28.0", "@esbuild/android-x64": "0.28.1",
"@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-arm64": "0.28.1",
"@esbuild/darwin-x64": "0.28.0", "@esbuild/darwin-x64": "0.28.1",
"@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.1",
"@esbuild/freebsd-x64": "0.28.0", "@esbuild/freebsd-x64": "0.28.1",
"@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm": "0.28.1",
"@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-arm64": "0.28.1",
"@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-ia32": "0.28.1",
"@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-loong64": "0.28.1",
"@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-mips64el": "0.28.1",
"@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-ppc64": "0.28.1",
"@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-riscv64": "0.28.1",
"@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-s390x": "0.28.1",
"@esbuild/linux-x64": "0.28.0", "@esbuild/linux-x64": "0.28.1",
"@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.1",
"@esbuild/netbsd-x64": "0.28.0", "@esbuild/netbsd-x64": "0.28.1",
"@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.1",
"@esbuild/openbsd-x64": "0.28.0", "@esbuild/openbsd-x64": "0.28.1",
"@esbuild/openharmony-arm64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.1",
"@esbuild/sunos-x64": "0.28.0", "@esbuild/sunos-x64": "0.28.1",
"@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-arm64": "0.28.1",
"@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-ia32": "0.28.1",
"@esbuild/win32-x64": "0.28.0" "@esbuild/win32-x64": "0.28.1"
} }
}, },
"node_modules/esbuild-node-externals": { "node_modules/esbuild-node-externals": {
@@ -12191,16 +12155,16 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.5", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2", "hasown": "^2.0.4",
"mime-types": "^2.1.12" "mime-types": "^2.1.35"
}, },
"engines": { "engines": {
"node": ">= 6" "node": ">= 6"
@@ -12629,9 +12593,9 @@
} }
}, },
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"function-bind": "^1.1.2" "function-bind": "^1.1.2"
@@ -13425,9 +13389,19 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.1", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
@@ -13767,9 +13741,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -13791,9 +13762,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -14574,9 +14542,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "8.0.9", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.9.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.1.tgz",
"integrity": "sha512-5ofa7BUN8+C+Hckh5V2GjeeOGRQBx0CJQA6KxrvuZfC8iU4/q7sLn8XrtEEhJkjV6HdyIiQs7Bba6bTao8JhkA==", "integrity": "sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh5ME+uesJUDRbR3Ye8Bw==",
"license": "MIT-0", "license": "MIT-0",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
@@ -15001,9 +14969,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -15020,9 +14985,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [

View File

@@ -93,7 +93,7 @@
"input-otp": "1.4.2", "input-otp": "1.4.2",
"ioredis": "5.11.0", "ioredis": "5.11.0",
"jmespath": "0.16.0", "jmespath": "0.16.0",
"js-yaml": "4.1.1", "js-yaml": "4.2.0",
"jsonwebtoken": "9.0.3", "jsonwebtoken": "9.0.3",
"lucide-react": "1.17.0", "lucide-react": "1.17.0",
"maxmind": "5.0.6", "maxmind": "5.0.6",
@@ -103,7 +103,7 @@
"next-themes": "0.4.6", "next-themes": "0.4.6",
"nextjs-toploader": "3.9.17", "nextjs-toploader": "3.9.17",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "8.0.9", "nodemailer": "9.0.1",
"oslo": "1.2.1", "oslo": "1.2.1",
"pg": "8.21.0", "pg": "8.21.0",
"posthog-node": "5.35.6", "posthog-node": "5.35.6",
@@ -165,7 +165,7 @@
"@types/yargs": "17.0.35", "@types/yargs": "17.0.35",
"babel-plugin-react-compiler": "1.0.0", "babel-plugin-react-compiler": "1.0.0",
"drizzle-kit": "0.31.10", "drizzle-kit": "0.31.10",
"esbuild": "0.28.0", "esbuild": "0.28.1",
"esbuild-node-externals": "1.22.0", "esbuild-node-externals": "1.22.0",
"eslint": "10.4.0", "eslint": "10.4.0",
"eslint-config-next": "16.2.6", "eslint-config-next": "16.2.6",
@@ -179,7 +179,7 @@
"typescript-eslint": "8.60.0" "typescript-eslint": "8.60.0"
}, },
"overrides": { "overrides": {
"esbuild": "0.28.0", "esbuild": "0.28.1",
"dompurify": "3.4.0", "dompurify": "3.4.0",
"postcss": "8.5.15" "postcss": "8.5.15"
} }

74
server/lib/pathMatch.ts Normal file
View File

@@ -0,0 +1,74 @@
const MAX_RECURSION_DEPTH = 100;
const segmentRegexCache = new Map<string, RegExp>();
function getSegmentRegex(patternPart: string): RegExp {
let regex = segmentRegexCache.get(patternPart);
if (!regex) {
const regexPattern = patternPart
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
regex = new RegExp(`^${regexPattern}$`);
segmentRegexCache.set(patternPart, regex);
}
return regex;
}
export function isPathAllowed(pattern: string, path: string): boolean {
const normalize = (p: string) => p.split("/").filter(Boolean);
const patternParts = normalize(pattern);
const pathParts = normalize(path);
function matchSegments(
patternIndex: number,
pathIndex: number,
depth: number = 0
): boolean {
if (depth > MAX_RECURSION_DEPTH) {
return false;
}
const currentPatternPart = patternParts[patternIndex];
const currentPathPart = pathParts[pathIndex];
if (patternIndex >= patternParts.length) {
return pathIndex >= pathParts.length;
}
if (pathIndex >= pathParts.length) {
return patternParts.slice(patternIndex).every((p) => p === "*");
}
if (currentPatternPart === "*") {
if (matchSegments(patternIndex + 1, pathIndex, depth + 1)) {
return true;
}
if (matchSegments(patternIndex, pathIndex + 1, depth + 1)) {
return true;
}
return false;
}
if (currentPatternPart.includes("*")) {
const regex = getSegmentRegex(currentPatternPart);
if (regex.test(currentPathPart)) {
return matchSegments(
patternIndex + 1,
pathIndex + 1,
depth + 1
);
}
return false;
}
if (currentPatternPart !== currentPathPart) {
return false;
}
return matchSegments(patternIndex + 1, pathIndex + 1, depth + 1);
}
return matchSegments(0, 0, 0);
}

View File

@@ -511,6 +511,12 @@ export class TraefikConfigManager {
let traefikConfig; let traefikConfig;
try { try {
const currentExitNode = await getCurrentExitNodeId(); const currentExitNode = await getCurrentExitNodeId();
const maintenancePort = config.getRawConfig().server.next_port;
const maintenanceHost =
config.getRawConfig().server.internal_hostname;
const pangolinUIUrl = `http://${maintenanceHost}:${maintenancePort}`;
// logger.debug(`Fetching traefik config for exit node: ${currentExitNode}`); // logger.debug(`Fetching traefik config for exit node: ${currentExitNode}`);
traefikConfig = await getTraefikConfig( traefikConfig = await getTraefikConfig(
// this is called by the local exit node to get its own config // this is called by the local exit node to get its own config
@@ -521,7 +527,8 @@ export class TraefikConfigManager {
build == "saas" build == "saas"
? false ? false
: config.getRawConfig().traefik.allow_raw_resources, // dont allow raw resources on saas otherwise use config : config.getRawConfig().traefik.allow_raw_resources, // dont allow raw resources on saas otherwise use config
build != "oss" // generate browser gateway targets on cloud and enterprise pangolinUIUrl, // generate maintenance pages on cloud and hybrid
pangolinUIUrl // generate browser gateway targets on cloud and hybrid
); );
const domains = new Set<string>(); const domains = new Set<string>();

View File

@@ -44,8 +44,8 @@ export async function getTraefikConfig(
filterOutNamespaceDomains = false, // UNUSED BUT USED IN PRIVATE filterOutNamespaceDomains = false, // UNUSED BUT USED IN PRIVATE
generateLoginPageRouters = false, // UNUSED BUT USED IN PRIVATE generateLoginPageRouters = false, // UNUSED BUT USED IN PRIVATE
allowRawResources = true, allowRawResources = true,
allowMaintenancePage = true, // UNUSED BUT USED IN PRIVATE maintenancePageUiUrl: string | null = null, // UNUSED BUT USED IN PRIVATE
allowBrowserGatewayResources = true browserGatewayUiUrl: string | null = null // UNUSED BUT USED IN PRIVATE
): Promise<any> { ): Promise<any> {
// Get resources with their targets and sites in a single optimized query // Get resources with their targets and sites in a single optimized query
// Start from sites on this exit node, then join to targets and resources // Start from sites on this exit node, then join to targets and resources

View File

@@ -84,8 +84,8 @@ export async function getTraefikConfig(
filterOutNamespaceDomains = false, filterOutNamespaceDomains = false,
generateLoginPageRouters = false, generateLoginPageRouters = false,
allowRawResources = true, allowRawResources = true,
allowMaintenancePage = true, maintenancePageUiUrl: string | null = null,
allowBrowserGatewayResources = true browserGatewayUiUrl: string | null = null
): Promise<any> { ): Promise<any> {
// Get resources with their targets and sites in a single optimized query // Get resources with their targets and sites in a single optimized query
// Start from sites on this exit node, then join to targets and resources // Start from sites on this exit node, then join to targets and resources
@@ -317,7 +317,7 @@ export async function getTraefikConfig(
BrowserGatewayResourceEntry BrowserGatewayResourceEntry
>(); >();
if (allowBrowserGatewayResources) { if (browserGatewayUiUrl) {
for (const row of resourcesWithTargetsAndSites) { for (const row of resourcesWithTargetsAndSites) {
if (!["ssh", "vnc", "rdp"].includes(row.mode)) { if (!["ssh", "vnc", "rdp"].includes(row.mode)) {
continue; continue;
@@ -630,10 +630,11 @@ export async function getTraefikConfig(
} }
} }
if (showMaintenancePage && allowMaintenancePage) { if (showMaintenancePage && maintenancePageUiUrl) {
const maintenanceServiceName = `${key}-maintenance-service`; const maintenanceServiceName = `${key}-maintenance-service`;
const maintenanceRouterName = `${key}-maintenance-router`; const maintenanceRouterName = `${key}-maintenance-router`;
const rewriteMiddlewareName = `${key}-maintenance-rewrite`; const rewriteMiddlewareName = `${key}-maintenance-rewrite`;
const maintenanceHeadersMiddlewareName = `${key}-maintenance-headers`;
const entrypointHttp = const entrypointHttp =
config.getRawConfig().traefik.http_entrypoint; config.getRawConfig().traefik.http_entrypoint;
@@ -646,15 +647,11 @@ export async function getTraefikConfig(
? `*.${domainParts.slice(1).join(".")}` ? `*.${domainParts.slice(1).join(".")}`
: fullDomain; : fullDomain;
const maintenancePort = config.getRawConfig().server.next_port;
const maintenanceHost =
config.getRawConfig().server.internal_hostname;
config_output.http.services[maintenanceServiceName] = { config_output.http.services[maintenanceServiceName] = {
loadBalancer: { loadBalancer: {
servers: [ servers: [
{ {
url: `http://${maintenanceHost}:${maintenancePort}` url: maintenancePageUiUrl
} }
], ],
passHostHeader: true passHostHeader: true
@@ -673,12 +670,26 @@ export async function getTraefikConfig(
} }
}; };
config_output.http.middlewares[
maintenanceHeadersMiddlewareName
] = {
headers: {
customRequestHeaders: {
Host: "app.pangolin.net", // if we are sending to the cloud the host needs to be this but we will pull the p-host to find the resource
"p-host": fullDomain
}
}
};
config_output.http.routers[maintenanceRouterName] = { config_output.http.routers[maintenanceRouterName] = {
entryPoints: [ entryPoints: [
resource.ssl ? entrypointHttps : entrypointHttp resource.ssl ? entrypointHttps : entrypointHttp
], ],
service: maintenanceServiceName, service: maintenanceServiceName,
middlewares: [rewriteMiddlewareName], middlewares: [
rewriteMiddlewareName,
maintenanceHeadersMiddlewareName
],
rule: rule, rule: rule,
priority: 2000, priority: 2000,
...(resource.ssl ? { tls } : {}) ...(resource.ssl ? { tls } : {})
@@ -691,6 +702,7 @@ export async function getTraefikConfig(
resource.ssl ? entrypointHttps : entrypointHttp resource.ssl ? entrypointHttps : entrypointHttp
], ],
service: maintenanceServiceName, service: maintenanceServiceName,
middlewares: [maintenanceHeadersMiddlewareName],
rule: `${rule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`)) `, rule: `${rule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`)) `,
priority: 2001, priority: 2001,
...(resource.ssl ? { tls } : {}) ...(resource.ssl ? { tls } : {})
@@ -1027,7 +1039,7 @@ export async function getTraefikConfig(
} }
} }
if (allowBrowserGatewayResources) { if (browserGatewayUiUrl) {
// Generate Traefik config for browser gateway resources // Generate Traefik config for browser gateway resources
const browserGatewayPort = 39999; const browserGatewayPort = 39999;
for (const [, bgResource] of browserGatewayResourcesMap.entries()) { for (const [, bgResource] of browserGatewayResourcesMap.entries()) {
@@ -1119,20 +1131,17 @@ export async function getTraefikConfig(
} }
} }
if (showBgMaintenancePage && allowMaintenancePage) { if (showBgMaintenancePage && maintenancePageUiUrl) {
const bgMaintenanceServiceName = `bg-r${bgResource.resourceId}-maintenance-service`; const bgMaintenanceServiceName = `bg-r${bgResource.resourceId}-maintenance-service`;
const bgMaintenanceRouterName = `bg-r${bgResource.resourceId}-maintenance-router`; const bgMaintenanceRouterName = `bg-r${bgResource.resourceId}-maintenance-router`;
const bgRewriteMiddlewareName = `bg-r${bgResource.resourceId}-maintenance-rewrite`; const bgRewriteMiddlewareName = `bg-r${bgResource.resourceId}-maintenance-rewrite`;
const bgMaintenanceHeadersMiddlewareName = `bg-r${bgResource.resourceId}-maintenance-headers`;
const entrypointHttp = const entrypointHttp =
config.getRawConfig().traefik.http_entrypoint; config.getRawConfig().traefik.http_entrypoint;
const entrypointHttps = const entrypointHttps =
config.getRawConfig().traefik.https_entrypoint; config.getRawConfig().traefik.https_entrypoint;
const maintenancePort = config.getRawConfig().server.next_port;
const maintenanceHost =
config.getRawConfig().server.internal_hostname;
if (!config_output.http.services) if (!config_output.http.services)
config_output.http.services = {}; config_output.http.services = {};
if (!config_output.http.middlewares) if (!config_output.http.middlewares)
@@ -1144,7 +1153,7 @@ export async function getTraefikConfig(
loadBalancer: { loadBalancer: {
servers: [ servers: [
{ {
url: `http://${maintenanceHost}:${maintenancePort}` url: maintenancePageUiUrl
} }
], ],
passHostHeader: true passHostHeader: true
@@ -1158,12 +1167,26 @@ export async function getTraefikConfig(
} }
}; };
config_output.http.middlewares![
bgMaintenanceHeadersMiddlewareName
] = {
headers: {
customRequestHeaders: {
Host: "app.pangolin.net", // if we are sending to the cloud the host needs to be this but we will pull the p-host to find the resource
"p-host": fullDomain
}
}
};
config_output.http.routers![bgMaintenanceRouterName] = { config_output.http.routers![bgMaintenanceRouterName] = {
entryPoints: [ entryPoints: [
bgResource.ssl ? entrypointHttps : entrypointHttp bgResource.ssl ? entrypointHttps : entrypointHttp
], ],
service: bgMaintenanceServiceName, service: bgMaintenanceServiceName,
middlewares: [bgRewriteMiddlewareName], middlewares: [
bgRewriteMiddlewareName,
bgMaintenanceHeadersMiddlewareName
],
rule: hostRule, rule: hostRule,
priority: 2000, priority: 2000,
...(bgResource.ssl ? { tls } : {}) ...(bgResource.ssl ? { tls } : {})
@@ -1176,6 +1199,7 @@ export async function getTraefikConfig(
bgResource.ssl ? entrypointHttps : entrypointHttp bgResource.ssl ? entrypointHttps : entrypointHttp
], ],
service: bgMaintenanceServiceName, service: bgMaintenanceServiceName,
middlewares: [bgMaintenanceHeadersMiddlewareName],
rule: `${hostRule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`, rule: `${hostRule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`,
priority: 2001, priority: 2001,
...(bgResource.ssl ? { tls } : {}) ...(bgResource.ssl ? { tls } : {})
@@ -1234,9 +1258,8 @@ export async function getTraefikConfig(
// The primary type is used for the path rewrite (e.g. /rdp), mirroring // The primary type is used for the path rewrite (e.g. /rdp), mirroring
// how the maintenance page rewrites everything to /maintenance-screen. // how the maintenance page rewrites everything to /maintenance-screen.
const primaryType = typeMap.keys().next().value as string; const primaryType = typeMap.keys().next().value as string;
const internalHost = config.getRawConfig().server.internal_hostname;
const internalPort = config.getRawConfig().server.next_port;
const uiRewriteMiddlewareName = `bg-r${bgResource.resourceId}-ui-rewrite`; const uiRewriteMiddlewareName = `bg-r${bgResource.resourceId}-ui-rewrite`;
const uiHeadersMiddlewareName = `bg-r${bgResource.resourceId}-ui-headers`;
const entrypoint = bgResource.ssl const entrypoint = bgResource.ssl
? config.getRawConfig().traefik.https_entrypoint ? config.getRawConfig().traefik.https_entrypoint
: config.getRawConfig().traefik.http_entrypoint; : config.getRawConfig().traefik.http_entrypoint;
@@ -1252,22 +1275,33 @@ export async function getTraefikConfig(
} }
}; };
config_output.http.middlewares![uiHeadersMiddlewareName] = {
headers: {
customRequestHeaders: {
Host: "app.pangolin.net", // if we are sending to the cloud the host needs to be this but we will pull the p-host to find the resource
"p-host": fullDomain
}
}
};
config_output.http.services![bgUiServiceName] = { config_output.http.services![bgUiServiceName] = {
loadBalancer: { loadBalancer: {
servers: [ servers: [
{ {
url: `http://${internalHost}:${internalPort}` url: browserGatewayUiUrl
} }
] ]
} }
}; };
// Assets router at higher priority so /_next files load without rewrite // Assets router at higher priority so /_next files load without rewrite.
// Do NOT apply the path-rewrite middleware here — static assets must
// keep their original path; only the host headers are needed.
config_output.http.routers![ config_output.http.routers![
`bg-r${bgResource.resourceId}-assets-router` `bg-r${bgResource.resourceId}-assets-router`
] = { ] = {
entryPoints: [entrypoint], entryPoints: [entrypoint],
middlewares: routerMiddlewares, middlewares: [...routerMiddlewares, uiHeadersMiddlewareName],
service: bgUiServiceName, service: bgUiServiceName,
rule: `${hostRule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`, rule: `${hostRule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`,
priority: 101, priority: 101,
@@ -1279,7 +1313,11 @@ export async function getTraefikConfig(
`bg-r${bgResource.resourceId}-ui-router` `bg-r${bgResource.resourceId}-ui-router`
] = { ] = {
entryPoints: [entrypoint], entryPoints: [entrypoint],
middlewares: [...routerMiddlewares, uiRewriteMiddlewareName], middlewares: [
...routerMiddlewares,
uiRewriteMiddlewareName,
uiHeadersMiddlewareName
],
service: bgUiServiceName, service: bgUiServiceName,
rule: hostRule, rule: hostRule,
priority: 100, priority: 100,
@@ -1312,10 +1350,6 @@ export async function getTraefikConfig(
const siteResourceRouterName = `${srKey}-router`; const siteResourceRouterName = `${srKey}-router`;
const siteResourceRewriteMiddlewareName = `${srKey}-rewrite`; const siteResourceRewriteMiddlewareName = `${srKey}-rewrite`;
const maintenancePort = config.getRawConfig().server.next_port;
const maintenanceHost =
config.getRawConfig().server.internal_hostname;
if (!config_output.http.routers) { if (!config_output.http.routers) {
config_output.http.routers = {}; config_output.http.routers = {};
} }
@@ -1331,7 +1365,7 @@ export async function getTraefikConfig(
loadBalancer: { loadBalancer: {
servers: [ servers: [
{ {
url: `http://${maintenanceHost}:${maintenancePort}` url: maintenancePageUiUrl
} }
], ],
passHostHeader: true passHostHeader: true

View File

@@ -277,6 +277,8 @@ hybridRouter.get(
); );
} }
const pangolinUIUrl = config.getRawConfig().app.dashboard_url; // points to the dashboard to serve from there
try { try {
const traefikConfig = await getTraefikConfig( const traefikConfig = await getTraefikConfig(
remoteExitNode.exitNodeId, remoteExitNode.exitNodeId,
@@ -284,8 +286,8 @@ hybridRouter.get(
true, // But don't allow domain namespace resources true, // But don't allow domain namespace resources
false, // Dont include login pages, false, // Dont include login pages,
true, // allow raw resources true, // allow raw resources
false, // dont generate maintenance page pangolinUIUrl, // dont generate maintenance page
false // dont generate browser gateway targets pangolinUIUrl // generate browser gateway targets
); );
return response(res, { return response(res, {

View File

@@ -1,5 +1,6 @@
import { assertEquals } from "@test/assert"; import { assertEquals } from "@test/assert";
import { REGIONS } from "@server/db/regions"; import { REGIONS } from "@server/db/regions";
import { isPathAllowed } from "@server/lib/pathMatch";
function isIpInRegion( function isIpInRegion(
ipCountryCode: string | undefined, ipCountryCode: string | undefined,
@@ -33,76 +34,6 @@ function isIpInRegion(
return false; return false;
} }
function isPathAllowed(pattern: string, path: string): boolean {
// Normalize and split paths into segments
const normalize = (p: string) => p.split("/").filter(Boolean);
const patternParts = normalize(pattern);
const pathParts = normalize(path);
// Recursive function to try different wildcard matches
function matchSegments(patternIndex: number, pathIndex: number): boolean {
const indent = " ".repeat(pathIndex); // Indent based on recursion depth
const currentPatternPart = patternParts[patternIndex];
const currentPathPart = pathParts[pathIndex];
// If we've consumed all pattern parts, we should have consumed all path parts
if (patternIndex >= patternParts.length) {
const result = pathIndex >= pathParts.length;
return result;
}
// If we've consumed all path parts but still have pattern parts
if (pathIndex >= pathParts.length) {
// The only way this can match is if all remaining pattern parts are wildcards
const remainingPattern = patternParts.slice(patternIndex);
const result = remainingPattern.every((p) => p === "*");
return result;
}
// For full segment wildcards, try consuming different numbers of path segments
if (currentPatternPart === "*") {
// Try consuming 0 segments (skip the wildcard)
if (matchSegments(patternIndex + 1, pathIndex)) {
return true;
}
// Try consuming current segment and recursively try rest
if (matchSegments(patternIndex, pathIndex + 1)) {
return true;
}
return false;
}
// Check for in-segment wildcard (e.g., "prefix*" or "prefix*suffix")
if (currentPatternPart.includes("*")) {
// Convert the pattern segment to a regex pattern
const regexPattern = currentPatternPart
.replace(/\*/g, ".*") // Replace * with .* for regex wildcard
.replace(/\?/g, "."); // Replace ? with . for single character wildcard if needed
const regex = new RegExp(`^${regexPattern}$`);
if (regex.test(currentPathPart)) {
return matchSegments(patternIndex + 1, pathIndex + 1);
}
return false;
}
// For regular segments, they must match exactly
if (currentPatternPart !== currentPathPart) {
return false;
}
// Move to next segments in both pattern and path
return matchSegments(patternIndex + 1, pathIndex + 1);
}
const result = matchSegments(0, 0);
return result;
}
function runTests() { function runTests() {
console.log("Running path matching tests..."); console.log("Running path matching tests...");
@@ -308,6 +239,121 @@ function runTests() {
console.log("All path matching tests passed!"); console.log("All path matching tests passed!");
} }
function runSpecialCharacterTests() {
console.log("\nRunning special character tests...");
let threw = false;
try {
isPathAllowed("(api*", "anything");
isPathAllowed("a(b*", "a(bc");
isPathAllowed("c[d*", "c[de");
isPathAllowed("x{2}*", "x{2}y");
isPathAllowed("a|b*", "a|bc");
isPathAllowed("back\\slash*", "back\\slashed");
} catch (e) {
threw = true;
console.error(
"Patterns accepted by isValidUrlGlobPattern crashed the matcher:",
e instanceof Error ? e.message : e
);
}
assertEquals(
threw,
false,
"Patterns with regex metacharacters must not throw"
);
assertEquals(
isPathAllowed("(api*", "(api-v1"),
true,
"Parenthesis should be treated as a literal character"
);
assertEquals(
isPathAllowed("(api*", "xapi-v1"),
false,
"Parenthesis should not match other characters"
);
assertEquals(
isPathAllowed("a(b)*", "a(b)c"),
true,
"Parentheses pair should be treated as literal characters"
);
assertEquals(
isPathAllowed("*.png", "image.png"),
true,
"Dot should match a literal dot"
);
assertEquals(
isPathAllowed("*.png", "imageXpng"),
false,
"Dot should not act as a regex wildcard"
);
assertEquals(
isPathAllowed("v1.0*", "v1.0.1"),
true,
"Version-like literal should match itself"
);
assertEquals(
isPathAllowed("v1.0*", "v1x0-beta"),
false,
"Version-like literal should not match arbitrary characters"
);
assertEquals(
isPathAllowed("a+b*", "a+bc"),
true,
"Plus should be treated as a literal character"
);
assertEquals(
isPathAllowed("a+b*", "aaabc"),
false,
"Plus should not act as a regex quantifier"
);
assertEquals(
isPathAllowed("$ref*", "$refs"),
true,
"Dollar sign should be treated as a literal character"
);
assertEquals(
isPathAllowed("price$*", "price$100"),
true,
"Dollar sign mid-pattern should be treated as a literal character"
);
assertEquals(
isPathAllowed("^start*", "^started"),
true,
"Caret should be treated as a literal character"
);
assertEquals(
isPathAllowed("a|b*", "a|bc"),
true,
"Pipe should be treated as a literal character"
);
assertEquals(
isPathAllowed("a|b*", "a"),
false,
"Pipe should not act as regex alternation"
);
assertEquals(
isPathAllowed("file?*", "fileX"),
true,
"Question mark should still act as a single-character wildcard"
);
assertEquals(
isPathAllowed("api/*", "api/" + "x/".repeat(50)),
true,
"Deeply nested paths should still match"
);
console.log("All special character tests passed!");
}
function runRegionTests() { function runRegionTests() {
console.log("\nRunning isIpInRegion tests..."); console.log("\nRunning isIpInRegion tests...");
@@ -367,6 +413,7 @@ function runRegionTests() {
// Run all tests // Run all tests
try { try {
runTests(); runTests();
runSpecialCharacterTests();
runRegionTests(); runRegionTests();
console.log("\n✅ All tests passed!"); console.log("\n✅ All tests passed!");
} catch (error) { } catch (error) {

View File

@@ -25,6 +25,7 @@ import {
} from "@server/db"; } from "@server/db";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { isIpInCidr, stripPortFromHost } from "@server/lib/ip"; import { isIpInCidr, stripPortFromHost } from "@server/lib/ip";
import { isPathAllowed } from "@server/lib/pathMatch";
import { response } from "@server/lib/response"; import { response } from "@server/lib/response";
import logger from "@server/logger"; import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -1090,143 +1091,7 @@ async function checkRules(
return; return;
} }
export function isPathAllowed(pattern: string, path: string): boolean { export { isPathAllowed };
logger.debug(`\nMatching path "${path}" against pattern "${pattern}"`);
// Normalize and split paths into segments
const normalize = (p: string) => p.split("/").filter(Boolean);
const patternParts = normalize(pattern);
const pathParts = normalize(path);
logger.debug(`Normalized pattern parts: [${patternParts.join(", ")}]`);
logger.debug(`Normalized path parts: [${pathParts.join(", ")}]`);
// Maximum recursion depth to prevent stack overflow and memory issues
const MAX_RECURSION_DEPTH = 100;
// Recursive function to try different wildcard matches
function matchSegments(
patternIndex: number,
pathIndex: number,
depth: number = 0
): boolean {
// Check recursion depth limit
if (depth > MAX_RECURSION_DEPTH) {
logger.warn(
`Path matching exceeded maximum recursion depth (${MAX_RECURSION_DEPTH}) for pattern "${pattern}" and path "${path}"`
);
return false;
}
const indent = " ".repeat(depth); // Indent based on recursion depth
const currentPatternPart = patternParts[patternIndex];
const currentPathPart = pathParts[pathIndex];
logger.debug(
`${indent}Checking patternIndex=${patternIndex} (${currentPatternPart || "END"}) vs pathIndex=${pathIndex} (${currentPathPart || "END"}) [depth=${depth}]`
);
// If we've consumed all pattern parts, we should have consumed all path parts
if (patternIndex >= patternParts.length) {
const result = pathIndex >= pathParts.length;
logger.debug(
`${indent}Reached end of pattern, remaining path: ${pathParts.slice(pathIndex).join("/")} -> ${result}`
);
return result;
}
// If we've consumed all path parts but still have pattern parts
if (pathIndex >= pathParts.length) {
// The only way this can match is if all remaining pattern parts are wildcards
const remainingPattern = patternParts.slice(patternIndex);
const result = remainingPattern.every((p) => p === "*");
logger.debug(
`${indent}Reached end of path, remaining pattern: ${remainingPattern.join("/")} -> ${result}`
);
return result;
}
// For full segment wildcards, try consuming different numbers of path segments
if (currentPatternPart === "*") {
logger.debug(
`${indent}Found wildcard at pattern index ${patternIndex}`
);
// Try consuming 0 segments (skip the wildcard)
logger.debug(
`${indent}Trying to skip wildcard (consume 0 segments)`
);
if (matchSegments(patternIndex + 1, pathIndex, depth + 1)) {
logger.debug(
`${indent}Successfully matched by skipping wildcard`
);
return true;
}
// Try consuming current segment and recursively try rest
logger.debug(
`${indent}Trying to consume segment "${currentPathPart}" for wildcard`
);
if (matchSegments(patternIndex, pathIndex + 1, depth + 1)) {
logger.debug(
`${indent}Successfully matched by consuming segment for wildcard`
);
return true;
}
logger.debug(`${indent}Failed to match wildcard`);
return false;
}
// Check for in-segment wildcard (e.g., "prefix*" or "prefix*suffix")
if (currentPatternPart.includes("*")) {
logger.debug(
`${indent}Found in-segment wildcard in "${currentPatternPart}"`
);
// Convert the pattern segment to a regex pattern
const regexPattern = currentPatternPart
.replace(/\*/g, ".*") // Replace * with .* for regex wildcard
.replace(/\?/g, "."); // Replace ? with . for single character wildcard if needed
const regex = new RegExp(`^${regexPattern}$`);
if (regex.test(currentPathPart)) {
logger.debug(
`${indent}Segment with wildcard matches: "${currentPatternPart}" matches "${currentPathPart}"`
);
return matchSegments(
patternIndex + 1,
pathIndex + 1,
depth + 1
);
}
logger.debug(
`${indent}Segment with wildcard mismatch: "${currentPatternPart}" doesn't match "${currentPathPart}"`
);
return false;
}
// For regular segments, they must match exactly
if (currentPatternPart !== currentPathPart) {
logger.debug(
`${indent}Segment mismatch: "${currentPatternPart}" != "${currentPathPart}"`
);
return false;
}
logger.debug(
`${indent}Segments match: "${currentPatternPart}" = "${currentPathPart}"`
);
// Move to next segments in both pattern and path
return matchSegments(patternIndex + 1, pathIndex + 1, depth + 1);
}
const result = matchSegments(0, 0, 0);
logger.debug(`Final result: ${result}`);
return result;
}
async function isIpInGeoIP( async function isIpInGeoIP(
ipCountryCode: string | undefined, ipCountryCode: string | undefined,

View File

@@ -17,7 +17,6 @@ import {
verifyApiKey, verifyApiKey,
verifyApiKeyOrgAccess, verifyApiKeyOrgAccess,
verifyApiKeyHasAction, verifyApiKeyHasAction,
verifyApiKeyCanSetUserOrgRoles,
verifyApiKeySiteAccess, verifyApiKeySiteAccess,
verifyApiKeyResourceAccess, verifyApiKeyResourceAccess,
verifyApiKeyTargetAccess, verifyApiKeyTargetAccess,
@@ -974,6 +973,13 @@ authenticated.get(
idp.getIdp idp.getIdp
); );
authenticated.delete(
"/idp/:idpId",
verifyApiKeyIsRoot,
verifyApiKeyHasAction(ActionsEnum.deleteIdp),
idp.deleteIdp
);
authenticated.put( authenticated.put(
"/idp/:idpId/org/:orgId", "/idp/:idpId/org/:orgId",
verifyApiKeyIsRoot, verifyApiKeyIsRoot,

View File

@@ -54,7 +54,7 @@ export const handleNewtGetConfigMessage: MessageHandler = async (context) => {
// TODO: somehow we should make sure a recent hole punch has happened if this occurs (hole punch could be from the last restart if done quickly) // TODO: somehow we should make sure a recent hole punch has happened if this occurs (hole punch could be from the last restart if done quickly)
} }
if (existingSite.lastHolePunch && now - existingSite.lastHolePunch > 5) { if (existingSite.lastHolePunch && now - existingSite.lastHolePunch > 12) {
logger.warn( logger.warn(
`Site last hole punch is too old; skipping this register. The site is failing to hole punch and identify its network address with the server. Can the site reach the server on UDP port ${config.getRawConfig().gerbil.clients_start_port}?` `Site last hole punch is too old; skipping this register. The site is failing to hole punch and identify its network address with the server. Can the site reach the server on UDP port ${config.getRawConfig().gerbil.clients_start_port}?`
); );

View File

@@ -348,7 +348,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
// this prevents us from accepting a register from an olm that has not hole punched yet. // this prevents us from accepting a register from an olm that has not hole punched yet.
// the olm will pump the register so we can keep checking // the olm will pump the register so we can keep checking
// TODO: I still think there is a better way to do this rather than locking it out here but ??? // TODO: I still think there is a better way to do this rather than locking it out here but ???
if (now - (client.lastHolePunch || 0) > 5 && sitesCount > 0) { if (now - (client.lastHolePunch || 0) > 12 && sitesCount > 0) {
logger.warn( logger.warn(
`[handleOlmRegisterMessage] Client last hole punch is too old and we have sites to send; skipping this register. The client is failing to hole punch and identify its network address with the server. Can the client reach the server on UDP port ${config.getRawConfig().gerbil.clients_start_port}?`, `[handleOlmRegisterMessage] Client last hole punch is too old and we have sites to send; skipping this register. The client is failing to hole punch and identify its network address with the server. Can the client reach the server on UDP port ${config.getRawConfig().gerbil.clients_start_port}?`,
{ orgId: client.orgId, clientId: client.clientId } { orgId: client.orgId, clientId: client.clientId }

View File

@@ -9,6 +9,7 @@ import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import { z } from "zod"; import { z } from "zod";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { applyInlinePolicyFields } from "./inlinePolicyFields";
const getResourceSchema = z.strictObject({ const getResourceSchema = z.strictObject({
resourceId: z resourceId: z
@@ -151,13 +152,7 @@ export async function getResource(
const policy = await queryInlinePolicy( const policy = await queryInlinePolicy(
resource.defaultResourcePolicyId! resource.defaultResourcePolicyId!
); );
returnData = { returnData = applyInlinePolicyFields(returnData, policy);
...returnData,
sso: policy?.sso || null,
emailWhitelistEnabled: policy?.emailWhitelistEnabled || null,
applyRules: policy?.applyRules || null,
skipToIdpId: policy?.idpId || null
};
} }
return response<GetResourceResponse>(res, { return response<GetResourceResponse>(res, {

View File

@@ -0,0 +1,74 @@
import { assertEquals } from "../../../test/assert";
import { applyInlinePolicyFields } from "./inlinePolicyFields";
function runTests() {
const resource = {
resourceId: 1,
name: "dashboard",
sso: null,
emailWhitelistEnabled: null,
applyRules: null,
skipToIdpId: null
} as any;
const enabledPolicy = {
sso: true,
emailWhitelistEnabled: true,
applyRules: true,
idpId: 42
};
const enabledResult = applyInlinePolicyFields(resource, enabledPolicy);
assertEquals(enabledResult.sso, true, "sso should mirror policy true");
assertEquals(
enabledResult.emailWhitelistEnabled,
true,
"email whitelist should mirror policy true"
);
assertEquals(
enabledResult.applyRules,
true,
"applyRules should mirror policy true"
);
assertEquals(
enabledResult.skipToIdpId,
42,
"skipToIdpId should use policy idpId"
);
const disabledPolicy = {
sso: false,
emailWhitelistEnabled: false,
applyRules: false,
idpId: null
};
const disabledResult = applyInlinePolicyFields(resource, disabledPolicy);
assertEquals(disabledResult.sso, false, "sso false must not become null");
assertEquals(
disabledResult.emailWhitelistEnabled,
false,
"email whitelist false must not become null"
);
assertEquals(
disabledResult.applyRules,
false,
"applyRules false must not become null"
);
assertEquals(
disabledResult.skipToIdpId,
null,
"missing idp should stay null"
);
const missingPolicyResult = applyInlinePolicyFields(resource, null);
assertEquals(
missingPolicyResult.sso,
null,
"missing policy should return nullable resource fields"
);
console.log("PASS: inline policy fields mirror policy values");
}
runTests();

View File

@@ -0,0 +1,19 @@
import type { Resource, ResourcePolicy } from "@server/db";
type InlinePolicyFields = Pick<
ResourcePolicy,
"sso" | "emailWhitelistEnabled" | "applyRules" | "idpId"
>;
export function applyInlinePolicyFields<T extends Resource>(
resource: T,
policy: InlinePolicyFields | null | undefined
): T {
return {
...resource,
sso: policy?.sso ?? null,
emailWhitelistEnabled: policy?.emailWhitelistEnabled ?? null,
applyRules: policy?.applyRules ?? null,
skipToIdpId: policy?.idpId ?? null
};
}

View File

@@ -47,6 +47,7 @@ import { build } from "@server/build";
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { tierMatrix } from "@server/lib/billing/tierMatrix";
import { isSubscribed } from "#dynamic/lib/isSubscribed"; import { isSubscribed } from "#dynamic/lib/isSubscribed";
import { applyInlinePolicyFields } from "./inlinePolicyFields";
const updateResourceParamsSchema = z.strictObject({ const updateResourceParamsSchema = z.strictObject({
resourceId: z.coerce.number().int().positive() resourceId: z.coerce.number().int().positive()
@@ -682,6 +683,12 @@ async function updateHttpResource(
.where(eq(resourcePolicies.resourcePolicyId, policyId)); .where(eq(resourcePolicies.resourcePolicyId, policyId));
} }
const [inlinePolicy] = await db
.select()
.from(resourcePolicies)
.where(eq(resourcePolicies.resourcePolicyId, policyId))
.limit(1);
const updatedResource = await db const updatedResource = await db
.update(resources) .update(resources)
.set({ ...resourceOnlyData, headers }) .set({ ...resourceOnlyData, headers })
@@ -698,7 +705,7 @@ async function updateHttpResource(
} }
return response(res, { return response(res, {
data: updatedResource[0], data: applyInlinePolicyFields(updatedResource[0], inlinePolicy),
success: true, success: true,
error: false, error: false,
message: "HTTP resource updated successfully", message: "HTTP resource updated successfully",

View File

@@ -3,7 +3,6 @@ import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { response as sendResponse } from "@server/lib/response"; import { response as sendResponse } from "@server/lib/response";
import config from "@server/lib/config";
import { build } from "@server/build"; import { build } from "@server/build";
import { APP_VERSION } from "@server/lib/consts"; import { APP_VERSION } from "@server/lib/consts";
import license from "#dynamic/license/license"; import license from "#dynamic/license/license";
@@ -22,9 +21,6 @@ export async function getServerInfo(
next: NextFunction next: NextFunction
): Promise<any> { ): Promise<any> {
try { try {
const supporterData = config.getSupporterData();
const supporterStatusValid = supporterData?.valid || false;
let enterpriseLicenseValid = false; let enterpriseLicenseValid = false;
let enterpriseLicenseType: string | null = null; let enterpriseLicenseType: string | null = null;
@@ -41,7 +37,7 @@ export async function getServerInfo(
return sendResponse<GetServerInfoResponse>(res, { return sendResponse<GetServerInfoResponse>(res, {
data: { data: {
version: APP_VERSION, version: APP_VERSION,
supporterStatusValid, supporterStatusValid: true,
build, build,
enterpriseLicenseValid, enterpriseLicenseValid,
enterpriseLicenseType enterpriseLicenseType

View File

@@ -17,13 +17,18 @@ export async function traefikConfigProvider(
// Get the current exit node name from config // Get the current exit node name from config
const currentExitNodeId = await getCurrentExitNodeId(); const currentExitNodeId = await getCurrentExitNodeId();
const maintenancePort = config.getRawConfig().server.next_port;
const maintenanceHost = config.getRawConfig().server.internal_hostname;
const pangolinUIUrl = `http://${maintenanceHost}:${maintenancePort}`;
const traefikConfig = await getTraefikConfig( const traefikConfig = await getTraefikConfig(
currentExitNodeId, currentExitNodeId,
config.getRawConfig().traefik.site_types, config.getRawConfig().traefik.site_types,
build == "oss", // filter out the namespace domains in open source build == "oss", // filter out the namespace domains in open source
build != "oss", // generate the login pages on the cloud and and enterprise, build != "oss", // generate the login pages on the cloud and and enterprise,
config.getRawConfig().traefik.allow_raw_resources, config.getRawConfig().traefik.allow_raw_resources,
build != "oss" // generate browser gateway resources on cloud and enterprise pangolinUIUrl,
pangolinUIUrl
); );
if (traefikConfig?.http?.middlewares) { if (traefikConfig?.http?.middlewares) {

View File

@@ -42,7 +42,14 @@ import {
SettingsSectionFooter SettingsSectionFooter
} from "@app/components/Settings"; } from "@app/components/Settings";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { ArrowRight, Check, ExternalLink, Heart, InfoIcon, TicketCheck } from "lucide-react"; import {
ArrowRight,
Check,
ExternalLink,
Heart,
InfoIcon,
TicketCheck
} from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import DismissableBanner from "@app/components/DismissableBanner"; import DismissableBanner from "@app/components/DismissableBanner";
import CopyTextBox from "@app/components/CopyTextBox"; import CopyTextBox from "@app/components/CopyTextBox";
@@ -50,7 +57,7 @@ import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { SitePriceCalculator } from "@app/components/SitePriceCalculator"; import { SitePriceCalculator } from "@app/components/SitePriceCalculator";
import { Checkbox } from "@app/components/ui/checkbox"; import { Checkbox } from "@app/components/ui/checkbox";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; // import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
const ENTERPRISE_DOCS_URL = const ENTERPRISE_DOCS_URL =
@@ -82,7 +89,7 @@ export default function LicensePage() {
const [isActivatingLicense, setIsActivatingLicense] = useState(false); const [isActivatingLicense, setIsActivatingLicense] = useState(false);
const [isDeletingLicense, setIsDeletingLicense] = useState(false); const [isDeletingLicense, setIsDeletingLicense] = useState(false);
const [isRecheckingLicense, setIsRecheckingLicense] = useState(false); const [isRecheckingLicense, setIsRecheckingLicense] = useState(false);
const { supporterStatus } = useSupporterStatusContext(); // const { supporterStatus } = useSupporterStatusContext();
const t = useTranslations(); const t = useTranslations();
@@ -347,9 +354,7 @@ export default function LicensePage() {
storageKey="license-banner-dismissed" storageKey="license-banner-dismissed"
version={1} version={1}
title={t("licenseBannerTitle")} title={t("licenseBannerTitle")}
titleIcon={ titleIcon={<TicketCheck className="w-5 h-5 text-primary" />}
<TicketCheck className="w-5 h-5 text-primary" />
}
description={t("licenseBannerDescription")} description={t("licenseBannerDescription")}
> >
<Link <Link

View File

@@ -68,15 +68,15 @@ export default async function RootLayout({
const env = pullEnv(); const env = pullEnv();
const locale = await getLocale(); const locale = await getLocale();
const supporterData = { // const supporterData = {
visible: true // visible: true
} as any; // } as any;
const res = await priv.get<AxiosResponse<IsSupporterKeyVisibleResponse>>( // const res = await priv.get<AxiosResponse<IsSupporterKeyVisibleResponse>>(
"supporter-key/visible" // "supporter-key/visible"
); // );
supporterData.visible = res.data.data.visible; // supporterData.visible = res.data.data.visible;
supporterData.tier = res.data.data.tier; // supporterData.tier = res.data.data.tier;
let licenseStatus: GetLicenseStatusResponse; let licenseStatus: GetLicenseStatusResponse;
if (build === "enterprise") { if (build === "enterprise") {
@@ -127,20 +127,20 @@ export default async function RootLayout({
<LicenseStatusProvider <LicenseStatusProvider
licenseStatus={licenseStatus} licenseStatus={licenseStatus}
> >
<SupportStatusProvider {/* <SupportStatusProvider
supporterStatus={supporterData} supporterStatus={supporterData}
> > */}
{/* Main content */} {/* Main content */}
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
<SplashImage> <SplashImage>
<LicenseViolation />
{children}
</SplashImage>
<LicenseViolation /> <LicenseViolation />
</div> {children}
</SplashImage>
<LicenseViolation />
</div> </div>
</SupportStatusProvider> </div>
{/* </SupportStatusProvider> */}
</LicenseStatusProvider> </LicenseStatusProvider>
<Toaster /> <Toaster />
</TanstackQueryProvider> </TanstackQueryProvider>

View File

@@ -28,7 +28,7 @@ export default async function MaintenanceScreen() {
try { try {
const headersList = await headers(); const headersList = await headers();
const host = headersList.get("host") || ""; const host = headersList.get("p-host") || headersList.get("host") || "";
const hostname = host.split(":")[0]; const hostname = host.split(":")[0];
const res = await priv.get<AxiosResponse<GetMaintenanceInfoResponse>>( const res = await priv.get<AxiosResponse<GetMaintenanceInfoResponse>>(

View File

@@ -1,24 +1,24 @@
"use client"; "use client";
import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; // import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { build } from "@server/build"; import { build } from "@server/build";
export default function AuthPageFooterNotices() { export default function AuthPageFooterNotices() {
const t = useTranslations(); const t = useTranslations();
const { supporterStatus } = useSupporterStatusContext(); // const { supporterStatus } = useSupporterStatusContext();
const { isUnlocked, licenseStatus } = useLicenseStatusContext(); const { isUnlocked, licenseStatus } = useLicenseStatusContext();
return ( return (
<> <>
{supporterStatus?.visible && ( {/* {supporterStatus?.visible && (
<div className="text-center mt-2"> <div className="text-center mt-2">
<span className="text-sm text-muted-foreground opacity-50"> <span className="text-sm text-muted-foreground opacity-50">
{t("noSupportKey")} {t("noSupportKey")}
</span> </span>
</div> </div>
)} )} */}
{build === "enterprise" && !isUnlocked() ? ( {build === "enterprise" && !isUnlocked() ? (
<div className="text-center mt-2"> <div className="text-center mt-2">
<span className="text-sm font-medium text-muted-foreground"> <span className="text-sm font-medium text-muted-foreground">

View File

@@ -9,33 +9,34 @@ export default function SupporterMessage({ tier }: { tier: string }) {
const t = useTranslations(); const t = useTranslations();
return ( return (
<div className="relative flex items-center space-x-2 whitespace-nowrap group"> <></>
<span // <div className="relative flex items-center space-x-2 whitespace-nowrap group">
className="cursor-pointer" // <span
onClick={(e) => { // className="cursor-pointer"
// Get the bounding box of the element // onClick={(e) => {
const rect = ( // // Get the bounding box of the element
e.target as HTMLElement // const rect = (
).getBoundingClientRect(); // e.target as HTMLElement
// ).getBoundingClientRect();
// Trigger confetti centered on the word "Pangolin" // // Trigger confetti centered on the word "Pangolin"
confetti({ // confetti({
particleCount: 100, // particleCount: 100,
spread: 70, // spread: 70,
origin: { // origin: {
x: (rect.left + rect.width / 2) / window.innerWidth, // x: (rect.left + rect.width / 2) / window.innerWidth,
y: rect.top / window.innerHeight // y: rect.top / window.innerHeight
}, // },
colors: ["#FFA500", "#FF4500", "#FFD700"] // colors: ["#FFA500", "#FF4500", "#FFD700"]
}); // });
}} // }}
> // >
Pangolin // Pangolin
</span> // </span>
<Star className="w-3 h-3" /> // <Star className="w-3 h-3" />
<div className="absolute left-1/2 transform -translate-x-1/2 -top-10 hidden group-hover:block text-primary text-sm rounded-md border shadow-md px-4 py-2 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity"> // <div className="absolute left-1/2 transform -translate-x-1/2 -top-10 hidden group-hover:block text-primary text-sm rounded-md border shadow-md px-4 py-2 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity">
{t("componentsSupporterMessage", { tier: tier })} // {t("componentsSupporterMessage", { tier: tier })}
</div> // </div>
</div> // </div>
); );
} }

View File

@@ -3,7 +3,7 @@
// THIS IS DEPRECATED AND IS NO LONGER SHOWED TO THE USER WITH THE DISCONTINUATION // THIS IS DEPRECATED AND IS NO LONGER SHOWED TO THE USER WITH THE DISCONTINUATION
// OF THE SUPPORTER PROGRAM. IT MAY BE REMOVED IN A FUTURE UPDATE. // OF THE SUPPORTER PROGRAM. IT MAY BE REMOVED IN A FUTURE UPDATE.
import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; // import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext";
import { useState, useTransition } from "react"; import { useState, useTransition } from "react";
import { import {
Tooltip, Tooltip,
@@ -58,134 +58,134 @@ interface SupporterStatusProps {
export default function SupporterStatus({ export default function SupporterStatus({
isCollapsed = false isCollapsed = false
}: SupporterStatusProps) { }: SupporterStatusProps) {
const { supporterStatus, updateSupporterStatus } = // const { supporterStatus, updateSupporterStatus } =
useSupporterStatusContext(); // useSupporterStatusContext();
const [supportOpen, setSupportOpen] = useState(false); // const [supportOpen, setSupportOpen] = useState(false);
const [keyOpen, setKeyOpen] = useState(false); // const [keyOpen, setKeyOpen] = useState(false);
const [purchaseOptionsOpen, setPurchaseOptionsOpen] = useState(false); // const [purchaseOptionsOpen, setPurchaseOptionsOpen] = useState(false);
const { env } = useEnvContext(); // const { env } = useEnvContext();
const api = createApiClient({ env }); // const api = createApiClient({ env });
const t = useTranslations(); // const t = useTranslations();
const formSchema = z.object({ // const formSchema = z.object({
githubUsername: z.string().nonempty({ // githubUsername: z.string().nonempty({
error: "GitHub username is required" // error: "GitHub username is required"
}), // }),
key: z.string().nonempty({ // key: z.string().nonempty({
error: "Supporter key is required" // error: "Supporter key is required"
}) // })
}); // });
const form = useForm({ // const form = useForm({
resolver: zodResolver(formSchema), // resolver: zodResolver(formSchema),
defaultValues: { // defaultValues: {
githubUsername: "", // githubUsername: "",
key: "" // key: ""
} // }
}); // });
async function hide() { // async function hide() {
await api.post("/supporter-key/hide"); // await api.post("/supporter-key/hide");
updateSupporterStatus({ // updateSupporterStatus({
visible: false // visible: false
}); // });
} // }
async function onSubmit(values: z.infer<typeof formSchema>) { // async function onSubmit(values: z.infer<typeof formSchema>) {
try { // try {
const res = await api.post< // const res = await api.post<
AxiosResponse<ValidateSupporterKeyResponse> // AxiosResponse<ValidateSupporterKeyResponse>
>("/supporter-key/validate", { // >("/supporter-key/validate", {
githubUsername: values.githubUsername, // githubUsername: values.githubUsername,
key: values.key // key: values.key
}); // });
const data = res.data.data; // const data = res.data.data;
if (!data || !data.valid) { // if (!data || !data.valid) {
toast({ // toast({
variant: "destructive", // variant: "destructive",
title: t("supportKeyInvalid"), // title: t("supportKeyInvalid"),
description: t("supportKeyInvalidDescription") // description: t("supportKeyInvalidDescription")
}); // });
return; // return;
} // }
// Trigger the toast // // Trigger the toast
toast({ // toast({
variant: "default", // variant: "default",
title: t("supportKeyValid"), // title: t("supportKeyValid"),
description: t("supportKeyValidDescription") // description: t("supportKeyValidDescription")
}); // });
// Fireworks-style confetti // // Fireworks-style confetti
const duration = 5 * 1000; // 5 seconds // const duration = 5 * 1000; // 5 seconds
const animationEnd = Date.now() + duration; // const animationEnd = Date.now() + duration;
const defaults = { // const defaults = {
startVelocity: 30, // startVelocity: 30,
spread: 360, // spread: 360,
ticks: 60, // ticks: 60,
zIndex: 0, // zIndex: 0,
colors: ["#FFA500", "#FF4500", "#FFD700"] // Orange hues // colors: ["#FFA500", "#FF4500", "#FFD700"] // Orange hues
}; // };
function randomInRange(min: number, max: number) { // function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min; // return Math.random() * (max - min) + min;
} // }
const interval = setInterval(() => { // const interval = setInterval(() => {
const timeLeft = animationEnd - Date.now(); // const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) { // if (timeLeft <= 0) {
clearInterval(interval); // clearInterval(interval);
return; // return;
} // }
const particleCount = 50 * (timeLeft / duration); // const particleCount = 50 * (timeLeft / duration);
// Launch confetti from two random horizontal positions // // Launch confetti from two random horizontal positions
confetti({ // confetti({
...defaults, // ...defaults,
particleCount, // particleCount,
origin: { // origin: {
x: randomInRange(0.1, 0.3), // x: randomInRange(0.1, 0.3),
y: Math.random() - 0.2 // y: Math.random() - 0.2
} // }
}); // });
confetti({ // confetti({
...defaults, // ...defaults,
particleCount, // particleCount,
origin: { // origin: {
x: randomInRange(0.7, 0.9), // x: randomInRange(0.7, 0.9),
y: Math.random() - 0.2 // y: Math.random() - 0.2
} // }
}); // });
}, 250); // }, 250);
setPurchaseOptionsOpen(false); // setPurchaseOptionsOpen(false);
setKeyOpen(false); // setKeyOpen(false);
updateSupporterStatus({ // updateSupporterStatus({
visible: false // visible: false
}); // });
} catch (error) { // } catch (error) {
toast({ // toast({
variant: "destructive", // variant: "destructive",
title: t("error"), // title: t("error"),
description: formatAxiosError( // description: formatAxiosError(
error, // error,
t("supportKeyErrorValidationDescription") // t("supportKeyErrorValidationDescription")
) // )
}); // });
return; // return;
} // }
} // }
return ( return (
<> <>
<Credenza {/* <Credenza
open={purchaseOptionsOpen} open={purchaseOptionsOpen}
onOpenChange={(val) => { onOpenChange={(val) => {
setPurchaseOptionsOpen(val); setPurchaseOptionsOpen(val);
@@ -469,7 +469,7 @@ export default function SupporterStatus({
{t("supportKeyBuy")} {t("supportKeyBuy")}
</Button> </Button>
) )
) : null} ) : null} */}
</> </>
); );
} }

View File

@@ -6,7 +6,7 @@ import { cache } from "react";
export const getBrowserTargetForRequest = cache(async () => { export const getBrowserTargetForRequest = cache(async () => {
const headersList = await headers(); const headersList = await headers();
const host = headersList.get("host") || ""; const host = headersList.get("p-host") || headersList.get("host") || "";
const hostname = host.split(":")[0]; const hostname = host.split(":")[0];
try { try {