Compare commits

...

42 Commits

Author SHA1 Message Date
Owen
a5bab6bb80 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2025-10-19 12:04:59 -07:00
miloschwartz
7536c03f63 add int api routes for add/remote email to resource email whitelist 2025-10-19 12:04:20 -07:00
Owen
ada5d2ef0e Update domain 2025-10-19 11:59:10 -07:00
Owen
b8bead0590 Select exit node for local sites 2025-10-19 11:13:33 -07:00
Milo Schwartz
68f852d6d1 Merge pull request #1699 from Pallavikumarimdb/make-easier-to-delete
Make it easier to delete things
2025-10-19 14:00:19 -04:00
Owen
d9fe5a8819 Always set exit node to online
Fixes #1692
2025-10-19 10:47:32 -07:00
Owen
346183a23f Only allow nodes to pull with defined exitNodeID 2025-10-19 10:46:25 -07:00
Owen
dcfd7f5443 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2025-10-19 10:43:39 -07:00
Pallavi Kumari
e59cd6672b fix space 2025-10-19 22:23:57 +05:30
Pallavi Kumari
7c8c440f67 fix text 2025-10-19 21:36:47 +05:30
Pallavi Kumari
f258c41f15 easier to delete 2025-10-19 20:37:07 +05:30
Pallavi Kumari
ae4a24f4aa easier to delete resources 2025-10-19 15:50:00 +05:30
Pallavi Kumari
476cdcfe86 easier to delete sites 2025-10-19 15:02:35 +05:30
Owen
f869df2f65 Working on fixing exit node issue 2025-10-18 21:32:26 -07:00
Owen Schwartz
03cfabacd9 Merge pull request #1695 from Pallavikumarimdb/fix/rule-priority-input
Make priority input box focused on pressing the up/down arrows
2025-10-18 13:38:54 -07:00
miloschwartz
47ac5875f3 change digpangolin.com to pangolin.net 2025-10-18 11:51:09 -07:00
miloschwartz
f67327358e Merge branch 'main' into dev 2025-10-18 11:41:06 -07:00
Milo Schwartz
4901823f15 Update README.md 2025-10-18 14:25:22 -04:00
Pallavi Kumari
5407e3c821 make priority input box focus on up/down click 2025-10-18 23:38:14 +05:30
Owen Schwartz
1d5cdad8b7 Merge pull request #1693 from Pallavikumarimdb/fix/sorting-resources-alphabetically-by-default
Sorting Resources Alphabetically by Default
2025-10-18 10:03:28 -07:00
Owen Schwartz
cd2424cb77 Merge pull request #1691 from fosrl/dependabot/npm_and_yarn/prod-patch-updates-30703f013a
Bump the prod-patch-updates group across 1 directory with 4 updates
2025-10-18 10:03:23 -07:00
Pallavi Kumari
c17efde6bf Sorting Resources Alphabetically by Default 2025-10-18 14:43:54 +05:30
Owen
40cd8cdec7 Merge branch 'dev' 2025-10-17 16:25:01 -07:00
Owen
6768672a44 Merge branch 'main' of github.com:fosrl/pangolin 2025-10-17 16:24:55 -07:00
Owen
240c5b005b Add more transactions support 2025-10-17 16:22:43 -07:00
dependabot[bot]
8dde170a35 Bump the prod-patch-updates group across 1 directory with 4 updates
Bumps the prod-patch-updates group with 3 updates in the / directory: [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components), [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) and [next](https://github.com/vercel/next.js).


Updates `@react-email/components` from 0.5.6 to 0.5.7
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/components/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/@react-email/components@0.5.7/packages/components)

Updates `@react-email/render` from 1.3.2 to 1.4.0
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/render/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/@react-email/render@1.4.0/packages/render)

Updates `eslint-config-next` from 15.5.5 to 15.5.6
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.5.6/packages/eslint-config-next)

Updates `next` from 15.5.5 to 15.5.6
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.5.5...v15.5.6)

---
updated-dependencies:
- dependency-name: "@react-email/components"
  dependency-version: 0.5.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@react-email/render"
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-patch-updates
- dependency-name: eslint-config-next
  dependency-version: 15.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: next
  dependency-version: 15.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-17 21:06:44 +00:00
Owen
c07abf8ff9 Pass through transaction 2025-10-17 14:05:17 -07:00
Owen
e5a436593f Delete all before migrating 2025-10-17 14:05:17 -07:00
Owen
bb6e093ac6 Priority needs to be def 2025-10-17 14:05:17 -07:00
Milo Schwartz
59a334ce24 Update README.md 2025-10-17 14:05:17 -07:00
Owen
d241dcfb27 Fix typo 2025-10-17 14:05:17 -07:00
Owen
af263e7913 Pass through transaction 2025-10-17 14:04:49 -07:00
Owen Schwartz
6610e7d405 Merge pull request #1673 from fosrl/dependabot/npm_and_yarn/prod-patch-updates-ac45ae572b
Bump the prod-patch-updates group across 1 directory with 2 updates
2025-10-17 14:02:36 -07:00
Owen Schwartz
c476e65cf2 Merge pull request #1677 from fosrl/dependabot/npm_and_yarn/dev-patch-updates-3f2a7d9f8f
Bump the dev-patch-updates group across 1 directory with 2 updates
2025-10-17 14:01:57 -07:00
Owen Schwartz
b69b2eeeb3 Merge pull request #1689 from barnabehvrd/patch-2
FR translation update
2025-10-17 13:57:09 -07:00
Barnabé Havard
89dab0917b Fixed (again ...) indentation issues 2025-10-17 22:42:07 +02:00
Barnabé Havard
73efdb95ae Fixed indentation issues 2025-10-17 22:36:08 +02:00
Barnabé Havard
1bcca88614 Updated several translation 2025-10-17 22:32:51 +02:00
Owen Schwartz
8387571c1d Merge pull request #1684 from Pallavikumarimdb/fix/make-priority-optional
Make priority optional in schema
2025-10-17 10:14:01 -07:00
Pallavi Kumari
1d017f60b4 make priority optional in schema 2025-10-17 19:51:32 +05:30
dependabot[bot]
81effda9e8 Bump the prod-patch-updates group across 1 directory with 2 updates
Bumps the prod-patch-updates group with 2 updates in the / directory: [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) and [next](https://github.com/vercel/next.js).


Updates `eslint-config-next` from 15.5.4 to 15.5.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.5.5/packages/eslint-config-next)

Updates `next` from 15.5.4 to 15.5.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.5.4...v15.5.5)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 15.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: next
  dependency-version: 15.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-17 01:22:32 +00:00
dependabot[bot]
9343906ab1 Bump the dev-patch-updates group across 1 directory with 2 updates
Bumps the dev-patch-updates group with 2 updates in the / directory: [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) and [esbuild](https://github.com/evanw/esbuild).


Updates `@types/react-dom` from 19.2.1 to 19.2.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `esbuild` from 0.25.10 to 0.25.11
- [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.25.10...v0.25.11)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: esbuild
  dependency-version: 0.25.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-17 01:20:39 +00:00
86 changed files with 1488 additions and 559 deletions

View File

@@ -4,7 +4,7 @@ Contributions are welcome!
Please see the contribution and local development guide on the docs page before getting started:
https://docs.digpangolin.com/development/contributing
https://docs.pangolin.net/development/contributing
### Licensing Considerations

View File

@@ -1,6 +1,6 @@
<div align="center">
<h2>
<a href="https://digpangolin.com">
<a href="https://pangolin.net/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="public/logo/word_mark_white.png">
<img alt="Pangolin Logo" src="public/logo/word_mark_black.png" width="350">
@@ -11,15 +11,15 @@
<div align="center">
<h5>
<a href="https://digpangolin.com">
<a href="https://pangolin.net/">
Website
</a>
<span> | </span>
<a href="https://docs.digpangolin.com/">
<a href="https://docs.pangolin.net/">
Documentation
</a>
<span> | </span>
<a href="mailto:contact@fossorial.io">
<a href="mailto:contact@pangolin.net">
Contact Us
</a>
</h5>
@@ -28,7 +28,7 @@
<div align="center">
[![Discord](https://img.shields.io/discord/1325658630518865980?logo=discord&style=flat-square)](https://discord.gg/HCJR8Xhme4)
[![Slack](https://img.shields.io/badge/chat-slack-yellow?style=flat-square&logo=slack)](https://digpangolin.com/slack)
[![Slack](https://img.shields.io/badge/chat-slack-yellow?style=flat-square&logo=slack)](https://pangolin.net/slack)
[![Docker](https://img.shields.io/docker/pulls/fosrl/pangolin?style=flat-square)](https://hub.docker.com/r/fosrl/pangolin)
![Stars](https://img.shields.io/github/stars/fosrl/pangolin?style=flat-square)
[![YouTube](https://img.shields.io/badge/YouTube-red?logo=youtube&logoColor=white&style=flat-square)](https://www.youtube.com/@fossorial-app)
@@ -37,7 +37,7 @@
<p align="center">
<strong>
Start testing Pangolin at <a href="https://pangolin.fossorial.io/auth/signup">pangolin.fossorial.io</a>
Start testing Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
</strong>
</p>
@@ -45,7 +45,7 @@ Pangolin is a self-hosted tunneled reverse proxy server with identity and contex
## Installation
Check out the [quick install guide](https://docs.digpangolin.com/self-host/quick-install) for how to install and set up Pangolin.
Check out the [quick install guide](https://docs.pangolin.net/self-host/quick-install) for how to install and set up Pangolin.
## Deployment Options
@@ -53,7 +53,7 @@ Check out the [quick install guide](https://docs.digpangolin.com/self-host/quick
|-----------------|--------------|
| **Self-Host: Community Edition** | Free, open source, and licensed under AGPL-3. |
| **Self-Host: Enterprise Edition** | Licensed under Fossorial Commercial License. Free for personal and hobbyist use, and for businesses earning under \$100K USD annually. |
| **Pangolin Cloud** | Fully managed service with instant setup and pay-as-you-go pricing — no infrastructure required. Or, self-host your own [remote node](https://docs.digpangolin.com/manage/remote-node/nodes) and connect to our control plane. |
| **Pangolin Cloud** | Fully managed service with instant setup and pay-as-you-go pricing — no infrastructure required. Or, self-host your own [remote node](https://docs.pangolin.net/manage/remote-node/nodes) and connect to our control plane. |
## Key Features
@@ -71,17 +71,17 @@ Pangolin packages everything you need for seamless application access and exposu
### Check out the docs
We encourage everyone to read the full documentation first, which is
available at [docs.digpangolin.com](https://docs.digpangolin.com). This README provides only a very brief subset of
available at [docs.pangolin.net](https://docs.pangolin.net). This README provides only a very brief subset of
the docs to illustrate some basic ideas.
### Sign up and try now
For Pangolin's managed service, you will first need to create an account at
[pangolin.fossorial.io](https://pangolin.fossorial.io). We have a generous free tier to get started.
[app.pangolin.net](https://app.pangolin.net). We have a generous free tier to get started.
## Licensing
Pangolin is dual licensed under the AGPL-3 and the [Fossorial Commercial License](https://digpangolin.com/fcl.html). For inquiries about commercial licensing, please contact us at [contact@fossorial.io](mailto:contact@fossorial.io).
Pangolin is dual licensed under the AGPL-3 and the [Fossorial Commercial License](https://pangolin.net/fcl.html). For inquiries about commercial licensing, please contact us at [contact@pangolin.net](mailto:contact@pangolin.net).
## Contributions

View File

@@ -3,7 +3,7 @@
If you discover a security vulnerability, please follow the steps below to responsibly disclose it to us:
1. **Do not create a public GitHub issue or discussion post.** This could put the security of other users at risk.
2. Send a detailed report to [security@fossorial.io](mailto:security@fossorial.io) or send a **private** message to a maintainer on [Discord](https://discord.gg/HCJR8Xhme4). Include:
2. Send a detailed report to [security@pangolin.net](mailto:security@pangolin.net) or send a **private** message to a maintainer on [Discord](https://discord.gg/HCJR8Xhme4). Include:
- Description and location of the vulnerability.
- Potential impact of the vulnerability.

View File

@@ -8,7 +8,7 @@ import base64
YAML_FILE_PATH = 'blueprint.yaml'
# The API endpoint and headers from the curl request
API_URL = 'http://api.pangolin.fossorial.io/v1/org/test/blueprint'
API_URL = 'http://api.pangolin.net/v1/org/test/blueprint'
HEADERS = {
'accept': '*/*',
'Authorization': 'Bearer <your_token_here>',

View File

@@ -28,9 +28,9 @@ proxy-resources:
# sso-roles:
# - Member
# sso-users:
# - owen@fossorial.io
# - owen@pangolin.net
# whitelist-users:
# - owen@fossorial.io
# - owen@pangolin.net
headers:
- name: X-Example-Header
value: example-value

View File

@@ -12,7 +12,7 @@ post {
body:json {
{
"email": "owen@fossorial.io",
"email": "owen@pangolin.net",
"password": "Password123!"
}
}

View File

@@ -12,6 +12,6 @@ post {
body:json {
{
"email": "milo@fossorial.io"
"email": "milo@pangolin.net"
}
}

View File

@@ -12,7 +12,7 @@ put {
body:json {
{
"email": "numbat@fossorial.io",
"email": "numbat@pangolin.net",
"password": "Password123!"
}
}

View File

@@ -1,5 +1,5 @@
# To see all available options, please visit the docs:
# https://docs.digpangolin.com/self-host/advanced/config-file
# https://docs.pangolin.net/self-host/advanced/config-file
app:
dashboard_url: http://localhost:3002

View File

@@ -1,5 +1,5 @@
# To see all available options, please visit the docs:
# https://docs.digpangolin.com/
# https://docs.pangolin.net/
gerbil:
start_port: 51820
@@ -36,4 +36,4 @@ flags:
require_email_verification: {{.EnableEmail}}
disable_signup_without_invite: true
disable_user_create_org: false
allow_raw_resources: true
allow_raw_resources: true

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Безплатен план",
"billingWarningOverLimit": "Предупреждение: Превишили сте една или повече лимити за използване. Вашите сайтове няма да се свържат, докато не промените абонамента си или не коригирате използването.",
"billingUsageLimitsOverview": "Преглед на лимитите за използване",
"billingMonitorUsage": "Следете използването спрямо конфигурираните лимити. Ако ви е необходимо увеличаване на лимитите, моля, свържете се с нас на support@fossorial.io.",
"billingMonitorUsage": "Следете използването спрямо конфигурираните лимити. Ако ви е необходимо увеличаване на лимитите, моля, свържете се с нас на support@pangolin.net.",
"billingDataUsage": "Използване на данни",
"billingOnlineTime": "Време на работа на сайта",
"billingUsers": "Активни потребители",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Volná úroveň",
"billingWarningOverLimit": "Upozornění: Překročili jste jeden nebo více omezení používání. Vaše stránky se nepřipojí dokud nezměníte předplatné nebo neupravíte své používání.",
"billingUsageLimitsOverview": "Přehled omezení použití",
"billingMonitorUsage": "Sledujte vaše využití pomocí nastavených limitů. Pokud potřebujete zvýšit limity, kontaktujte nás prosím support@fossorial.io.",
"billingMonitorUsage": "Sledujte vaše využití pomocí nastavených limitů. Pokud potřebujete zvýšit limity, kontaktujte nás prosím support@pangolin.net.",
"billingDataUsage": "Využití dat",
"billingOnlineTime": "Stránka online čas",
"billingUsers": "Aktivní uživatelé",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Kostenlose Stufe",
"billingWarningOverLimit": "Warnung: Sie haben ein oder mehrere Nutzungslimits überschritten. Ihre Webseiten werden nicht verbunden, bis Sie Ihr Abonnement ändern oder Ihren Verbrauch anpassen.",
"billingUsageLimitsOverview": "Übersicht über Nutzungsgrenzen",
"billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@fossorial.io.",
"billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@pangolin.net.",
"billingDataUsage": "Datenverbrauch",
"billingOnlineTime": "Online-Zeit der Seite",
"billingUsers": "Aktive Benutzer",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -47,9 +47,8 @@
"edit": "Edit",
"siteConfirmDelete": "Confirm Delete Site",
"siteDelete": "Delete Site",
"siteMessageRemove": "Once removed, the site will no longer be accessible. All resources and targets associated with the site will also be removed.",
"siteMessageConfirm": "To confirm, please type the name of the site below.",
"siteQuestionRemove": "Are you sure you want to remove the site {selectedSite} from the organization?",
"siteMessageRemove": "Once removed the site will no longer be accessible. All targets associated with the site will also be removed.",
"siteQuestionRemove": "Are you sure you want to remove the site from the organization?",
"siteManageSites": "Manage Sites",
"siteDescription": "Allow connectivity to your network through secure tunnels",
"siteCreate": "Create Site",
@@ -154,8 +153,7 @@
"protected": "Protected",
"notProtected": "Not Protected",
"resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.",
"resourceMessageConfirm": "To confirm, please type the name of the resource below.",
"resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?",
"resourceQuestionRemove": "Are you sure you want to remove the resource from the organization?",
"resourceHTTP": "HTTPS Resource",
"resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.",
"resourceRaw": "Raw TCP/UDP Resource",
@@ -220,7 +218,7 @@
"orgDeleteConfirm": "Confirm Delete Organization",
"orgMessageRemove": "This action is irreversible and will delete all associated data.",
"orgMessageConfirm": "To confirm, please type the name of the organization below.",
"orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?",
"orgQuestionRemove": "Are you sure you want to remove the organization?",
"orgUpdated": "Organization updated",
"orgUpdatedDescription": "The organization has been updated.",
"orgErrorUpdate": "Failed to update organization",
@@ -287,9 +285,8 @@
"apiKeysAdd": "Generate API Key",
"apiKeysErrorDelete": "Error deleting API key",
"apiKeysErrorDeleteMessage": "Error deleting API key",
"apiKeysQuestionRemove": "Are you sure you want to remove the API key {selectedApiKey} from the organization?",
"apiKeysQuestionRemove": "Are you sure you want to remove the API key from the organization?",
"apiKeysMessageRemove": "Once removed, the API key will no longer be able to be used.",
"apiKeysMessageConfirm": "To confirm, please type the name of the API key below.",
"apiKeysDeleteConfirm": "Confirm Delete API Key",
"apiKeysDelete": "Delete API Key",
"apiKeysManage": "Manage API Keys",
@@ -305,8 +302,7 @@
"userDeleteConfirm": "Confirm Delete User",
"userDeleteServer": "Delete User from Server",
"userMessageRemove": "The user will be removed from all organizations and be completely removed from the server.",
"userMessageConfirm": "To confirm, please type the name of the user below.",
"userQuestionRemove": "Are you sure you want to permanently delete {selectedUser} from the server?",
"userQuestionRemove": "Are you sure you want to permanently delete user from the server?",
"licenseKey": "License Key",
"valid": "Valid",
"numberOfSites": "Number of Sites",
@@ -339,7 +335,7 @@
"fossorialLicense": "View Fossorial Commercial License & Subscription Terms",
"licenseMessageRemove": "This will remove the license key and all associated permissions granted by it.",
"licenseMessageConfirm": "To confirm, please type the license key below.",
"licenseQuestionRemove": "Are you sure you want to delete the license key {selectedKey} ?",
"licenseQuestionRemove": "Are you sure you want to delete the license key ?",
"licenseKeyDelete": "Delete License Key",
"licenseKeyDeleteConfirm": "Confirm Delete License Key",
"licenseTitle": "Manage License Status",
@@ -372,7 +368,7 @@
"inviteRemoveErrorDescription": "An error occurred while removing the invitation.",
"inviteRemoved": "Invitation removed",
"inviteRemovedDescription": "The invitation for {email} has been removed.",
"inviteQuestionRemove": "Are you sure you want to remove the invitation {email}?",
"inviteQuestionRemove": "Are you sure you want to remove the invitation?",
"inviteMessageRemove": "Once removed, this invitation will no longer be valid. You can always re-invite the user later.",
"inviteMessageConfirm": "To confirm, please type the email address of the invitation below.",
"inviteQuestionRegenerate": "Are you sure you want to regenerate the invitation for {email}? This will revoke the previous invitation.",
@@ -398,9 +394,8 @@
"userErrorOrgRemoveDescription": "An error occurred while removing the user.",
"userOrgRemoved": "User removed",
"userOrgRemovedDescription": "The user {email} has been removed from the organization.",
"userQuestionOrgRemove": "Are you sure you want to remove {email} from the organization?",
"userQuestionOrgRemove": "Are you sure you want to remove this user from the organization?",
"userMessageOrgRemove": "Once removed, this user will no longer have access to the organization. You can always re-invite them later, but they will need to accept the invitation again.",
"userMessageOrgConfirm": "To confirm, please type the name of the of the user below.",
"userRemoveOrgConfirm": "Confirm Remove User",
"userRemoveOrg": "Remove User from Organization",
"users": "Users",
@@ -742,7 +737,7 @@
"idpManageDescription": "View and manage identity providers in the system",
"idpDeletedDescription": "Identity provider deleted successfully",
"idpOidc": "OAuth2/OIDC",
"idpQuestionRemove": "Are you sure you want to permanently delete the identity provider {name}?",
"idpQuestionRemove": "Are you sure you want to permanently delete the identity provider?",
"idpMessageRemove": "This will remove the identity provider and all associated configurations. Users who authenticate through this provider will no longer be able to log in.",
"idpMessageConfirm": "To confirm, please type the name of the identity provider below.",
"idpConfirmDelete": "Confirm Delete Identity Provider",
@@ -1211,9 +1206,8 @@
"domainCreate": "Create Domain",
"domainCreatedDescription": "Domain created successfully",
"domainDeletedDescription": "Domain deleted successfully",
"domainQuestionRemove": "Are you sure you want to remove the domain {domain} from your account?",
"domainQuestionRemove": "Are you sure you want to remove the domain from your account?",
"domainMessageRemove": "Once removed, the domain will no longer be associated with your account.",
"domainMessageConfirm": "To confirm, please type the domain name below.",
"domainConfirmDelete": "Confirm Delete Domain",
"domainDelete": "Delete Domain",
"domain": "Domain",
@@ -1280,7 +1274,7 @@
"billingFreeTier": "Free Tier",
"billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.",
"billingUsageLimitsOverview": "Usage Limits Overview",
"billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.",
"billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@pangolin.net.",
"billingDataUsage": "Data Usage",
"billingOnlineTime": "Site Online Time",
"billingUsers": "Active Users",
@@ -1563,9 +1557,8 @@
"searchRemoteExitNodes": "Search nodes...",
"remoteExitNodeAdd": "Add Node",
"remoteExitNodeErrorDelete": "Error deleting node",
"remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?",
"remoteExitNodeQuestionRemove": "Are you sure you want to remove the node from the organization?",
"remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.",
"remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.",
"remoteExitNodeConfirmDelete": "Confirm Delete Node",
"remoteExitNodeDelete": "Delete Node",
"sidebarRemoteExitNodes": "Remote Nodes",
@@ -1819,7 +1812,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {
@@ -1894,5 +1887,9 @@
"pathRewriteRegex": "Regex",
"pathRewriteStrip": "Strip",
"pathRewriteStripLabel": "strip",
"sidebarEnableEnterpriseLicense": "Enable Enterprise License"
"sidebarEnableEnterpriseLicense": "Enable Enterprise License",
"cannotbeUndone": "This can not be undone.",
"toConfirm": "to confirm",
"deleteClientQuestion": "Are you sure you want to remove the client from the site and organization?",
"clientMessageRemove": "Once removed, the client will no longer be able to connect to the site."
}

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Nivel Gratis",
"billingWarningOverLimit": "Advertencia: Has excedido uno o más límites de uso. Tus sitios no se conectarán hasta que modifiques tu suscripción o ajustes tu uso.",
"billingUsageLimitsOverview": "Descripción general de los límites de uso",
"billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@fossorial.io.",
"billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@pangolin.net.",
"billingDataUsage": "Uso de datos",
"billingOnlineTime": "Tiempo en línea del sitio",
"billingUsers": "Usuarios activos",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -6,45 +6,45 @@
"setupOrgName": "Nom de l'organisation",
"orgDisplayName": "Ceci est le nom d'affichage de votre organisation.",
"orgId": "ID de l'organisation",
"setupIdentifierMessage": "Ceci est l'identifiant unique pour votre organisation. Il est séparé du nom affiché.",
"setupErrorIdentifier": "L'ID de l'organisation est déjà pris. Veuillez en choisir un autre.",
"setupIdentifierMessage": "Ceci est l'identifiant de votre organisation. Il est différent du nom affiché.",
"setupErrorIdentifier": "Cet identifiant est déjà pris. Veuillez en choisir un autre.",
"componentsErrorNoMemberCreate": "Vous n'êtes actuellement membre d'aucune organisation. Créez une organisation pour commencer.",
"componentsErrorNoMember": "Vous n'êtes actuellement membre d'aucune organisation.",
"welcome": "Bienvenue à Pangolin",
"welcome": "Bienvenue sur Pangolin",
"welcomeTo": "Bienvenue chez",
"componentsCreateOrg": "Créer une organisation",
"componentsMember": "Vous êtes membre de {count, plural, =0 {aucune organisation} one {une organisation} other {# organisations}}.",
"componentsMember": "Vous {count, plural, =0 {n'} other {}}êtes membre {count, plural, =0 {d'aucune organisation} one {d'une organisation} other {de # organisations}}.",
"componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Suivez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
"dismiss": "Refuser",
"componentsLicenseViolation": "Violation de licence : Ce serveur utilise des sites {usedSites} qui dépassent la limite autorisée des sites {maxSites} . Suivez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
"componentsSupporterMessage": "Merci de soutenir Pangolin en tant que {tier}!",
"inviteErrorNotValid": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder n'ait pas été acceptée ou n'est plus valide.",
"inviteErrorUser": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder ne soit pas pour cet utilisateur.",
"inviteLoginUser": "Assurez-vous que vous êtes bien connecté en tant qu'utilisateur correct.",
"inviteErrorNoUser": "Nous sommes désolés, mais il semble que l'invitation que vous essayez d'accéder ne soit pas pour un utilisateur qui existe.",
"inviteCreateUser": "Veuillez d'abord créer un compte.",
"goHome": "Retour à la maison",
"inviteErrorNotValid": "Nous sommes désolés, mais il semble que l'invitation via laquelle vous essayez d'accéder n'ait pas été acceptée ou n'est plus valide.",
"inviteErrorUser": "Nous sommes désolés, mais il semble que l'invitation via laquelle vous essayez d'accéder ne soit pas pour cet utilisateur.",
"inviteLoginUser": "Assurez-vous d'etre bien connecté au bon compte.",
"inviteErrorNoUser": "Nous sommes désolés, mais il semble que l'invitation via laquelle vous essayez d'accéder ne soit pas pour un utilisateur qui existe.",
"inviteCreateUser": "Vous n'avez aucun compte, veuillez en créer un.",
"goHome": "Retour à l'accueil",
"inviteLogInOtherUser": "Se connecter en tant qu'utilisateur différent",
"createAnAccount": "Créer un compte",
"inviteNotAccepted": "Invitation non acceptée",
"authCreateAccount": "Créez un compte pour commencer",
"authNoAccount": "Vous n'avez pas de compte ?",
"email": "Courriel",
"email": "Adresse email",
"password": "Mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"createAccount": "Créer un compte",
"viewSettings": "Afficher les paramètres",
"delete": "Supprimez",
"delete": "Supprimer",
"name": "Nom",
"online": "En ligne",
"offline": "Hors ligne",
"site": "Site",
"dataIn": "Données dans",
"dataOut": "Données épuisées",
"dataIn": "Données reçues",
"dataOut": "Données émises",
"connectionType": "Type de connexion",
"tunnelType": "Type de tunnel",
"local": "Locale",
"edit": "Editer",
"edit": "Modifier",
"siteConfirmDelete": "Confirmer la suppression du site",
"siteDelete": "Supprimer le site",
"siteMessageRemove": "Une fois supprimé, le site ne sera plus accessible. Toutes les ressources et cibles associées au site seront également supprimées.",
@@ -64,11 +64,11 @@
"siteLearnNewt": "Apprenez à installer Newt sur votre système",
"siteSeeConfigOnce": "Vous ne pourrez voir la configuration qu'une seule fois.",
"siteLoadWGConfig": "Chargement de la configuration WireGuard...",
"siteDocker": "Développer les détails du déploiement Docker",
"siteDocker": "Afficher les détails du déploiement Docker",
"toggle": "Activer/désactiver",
"dockerCompose": "Composition Docker",
"dockerRun": "Exécution Docker",
"siteLearnLocal": "Les sites locaux ne tunnel, en savoir plus",
"dockerCompose": "Docker Compose",
"dockerRun": "Docker Run",
"siteLearnLocal": "Les sites locaux ne permettent pas d'utiliser les tunnel, en savoir plus",
"siteConfirmCopy": "J'ai copié la configuration",
"searchSitesProgress": "Rechercher des sites...",
"siteAdd": "Ajouter un site",
@@ -79,9 +79,9 @@
"operatingSystem": "Système d'exploitation",
"commands": "Commandes",
"recommended": "Recommandé",
"siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Il utilise WireGuard sous le capot et vous permet d'adresser vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.",
"siteRunsInDocker": "Exécute dans Docker",
"siteRunsInShell": "Exécute en shell sur macOS, Linux et Windows",
"siteNewtDescription": "Pour une meilleure expérience d'utilisateur, utilisez Newt. Newt se base sur WireGuard et vous permet d'adresser vos ressources privées par leur adresse LAN sur votre réseau privé à partir du tableau de bord Pangolin.",
"siteRunsInDocker": "S'exécute dans Docker",
"siteRunsInShell": "S'exécute en shell sur macOS, Linux et Windows",
"siteErrorDelete": "Erreur lors de la suppression du site",
"siteErrorUpdate": "Impossible de mettre à jour le site",
"siteErrorUpdateDescription": "Une erreur s'est produite lors de la mise à jour du site.",
@@ -89,18 +89,18 @@
"siteUpdatedDescription": "Le site a été mis à jour.",
"siteGeneralDescription": "Configurer les paramètres généraux de ce site",
"siteSettingDescription": "Configurer les paramètres de votre site",
"siteSetting": "Réglages {siteName}",
"siteSetting": "Réglages de {siteName}",
"siteNewtTunnel": "Tunnel Newt (Recommandé)",
"siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.",
"siteWg": "WireGuard basique",
"siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.",
"siteWgDescriptionSaas": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES",
"siteLocalDescription": "Ressources locales seulement. Pas de tunneling.",
"siteLocalDescriptionSaas": "Local resources only. No tunneling. Only available on remote nodes.",
"siteLocalDescriptionSaas": "Ressources locales seulement. Pas de tunneling. Seulement disponible sur les noeuds distants",
"siteSeeAll": "Voir tous les sites",
"siteTunnelDescription": "Déterminez comment vous voulez vous connecter à votre site",
"siteNewtCredentials": "Identifiants Newt",
"siteNewtCredentialsDescription": "C'est ainsi que Newt s'authentifiera avec le serveur",
"siteNewtCredentialsDescription": "C'est comme cela que Newt s'authentifiera avec le serveur",
"siteCredentialsSave": "Enregistrez vos identifiants",
"siteCredentialsSaveDescription": "Vous ne pourrez voir cela qu'une seule fois. Assurez-vous de le copier dans un endroit sécurisé.",
"siteInfo": "Informations sur le site",
@@ -112,7 +112,7 @@
"shareErrorDelete": "Impossible de supprimer le lien",
"shareErrorDeleteMessage": "Une erreur s'est produite lors de la suppression du lien",
"shareDeleted": "Lien supprimé",
"shareDeletedDescription": "Le lien a été supprimé",
"shareDeletedDescription": "Le lien de partage a été supprimé",
"shareTokenDescription": "Votre jeton d'accès peut être passé de deux façons : en tant que paramètre de requête ou dans les en-têtes de la requête. Elles doivent être transmises par le client à chaque demande d'accès authentifié.",
"accessToken": "Jeton d'accès",
"usageExamples": "Exemples d'utilisation",
@@ -134,7 +134,7 @@
"shareExpireDescription": "Le temps d'expiration est combien de temps le lien sera utilisable et fournira un accès à la ressource. Après cette période, le lien ne fonctionnera plus et les utilisateurs qui ont utilisé ce lien perdront l'accès à la ressource.",
"shareSeeOnce": "Vous ne pourrez voir ce lien. Assurez-vous de le copier.",
"shareAccessHint": "N'importe qui avec ce lien peut accéder à la ressource. Partagez-le avec soin.",
"shareTokenUsage": "Voir Utilisation du jeton d'accès",
"shareTokenUsage": "Voir l'utilisation du jeton d'accès",
"createLink": "Créer un lien",
"resourcesNotFound": "Aucune ressource trouvée",
"resourceSearch": "Rechercher des ressources",
@@ -1234,7 +1234,7 @@
"billing": "Facturation",
"orgBillingDescription": "Gérez vos informations de facturation et vos abonnements",
"github": "GitHub",
"pangolinHosted": "Pangolin Hébergement",
"pangolinHosted": "Hebergé par Pangolin",
"fossorial": "Fossorial",
"completeAccountSetup": "Complétez la configuration du compte",
"completeAccountSetupDescription": "Définissez votre mot de passe pour commencer",
@@ -1280,7 +1280,7 @@
"billingFreeTier": "Niveau gratuit",
"billingWarningOverLimit": "Attention : Vous avez dépassé une ou plusieurs limites d'utilisation. Vos sites ne se connecteront pas tant que vous n'avez pas modifié votre abonnement ou ajusté votre utilisation.",
"billingUsageLimitsOverview": "Vue d'ensemble des limites d'utilisation",
"billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@fossorial.io.",
"billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@pangolin.net.",
"billingDataUsage": "Utilisation des données",
"billingOnlineTime": "Temps en ligne du site",
"billingUsers": "Utilisateurs actifs",
@@ -1316,7 +1316,7 @@
"billingRemoteExitNodesInfo": "Vous êtes facturé pour chaque nœud géré dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de nœuds gérés actifs dans votre organisation.",
"domainNotFound": "Domaine introuvable",
"domainNotFoundDescription": "Cette ressource est désactivée car le domaine n'existe plus dans notre système. Veuillez définir un nouveau domaine pour cette ressource.",
"failed": "Échec",
"failed": "Erreur",
"createNewOrgDescription": "Créer une nouvelle organisation",
"organization": "Organisation",
"port": "Port",
@@ -1370,7 +1370,7 @@
"createDomainARecords": "Enregistrements A",
"createDomainRecordNumber": "Enregistrement {number}",
"createDomainTxtRecords": "Enregistrements TXT",
"createDomainSaveTheseRecords": "Enregistrez ces enregistrements",
"createDomainSaveTheseRecords": "Sauvegardez ces enregistrements",
"createDomainSaveTheseRecordsDescription": "Assurez-vous de sauvegarder ces enregistrements DNS car vous ne les reverrez pas.",
"createDomainDnsPropagation": "Propagation DNS",
"createDomainDnsPropagationDescription": "Les modifications DNS peuvent mettre du temps à se propager sur internet. Cela peut prendre de quelques minutes à 48 heures selon votre fournisseur DNS et les réglages TTL.",
@@ -1445,7 +1445,7 @@
"IntervalSeconds": "Intervalle sain",
"timeoutSeconds": "Délai",
"timeIsInSeconds": "Le temps est exprimé en secondes",
"retryAttempts": "Tentatives de réessai",
"retryAttempts": "Tentatives",
"expectedResponseCodes": "Codes de réponse attendus",
"expectedResponseCodesDescription": "Code de statut HTTP indiquant un état de santé satisfaisant. Si non renseigné, 200-300 est considéré comme satisfaisant.",
"customHeaders": "En-têtes personnalisés",
@@ -1760,9 +1760,9 @@
"enterpriseEdition": "Enterprise Edition",
"unlicensed": "Unlicensed",
"beta": "Beta",
"manageClients": "Manage Clients",
"manageClients": "Gérer les clients",
"manageClientsDescription": "Clients are devices that can connect to your sites",
"licenseTableValidUntil": "Valid Until",
"licenseTableValidUntil": "Valide jusqu'au",
"saasLicenseKeysSettingsTitle": "Enterprise Licenses",
"saasLicenseKeysSettingsDescription": "Generate and manage Enterprise license keys for self-hosted Pangolin instances",
"sidebarEnterpriseLicenses": "Licenses",
@@ -1771,8 +1771,8 @@
"validation": {
"emailRequired": "Please enter a valid email address",
"useCaseTypeRequired": "Please select a use case type",
"firstNameRequired": "First name is required",
"lastNameRequired": "Last name is required",
"firstNameRequired": "Le prénom est requis",
"lastNameRequired": "Le nom est requis",
"primaryUseRequired": "Please describe your primary use",
"jobTitleRequiredBusiness": "Job title is required for business use",
"industryRequiredBusiness": "Industry is required for business use",
@@ -1786,7 +1786,7 @@
},
"useCaseOptions": {
"personal": {
"title": "Personal Use",
"title": "Utilisation personelle",
"description": "For individual, non-commercial use such as learning, personal projects, or experimentation."
},
"business": {
@@ -1819,32 +1819,32 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {
"useCaseQuestion": "Are you using Pangolin for personal or business use?",
"firstName": "First Name",
"lastName": "Last Name",
"jobTitle": "Job Title",
"firstName": "Prénom",
"lastName": "Nom",
"jobTitle": "profession",
"primaryUseQuestion": "What do you primarily plan to use Pangolin for?",
"industryQuestion": "What is your industry?",
"prospectiveUsersQuestion": "How many prospective users do you expect to have?",
"prospectiveSitesQuestion": "How many prospective sites (tunnels) do you expect to have?",
"companyName": "Company name",
"countryOfResidence": "Country of residence",
"stateProvinceRegion": "State / Province / Region",
"postalZipCode": "Postal / ZIP Code",
"companyWebsite": "Company website",
"companyPhoneNumber": "Company phone number",
"country": "Country",
"phoneNumberOptional": "Phone number (optional)",
"companyName": "Entreprise",
"countryOfResidence": "Pays de résidence",
"stateProvinceRegion": "État / Province / Région",
"postalZipCode": "Code postal",
"companyWebsite": "Site de l'entreprise",
"companyPhoneNumber": "Numéro de téléphone professionnel",
"country": "Pays",
"phoneNumberOptional": "Numéro de téléphone (optionnel)",
"complianceConfirmation": "I confirm that I am in compliance with the Fossorial Commercial License and that reporting inaccurate information or misidentifying use of the product is a violation of the license."
},
"buttons": {
"close": "Close",
"previous": "Previous",
"next": "Next",
"close": "Fermer",
"previous": "Précédent",
"next": "Suivant",
"generateLicenseKey": "Generate License Key"
},
"toasts": {
@@ -1860,16 +1860,16 @@
},
"priority": "Priorité",
"priorityDescription": "Les routes de haute priorité sont évaluées en premier. La priorité = 100 signifie l'ordre automatique (décision du système). Utilisez un autre nombre pour imposer la priorité manuelle.",
"instanceName": "Instance Name",
"instanceName": "Nom de l'instance",
"pathMatchModalTitle": "Configure Path Matching",
"pathMatchModalDescription": "Set up how incoming requests should be matched based on their path.",
"pathMatchType": "Match Type",
"pathMatchPrefix": "Prefix",
"pathMatchPrefix": "Préfix",
"pathMatchExact": "Exact",
"pathMatchRegex": "Regex",
"pathMatchValue": "Path Value",
"clear": "Clear",
"saveChanges": "Save Changes",
"saveChanges": "Sauvegarder",
"pathMatchRegexPlaceholder": "^/api/.*",
"pathMatchDefaultPlaceholder": "/path",
"pathMatchPrefixHelp": "Example: /api matches /api, /api/users, etc.",
@@ -1889,7 +1889,7 @@
"pathRewriteExactHelp": "Replace the entire path with this value when the path matches exactly",
"pathRewriteRegexHelp": "Use capture groups like $1, $2 for replacement",
"pathRewriteStripPrefixHelp": "Leave empty to strip prefix or provide new prefix",
"pathRewritePrefix": "Prefix",
"pathRewritePrefix": "Préfix",
"pathRewriteExact": "Exact",
"pathRewriteRegex": "Regex",
"pathRewriteStrip": "Strip",

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Piano Gratuito",
"billingWarningOverLimit": "Avviso: Hai superato uno o più limiti di utilizzo. I tuoi siti non si connetteranno finché non modifichi il tuo abbonamento o non adegui il tuo utilizzo.",
"billingUsageLimitsOverview": "Panoramica dei Limiti di Utilizzo",
"billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@fossorial.io.",
"billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@pangolin.net.",
"billingDataUsage": "Utilizzo dei Dati",
"billingOnlineTime": "Tempo Online del Sito",
"billingUsers": "Utenti Attivi",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "무료 티어",
"billingWarningOverLimit": "경고: 하나 이상의 사용 한도를 초과했습니다. 구독을 수정하거나 사용량을 조정하기 전까지 사이트는 연결되지 않습니다.",
"billingUsageLimitsOverview": "사용 한도 개요",
"billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@fossorial.io로 연락하십시오.",
"billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@pangolin.net로 연락하십시오.",
"billingDataUsage": "데이터 사용량",
"billingOnlineTime": "사이트 온라인 시간",
"billingUsers": "활성 사용자",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Gratis nivå",
"billingWarningOverLimit": "Advarsel: Du har overskredet en eller flere bruksgrenser. Nettstedene dine vil ikke koble til før du endrer abonnementet ditt eller justerer bruken.",
"billingUsageLimitsOverview": "Oversikt over bruksgrenser",
"billingMonitorUsage": "Overvåk bruken din i forhold til konfigurerte grenser. Hvis du trenger økte grenser, vennligst kontakt support@fossorial.io.",
"billingMonitorUsage": "Overvåk bruken din i forhold til konfigurerte grenser. Hvis du trenger økte grenser, vennligst kontakt support@pangolin.net.",
"billingDataUsage": "Databruk",
"billingOnlineTime": "Online tid for nettsteder",
"billingUsers": "Aktive brukere",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Gratis Niveau",
"billingWarningOverLimit": "Waarschuwing: U hebt een of meer gebruikslimieten overschreden. Uw sites maken geen verbinding totdat u uw abonnement aanpast of uw gebruik aanpast.",
"billingUsageLimitsOverview": "Overzicht gebruikslimieten",
"billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@fossorial.io.",
"billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@pangolin.net.",
"billingDataUsage": "Gegevensgebruik",
"billingOnlineTime": "Site Online Tijd",
"billingUsers": "Actieve Gebruikers",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Darmowy pakiet",
"billingWarningOverLimit": "Ostrzeżenie: Przekroczyłeś jeden lub więcej limitów użytkowania. Twoje witryny nie połączą się, dopóki nie zmienisz subskrypcji lub nie dostosujesz użytkowania.",
"billingUsageLimitsOverview": "Przegląd Limitów Użytkowania",
"billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@fossorial.io.",
"billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@pangolin.net.",
"billingDataUsage": "Użycie danych",
"billingOnlineTime": "Czas Online Strony",
"billingUsers": "Aktywni użytkownicy",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Plano Gratuito",
"billingWarningOverLimit": "Aviso: Você ultrapassou um ou mais limites de uso. Seus sites não se conectarão até você modificar sua assinatura ou ajustar seu uso.",
"billingUsageLimitsOverview": "Visão Geral dos Limites de Uso",
"billingMonitorUsage": "Monitore seu uso em relação aos limites configurados. Se precisar aumentar esses limites, entre em contato conosco support@fossorial.io.",
"billingMonitorUsage": "Monitore seu uso em relação aos limites configurados. Se precisar aumentar esses limites, entre em contato conosco support@pangolin.net.",
"billingDataUsage": "Uso de Dados",
"billingOnlineTime": "Tempo Online do Site",
"billingUsers": "Usuários Ativos",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Бесплатный уровень",
"billingWarningOverLimit": "Предупреждение: Вы превысили одну или несколько границ использования. Ваши сайты не подключатся, пока вы не измените подписку или не скорректируете использование.",
"billingUsageLimitsOverview": "Обзор лимитов использования",
"billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@fossorial.io.",
"billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@pangolin.net.",
"billingDataUsage": "Использование данных",
"billingOnlineTime": "Время работы сайта",
"billingUsers": "Активные пользователи",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "Ücretsiz Dilim",
"billingWarningOverLimit": "Uyarı: Bir veya daha fazla kullanım limitini aştınız. Aboneliğinizi değiştirmediğiniz veya kullanımı ayarlamadığınız sürece siteleriniz bağlanmayacaktır.",
"billingUsageLimitsOverview": "Kullanım Limitleri Genel Görünümü",
"billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@fossorial.io adresinden bizimle iletişime geçin.",
"billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@pangolin.net adresinden bizimle iletişime geçin.",
"billingDataUsage": "Veri Kullanımı",
"billingOnlineTime": "Site Çevrimiçi Süresi",
"billingUsers": "Aktif Kullanıcılar",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

View File

@@ -1280,7 +1280,7 @@
"billingFreeTier": "免费层",
"billingWarningOverLimit": "警告:您已超出一个或多个使用限制。在您修改订阅或调整使用情况之前,您的站点将无法连接。",
"billingUsageLimitsOverview": "使用限制概览",
"billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@fossorial.io。",
"billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@pangolin.net。",
"billingDataUsage": "数据使用情况",
"billingOnlineTime": "站点在线时间",
"billingUsers": "活跃用户",
@@ -1819,7 +1819,7 @@
},
"trialPeriodInformation": {
"title": "Trial Period Information",
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@fossorial.io."
"description": "This License Key enables Enterprise features for a 7-day evaluation period. Continued access to Paid Features beyond the evaluation period requires activation under a valid Personal or Enterprise License. For Enterprise licensing, contact sales@pangolin.net."
}
},
"form": {

919
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -56,7 +56,7 @@
"@radix-ui/react-tabs": "1.1.13",
"@radix-ui/react-toast": "1.2.15",
"@radix-ui/react-tooltip": "^1.2.8",
"@react-email/components": "0.5.6",
"@react-email/components": "0.5.7",
"@react-email/render": "^1.3.2",
"@react-email/tailwind": "1.2.2",
"@simplewebauthn/browser": "^13.2.2",
@@ -77,7 +77,7 @@
"crypto-js": "^4.2.0",
"drizzle-orm": "0.44.6",
"eslint": "9.37.0",
"eslint-config-next": "15.5.4",
"eslint-config-next": "15.5.6",
"express": "5.1.0",
"express-rate-limit": "8.1.0",
"glob": "11.0.3",
@@ -92,7 +92,7 @@
"lucide-react": "^0.545.0",
"maxmind": "5.0.0",
"moment": "2.30.1",
"next": "15.5.4",
"next": "15.5.6",
"next-intl": "^4.3.12",
"next-themes": "0.4.6",
"node-cache": "5.1.2",
@@ -143,13 +143,13 @@
"@types/nodemailer": "7.0.2",
"@types/pg": "8.15.5",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.1",
"@types/react-dom": "19.2.2",
"@types/semver": "^7.7.1",
"@types/swagger-ui-express": "^4.1.8",
"@types/ws": "8.18.1",
"@types/yargs": "17.0.33",
"drizzle-kit": "0.31.5",
"esbuild": "0.25.10",
"esbuild": "0.25.11",
"esbuild-node-externals": "1.18.0",
"postcss": "^8",
"react-email": "4.3.0",

View File

@@ -126,7 +126,7 @@ export const targets = pgTable("targets", {
pathMatchType: text("pathMatchType"), // exact, prefix, regex
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix
priority: integer("priority").notNull().default(100)
priority: integer("priority").default(100)
});
export const targetHealthCheck = pgTable("targetHealthCheck", {

View File

@@ -138,7 +138,7 @@ export const targets = sqliteTable("targets", {
pathMatchType: text("pathMatchType"), // exact, prefix, regex
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix
priority: integer("priority").notNull().default(100)
priority: integer("priority").default(100)
});
export const targetHealthCheck = sqliteTable("targetHealthCheck", {

View File

@@ -88,7 +88,7 @@ export const WelcomeQuickStart = ({
To learn how to use Newt, including more
installation methods, visit the{" "}
<a
href="https://docs.digpangolin.com/manage/sites/install-site"
href="https://docs.pangolin.net/manage/sites/install-site"
className="underline"
>
docs

View File

@@ -89,7 +89,7 @@ export function EmailFooter({ children }: { children: React.ReactNode }) {
<p className="text-xs text-gray-400 mt-4">
For any questions or support, please contact us at:
<br />
support@fossorial.io
support@pangolin.net
</p>
<p className="text-xs text-gray-300 text-center mt-4">
&copy; {new Date().getFullYear()} Fossorial, Inc. All

View File

@@ -612,7 +612,8 @@ export class UsageService {
public async getUsage(
orgId: string,
featureId: FeatureId
featureId: FeatureId,
trx: Transaction | typeof db = db
): Promise<Usage | null> {
if (noop()) {
return null;
@@ -621,7 +622,7 @@ export class UsageService {
const usageId = `${orgId}-${featureId}`;
try {
const [result] = await db
const [result] = await trx
.select()
.from(usage)
.where(eq(usage.usageId, usageId))
@@ -635,7 +636,7 @@ export class UsageService {
const meterId = getFeatureMeterId(featureId);
try {
const [newUsage] = await db
const [newUsage] = await trx
.insert(usage)
.values({
usageId,
@@ -652,7 +653,7 @@ export class UsageService {
return newUsage;
} else {
// Record was created by another process, fetch it
const [existingUsage] = await db
const [existingUsage] = await trx
.select()
.from(usage)
.where(eq(usage.usageId, usageId))
@@ -665,7 +666,7 @@ export class UsageService {
`Insert failed for ${orgId}/${featureId}, attempting to fetch existing record:`,
insertError
);
const [existingUsage] = await db
const [existingUsage] = await trx
.select()
.from(usage)
.where(eq(usage.usageId, usageId))
@@ -812,7 +813,8 @@ export class UsageService {
orgId: string,
kickSites = false,
featureId?: FeatureId,
usage?: Usage
usage?: Usage,
trx: Transaction | typeof db = db
): Promise<boolean> {
if (noop()) {
return false;
@@ -825,7 +827,7 @@ export class UsageService {
let orgLimits: Limit[] = [];
if (featureId) {
// Get all limits set for this organization
orgLimits = await db
orgLimits = await trx
.select()
.from(limits)
.where(
@@ -836,7 +838,7 @@ export class UsageService {
);
} else {
// Get all limits set for this organization
orgLimits = await db
orgLimits = await trx
.select()
.from(limits)
.where(eq(limits.orgId, orgId));
@@ -855,7 +857,8 @@ export class UsageService {
} else {
currentUsage = await this.getUsage(
orgId,
limit.featureId as FeatureId
limit.featureId as FeatureId,
trx
);
}
@@ -890,7 +893,7 @@ export class UsageService {
);
// Get all sites for this organization
const orgSites = await db
const orgSites = await trx
.select()
.from(sites)
.where(eq(sites.orgId, orgId));
@@ -902,7 +905,7 @@ export class UsageService {
// Send termination messages to newt sites
for (const site of orgSites) {
if (site.type === "newt") {
const [newt] = await db
const [newt] = await trx
.select()
.from(newts)
.where(eq(newts.siteId, site.siteId))
@@ -917,7 +920,7 @@ export class UsageService {
};
// Don't await to prevent blocking
sendToClient(newt.newtId, payload).catch(
await sendToClient(newt.newtId, payload).catch(
(error: any) => {
logger.error(
`Failed to send termination message to newt ${newt.newtId}:`,

View File

@@ -139,8 +139,8 @@ export async function applyBlueprint(
// password: "sadfasdfadsf",
// "sso-enabled": true,
// "sso-roles": ["Member"],
// "sso-users": ["owen@fossorial.io"],
// "whitelist-users": ["owen@fossorial.io"]
// "sso-users": ["owen@pangolin.net"],
// "whitelist-users": ["owen@pangolin.net"]
// },
// targets: [
// {

View File

@@ -87,8 +87,8 @@ export function convertValue(value: string): any {
// "resources.resource-nice-id.auth.password": "sadfasdfadsf",
// "resources.resource-nice-id.auth.sso-enabled": "true",
// "resources.resource-nice-id.auth.sso-roles[0]": "Member",
// "resources.resource-nice-id.auth.sso-users[0]": "owen@fossorial.io",
// "resources.resource-nice-id.auth.whitelist-users[0]": "owen@fossorial.io",
// "resources.resource-nice-id.auth.sso-users[0]": "owen@pangolin.net",
// "resources.resource-nice-id.auth.whitelist-users[0]": "owen@pangolin.net",
// "resources.resource-nice-id.targets[0].hostname": "localhost",
// "resources.resource-nice-id.targets[0].method": "http",
// "resources.resource-nice-id.targets[0].port": "8000",

View File

@@ -1,4 +1,4 @@
import { db, exitNodes } from "@server/db";
import { db, exitNodes, Transaction } from "@server/db";
import logger from "@server/logger";
import { ExitNodePingResult } from "@server/routers/newt";
import { eq } from "drizzle-orm";
@@ -59,7 +59,11 @@ export function selectBestExitNode(
return pingResults[0];
}
export async function checkExitNodeOrg(exitNodeId: number, orgId: string) {
export async function checkExitNodeOrg(
exitNodeId: number,
orgId: string,
trx?: Transaction | typeof db
): Promise<boolean> {
return false;
}

View File

@@ -314,14 +314,11 @@ export const configSchema = z
nameservers: z
.array(z.string().optional().optional())
.optional()
.default(["ns1.fossorial.io", "ns2.fossorial.io"]),
cname_extension: z.string().optional().default("fossorial.io")
.default(["ns1.pangolin.net", "ns2.pangolin.net", "ns3.pangolin.net"]),
cname_extension: z.string().optional().default("cname.pangolin.net")
})
.optional()
.default({
nameservers: ["ns1.fossorial.io", "ns2.fossorial.io"],
cname_extension: "fossorial.io"
})
.default({})
})
.refine(
(data) => {
@@ -392,7 +389,7 @@ export function readConfigFile() {
if (!environment) {
throw new Error(
"No configuration file found. Please create one. https://docs.digpangolin.com/self-host/advanced/config-file"
"No configuration file found. Please create one. https://docs.pangolin.net/self-host/advanced/config-file"
);
}

View File

@@ -33,7 +33,7 @@ class TelemetryClient {
this.client = new PostHog(
"phc_QYuATSSZt6onzssWcYJbXLzQwnunIpdGGDTYhzK3VjX",
{
host: "https://digpangolin.com/relay-O7yI"
host: "https://pangolin.net/relay-O7yI"
}
);
@@ -48,11 +48,11 @@ class TelemetryClient {
this.startAnalyticsInterval();
logger.info(
"Pangolin now gathers anonymous usage data to help us better understand how the software is used and guide future improvements and feature development. You can find more details, including instructions for opting out of this anonymous data collection, at: https://docs.digpangolin.com/telemetry"
"Pangolin now gathers anonymous usage data to help us better understand how the software is used and guide future improvements and feature development. You can find more details, including instructions for opting out of this anonymous data collection, at: https://docs.pangolin.net/telemetry"
);
} else if (!this.enabled) {
logger.info(
"Analytics usage statistics collection is disabled. If you enable this, you can help us make Pangolin better for everyone. Learn more at: https://docs.digpangolin.com/telemetry"
"Analytics usage statistics collection is disabled. If you enable this, you can help us make Pangolin better for everyone. Learn more at: https://docs.pangolin.net/telemetry"
);
}
}

View File

@@ -88,13 +88,7 @@ export async function getTraefikConfig(
and(
eq(targets.enabled, true),
eq(resources.enabled, true),
or(
eq(sites.exitNodeId, exitNodeId),
and(
isNull(sites.exitNodeId),
sql`(${siteTypes.includes("local") ? 1 : 0} = 1)` // only allow local sites if "local" is in siteTypes
)
),
eq(sites.exitNodeId, exitNodeId),
or(
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record

View File

@@ -18,7 +18,8 @@ import {
resources,
targets,
sites,
targetHealthCheck
targetHealthCheck,
Transaction
} from "@server/db";
import logger from "@server/logger";
import { ExitNodePingResult } from "@server/routers/newt";
@@ -333,8 +334,8 @@ export function selectBestExitNode(
return fallbackNode;
}
export async function checkExitNodeOrg(exitNodeId: number, orgId: string) {
const [exitNodeOrg] = await db
export async function checkExitNodeOrg(exitNodeId: number, orgId: string, trx: Transaction | typeof db = db) {
const [exitNodeOrg] = await trx
.select()
.from(exitNodeOrgs)
.where(

View File

@@ -76,7 +76,7 @@ export const privateConfigSchema = z.object({
local_exit_node_reachable_at: z
.string()
.optional()
.default("http://gerbil:3003")
.default("http://gerbil:3004")
})
.optional()
.default({}),

View File

@@ -120,19 +120,12 @@ export async function getTraefikConfig(
and(
eq(targets.enabled, true),
eq(resources.enabled, true),
or(
eq(sites.exitNodeId, exitNodeId),
and(
isNull(sites.exitNodeId),
sql`(${siteTypes.includes("local") ? 1 : 0} = 1)` // only allow local sites if "local" is in siteTypes
)
),
eq(sites.exitNodeId, exitNodeId),
or(
ne(targetHealthCheck.hcHealth, "unhealthy"), // Exclude unhealthy targets
isNull(targetHealthCheck.hcHealth) // Include targets with no health check record
),
inArray(sites.type, siteTypes),
// lets rewrite this using sql
config.getRawConfig().traefik.allow_raw_resources
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
: eq(resources.http, true)
@@ -238,7 +231,7 @@ export async function getTraefikConfig(
}
// get the valid certs for these domains
validCerts = await getValidCertificatesForDomains(domains, true); // we are caching here because this is called often
logger.debug(`Valid certs for domains: ${JSON.stringify(validCerts)}`);
// logger.debug(`Valid certs for domains: ${JSON.stringify(validCerts)}`);
}
const config_output: any = {

View File

@@ -65,10 +65,12 @@ export async function createExitNode(
[exitNode] = await db
.update(exitNodes)
.set({
reachableAt
reachableAt,
online: true
})
.where(eq(exitNodes.exitNodeId, exitNodeQuery.exitNodeId))
.returning();
logger.info(`Updated exit node reachableAt to ${reachableAt}`);
}

View File

@@ -99,7 +99,7 @@ export async function createRemoteExitNode(
return next(
createHttpError(
HttpCode.FORBIDDEN,
"Remote exit node limit exceeded. Please upgrade your plan or contact us at support@fossorial.io"
"Remote exit node limit exceeded. Please upgrade your plan or contact us at support@pangolin.net"
)
);
}

View File

@@ -47,7 +47,8 @@ export async function createExitNode(publicKey: string, reachableAt: string | un
.update(exitNodes)
.set({
reachableAt,
publicKey
publicKey,
online: true
})
.where(eq(exitNodes.publicKey, publicKey))
.returning();

View File

@@ -98,7 +98,8 @@ export async function updateSiteBandwidth(
if (
await checkExitNodeOrg(
exitNodeId,
updatedSite.orgId
updatedSite.orgId,
trx
)
) {
// not allowed
@@ -148,7 +149,8 @@ export async function updateSiteBandwidth(
orgId,
true,
FeatureId.EGRESS_DATA_MB,
bandwidthUsage
bandwidthUsage,
trx
)
.catch((error: any) => {
logger.error(
@@ -174,7 +176,8 @@ export async function updateSiteBandwidth(
orgId,
true,
FeatureId.SITE_UPTIME,
uptimeUsage
uptimeUsage,
trx
)
.catch((error: any) => {
logger.error(
@@ -242,7 +245,8 @@ export async function updateSiteBandwidth(
if (
await checkExitNodeOrg(
exitNodeId,
updatedSite.orgId
updatedSite.orgId,
trx
)
) {
// not allowed

View File

@@ -414,6 +414,20 @@ authenticated.post(
resource.setResourceWhitelist
);
authenticated.get(
`/resource/:resourceId/whitelist/add`,
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
resource.addEmailToResourceWhitelist
);
authenticated.get(
`/resource/:resourceId/whitelist/remove`,
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
resource.removeEmailFromResourceWhitelist
);
authenticated.get(
`/resource/:resourceId/whitelist`,
verifyApiKeyResourceAccess,

View File

@@ -0,0 +1,147 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, resourceWhitelist } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { and, eq } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const addEmailToResourceWhitelistBodySchema = z
.object({
email: z
.string()
.email()
.or(
z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, {
message:
"Invalid email address. Wildcard (*) must be the entire local part."
})
)
.transform((v) => v.toLowerCase())
})
.strict();
const addEmailToResourceWhitelistParamsSchema = z
.object({
resourceId: z
.string()
.transform(Number)
.pipe(z.number().int().positive())
})
.strict();
registry.registerPath({
method: "post",
path: "/resource/{resourceId}/whitelist/add",
description: "Add a single email to the resource whitelist.",
tags: [OpenAPITags.Resource],
request: {
params: addEmailToResourceWhitelistParamsSchema,
body: {
content: {
"application/json": {
schema: addEmailToResourceWhitelistBodySchema
}
}
}
},
responses: {}
});
export async function addEmailToResourceWhitelist(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = addEmailToResourceWhitelistBodySchema.safeParse(
req.body
);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { email } = parsedBody.data;
const parsedParams = addEmailToResourceWhitelistParamsSchema.safeParse(
req.params
);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { resourceId } = parsedParams.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId));
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
if (!resource.emailWhitelistEnabled) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Email whitelist is not enabled for this resource"
)
);
}
// Check if email already exists in whitelist
const existingEntry = await db
.select()
.from(resourceWhitelist)
.where(
and(
eq(resourceWhitelist.resourceId, resourceId),
eq(resourceWhitelist.email, email)
)
);
if (existingEntry.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
"Email already exists in whitelist"
)
);
}
await db.insert(resourceWhitelist).values({
email,
resourceId
});
return response(res, {
data: {},
success: true,
error: false,
message: "Email added to whitelist successfully",
status: HttpCode.CREATED
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -23,3 +23,5 @@ export * from "./listResourceRules";
export * from "./updateResourceRule";
export * from "./getUserResources";
export * from "./setResourceHeaderAuth";
export * from "./addEmailToResourceWhitelist";
export * from "./removeEmailFromResourceWhitelist";

View File

@@ -0,0 +1,150 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { resources, resourceWhitelist } from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { and, eq } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
const removeEmailFromResourceWhitelistBodySchema = z
.object({
email: z
.string()
.email()
.or(
z.string().regex(/^\*@[\w.-]+\.[a-zA-Z]{2,}$/, {
message:
"Invalid email address. Wildcard (*) must be the entire local part."
})
)
.transform((v) => v.toLowerCase())
})
.strict();
const removeEmailFromResourceWhitelistParamsSchema = z
.object({
resourceId: z
.string()
.transform(Number)
.pipe(z.number().int().positive())
})
.strict();
registry.registerPath({
method: "post",
path: "/resource/{resourceId}/whitelist/remove",
description: "Remove a single email from the resource whitelist.",
tags: [OpenAPITags.Resource],
request: {
params: removeEmailFromResourceWhitelistParamsSchema,
body: {
content: {
"application/json": {
schema: removeEmailFromResourceWhitelistBodySchema
}
}
}
},
responses: {}
});
export async function removeEmailFromResourceWhitelist(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = removeEmailFromResourceWhitelistBodySchema.safeParse(
req.body
);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString()
)
);
}
const { email } = parsedBody.data;
const parsedParams =
removeEmailFromResourceWhitelistParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { resourceId } = parsedParams.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId));
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
if (!resource.emailWhitelistEnabled) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Email whitelist is not enabled for this resource"
)
);
}
// Check if email exists in whitelist
const existingEntry = await db
.select()
.from(resourceWhitelist)
.where(
and(
eq(resourceWhitelist.resourceId, resourceId),
eq(resourceWhitelist.email, email)
)
);
if (existingEntry.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
"Email not found in whitelist"
)
);
}
await db
.delete(resourceWhitelist)
.where(
and(
eq(resourceWhitelist.resourceId, resourceId),
eq(resourceWhitelist.email, email)
)
);
return response(res, {
data: {},
success: true,
error: false,
message: "Email removed from whitelist successfully",
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -17,6 +17,7 @@ import { hashPassword } from "@server/auth/password";
import { isValidIP } from "@server/lib/validators";
import { isIpInCidr } from "@server/lib/ip";
import { verifyExitNodeOrgAccess } from "#dynamic/lib/exitNodes";
import { build } from "@server/build";
const createSiteParamsSchema = z
.object({
@@ -203,10 +204,10 @@ export async function createSite(
const niceId = await getUniqueSiteName(orgId);
await db.transaction(async (trx) => {
let newSite: Site;
let newSite: Site;
if ((type == "wireguard" || type == "newt") && exitNodeId) {
await db.transaction(async (trx) => {
if (type == "wireguard" || type == "newt") {
// we are creating a site with an exit node (tunneled)
if (!subnet) {
return next(
@@ -217,11 +218,19 @@ export async function createSite(
);
}
const { exitNode, hasAccess } =
await verifyExitNodeOrgAccess(
exitNodeId,
orgId
if (!exitNodeId) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Exit node ID is required for tunneled sites"
)
);
}
const { exitNode, hasAccess } = await verifyExitNodeOrgAccess(
exitNodeId,
orgId
);
if (!exitNode) {
logger.warn("Exit node not found");
@@ -257,13 +266,51 @@ export async function createSite(
...(pubKey && type == "wireguard" && { pubKey })
})
.returning();
} else {
// we are creating a site with no tunneling
} else if (type == "local") {
let exitNodeIdToCreate = exitNodeId;
if (!exitNodeIdToCreate) {
if (build == "saas") {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Exit node ID of a remote node is required for local sites"
)
);
}
// select the exit node for local sites
// TODO: THIS SHOULD BE CHOSEN IN THE FRONTEND OR SOMETHING BECAUSE
// YOU CAN HAVE MORE THAN ONE NODE IN THE SYSTEM AND YOU SHOULD SELECT
// WHICH GERBIL NODE TO PUT THE SITE ON BUT FOR NOW THIS WILL DO
const [localExitNode] = await trx
.select()
.from(exitNodes)
.where(eq(exitNodes.type, "gerbil"))
.limit(1);
if (!localExitNode) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"No gerbil exit node found for organization. Please create a gerbil exit node first."
)
);
}
exitNodeIdToCreate = localExitNode.exitNodeId;
} else {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Site type not recognized"
)
);
}
[newSite] = await trx
.insert(sites)
.values({
exitNodeId: exitNodeId,
exitNodeId: exitNodeIdToCreate,
orgId,
name,
niceId,

View File

@@ -0,0 +1,45 @@
import { db } from "@server/db/pg/driver";
import { sql } from "drizzle-orm";
const version = "1.11.1";
export default async function migration() {
console.log(`Running setup script ${version}...`);
try {
// Get the first exit node with type 'gerbil'
const exitNodesQuery = await db.execute(
sql`SELECT "exitNodeId" FROM "exitNodes" WHERE "type" = 'gerbil' LIMIT 1`
);
const exitNodes = exitNodesQuery.rows as {
exitNodeId: number;
}[];
const exitNodeId = exitNodes.length > 0 ? exitNodes[0].exitNodeId : null;
// Get all sites with type 'local'
const sitesQuery = await db.execute(
sql`SELECT "siteId" FROM "sites" WHERE "type" = 'local'`
);
const sites = sitesQuery.rows as {
siteId: number;
}[];
// Update sites to use the exit node
for (const site of sites) {
await db.execute(sql`
UPDATE "sites" SET "exitNodeId" = ${exitNodeId} WHERE "siteId" = ${site.siteId}
`);
}
await db.execute(sql`COMMIT`);
console.log(`Updated sites with exit node`);
} catch (e) {
await db.execute(sql`ROLLBACK`);
console.log("Unable to update sites with exit node");
console.log(e);
throw e;
}
console.log(`${version} migration complete`);
}

View File

@@ -77,7 +77,7 @@ export default async function migration() {
fs.writeFileSync(filePath, updatedYaml, "utf8");
} catch (e) {
console.log(
`Failed to add resource_session_request_param to config. Please add it manually. https://docs.digpangolin.com/self-host/advanced/config-file`
`Failed to add resource_session_request_param to config. Please add it manually. https://docs.pangolin.net/self-host/advanced/config-file`
);
trx.rollback();
return;

View File

@@ -0,0 +1,37 @@
import { APP_PATH } from "@server/lib/consts";
import Database from "better-sqlite3";
import path from "path";
const version = "1.11.1";
export default async function migration() {
console.log(`Running setup script ${version}...`);
const location = path.join(APP_PATH, "db", "db.sqlite");
const db = new Database(location);
db.transaction(() => {
const exitNodes = db.prepare(`SELECT * FROM exitNodes WHERE type = 'gerbil' LIMIT 1`).all() as {
exitNodeId: number;
name: string;
}[];
const exitNodeId = exitNodes.length > 0 ? exitNodes[0].exitNodeId : null;
// get all of the targets
const sites = db.prepare(`SELECT * FROM sites WHERE type = 'local'`).all() as {
siteId: number;
exitNodeId: number | null;
}[];
const defineExitNodeOnSite = db.prepare(
`UPDATE sites SET exitNodeId = ? WHERE siteId = ?`
);
for (const site of sites) {
defineExitNodeOnSite.run(exitNodeId, site.siteId);
}
})();
console.log(`${version} migration complete`);
}

View File

@@ -62,7 +62,7 @@ export default async function migration() {
console.log(`Added new config option: resource_access_token_headers`);
} catch (e) {
console.log(
`Unable to add new config option: resource_access_token_headers. Please add it manually. https://docs.digpangolin.com/self-host/advanced/config-file`
`Unable to add new config option: resource_access_token_headers. Please add it manually. https://docs.pangolin.net/self-host/advanced/config-file`
);
console.error(e);
}

View File

@@ -401,7 +401,7 @@ export default function GeneralPage() {
</Badge>
<Link
className="flex items-center gap-2 text-primary hover:underline"
href="https://digpangolin.com/pricing"
href="https://pangolin.net/pricing"
target="_blank"
rel="noopener noreferrer"
>

View File

@@ -270,17 +270,12 @@ export default function ExitNodesTable({
setSelectedNode(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("remoteExitNodeQuestionRemove", {
selectedNode:
selectedNode?.name || selectedNode?.id
})}
{t("remoteExitNodeQuestionRemove")}
</p>
<p>{t("remoteExitNodeMessageRemove")}</p>
<p>{t("remoteExitNodeMessageConfirm")}</p>
</div>
}
buttonText={t("remoteExitNodeConfirmDelete")}

View File

@@ -42,7 +42,7 @@ import {
FaFreebsd,
FaWindows
} from "react-icons/fa";
import {
import {
SiNixos,
SiKubernetes
} from "react-icons/si";
@@ -150,33 +150,33 @@ export default function Page() {
const commands = {
mac: {
"Apple Silicon (arm64)": [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
],
"Intel x64 (amd64)": [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
]
},
linux: {
amd64: [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
],
arm64: [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
],
arm32: [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
],
arm32v6: [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
],
riscv64: [
`curl -fsSL https://digpangolin.com/get-olm.sh | bash`,
`curl -fsSL https://pangolin.net/get-olm.sh | bash`,
`sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
]
},
@@ -342,14 +342,14 @@ export default function Page() {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);
const response = await fetch(
`https://api.github.com/repos/fosrl/olm/releases/latest`,
{ signal: controller.signal }
);
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(
t("olmErrorFetchReleases", {

View File

@@ -168,13 +168,10 @@ export default function GeneralPage() {
}}
dialog={
<div>
<p className="mb-2">
{t("orgQuestionRemove", {
selectedOrg: org?.org.name
})}
<p>
{t("orgQuestionRemove")}
</p>
<p className="mb-2">{t("orgMessageRemove")}</p>
<p>{t("orgMessageConfirm")}</p>
<p>{t("orgMessageRemove")}</p>
</div>
}
buttonText={t("orgDeleteConfirm")}

View File

@@ -854,6 +854,7 @@ export default function ReverseProxyTargets(props: {
type="number"
min="1"
max="1000"
onClick={(e) => e.currentTarget.focus()}
defaultValue={row.original.priority || 100}
className="w-full max-w-20"
onBlur={(e) => {

View File

@@ -438,6 +438,7 @@ export default function ResourceRules(props: {
defaultValue={row.original.priority}
className="w-[75px]"
type="number"
onClick={(e) => e.currentTarget.focus()}
onBlur={(e) => {
const parsed = z.coerce
.number()

View File

@@ -1874,7 +1874,7 @@ export default function Page() {
<Link
className="text-sm text-primary flex items-center gap-1"
href="https://docs.digpangolin.com/manage/resources/tcp-udp-resources"
href="https://docs.pangolin.net/manage/resources/tcp-udp-resources"
target="_blank"
rel="noopener noreferrer"
>

View File

@@ -43,7 +43,7 @@ export default async function ResourcesPage(props: ResourcesPageProps) {
await authCookieHeader()
);
resources = res.data.data.resources;
} catch (e) {}
} catch (e) { }
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
try {
@@ -51,7 +51,7 @@ export default async function ResourcesPage(props: ResourcesPageProps) {
AxiosResponse<ListAllSiteResourcesByOrgResponse>
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
siteResources = res.data.data.siteResources;
} catch (e) {}
} catch (e) { }
let org = null;
try {
@@ -88,8 +88,8 @@ export default async function ResourcesPage(props: ResourcesPageProps) {
resource.passwordId !== null ||
resource.whitelist ||
resource.headerAuthId
? "protected"
: "not_protected",
? "protected"
: "not_protected",
enabled: resource.enabled,
domainId: resource.domainId || undefined,
ssl: resource.ssl
@@ -128,6 +128,10 @@ export default async function ResourcesPage(props: ResourcesPageProps) {
defaultView={
env.flags.enableClients ? defaultView : "proxy"
}
defaultSort={{
id: "name",
desc: false
}}
/>
</OrgProvider>
</>

View File

@@ -238,7 +238,7 @@ export default function GeneralPage() {
"enableDockerSocketDescription"
)}{" "}
<Link
href="https://docs.digpangolin.com/manage/sites/configure-site#docker-socket-integration"
href="https://docs.pangolin.net/manage/sites/configure-site#docker-socket-integration"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline inline-flex items-center"

View File

@@ -252,43 +252,43 @@ PersistentKeepalive = 5`;
const commands = {
mac: {
All: [
`curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
`curl -fsSL https://pangolin.net/get-newt.sh | bash`,
`newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
]
// "Intel x64 (amd64)": [
// `curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
// `curl -fsSL https://pangolin.net/get-newt.sh | bash`,
// `newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
// ]
},
linux: {
All: [
`curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
`curl -fsSL https://pangolin.net/get-newt.sh | bash`,
`newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
]
// arm64: [
// `curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
// `curl -fsSL https://pangolin.net/get-newt.sh | bash`,
// `newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
// ],
// arm32: [
// `curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
// `curl -fsSL https://pangolin.net/get-newt.sh | bash`,
// `newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
// ],
// arm32v6: [
// `curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
// `curl -fsSL https://pangolin.net/get-newt.sh | bash`,
// `newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
// ],
// riscv64: [
// `curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
// `curl -fsSL https://pangolin.net/get-newt.sh | bash`,
// `newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
// ]
},
freebsd: {
All: [
`curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
`curl -fsSL https://pangolin.net/get-newt.sh | bash`,
`newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
]
// arm64: [
// `curl -fsSL https://digpangolin.com/get-newt.sh | bash`,
// `curl -fsSL https://pangolin.net/get-newt.sh | bash`,
// `newt --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}`
// ]
},

View File

@@ -326,7 +326,7 @@ export default function PoliciesPage() {
{/*TODO(vlalx): Validate replacing */}
{t('orgPoliciesAboutDescription')}{" "}
<Link
href="https://docs.digpangolin.com/manage/identity-providers/auto-provisioning"
href="https://docs.pangolin.net/manage/identity-providers/auto-provisioning"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"

View File

@@ -315,18 +315,13 @@ export default function LicensePage() {
setSelectedLicenseKey(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("licenseQuestionRemove", {
selectedKey: obfuscateLicenseKey(
selectedLicenseKey.licenseKey
)
})}
{t("licenseQuestionRemove")}
</p>
<p>
<b>{t("licenseMessageRemove")}</b>
</p>
<p>{t("licenseMessageConfirm")}</p>
</div>
}
buttonText={t("licenseKeyDeleteConfirm")}

View File

@@ -237,21 +237,14 @@ export default function UsersTable({ users }: Props) {
setSelected(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("userQuestionRemove", {
selectedUser:
selected?.email ||
selected?.name ||
selected?.username
})}
{t("userQuestionRemove")}
</p>
<p>
<b>{t("userMessageRemove")}</b>
{t("userMessageRemove")}
</p>
<p>{t("userMessageConfirm")}</p>
</div>
}
buttonText={t("userDeleteConfirm")}

View File

@@ -147,7 +147,7 @@ export default async function OrgAuthPage(props: {
<span className="text-sm text-muted-foreground">
{t("poweredBy")}{" "}
<Link
href="https://digpangolin.com/"
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"

View File

@@ -193,7 +193,7 @@ export default function IdpTable({ idps }: Props) {
setSelectedIdp(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("idpQuestionRemove", {
name: selectedIdp.name

View File

@@ -256,7 +256,7 @@ export default function UsersTable({ users }: Props) {
setSelected(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("userQuestionRemove", {
selectedUser:

View File

@@ -177,19 +177,14 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
setSelected(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("apiKeysQuestionRemove", {
selectedApiKey:
selected?.name || selected?.id
})}
{t("apiKeysQuestionRemove")}
</p>
<p>
<b>{t("apiKeysMessageRemove")}</b>
{t("apiKeysMessageRemove")}
</p>
<p>{t("apiKeysMessageConfirm")}</p>
</div>
}
buttonText={t("apiKeysDeleteConfirm")}

View File

@@ -277,25 +277,12 @@ export default function ClientsTable({ clients, orgId }: ClientTableProps) {
setSelectedClient(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
Are you sure you want to remove the client{" "}
<b>
{selectedClient?.name || selectedClient?.id}
</b>{" "}
from the site and organization?
{t("deleteClientQuestion")}
</p>
<p>
<b>
Once removed, the client will no longer be
able to connect to the site.{" "}
</b>
</p>
<p>
To confirm, please type the name of the client
below.
{t("clientMessageRemove")}
</p>
</div>
}

View File

@@ -44,6 +44,7 @@ import { Description } from "@radix-ui/react-toast";
import { createApiClient } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useTranslations } from "next-intl";
import CopyToClipboard from "./CopyToClipboard";
type InviteUserFormProps = {
open: boolean;
@@ -110,6 +111,17 @@ export default function InviteUserForm({
<CredenzaBody>
<div className="mb-4 break-all overflow-hidden">
{dialog}
<div className="mt-2 mb-6 font-bold text-red-700">
{t("cannotbeUndone")}
</div>
<div>
<div className="flex items-center gap-2">
{t("type")}
<span className="px-2 py-1 rounded-md bg-secondary"><CopyToClipboard text={string} /></span>
{t("toConfirm")}
</div>
</div>
</div>
<Form {...form}>
<form

View File

@@ -237,16 +237,13 @@ export default function DomainsTable({ domains }: Props) {
setSelectedDomain(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("domainQuestionRemove", {
domain: selectedDomain.baseDomain
})}
{t("domainQuestionRemove")}
</p>
<p>
<b>{t("domainMessageRemove")}</b>
{t("domainMessageRemove")}
</p>
<p>{t("domainMessageConfirm")}</p>
</div>
}
buttonText={t("domainConfirmDelete")}

View File

@@ -401,7 +401,7 @@ export default function GenerateLicenseKeyForm({
{part}
{index === 0 && (
<a
href="https://digpangolin.com/fcl.html"
href="https://pangolin.net/fcl.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
@@ -580,7 +580,7 @@ export default function GenerateLicenseKeyForm({
"signUpTerms.IAgreeToThe"
)}{" "}
<a
href="https://digpangolin.com/terms-of-service.html"
href="https://pangolin.net/terms-of-service.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
@@ -593,7 +593,7 @@ export default function GenerateLicenseKeyForm({
"signUpTerms.and"
)}{" "}
<a
href="https://digpangolin.com/privacy-policy.html"
href="https://pangolin.net/privacy-policy.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
@@ -637,12 +637,12 @@ export default function GenerateLicenseKeyForm({
license
details:{" "}
<a
href="https://digpangolin.com/fcl.html"
href="https://pangolin.net/fcl.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
https://digpangolin.com/fcl.html
https://pangolin.net/fcl.html
</a>
</div>
</FormLabel>
@@ -966,7 +966,7 @@ export default function GenerateLicenseKeyForm({
"signUpTerms.IAgreeToThe"
)}{" "}
<a
href="https://digpangolin.com/terms-of-service.html"
href="https://pangolin.net/terms-of-service.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
@@ -979,7 +979,7 @@ export default function GenerateLicenseKeyForm({
"signUpTerms.and"
)}{" "}
<a
href="https://digpangolin.com/privacy-policy.html"
href="https://pangolin.net/privacy-policy.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
@@ -1023,12 +1023,12 @@ export default function GenerateLicenseKeyForm({
license
details:{" "}
<a
href="https://digpangolin.com/fcl.html"
href="https://pangolin.net/fcl.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
https://digpangolin.com/fcl.html
https://pangolin.net/fcl.html
</a>
</div>
</FormLabel>

View File

@@ -175,14 +175,11 @@ export default function InvitationsTable({
setSelectedInvitation(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("inviteQuestionRemove", {
email: selectedInvitation?.email || ""
})}
{t("inviteQuestionRemove")}
</p>
<p>{t("inviteMessageRemove")}</p>
<p>{t("inviteMessageConfirm")}</p>
</div>
}
buttonText={t("inviteRemoveConfirm")}

View File

@@ -185,19 +185,14 @@ export default function OrgApiKeysTable({
setSelected(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("apiKeysQuestionRemove", {
selectedApiKey:
selected?.name || selected?.id
})}
{t("apiKeysQuestionRemove")}
</p>
<p>
<b>{t("apiKeysMessageRemove")}</b>
{t("apiKeysMessageRemove")}
</p>
<p>{t("apiKeysMessageConfirm")}</p>
</div>
}
buttonText={t("apiKeysDeleteConfirm")}

View File

@@ -348,7 +348,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
<span className="text-sm text-muted-foreground">
{t("poweredBy")}{" "}
<Link
href="https://digpangolin.com/"
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
@@ -363,7 +363,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
<span className="text-sm text-muted-foreground">
{t("poweredBy")}{" "}
<Link
href="https://digpangolin.com/"
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"

View File

@@ -100,6 +100,10 @@ type ResourcesTableProps = {
internalResources: InternalResourceRow[];
orgId: string;
defaultView?: "proxy" | "internal";
defaultSort?: {
id: string;
desc: boolean;
};
};
@@ -143,7 +147,8 @@ export default function ResourcesTable({
resources,
internalResources,
orgId,
defaultView = "proxy"
defaultView = "proxy",
defaultSort
}: ResourcesTableProps) {
const router = useRouter();
const searchParams = useSearchParams();
@@ -171,12 +176,16 @@ export default function ResourcesTable({
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
const [sites, setSites] = useState<Site[]>([]);
const [proxySorting, setProxySorting] = useState<SortingState>([]);
const [proxySorting, setProxySorting] = useState<SortingState>(
defaultSort ? [defaultSort] : []
);
const [proxyColumnFilters, setProxyColumnFilters] =
useState<ColumnFiltersState>([]);
const [proxyGlobalFilter, setProxyGlobalFilter] = useState<any>([]);
const [internalSorting, setInternalSorting] = useState<SortingState>([]);
const [internalSorting, setInternalSorting] = useState<SortingState>(
defaultSort ? [defaultSort] : []
);
const [internalColumnFilters, setInternalColumnFilters] =
useState<ColumnFiltersState>([]);
const [internalGlobalFilter, setInternalGlobalFilter] = useState<any>([]);
@@ -695,17 +704,12 @@ export default function ResourcesTable({
}}
dialog={
<div>
<p className="mb-2">
{t("resourceQuestionRemove", {
selectedResource:
selectedResource?.name ||
selectedResource?.id
})}
<p>
{t("resourceQuestionRemove")}
</p>
<p>
{t("resourceMessageRemove")}
</p>
<p className="mb-2">{t("resourceMessageRemove")}</p>
<p>{t("resourceMessageConfirm")}</p>
</div>
}
buttonText={t("resourceDeleteConfirm")}
@@ -724,17 +728,12 @@ export default function ResourcesTable({
}}
dialog={
<div>
<p className="mb-2">
{t("resourceQuestionRemove", {
selectedResource:
selectedInternalResource?.name ||
selectedInternalResource?.id
})}
<p>
{t("resourceQuestionRemove")}
</p>
<p>
{t("resourceMessageRemove")}
</p>
<p className="mb-2">{t("resourceMessageRemove")}</p>
<p>{t("resourceMessageConfirm")}</p>
</div>
}
buttonText={t("resourceDeleteConfirm")}

View File

@@ -21,7 +21,7 @@ export default function SidebarLicenseButton({
}: SidebarLicenseButtonProps) {
const { licenseStatus, updateLicenseStatus } = useLicenseStatusContext();
const url = "https://docs.digpangolin.com/self-host/enterprise-edition";
const url = "https://docs.pangolin.net/self-host/enterprise-edition";
const t = useTranslations();

View File

@@ -512,7 +512,7 @@ export default function SignupForm({
"signUpTerms.IAgreeToThe"
)}{" "}
<a
href="https://digpangolin.com/terms-of-service.html"
href="https://pangolin.net/terms-of-service.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
@@ -523,7 +523,7 @@ export default function SignupForm({
</a>
{t("signUpTerms.and")}{" "}
<a
href="https://digpangolin.com/privacy-policy.html"
href="https://pangolin.net/privacy-policy.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"

View File

@@ -64,7 +64,7 @@ export const SitesSplashCard = () => {
<div className="mt-4">
<Link
href="https://docs.digpangolin.com/manage/sites/install-site"
href="https://docs.pangolin.net/manage/sites/install-site"
target="_blank"
rel="noopener noreferrer"
>

View File

@@ -418,17 +418,11 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
setSelectedSite(null);
}}
dialog={
<div className="space-y-4">
<div className="">
<p>
{t("siteQuestionRemove", {
selectedSite:
selectedSite?.name || selectedSite?.id
})}
{t("siteQuestionRemove")}
</p>
<p>{t("siteMessageRemove")}</p>
<p>{t("siteMessageConfirm")}</p>
</div>
}
buttonText={t("siteConfirmDelete")}

View File

@@ -219,7 +219,7 @@ export default function SupporterStatus({ isCollapsed = false }: SupporterStatus
</Link>{" "}
{t('supportKeyPurchase2')}{" "}
<Link
href="https://docs.digpangolin.com/self-host/supporter-program"
href="https://docs.pangolin.net/self-host/supporter-program"
target="_blank"
rel="noopener noreferrer"
className="underline"

View File

@@ -273,20 +273,11 @@ export default function UsersTable({ users: u }: UsersTableProps) {
setSelectedUser(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("userQuestionOrgRemove", {
email:
selectedUser?.email ||
selectedUser?.name ||
selectedUser?.username ||
""
})}
{t("userQuestionOrgRemove")}
</p>
<p>{t("userMessageOrgRemove")}</p>
<p>{t("userMessageOrgConfirm")}</p>
</div>
}
buttonText={t("userRemoveOrgConfirm")}

View File

@@ -177,16 +177,13 @@ export default function IdpTable({ idps, orgId }: Props) {
setSelectedIdp(null);
}}
dialog={
<div className="space-y-4">
<div>
<p>
{t("idpQuestionRemove", {
name: selectedIdp.name
})}
{t("idpQuestionRemove")}
</p>
<p>
<b>{t("idpMessageRemove")}</b>
{t("idpMessageRemove")}
</p>
<p>{t("idpMessageConfirm")}</p>
</div>
}
buttonText={t("idpConfirmDelete")}