Compare commits

..

614 Commits

Author SHA1 Message Date
dependabot[bot]
77d1a466b3 Bump the prod-minor-updates group with 8 updates
Bumps the prod-minor-updates group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@asteasolutions/zod-to-openapi](https://github.com/asteasolutions/zod-to-openapi) | `8.4.1` | `8.5.0` |
| [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.1011.0` | `3.1020.0` |
| [@faker-js/faker](https://github.com/faker-js/faker) | `10.3.0` | `10.4.0` |
| [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) | `5.90.21` | `5.95.2` |
| [axios](https://github.com/axios/axios) | `1.13.5` | `1.14.1` |
| [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.71.2` | `7.72.0` |
| [resend](https://github.com/resend/resend-node) | `6.9.2` | `6.10.0` |
| [ws](https://github.com/websockets/ws) | `8.19.0` | `8.20.0` |


Updates `@asteasolutions/zod-to-openapi` from 8.4.1 to 8.5.0
- [Release notes](https://github.com/asteasolutions/zod-to-openapi/releases)
- [Commits](https://github.com/asteasolutions/zod-to-openapi/compare/v8.4.1...v8.5.0)

Updates `@aws-sdk/client-s3` from 3.1011.0 to 3.1020.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1020.0/clients/client-s3)

Updates `@faker-js/faker` from 10.3.0 to 10.4.0
- [Release notes](https://github.com/faker-js/faker/releases)
- [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md)
- [Commits](https://github.com/faker-js/faker/compare/v10.3.0...v10.4.0)

Updates `@tanstack/react-query` from 5.90.21 to 5.95.2
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.95.2/packages/react-query)

Updates `axios` from 1.13.5 to 1.14.1
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/commits)

Updates `react-hook-form` from 7.71.2 to 7.72.0
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.71.2...v7.72.0)

Updates `resend` from 6.9.2 to 6.10.0
- [Release notes](https://github.com/resend/resend-node/releases)
- [Commits](https://github.com/resend/resend-node/compare/v6.9.2...v6.10.0)

Updates `ws` from 8.19.0 to 8.20.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.19.0...8.20.0)

---
updated-dependencies:
- dependency-name: "@asteasolutions/zod-to-openapi"
  dependency-version: 8.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1020.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@faker-js/faker"
  dependency-version: 10.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.95.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: axios
  dependency-version: 1.14.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: react-hook-form
  dependency-version: 7.72.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: resend
  dependency-version: 6.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: ws
  dependency-version: 8.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-31 22:34:30 +00:00
Owen Schwartz
ddf417f4ca Merge pull request #2753 from fosrl/dev
Update security
2026-03-31 15:27:47 -07:00
Owen
f2abbf01e5 Update security 2026-03-31 15:26:39 -07:00
Owen Schwartz
d08be59055 Merge pull request #2752 from fosrl/dev
1.17.0-rc.0
2026-03-31 15:24:25 -07:00
Owen Schwartz
44bb87e4ac Merge pull request #2751 from fosrl/crowdin_dev
New Crowdin updates
2026-03-31 15:21:34 -07:00
Owen Schwartz
1d2f1405aa New translations en-us.json (Norwegian Bokmal) 2026-03-31 15:20:05 -07:00
Owen Schwartz
ff64a79014 New translations en-us.json (Chinese Simplified) 2026-03-31 15:20:04 -07:00
Owen Schwartz
f6cdadbc2d New translations en-us.json (Turkish) 2026-03-31 15:20:02 -07:00
Owen Schwartz
546769ca66 New translations en-us.json (Spanish) 2026-03-31 15:20:00 -07:00
Owen Schwartz
d07996d435 New translations en-us.json (Russian) 2026-03-31 15:19:59 -07:00
Owen Schwartz
467808f174 New translations en-us.json (Portuguese) 2026-03-31 15:19:57 -07:00
Owen Schwartz
64d3c6b2d9 New translations en-us.json (Polish) 2026-03-31 15:19:56 -07:00
Owen Schwartz
75193bb0a2 New translations en-us.json (Dutch) 2026-03-31 15:19:54 -07:00
Owen Schwartz
82ba2bd809 New translations en-us.json (Korean) 2026-03-31 15:19:52 -07:00
Owen Schwartz
8559942c5c New translations en-us.json (Italian) 2026-03-31 15:19:51 -07:00
Owen Schwartz
a7fefc84a8 New translations en-us.json (German) 2026-03-31 15:19:49 -07:00
Owen Schwartz
c8e83fedeb New translations en-us.json (Czech) 2026-03-31 15:19:48 -07:00
Owen Schwartz
4bf148a4bf New translations en-us.json (Bulgarian) 2026-03-31 15:19:46 -07:00
Owen Schwartz
44664faf3c New translations en-us.json (French) 2026-03-31 15:19:45 -07:00
Owen Schwartz
322c136d1f Merge pull request #2748 from jaydeep-pipaliya/fix/empty-targets-toast-message
fix: show contextual toast when saving with no targets
2026-03-31 15:11:12 -07:00
Owen
3b8dd45a73 Translate siem 2026-03-31 15:09:14 -07:00
Owen
c1bd36231d Default to approved 2026-03-31 14:46:23 -07:00
Owen
2cee723f0e Handle online and offline for wireguard sites 2026-03-31 14:41:02 -07:00
Owen
edfeec900d Define db type 2026-03-31 14:25:47 -07:00
Owen
958bde2090 Merge branch 'siem' into dev 2026-03-31 14:20:46 -07:00
Owen
29b272f5d5 Restrict saas 2026-03-31 14:08:50 -07:00
Owen
9162ac6d91 Add missing headers 2026-03-31 14:07:28 -07:00
Owen
fe30bb280e Add option for how to batch 2026-03-31 14:04:58 -07:00
Owen
a1e9396999 Handle backlog better 2026-03-31 13:47:32 -07:00
miloschwartz
2a1c290dff dont show identifier on create private resource 2026-03-31 12:32:03 -07:00
Owen
d155d7e31b Update formatting 2026-03-31 12:26:31 -07:00
Owen
3dc258da16 Log streaming manager pass 1 2026-03-31 12:22:37 -07:00
Owen
0db1397f2f Add log connection audit dedicated file 2026-03-31 12:02:02 -07:00
Owen
0254fb1695 Small ui tweaks 2026-03-31 11:56:19 -07:00
Owen
954b492aa9 Fix imports 2026-03-31 11:52:08 -07:00
Owen
8aadc10530 Merge branch 'main' into dev 2026-03-31 11:44:31 -07:00
Owen
6ea719c50f Pin 2026-03-31 11:44:18 -07:00
Owen
b50886179a Implement fixes to ui 2026-03-31 11:43:52 -07:00
jaydeep-pipaliya
e06f2f47b1 fix: show contextual toast when saving with no targets
Instead of always showing "Settings updated" when saving, show
"Targets cleared" when the target list is empty. This gives the user
accurate feedback without blocking the save action.

Fixes #586
2026-03-31 11:48:56 +05:30
Owen
ed8c8bedcd Add picker 2026-03-30 21:46:11 -07:00
Owen
1711e39219 Add logos 2026-03-30 21:42:41 -07:00
Owen
a73879ec7a Fix formatting 2026-03-30 21:35:37 -07:00
Owen
45c613dec4 Tweaking the ui 2026-03-30 21:09:14 -07:00
Owen
5150a2c386 Basic ui done 2026-03-30 21:00:05 -07:00
Owen
ca0dd09964 Add crud 2026-03-30 20:35:00 -07:00
Owen
5e0e4f1452 Update book a demo 2026-03-30 20:16:35 -07:00
Owen
3ed72dd96b Add missing colums to migrations 2026-03-30 18:16:22 -07:00
Owen
b8d7d5c910 Add name to provisioning 2026-03-30 17:18:38 -07:00
Owen
673b8b7af5 Add userInviteRoles migration 2026-03-30 17:03:12 -07:00
Owen
a651e50759 Fix merge from main 2026-03-30 16:59:05 -07:00
Owen
6484e8e302 Make work for demo 2026-03-30 16:57:36 -07:00
Owen
b01d266629 Migration 1.17 first pass 2026-03-30 16:55:37 -07:00
Owen
4465b05404 Merge branch 'provisioning-room' into dev 2026-03-30 16:19:11 -07:00
Owen
d1182c3a59 Merge branch 'main' into dev 2026-03-30 15:53:46 -07:00
Owen
cb6c47678b Add regions to blueprints 2026-03-30 14:43:49 -07:00
Owen Schwartz
8106620a19 Merge pull request #2149 from infiniteWays/feature/region-rules
Region-based Resource Rules
2026-03-30 14:37:17 -07:00
Owen
be3e066843 Merge branch 'dev' into feature/region-rules 2026-03-30 14:36:50 -07:00
Owen Schwartz
e345c6ee6e Merge pull request #2627 from shreyaspapi/fix/1547-persist-user-locale
fix: persist user locale preference to database (#1547)
2026-03-30 14:29:15 -07:00
Owen
073b89b355 make path the default 2026-03-30 14:18:10 -07:00
Owen Schwartz
5cad07f8ad Merge pull request #2623 from rodneyosodo/fix/errcheck
refactor(install): improve resource cleanup and remove unused funcs
2026-03-30 14:12:41 -07:00
Owen Schwartz
f9d872558e Merge pull request #2624 from fosrl/dependabot/go_modules/install/github.com/charmbracelet/huh-1.0.0
Bump github.com/charmbracelet/huh from 0.8.0 to 1.0.0 in /install
2026-03-30 14:08:26 -07:00
Owen Schwartz
c5015d02ae Merge pull request #2646 from LaurenceJJones/feature/installer-default-dir
feat(installer): add default install directory with existing install …
2026-03-30 14:08:00 -07:00
Owen Schwartz
48013228c1 Merge pull request #2653 from shleeable/patch-1
feat(installer): Update docker-compose.yml with HTTPS/3 + QUIC support via traefik
2026-03-30 14:07:34 -07:00
Owen
dbafffe73d Update crowdsec and add comment 2026-03-30 14:06:56 -07:00
Owen Schwartz
61cbcb2a06 Merge pull request #2741 from fosrl/dependabot/npm_and_yarn/multi-0b8106bf31
Bump fast-xml-parser and @aws-sdk/xml-builder
2026-03-30 13:58:19 -07:00
Owen Schwartz
89c1ad5d98 Merge pull request #2738 from fosrl/dependabot/github_actions/sigstore/cosign-installer-4.1.1
Bump sigstore/cosign-installer from 4.1.0 to 4.1.1
2026-03-30 13:55:27 -07:00
Owen Schwartz
b343ca6290 Merge pull request #2687 from fosrl/dependabot/npm_and_yarn/next-15.5.14
Bump next from 15.5.12 to 15.5.14
2026-03-30 13:55:19 -07:00
dependabot[bot]
b913466671 Bump next from 15.5.12 to 15.5.14
Bumps [next](https://github.com/vercel/next.js) from 15.5.12 to 15.5.14.
- [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.12...v15.5.14)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.5.14
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 20:37:38 +00:00
Owen Schwartz
9054f4f9c3 Merge pull request #2683 from fosrl/dependabot/npm_and_yarn/flatted-3.4.2
Bump flatted from 3.3.3 to 3.4.2
2026-03-30 13:37:18 -07:00
Owen Schwartz
3915024d9a Merge pull request #2714 from fosrl/dependabot/npm_and_yarn/dev-patch-updates-3753551584
Bump the dev-patch-updates group across 1 directory with 3 updates
2026-03-30 13:36:49 -07:00
dependabot[bot]
7d1085b43f Bump fast-xml-parser and @aws-sdk/xml-builder
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and [@aws-sdk/xml-builder](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/xml-builder). These dependencies needed to be updated together.

Updates `fast-xml-parser` from 5.5.6 to 5.5.8
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.5.6...v5.5.8)

Updates `@aws-sdk/xml-builder` from 3.972.12 to 3.972.16
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/xml-builder/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/xml-builder)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.5.8
  dependency-type: indirect
- dependency-name: "@aws-sdk/xml-builder"
  dependency-version: 3.972.16
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 20:36:35 +00:00
Owen Schwartz
7c2477cccc Merge pull request #2717 from fosrl/dependabot/npm_and_yarn/multi-bf05dc1ecf
Bump picomatch
2026-03-30 13:36:21 -07:00
Owen Schwartz
5aecb5fb90 Merge pull request #2727 from fosrl/dependabot/npm_and_yarn/yaml-2.8.3
Bump yaml from 2.8.2 to 2.8.3
2026-03-30 13:36:06 -07:00
Owen Schwartz
f86d040ee4 Merge pull request #2728 from fosrl/dependabot/npm_and_yarn/nodemailer-8.0.4
Bump nodemailer from 8.0.1 to 8.0.4
2026-03-30 13:35:48 -07:00
Owen Schwartz
ed32717b3f Merge pull request #2730 from fosrl/dependabot/npm_and_yarn/multi-95b84c9cdf
Bump brace-expansion
2026-03-30 13:35:31 -07:00
Owen Schwartz
aab8462134 Merge pull request #2733 from fosrl/dependabot/npm_and_yarn/path-to-regexp-8.4.0
Bump path-to-regexp from 8.3.0 to 8.4.0
2026-03-30 13:34:56 -07:00
Owen
04943fb4a6 Fix access log 2026-03-30 11:58:48 -07:00
Owen
e0c96e7224 Configure connection log retention time 2026-03-30 11:31:46 -07:00
Owen
caacd1e677 Remove rewrite if match is removed 2026-03-30 11:11:18 -07:00
Owen
c995c5a674 Dont batch set on sqlite 2026-03-30 11:02:09 -07:00
Owen
1e9544af07 Customize table a little more 2026-03-29 20:29:31 -07:00
dependabot[bot]
c20dfdabfb Bump the dev-patch-updates group across 1 directory with 3 updates
Bumps the dev-patch-updates group with 3 updates in the / directory: [@tailwindcss/postcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss), [esbuild](https://github.com/evanw/esbuild) and [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss).


Updates `@tailwindcss/postcss` from 4.2.1 to 4.2.2
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.2/packages/@tailwindcss-postcss)

Updates `esbuild` from 0.27.3 to 0.27.4
- [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.27.3...v0.27.4)

Updates `tailwindcss` from 4.2.1 to 4.2.2
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.2/packages/tailwindcss)

---
updated-dependencies:
- dependency-name: "@tailwindcss/postcss"
  dependency-version: 4.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: esbuild
  dependency-version: 0.27.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: tailwindcss
  dependency-version: 4.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 01:35:59 +00:00
dependabot[bot]
11a6f1f47f Bump sigstore/cosign-installer from 4.1.0 to 4.1.1
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](ba7bc0a3fe...cad07c2e89)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 01:34:28 +00:00
Owen
fcf92d4e2c Add basic provisioning room v1 and update keys 2026-03-29 16:28:51 -07:00
Owen
77cef554be Provisioning room basics done 2026-03-29 14:20:58 -07:00
Owen
9dc9b6a2c3 Merge branch 'logging-provision' into dev 2026-03-29 13:59:14 -07:00
Owen
9808a48da0 Merge branch 'multi-role' into dev 2026-03-29 13:55:23 -07:00
Milo Schwartz
8a6960d9c3 Merge pull request #2670 from Fredkiss3/feat/selector-filtering
Feat: selector filtering
2026-03-29 12:26:51 -07:00
Milo Schwartz
d966ef66e1 Merge pull request #2643 from Fredkiss3/fix/wireguard-site-ip
fix: Display actual values for WireGuard site credentials
2026-03-29 12:23:16 -07:00
Milo Schwartz
ed97cf5d97 Merge pull request #2697 from Fredkiss3/feat/modify-private-resource-niceid
feat: edit niceid in private resources
2026-03-29 12:18:21 -07:00
Owen
a3b088f8d2 Merge branch 'dev' into logging-provision 2026-03-29 12:18:13 -07:00
miloschwartz
2828dee94c support multi role on create user and invites 2026-03-29 12:11:22 -07:00
Owen Schwartz
bdc45887f9 Add chainId to dedup messages (#2737)
* ChainId send through on sensitive messages
2026-03-29 12:08:29 -07:00
miloschwartz
ee6fb34906 Merge branch 'multi-role' of https://github.com/fosrl/pangolin into multi-role 2026-03-29 12:01:55 -07:00
miloschwartz
bff2ba7cc2 add learn more link 2026-03-29 11:34:07 -07:00
Owen
8e821b397f Add migration 2026-03-28 21:41:21 -07:00
Owen
6f71af278e Add basic migration files 2026-03-28 21:29:32 -07:00
Owen
757bb39622 Support overriding badger for testing 2026-03-28 21:24:13 -07:00
Owen
00ef6d617f Handle the roles better in the verify session 2026-03-28 21:24:13 -07:00
miloschwartz
d1b2105c80 support search in tags input 2026-03-28 18:29:40 -07:00
miloschwartz
50ee28b1f7 fix no admin user in roles dropdown 2026-03-28 18:23:07 -07:00
miloschwartz
ba529ad14e hide google and azure idp properly 2026-03-28 18:20:56 -07:00
miloschwartz
6ab0555148 respect full rbac feature in auto provisioning 2026-03-28 18:09:36 -07:00
miloschwartz
c6f269b3fa set roles 1:1 on auto provision 2026-03-28 17:29:01 -07:00
dependabot[bot]
8e160902af Bump path-to-regexp from 8.3.0 to 8.4.0
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 8.3.0 to 8.4.0.
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v8.3.0...v8.4.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-version: 8.4.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-28 18:37:06 +00:00
miloschwartz
7bcb852dba add google and azure templates to global idp 2026-03-27 18:10:19 -07:00
miloschwartz
ed604c8810 Merge branch 'multi-role' of https://github.com/fosrl/pangolin into multi-role 2026-03-27 17:35:50 -07:00
miloschwartz
bea20674a8 support policy buildiner in global idp 2026-03-27 17:35:35 -07:00
Owen
177926932b Update hybrid for multi role 2026-03-27 17:07:58 -07:00
Owen
04dfbd0a14 Chainid send through 2026-03-27 12:04:41 -07:00
dependabot[bot]
06f840a680 Bump brace-expansion
Bumps  and [brace-expansion](https://github.com/juliangruber/brace-expansion). These dependencies needed to be updated together.

Updates `brace-expansion` from 5.0.4 to 5.0.5
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.4...v5.0.5)

Updates `brace-expansion` from 1.1.12 to 1.1.13
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.4...v5.0.5)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.5
  dependency-type: indirect
- dependency-name: brace-expansion
  dependency-version: 1.1.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 14:43:09 +00:00
dependabot[bot]
5ddcfeb506 Bump nodemailer from 8.0.1 to 8.0.4
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.1 to 8.0.4.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v8.0.1...v8.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 06:56:52 +00:00
Owen
a143b7de7c Merge branch 'multi-role' of github.com:fosrl/pangolin into multi-role 2026-03-26 21:47:13 -07:00
Owen
63372b174f Merge branch 'dev' into multi-role 2026-03-26 21:46:29 -07:00
miloschwartz
ad7d68d2b4 basic idp mapping builder 2026-03-26 21:46:01 -07:00
Owen
e05af54f76 Use standard component 2026-03-26 21:36:51 -07:00
dependabot[bot]
914e95e47f Bump yaml from 2.8.2 to 2.8.3
Bumps [yaml](https://github.com/eemeli/yaml) from 2.8.2 to 2.8.3.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.8.2...v2.8.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 02:36:44 +00:00
miloschwartz
13eadeaa8f support legacy one role per user 2026-03-26 18:19:10 -07:00
Owen
19a686b3e4 Add restrictions around provisioning key 2026-03-26 16:49:43 -07:00
miloschwartz
d046084e84 delete role move to new role 2026-03-26 16:44:30 -07:00
miloschwartz
e13a076939 ui improvements 2026-03-26 16:37:31 -07:00
Owen
b4ca6432db Fix double id 2026-03-26 16:24:44 -07:00
dependabot[bot]
5b9efc3c5f Bump picomatch
Bumps  and [picomatch](https://github.com/micromatch/picomatch). These dependencies needed to be updated together.

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `picomatch` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
- dependency-name: picomatch
  dependency-version: 4.0.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-26 08:51:09 +00:00
Owen Schwartz
6d7a19b0a0 Merge pull request #2716 from fosrl/patch-1
Add typecasts
2026-03-25 22:12:59 -07:00
Owen
6b3a6fa380 Add typecasts 2026-03-25 22:11:56 -07:00
Owen Schwartz
e2a65b4b74 Merge pull request #2715 from fosrl/batch-band
Batch set bandwidth
2026-03-25 21:54:44 -07:00
Owen
1f01108b62 Batch set bandwidth 2026-03-25 21:53:20 -07:00
Owen
c80c7df1d0 Batch set bandwidth 2026-03-25 20:44:08 -07:00
Owen
99a064b77a Fix import problems 2026-03-25 20:44:08 -07:00
Owen
9b84623d0c Cache token for thundering hurd 2026-03-25 20:44:08 -07:00
Owen
6bb6cf8a48 Clean up 2026-03-25 20:44:08 -07:00
Owen
348fcbcabf Try to solve th problem 2026-03-25 20:44:08 -07:00
Owen
1f4cde5f7f Add license script 2026-03-25 20:44:08 -07:00
Owen
3e3b02021c Add ssh access log 2026-03-25 20:44:08 -07:00
Owen
17eb93d045 Add better pooling controls 2026-03-25 20:44:08 -07:00
Owen
660420ddef Disable everything if not paid 2026-03-25 20:44:08 -07:00
Owen
395cab795c Batch set bandwidth 2026-03-25 20:35:21 -07:00
miloschwartz
0fecbe704b Merge branch 'dev' into multi-role 2026-03-24 22:01:13 -07:00
Owen
ce59a8a52b Merge branch 'main' into dev 2026-03-24 20:38:16 -07:00
miloschwartz
2091b5f359 Merge branch 'logging-provision' of https://github.com/fosrl/pangolin into logging-provision 2026-03-24 20:30:14 -07:00
Owen Schwartz
62c63ddcaa Merge pull request #2710 from fosrl/thundering-herd
thundering herd
2026-03-24 20:29:01 -07:00
Owen
dfd604c781 Fix import problems 2026-03-24 20:27:34 -07:00
miloschwartz
3525b367b3 move to private routes 2026-03-24 20:27:15 -07:00
Owen
0b5b6ed5a3 Adjust register endpoint 2026-03-24 18:26:10 -07:00
Owen
6fe9494df4 Merge branch 'logging-provision' of github.com:fosrl/pangolin into logging-provision 2026-03-24 18:17:42 -07:00
Owen
b2eab95a3b Pass at first endpoints 2026-03-24 18:17:33 -07:00
Owen
38d30b0214 Add license script 2026-03-24 18:13:57 -07:00
Owen
c96c5e8ae8 Cache token for thundering hurd 2026-03-24 18:12:51 -07:00
Owen
6f71e9f0f2 Clean up 2026-03-24 17:55:14 -07:00
Owen
d17ec6dc1f Try to solve th problem 2026-03-24 17:39:43 -07:00
miloschwartz
212b7a104f Merge branch 'logging-provision' of https://github.com/fosrl/pangolin into logging-provision 2026-03-24 17:01:36 -07:00
miloschwartz
d21dfb750e ui for provisioning key 2026-03-24 17:01:20 -07:00
Owen Schwartz
c36a019f5d Merge pull request #2709 from fosrl/pool-update
Update pool and disable idp
2026-03-24 16:48:28 -07:00
Owen
cf2dfdea5b Add better pooling controls 2026-03-24 16:38:50 -07:00
Owen
985e1bb9ab Disable everything if not paid 2026-03-24 16:38:46 -07:00
Owen
fff38aac85 Add ssh access log 2026-03-24 16:26:56 -07:00
miloschwartz
7db58f920c add site provisioning key crud 2026-03-24 16:19:00 -07:00
Fred KISSIE
e9b16b8801 Merge branch 'dev' into feat/modify-private-resource-niceid 2026-03-25 00:13:35 +01:00
Owen
5a2a97b23a Add better pooling controls 2026-03-24 16:12:13 -07:00
Owen
5b894e8682 Disable everything if not paid 2026-03-24 16:01:54 -07:00
Fred KISSIE
84925f724d 💄 update UI 2026-03-24 23:43:01 +01:00
Owen
7b78b91449 Fix resource link 2026-03-23 22:00:53 -07:00
Owen
f9bff5954f Add filters and refine table and query 2026-03-23 21:49:22 -07:00
Owen
2c6e9507b5 Connection log page working 2026-03-23 21:41:53 -07:00
Owen
6471571bc6 Add ui for connection logs 2026-03-23 20:18:03 -07:00
Owen
fe40ea58c1 Source client info into schema 2026-03-23 20:05:54 -07:00
Owen
0d4edcd1c7 make private 2026-03-23 17:23:51 -07:00
Owen
7d8797840a Add connection log 2026-03-23 17:01:34 -07:00
Owen Schwartz
19f8c1772f Merge pull request #2698 from fosrl/msg-opt
Improve proxy list message size
2026-03-23 16:05:24 -07:00
Owen
37d331e813 Update version 2026-03-23 16:05:05 -07:00
Owen
c660df55cd Merge branch 'dev' into msg-opt 2026-03-23 16:00:50 -07:00
Fred KISSIE
60982bf19f 🚧 edit niceid in private resources 2026-03-23 22:55:59 +01:00
Owen Schwartz
7c8b865379 Merge pull request #2695 from noe-charmet/redis-password-env
Allow setting Redis password from env
2026-03-23 12:02:45 -07:00
Noe Charmet
3cca0c09c0 Allow setting Redis password from env 2026-03-23 11:18:55 +01:00
Owen Schwartz
85335bfecc Merge pull request #2685 from fosrl/dev
1.16.2-s.16
2026-03-21 10:47:18 -07:00
Owen
7c2b4f422a Merge branch 'main' into dev 2026-03-21 10:45:13 -07:00
Owen
ad2a0ae127 Use the log database in hybrid as well 2026-03-21 10:42:31 -07:00
dependabot[bot]
871f14ef3a Bump flatted from 3.3.3 to 3.4.2
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-21 11:17:41 +00:00
miloschwartz
6c2c620c99 set cache ttl and default ttl 2026-03-20 17:52:07 -07:00
miloschwartz
f643abf19a dont show create org for oidc users 2026-03-20 16:04:00 -07:00
Owen Schwartz
a1729033cf Merge pull request #2682 from fosrl/dev
Fix offline issue
2026-03-20 15:31:38 -07:00
Owen
7311766512 Fix offline issue 2026-03-20 15:30:41 -07:00
Owen Schwartz
17105f3a51 Merge pull request #2681 from fosrl/dev
Extend santize into hybrid
2026-03-20 14:33:23 -07:00
Owen
edcfbd26e4 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-03-20 14:31:27 -07:00
Owen
0c4d9ea164 Extend santize into hybrid 2026-03-20 14:31:12 -07:00
Owen Schwartz
a5a5224f5c Merge pull request #2680 from fosrl/dev
Translation updates
2026-03-20 13:52:11 -07:00
Owen Schwartz
8773f7c0a7 Merge pull request #2679 from fosrl/crowdin_dev
New Crowdin updates
2026-03-20 13:51:16 -07:00
Owen Schwartz
f385bc2d22 Merge pull request #2678 from fosrl/dev
1.16.2-s.14
2026-03-20 11:25:03 -07:00
Owen Schwartz
a8c9d2e7e6 New translations en-us.json (Spanish) 2026-03-20 11:16:17 -07:00
Owen Schwartz
db3f90318b New translations en-us.json (Norwegian Bokmal) 2026-03-20 11:16:15 -07:00
Owen Schwartz
2d4d0df5ca New translations en-us.json (Chinese Simplified) 2026-03-20 11:16:14 -07:00
Owen Schwartz
569ebc671d New translations en-us.json (Turkish) 2026-03-20 11:16:12 -07:00
Owen Schwartz
8c8e4e6233 New translations en-us.json (Russian) 2026-03-20 11:16:11 -07:00
Owen Schwartz
c7901ef74b New translations en-us.json (Portuguese) 2026-03-20 11:16:09 -07:00
Owen Schwartz
be3bd72c1b New translations en-us.json (Polish) 2026-03-20 11:16:08 -07:00
Owen Schwartz
73d1f9288d New translations en-us.json (Dutch) 2026-03-20 11:16:06 -07:00
Owen Schwartz
fb7e9f6898 New translations en-us.json (Korean) 2026-03-20 11:16:05 -07:00
Owen Schwartz
38e4b3077f New translations en-us.json (Italian) 2026-03-20 11:16:03 -07:00
Owen Schwartz
312cdc563b New translations en-us.json (German) 2026-03-20 11:16:02 -07:00
Owen Schwartz
48ff6dd705 New translations en-us.json (Czech) 2026-03-20 11:16:01 -07:00
Owen Schwartz
695e831090 New translations en-us.json (Bulgarian) 2026-03-20 11:15:59 -07:00
Owen Schwartz
046b431bb8 New translations en-us.json (French) 2026-03-20 11:15:58 -07:00
Owen
ce2704fc1a Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-03-20 11:04:45 -07:00
Owen Schwartz
7e89b36188 Merge pull request #2677 from fosrl/crowdin_dev
New Crowdin updates
2026-03-20 11:04:37 -07:00
Owen
222dd6bba3 Santize inserts 2026-03-20 10:27:18 -07:00
Owen Schwartz
ca9ab65228 New translations en-us.json (Spanish) 2026-03-19 21:38:08 -07:00
Owen Schwartz
ee4e8f7029 New translations en-us.json (Norwegian Bokmal) 2026-03-19 21:38:07 -07:00
Owen Schwartz
f86a1eb32b New translations en-us.json (Chinese Simplified) 2026-03-19 21:38:06 -07:00
Owen Schwartz
ffd648ed74 New translations en-us.json (Turkish) 2026-03-19 21:38:05 -07:00
Owen Schwartz
b2b72169fd New translations en-us.json (Russian) 2026-03-19 21:38:03 -07:00
Owen Schwartz
76746fb6e1 New translations en-us.json (Portuguese) 2026-03-19 21:38:02 -07:00
Owen Schwartz
6258787c73 New translations en-us.json (Polish) 2026-03-19 21:38:00 -07:00
Owen Schwartz
720080e487 New translations en-us.json (Dutch) 2026-03-19 21:37:59 -07:00
Owen Schwartz
46ad1317e4 New translations en-us.json (Korean) 2026-03-19 21:37:58 -07:00
Owen Schwartz
cd28720e46 New translations en-us.json (Italian) 2026-03-19 21:37:56 -07:00
Owen Schwartz
38af02ad3c New translations en-us.json (German) 2026-03-19 21:37:55 -07:00
Owen Schwartz
5eed547f91 New translations en-us.json (Czech) 2026-03-19 21:37:54 -07:00
Owen Schwartz
d363ee02ed New translations en-us.json (Bulgarian) 2026-03-19 21:37:53 -07:00
Owen Schwartz
594ee31f43 New translations en-us.json (French) 2026-03-19 21:37:51 -07:00
Owen
56e25d01ae Fix spelling mistake 2026-03-19 20:54:05 -07:00
Fred KISSIE
e0fa5607e5 push 2026-03-20 04:37:57 +01:00
Fred KISSIE
572c9bf319 Merge branch 'dev' into feat/selector-filtering 2026-03-20 04:24:48 +01:00
Fred KISSIE
52cac4aa21 🚚 rename component 2026-03-20 04:17:35 +01:00
Fred KISSIE
e358d12765 ♻️ submit 2026-03-20 04:15:18 +01:00
Fred KISSIE
02697e27a4 ♻️ refactor 2026-03-20 04:02:51 +01:00
Fred KISSIE
ce58e71c44 ♻️ make machine selector a multi-combobox 2026-03-20 03:59:10 +01:00
Owen Schwartz
d9766b0f99 New translations en-us.json (Spanish) 2026-03-19 14:39:09 -07:00
Owen Schwartz
eeaa1d56ad New translations en-us.json (Norwegian Bokmal) 2026-03-19 14:39:07 -07:00
Owen Schwartz
e7f5bc585c New translations en-us.json (Chinese Simplified) 2026-03-19 14:39:06 -07:00
Owen Schwartz
4f26fb7750 New translations en-us.json (Turkish) 2026-03-19 14:39:04 -07:00
Owen Schwartz
cdbc190bfc New translations en-us.json (Russian) 2026-03-19 14:39:03 -07:00
Owen Schwartz
1b1f9ab4cf New translations en-us.json (Portuguese) 2026-03-19 14:39:02 -07:00
Owen Schwartz
2efe6cfdb3 New translations en-us.json (Polish) 2026-03-19 14:39:00 -07:00
Owen Schwartz
517c607ecf New translations en-us.json (Dutch) 2026-03-19 14:38:59 -07:00
Owen Schwartz
802e8f7a22 New translations en-us.json (Korean) 2026-03-19 14:38:57 -07:00
Owen Schwartz
c7cfe2efcb New translations en-us.json (Italian) 2026-03-19 14:38:56 -07:00
Owen Schwartz
ae1f36f39a New translations en-us.json (German) 2026-03-19 14:38:54 -07:00
Owen Schwartz
a479ef28ac New translations en-us.json (Czech) 2026-03-19 14:38:53 -07:00
Owen Schwartz
ce2cf50b5a New translations en-us.json (Bulgarian) 2026-03-19 14:38:52 -07:00
Owen Schwartz
f48d01acde New translations en-us.json (French) 2026-03-19 14:38:50 -07:00
Owen
991fed93ee Add warning when creating resource with provided 2026-03-19 14:26:14 -07:00
Owen
26ab63d0e4 Adjust remote node language 2026-03-19 12:10:58 -07:00
Fred KISSIE
e15703164d ♻️ resource selector in create share link form 2026-03-19 04:44:24 +01:00
Fred KISSIE
8f33e25782 ♻️ use site selector on private resources 2026-03-19 01:18:27 +01:00
Fred KISSIE
722595c131 ♻️ make site selector popover its own component 2026-03-19 00:35:26 +01:00
Owen Schwartz
4843268537 Merge pull request #2552 from huzky-v/feat-add-bandwidth-reset-api
feat: Adding an organization sites bandwidth reset API
2026-03-18 16:17:43 -07:00
Fred KISSIE
c9be84a8a8 Merge branch 'dev' into feat/selector-filtering 2026-03-18 23:38:25 +01:00
Owen Schwartz
03288d2a60 Merge pull request #2667 from LaurenceJJones/feature/newt-ipv6-format-endpoint
fix(newt): Format ipv6 targets for go
2026-03-18 15:34:36 -07:00
Owen Schwartz
f60ae13e4e Merge pull request #2668 from LaurenceJJones/docs/improve-cloud-messaging
chore(readme): Reorder and promote cloud
2026-03-18 15:32:53 -07:00
Owen Schwartz
e72697f8b8 Merge pull request #2669 from fosrl/dependabot/npm_and_yarn/multi-577d045ab6
Bump fast-xml-parser and @aws-sdk/xml-builder
2026-03-18 15:30:46 -07:00
dependabot[bot]
0c3dc1ad14 Bump fast-xml-parser and @aws-sdk/xml-builder
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and [@aws-sdk/xml-builder](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/xml-builder). These dependencies needed to be updated together.

Updates `fast-xml-parser` from 5.4.1 to 5.5.6
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.4.1...v5.5.6)

Updates `@aws-sdk/xml-builder` from 3.972.10 to 3.972.12
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/xml-builder/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/xml-builder)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.5.6
  dependency-type: indirect
- dependency-name: "@aws-sdk/xml-builder"
  dependency-version: 3.972.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 22:30:09 +00:00
Owen Schwartz
840fe86f78 Merge pull request #2633 from fosrl/dependabot/npm_and_yarn/eslint-10.0.3
Bump eslint from 9.39.2 to 10.0.3
2026-03-18 15:28:51 -07:00
Owen Schwartz
e079927a5b Merge pull request #2579 from fosrl/dependabot/github_actions/actions/setup-go-6.3.0
Bump actions/setup-go from 6.2.0 to 6.3.0
2026-03-18 15:28:26 -07:00
dependabot[bot]
63379964fa Bump eslint from 9.39.2 to 10.0.3
Bumps [eslint](https://github.com/eslint/eslint) from 9.39.2 to 10.0.3.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.2...v10.0.3)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 22:28:22 +00:00
Owen Schwartz
0cfaf6ed7f Merge pull request #2580 from fosrl/dependabot/github_actions/actions/upload-artifact-7.0.0
Bump actions/upload-artifact from 6.0.0 to 7.0.0
2026-03-18 15:28:12 -07:00
Owen Schwartz
043ee9e9d2 Merge pull request #2620 from fosrl/dependabot/github_actions/actions/setup-node-6.3.0
Bump actions/setup-node from 6.2.0 to 6.3.0
2026-03-18 15:27:52 -07:00
dependabot[bot]
1d5dfd6db2 Bump github.com/charmbracelet/huh from 0.8.0 to 1.0.0 in /install
Bumps [github.com/charmbracelet/huh](https://github.com/charmbracelet/huh) from 0.8.0 to 1.0.0.
- [Release notes](https://github.com/charmbracelet/huh/releases)
- [Commits](https://github.com/charmbracelet/huh/compare/v0.8.0...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/huh
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 22:27:43 +00:00
Owen Schwartz
b63e3e5888 Merge pull request #2621 from fosrl/dependabot/github_actions/docker/login-action-4.0.0
Bump docker/login-action from 3.7.0 to 4.0.0
2026-03-18 15:27:26 -07:00
Owen Schwartz
4f82470506 Merge pull request #2629 from fosrl/dependabot/npm_and_yarn/prod-minor-updates-47a8475ba0
Bump the prod-minor-updates group across 1 directory with 3 updates
2026-03-18 15:26:14 -07:00
Owen Schwartz
40e21b6f28 Merge pull request #2641 from fosrl/dependabot/go_modules/install/minor-updates-a98db8910e
Bump golang.org/x/term from 0.40.0 to 0.41.0 in /install in the minor-updates group
2026-03-18 15:25:44 -07:00
Owen Schwartz
67fab1928d Merge pull request #2656 from fosrl/dependabot/github_actions/sigstore/cosign-installer-4.1.0
Bump sigstore/cosign-installer from 4.0.0 to 4.1.0
2026-03-18 15:25:31 -07:00
Owen Schwartz
eb98374566 Merge pull request #2666 from fosrl/dependabot/npm_and_yarn/dev-patch-updates-6a5ea32984
Bump the dev-patch-updates group across 1 directory with 5 updates
2026-03-18 15:25:14 -07:00
miloschwartz
1169b68619 fix more info content on member page 2026-03-18 12:18:18 -07:00
Laurence
6c83e78256 chore(readme): Reorder and promote cloud
Simply moving the items around and improve the messaging around the cloud
2026-03-18 16:06:50 +00:00
Laurence
d3bfd67738 fix(newt): Format ipv6 targets for go
We added support https://github.com/fosrl/newt/releases/tag/1.10.3 for ipv6 targets from newt -> application, but we need to ensure that we handle if user provides a none bracketed ipv6 string
2026-03-18 13:26:38 +00:00
dependabot[bot]
0908f0f057 Bump the prod-minor-updates group across 1 directory with 3 updates
Bumps the prod-minor-updates group with 3 updates in the / directory: [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3), [@simplewebauthn/browser](https://github.com/MasterKale/SimpleWebAuthn/tree/HEAD/packages/browser) and [@simplewebauthn/server](https://github.com/MasterKale/SimpleWebAuthn/tree/HEAD/packages/server).


Updates `@aws-sdk/client-s3` from 3.1004.0 to 3.1006.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1006.0/clients/client-s3)

Updates `@simplewebauthn/browser` from 13.2.2 to 13.3.0
- [Release notes](https://github.com/MasterKale/SimpleWebAuthn/releases)
- [Changelog](https://github.com/MasterKale/SimpleWebAuthn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MasterKale/SimpleWebAuthn/commits/v13.3.0/packages/browser)

Updates `@simplewebauthn/server` from 13.2.3 to 13.3.0
- [Release notes](https://github.com/MasterKale/SimpleWebAuthn/releases)
- [Changelog](https://github.com/MasterKale/SimpleWebAuthn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MasterKale/SimpleWebAuthn/commits/v13.3.0/packages/server)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1006.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@simplewebauthn/browser"
  dependency-version: 13.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@simplewebauthn/server"
  dependency-version: 13.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 01:37:21 +00:00
dependabot[bot]
2785449c7a Bump the dev-patch-updates group across 1 directory with 5 updates
Bumps the dev-patch-updates group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@react-email/preview-server](https://github.com/resend/react-email/tree/HEAD/packages/preview-server) | `5.2.8` | `5.2.10` |
| [drizzle-kit](https://github.com/drizzle-team/drizzle-orm) | `0.31.9` | `0.31.10` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `16.1.6` | `16.1.7` |
| [postcss](https://github.com/postcss/postcss) | `8.5.6` | `8.5.8` |
| [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email) | `5.2.8` | `5.2.10` |



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

Updates `drizzle-kit` from 0.31.9 to 0.31.10
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.31.9...drizzle-kit@0.31.10)

Updates `eslint-config-next` from 16.1.6 to 16.1.7
- [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/v16.1.7/packages/eslint-config-next)

Updates `postcss` from 8.5.6 to 8.5.8
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.6...8.5.8)

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

---
updated-dependencies:
- dependency-name: "@react-email/preview-server"
  dependency-version: 5.2.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: drizzle-kit
  dependency-version: 0.31.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: eslint-config-next
  dependency-version: 16.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: postcss
  dependency-version: 8.5.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: react-email
  dependency-version: 5.2.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 01:36:08 +00:00
dependabot[bot]
d2419ba572 Bump golang.org/x/term in /install in the minor-updates group
Bumps the minor-updates group in /install with 1 update: [golang.org/x/term](https://github.com/golang/term).


Updates `golang.org/x/term` from 0.40.0 to 0.41.0
- [Commits](https://github.com/golang/term/compare/v0.40.0...v0.41.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 01:34:08 +00:00
miloschwartz
d44292cf33 pass access token params to badger 2026-03-17 16:57:31 -07:00
Fred KISSIE
435cae06a2 ♻️ refactor 2026-03-17 04:16:24 +01:00
Fred KISSIE
18ed38889f ♻️ filter sites server side in resource target 2026-03-17 04:07:02 +01:00
Owen Schwartz
aed86ce4ba Merge pull request #2663 from fosrl/dev
change route name
2026-03-16 20:03:56 -07:00
miloschwartz
2c2be50b19 change route name 2026-03-16 20:02:57 -07:00
Owen Schwartz
e2db4c6246 Merge pull request #2662 from fosrl/batch-add-client-to-resources
batch add client to resources
2026-03-16 19:53:47 -07:00
miloschwartz
c4839fee08 Merge branch 'dev' into batch-add-client-to-resources 2026-03-16 17:58:37 -07:00
miloschwartz
965b7026f0 add batch endpoint 2026-03-16 17:58:20 -07:00
Owen
e14e15fcbb Revert: Also update lastPing for legacy 2026-03-16 17:47:06 -07:00
Owen Schwartz
4ca5acf158 Merge pull request #2660 from fosrl/dev
Also update lastPing for legacy
2026-03-16 17:13:10 -07:00
Owen
ea41fcc566 Also update lastPing for legacy 2026-03-16 17:12:37 -07:00
Owen Schwartz
5736c1d8ce Merge pull request #2659 from fosrl/dev
Small improvements
2026-03-16 16:37:26 -07:00
Owen
d142366dd9 Merge branch 'main' into dev 2026-03-16 16:32:28 -07:00
Owen
bab09dff95 Add better metadata to ssh 2026-03-16 15:33:21 -07:00
Owen
23d3345ab9 Reduce writes 2026-03-16 14:37:27 -07:00
Owen Schwartz
09a64815d4 Merge pull request #2657 from fosrl/hotfix-jit
Fix jit on by default
2026-03-15 22:02:12 -07:00
Owen
6d5f969798 Fix jit on by default 2026-03-15 22:01:39 -07:00
dependabot[bot]
10349932f4 Bump sigstore/cosign-installer from 4.0.0 to 4.1.0
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](faadad0cce...ba7bc0a3fe)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 01:35:18 +00:00
Owen Schwartz
9c430b37aa Merge pull request #2655 from fosrl/dev
1.16.2-s.8
2026-03-15 16:47:09 -07:00
Shlee
ad3fe2fa76 Update traefik_config.yml 2026-03-15 19:39:36 +10:30
Shlee
863eb8efe9 Update docker-compose.yml 2026-03-15 19:37:15 +10:30
Owen
86bba494fe Disable intervals in saas 2026-03-14 16:03:43 -07:00
Owen
1a43f1ef4b Handle newt online offline with websocket 2026-03-14 11:59:20 -07:00
Owen
75ab074805 Attempt to improve handling bandwidth tracking 2026-03-13 12:06:01 -07:00
Owen
dc4e0253de Add message compression for large messages 2026-03-13 11:46:03 -07:00
Laurence
47a99e35ee feat(installer): add default install directory with existing install detection
- Default to /opt/pangolin for new installations
  - Check current directory and /opt/pangolin for existing installs
  - Prompt to use existing install if found at default location
  - Offer to change directory ownership when running via sudo
  - Create installation directory if it doesn't exist
2026-03-13 11:38:00 +00:00
Owen
cccf236042 Add optional compression 2026-03-12 17:49:21 -07:00
Owen
63fd63c65c Send less data down 2026-03-12 17:27:15 -07:00
Owen
beee1d692d revert: telemetry comment 2026-03-12 17:11:13 -07:00
Owen
fde786ca84 Add todo 2026-03-12 17:10:46 -07:00
Owen
3086fdd064 Merge branch 'dev' into jit 2026-03-12 16:58:23 -07:00
Owen
6c30f6db31 Dont send site if it missing public key 2026-03-12 16:33:33 -07:00
Fred KISSIE
84b082e194 ♻️ show actual values for wireguard site credentials whenever possible 2026-03-12 23:36:35 +01:00
Owen
f021b73458 Add alert about domain error 2026-03-11 18:00:23 -07:00
Owen
74f4751bcc Dont show raw resource option unless remote node 2026-03-11 17:47:15 -07:00
Owen
e5bce4e180 Merge branch 'main' into dev 2026-03-11 15:55:59 -07:00
Owen
9b0e7b381c Fix error to gerbil 2026-03-11 15:49:03 -07:00
Owen
90afe5a7ac Log errors 2026-03-11 15:42:40 -07:00
Owen
b24de85157 Handle gerbil rejecting 0
Closes #2605
2026-03-11 15:06:26 -07:00
Owen
eda43dffe1 Fix not pulling wildcard cert updates 2026-03-11 15:06:26 -07:00
Owen
82c9a1eb70 Add demo link 2026-03-11 15:06:26 -07:00
Owen
a3d4553d14 Merge branch 'main' into dev 2026-03-11 14:53:55 -07:00
Owen
1cc5f59f66 Implement email and ip banning 2026-03-11 11:42:31 -07:00
Owen
4e2d88efdd Add some logging to debug 2026-03-11 11:42:28 -07:00
Owen
4975cabb2c Use native drizzle count 2026-03-11 11:42:28 -07:00
Owen
225591094f Clean up 2026-03-11 11:42:28 -07:00
Owen
82f88f2cd3 Reorder delete 2026-03-11 11:42:28 -07:00
Owen
99e6bd31b6 Bump dompurify 2026-03-10 16:47:03 -07:00
Owen
5c50590d7b Bump esbuild 2026-03-10 16:47:03 -07:00
Owen
072c89e704 Bump dompurify 2026-03-10 16:43:40 -07:00
Owen
dbdff6812d Bump esbuild 2026-03-10 16:31:19 -07:00
dependabot[bot]
42b9d5158d Bump the prod-minor-updates group across 1 directory with 10 updates
Bumps the prod-minor-updates group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.989.0` | `3.1003.0` |
| [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit) | `8.2.1` | `8.3.0` |
| [ioredis](https://github.com/luin/ioredis) | `5.9.3` | `5.10.0` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.563.0` | `0.577.0` |
| [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) | `8.19.0` | `8.20.0` |
| [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) | `5.26.0` | `5.28.0` |
| [react-day-picker](https://github.com/gpbl/react-day-picker) | `9.13.2` | `9.14.0` |
| [react-icons](https://github.com/react-icons/react-icons) | `5.5.0` | `5.6.0` |
| reodotdev | `1.0.0` | `1.1.0` |
| [stripe](https://github.com/stripe/stripe-node) | `20.3.1` | `20.4.0` |



Updates `@aws-sdk/client-s3` from 3.989.0 to 3.1003.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1003.0/clients/client-s3)

Updates `express-rate-limit` from 8.2.1 to 8.3.0
- [Release notes](https://github.com/express-rate-limit/express-rate-limit/releases)
- [Commits](https://github.com/express-rate-limit/express-rate-limit/compare/v8.2.1...v8.3.0)

Updates `ioredis` from 5.9.3 to 5.10.0
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/luin/ioredis/compare/v5.9.3...v5.10.0)

Updates `lucide-react` from 0.563.0 to 0.577.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.577.0/packages/lucide-react)

Updates `pg` from 8.19.0 to 8.20.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.20.0/packages/pg)

Updates `posthog-node` from 5.26.0 to 5.28.0
- [Release notes](https://github.com/PostHog/posthog-js/releases)
- [Changelog](https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js/commits/posthog-node@5.28.0/packages/node)

Updates `react-day-picker` from 9.13.2 to 9.14.0
- [Release notes](https://github.com/gpbl/react-day-picker/releases)
- [Changelog](https://github.com/gpbl/react-day-picker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gpbl/react-day-picker/compare/v9.13.2...v9.14.0)

Updates `react-icons` from 5.5.0 to 5.6.0
- [Release notes](https://github.com/react-icons/react-icons/releases)
- [Commits](https://github.com/react-icons/react-icons/compare/v5.5.0...v5.6.0)

Updates `reodotdev` from 1.0.0 to 1.1.0

Updates `stripe` from 20.3.1 to 20.4.0
- [Release notes](https://github.com/stripe/stripe-node/releases)
- [Changelog](https://github.com/stripe/stripe-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stripe/stripe-node/compare/v20.3.1...v20.4.0)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1003.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: express-rate-limit
  dependency-version: 8.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: ioredis
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: lucide-react
  dependency-version: 0.577.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: pg
  dependency-version: 8.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: posthog-node
  dependency-version: 5.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: react-day-picker
  dependency-version: 9.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: react-icons
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: reodotdev
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: stripe
  dependency-version: 20.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 16:30:13 -07:00
dependabot[bot]
2ba225299e Bump the dev-minor-updates group across 1 directory with 5 updates
Bumps the dev-minor-updates group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@dotenvx/dotenvx](https://github.com/dotenvx/dotenvx) | `1.52.0` | `1.53.0` |
| [@tailwindcss/postcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss) | `4.1.18` | `4.2.1` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.2.3` | `25.3.5` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.1.18` | `4.2.1` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.55.0` | `8.56.1` |



Updates `@dotenvx/dotenvx` from 1.52.0 to 1.53.0
- [Release notes](https://github.com/dotenvx/dotenvx/releases)
- [Changelog](https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dotenvx/dotenvx/compare/v1.52.0...v1.53.0)

Updates `@tailwindcss/postcss` from 4.1.18 to 4.2.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.1/packages/@tailwindcss-postcss)

Updates `@types/node` from 25.2.3 to 25.3.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `tailwindcss` from 4.1.18 to 4.2.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.1/packages/tailwindcss)

Updates `typescript-eslint` from 8.55.0 to 8.56.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.56.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@dotenvx/dotenvx"
  dependency-version: 1.53.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: "@tailwindcss/postcss"
  dependency-version: 4.2.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: "@types/node"
  dependency-version: 25.3.5
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: tailwindcss
  dependency-version: 4.2.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: typescript-eslint
  dependency-version: 8.56.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 16:30:00 -07:00
Owen
cc841d5640 Add some logging to debug 2026-03-10 14:24:57 -07:00
Shreyas Papinwar
fa0818d3fa fix: ensure Credenza dialog max-height 2026-03-10 10:07:36 -07:00
Owen
dec358c4cd Use native drizzle count 2026-03-10 10:03:49 -07:00
Shreyas Papinwar
5455d1c118 fix: add locale to myDevice user query to fix type error 2026-03-10 12:33:05 +05:30
Shreyas Papinwar
ae39084a75 fix: persist user locale preference to database (#1547) 2026-03-10 12:21:06 +05:30
Owen
e98f873f81 Clean up 2026-03-09 21:16:37 -07:00
Owen
e9a2a7e752 Reorder delete 2026-03-09 20:46:27 -07:00
Owen
06015d5191 Handle gerbil rejecting 0
Closes #2605
2026-03-09 17:35:25 -07:00
Owen
af688d2a23 Add demo link 2026-03-09 17:35:04 -07:00
Owen
7d0b3ec6b5 Fix not pulling wildcard cert updates 2026-03-09 17:34:48 -07:00
Owen
cf5fb8dc33 Working on jit 2026-03-09 16:36:13 -07:00
Rodney Osodo
27d20eb1bc refactor(install): improve resource cleanup and remove unused funcs
Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
2026-03-09 11:17:36 +03:00
dependabot[bot]
2e2684c695 Bump docker/login-action from 3.7.0 to 4.0.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.7.0 to 4.0.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](c94ce9fb46...b45d80f862)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 01:36:08 +00:00
dependabot[bot]
7e2fd8f49d Bump actions/setup-node from 6.2.0 to 6.3.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.2.0 to 6.3.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](6044e13b5d...53b83947a5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 01:36:02 +00:00
Owen Schwartz
9a0a255445 Merge pull request #2524 from shreyaspapi/fix/2294-path-based-routing
fix: path-based routing broken due to key collisions in sanitize()
2026-03-07 21:18:59 -08:00
Owen Schwartz
91b7ceb2cf Merge pull request #2603 from Fizza-Mukhtar/fix/prevent-dashboard-domain-conflict-2595
fix: prevent resource from being created with dashboard's domain to avoid redirect loop
2026-03-07 21:15:53 -08:00
Owen Schwartz
d5a37436c0 Merge pull request #2616 from LaurenceJJones/fix/issue-240-hcStatus-missing
fix(newt): missing hcStatus in hc config on reconnect
2026-03-07 21:14:27 -08:00
Laurence
be609b5000 Fix missing hcStatus field in health check config on reconnect
The buildTargetConfigurationForNewtClient function was not including the
  hcStatus field when building health check targets for the newt/wg/connect
  message. This caused custom expected response codes (e.g., 409) to revert
  to the default 2xx range check after Pangolin server restart.

  Added hcStatus to both the database select query and the returned health
  check target object, matching the behavior in targets.ts addTargets.
2026-03-07 06:28:10 +00:00
Owen
0503c6e66e Handle JIT for ssh 2026-03-06 15:49:17 -08:00
Owen Schwartz
d4b830b9bb Merge pull request #2613 from fosrl/dependabot/npm_and_yarn/multi-43b302174d
Bump fast-xml-parser and @aws-sdk/xml-builder
2026-03-06 14:16:27 -08:00
dependabot[bot]
14d6ff25a7 Bump fast-xml-parser and @aws-sdk/xml-builder
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and [@aws-sdk/xml-builder](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/xml-builder). These dependencies needed to be updated together.

Updates `fast-xml-parser` from 5.3.6 to 5.4.1
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.6...v5.4.1)

Updates `@aws-sdk/xml-builder` from 3.972.5 to 3.972.10
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/xml-builder/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/xml-builder)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.4.1
  dependency-type: indirect
- dependency-name: "@aws-sdk/xml-builder"
  dependency-version: 3.972.10
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 22:14:56 +00:00
Owen Schwartz
1f62f305ce Merge pull request #2611 from fosrl/dependabot/npm_and_yarn/express-rate-limit-8.2.2
Bump express-rate-limit from 8.2.1 to 8.2.2
2026-03-06 14:13:32 -08:00
Owen
9405b0b70a Force jit above site limit 2026-03-06 14:09:57 -08:00
Owen
a26ee4ac1a Adjust billing upgrade language 2026-03-06 12:17:26 -08:00
dependabot[bot]
cebcf3e337 Bump express-rate-limit from 8.2.1 to 8.2.2
Bumps [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit) from 8.2.1 to 8.2.2.
- [Release notes](https://github.com/express-rate-limit/express-rate-limit/releases)
- [Commits](https://github.com/express-rate-limit/express-rate-limit/compare/v8.2.1...v8.2.2)

---
updated-dependencies:
- dependency-name: express-rate-limit
  dependency-version: 8.2.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 18:40:41 +00:00
Fizza-Mukhtar
4cfcc64481 fix: use config instead of process.env for dashboard URL check 2026-03-05 01:07:30 -08:00
Fizza-Mukhtar
1a2069a6d9 fix: prevent resource creation with dashboard domain to avoid redirect loop 2026-03-05 00:39:03 -08:00
Owen
2a5c9465e9 Add chainId field passthrough 2026-03-04 22:17:58 -08:00
Owen
f36b66e397 Merge branch 'dev' into jit 2026-03-04 17:58:50 -08:00
Owen
8c6d44677d Update lock 2026-03-04 17:48:58 -08:00
Owen
1bfff630bf Jit working for sites 2026-03-04 17:46:58 -08:00
miloschwartz
ebcef28b05 remove resend from config 2026-03-04 17:45:48 -08:00
miloschwartz
e87e12898c remove resend 2026-03-04 17:45:22 -08:00
miloschwartz
d60ab281cf remove resend from package.json 2026-03-04 17:42:25 -08:00
Owen Schwartz
483d54a9f0 Merge pull request #2598 from fosrl/marketing-consetn
add consent boolean to schema
2026-03-04 15:52:08 -08:00
miloschwartz
0ab6ff9148 add consent boolean to schema 2026-03-04 15:50:42 -08:00
Owen
c73a39f797 Allow JIT based on site or resource 2026-03-04 15:44:27 -08:00
Owen Schwartz
c87b6872e5 Merge pull request #2594 from fosrl/dev
Translations
2026-03-03 21:37:56 -08:00
Owen Schwartz
f315c8bc43 Merge pull request #2569 from fosrl/crowdin_dev
New Crowdin updates
2026-03-03 21:34:23 -08:00
Owen Schwartz
20fa1519fd New translations en-us.json (French) 2026-03-03 21:33:01 -08:00
Owen Schwartz
54430afc40 New translations en-us.json (Norwegian Bokmal) 2026-03-03 21:32:59 -08:00
Owen Schwartz
7990d08fee New translations en-us.json (Chinese Simplified) 2026-03-03 21:32:58 -08:00
Owen Schwartz
e9042d9e2e New translations en-us.json (Turkish) 2026-03-03 21:32:57 -08:00
Owen Schwartz
24a15841e4 New translations en-us.json (Russian) 2026-03-03 21:32:55 -08:00
Owen Schwartz
bb8f6e09fd New translations en-us.json (Portuguese) 2026-03-03 21:32:54 -08:00
Owen Schwartz
04bc8ab694 New translations en-us.json (Polish) 2026-03-03 21:32:52 -08:00
Owen Schwartz
6ac8335cf2 New translations en-us.json (Dutch) 2026-03-03 21:32:51 -08:00
Owen Schwartz
4c6144f8fb New translations en-us.json (Korean) 2026-03-03 21:32:50 -08:00
Owen Schwartz
255003794e New translations en-us.json (Italian) 2026-03-03 21:32:48 -08:00
Owen Schwartz
119d5c79a0 New translations en-us.json (German) 2026-03-03 21:32:47 -08:00
Owen Schwartz
8e2d7c25df New translations en-us.json (Czech) 2026-03-03 21:32:46 -08:00
Owen Schwartz
753dee3023 New translations en-us.json (Bulgarian) 2026-03-03 21:32:44 -08:00
Owen Schwartz
cac0272952 New translations en-us.json (Spanish) 2026-03-03 21:32:43 -08:00
Owen Schwartz
ee5b74f9fc Merge pull request #2593 from fosrl/dev
1.16.2-s.2
2026-03-03 21:17:10 -08:00
Owen
1362b72cd3 Restrict what can be a header 2026-03-03 21:10:52 -08:00
Owen Schwartz
35b1566962 New translations en-us.json (French) 2026-03-03 20:42:42 -08:00
Owen Schwartz
a4bcce5a0c New translations en-us.json (Norwegian Bokmal) 2026-03-03 20:42:40 -08:00
Owen Schwartz
c03f1946e8 New translations en-us.json (Chinese Simplified) 2026-03-03 20:42:39 -08:00
Owen Schwartz
c11e107758 New translations en-us.json (Turkish) 2026-03-03 20:42:37 -08:00
Owen Schwartz
3b4e49f63a New translations en-us.json (Russian) 2026-03-03 20:42:36 -08:00
Owen Schwartz
ea7253f7e8 New translations en-us.json (Portuguese) 2026-03-03 20:42:34 -08:00
Owen Schwartz
8a529f7946 New translations en-us.json (Polish) 2026-03-03 20:42:33 -08:00
Owen Schwartz
e76612e018 New translations en-us.json (Dutch) 2026-03-03 20:42:31 -08:00
Owen Schwartz
e1f99985d8 New translations en-us.json (Korean) 2026-03-03 20:42:30 -08:00
Owen Schwartz
e0c2735635 New translations en-us.json (Italian) 2026-03-03 20:42:28 -08:00
Owen Schwartz
8e6b4e243d New translations en-us.json (German) 2026-03-03 20:42:27 -08:00
Owen Schwartz
2623fa8f02 New translations en-us.json (Czech) 2026-03-03 20:42:25 -08:00
Owen Schwartz
7ff92d32cd New translations en-us.json (Bulgarian) 2026-03-03 20:42:24 -08:00
Owen Schwartz
c7f691b20a New translations en-us.json (Spanish) 2026-03-03 20:42:23 -08:00
Owen
db042e520e Adjust language 2026-03-03 20:34:56 -08:00
miloschwartz
4cab693cfc openapi and swagger ui improvements and cleanup 2026-03-03 14:54:17 -08:00
Owen
c9515ae77c Add comment about not needing exit node 2026-03-03 14:54:17 -08:00
miloschwartz
d14de86f65 fix org selector spacing on mobile 2026-03-03 14:54:17 -08:00
Laurence
f6ee9db730 enhance(sidebar): make mobile org selector sticky
Make org selector sticky on mobile sidebar

  Move OrgSelector outside the scrollable container so it stays fixed
  at the top while menu items scroll, matching the desktop sidebar
  behavior introduced in 9b2c0d0b.
2026-03-03 14:54:17 -08:00
ChanningHe
94353aea44 feat(integration): add domain CRUD endpoints to integration API 2026-03-03 14:54:17 -08:00
Owen
b01fcc70fe Fix ts and add note about ipv4 2026-03-03 14:45:18 -08:00
miloschwartz
ed95f10fcc openapi and swagger ui improvements and cleanup 2026-03-02 21:59:41 -08:00
Owen
35fed74e49 Merge branch 'dev' into msg-opt 2026-03-02 18:52:35 -08:00
Owen
64bae5b142 Merge branch 'main' into dev 2026-03-02 18:52:20 -08:00
Owen
6cf1b9b010 Support improved targets msg v2 2026-03-02 18:51:48 -08:00
Owen
dae169540b Fix defaults for orgs 2026-03-02 16:49:17 -08:00
Owen
19f9dda490 Add comment about not needing exit node 2026-03-02 16:28:01 -08:00
dependabot[bot]
a060c8029f Bump actions/upload-artifact from 6.0.0 to 7.0.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](b7c566a772...bbbca2ddaa)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 01:36:11 +00:00
dependabot[bot]
aca9d1e070 Bump actions/setup-go from 6.2.0 to 6.3.0
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.2.0 to 6.3.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](7a3fe6cf4c...4b73464bb3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 01:36:05 +00:00
Owen Schwartz
cdf79edb00 Merge pull request #2570 from Fizza-Mukhtar/fix/mixed-target-failover-2448
fix: local targets ignored when newt site is unhealthy (mixed target failover)
2026-03-01 15:58:25 -08:00
Owen Schwartz
df53dfc936 New translations en-us.json (French) 2026-03-01 11:17:30 -08:00
Owen Schwartz
8e2e09ab81 New translations en-us.json (Norwegian Bokmal) 2026-03-01 11:17:28 -08:00
Owen Schwartz
1eac7cbccd New translations en-us.json (Chinese Simplified) 2026-03-01 11:17:27 -08:00
Owen Schwartz
ddaaed65e4 New translations en-us.json (Turkish) 2026-03-01 11:17:26 -08:00
Owen Schwartz
8e633c21c7 New translations en-us.json (Russian) 2026-03-01 11:17:24 -08:00
Owen Schwartz
e7c4ef44d8 New translations en-us.json (Portuguese) 2026-03-01 11:17:23 -08:00
Owen Schwartz
3d71470bd2 New translations en-us.json (Polish) 2026-03-01 11:17:21 -08:00
Owen Schwartz
dd627a222e New translations en-us.json (Dutch) 2026-03-01 11:17:20 -08:00
Owen Schwartz
62cc20fa1c New translations en-us.json (Korean) 2026-03-01 11:17:19 -08:00
Owen Schwartz
0450fc9f57 New translations en-us.json (Italian) 2026-03-01 11:17:17 -08:00
Owen Schwartz
c58aaf5ba6 New translations en-us.json (German) 2026-03-01 11:17:16 -08:00
Owen Schwartz
655522d4e2 New translations en-us.json (Czech) 2026-03-01 11:17:15 -08:00
Owen Schwartz
225475dcae New translations en-us.json (Bulgarian) 2026-03-01 11:17:13 -08:00
Owen Schwartz
ccb977fdfb New translations en-us.json (Spanish) 2026-03-01 11:17:12 -08:00
Milo Schwartz
280cbb6e22 Merge pull request #2553 from LaurenceJJones/explore/static-org-dropdown
enhance(sidebar): make mobile org selector sticky
2026-03-01 11:14:16 -08:00
miloschwartz
c20babcb53 fix org selector spacing on mobile 2026-03-01 11:13:49 -08:00
Owen Schwartz
768eebe2cd Merge pull request #2432 from ChanningHe/feat-integration-api-domain-crud
feat(integration): add domain CRUD endpoints to integration API
2026-03-01 11:12:05 -08:00
Owen Schwartz
44e3eedffa Merge pull request #2567 from marcschaeferger/fix-kubernetes-install
feat(kubernetes): enable newtInstances by default and update installation instructions
2026-03-01 10:56:18 -08:00
Marc Schäfer
bb189874cb fix(newt-install): conditionally display Kubernetes installation info
Signed-off-by: Marc Schäfer <git@marcschaeferger.de>
2026-03-01 10:55:58 -08:00
Marc Schäfer
34dadd0e16 feat(kubernetes): enable newtInstances by default and update installation instructions
Signed-off-by: Marc Schäfer <git@marcschaeferger.de>
2026-03-01 10:55:58 -08:00
Owen Schwartz
87b5cd9988 Merge pull request #2573 from Fizza-Mukhtar/fix/container-search-excludes-labels-2228
fix: exclude labels from container search to prevent false positives
2026-03-01 10:52:50 -08:00
Marc Schäfer
6a537a23e8 fix(newt-install): conditionally display Kubernetes installation info
Signed-off-by: Marc Schäfer <git@marcschaeferger.de>
2026-03-01 18:17:45 +01:00
Fizza-Mukhtar
e63a6e9b77 fix: treat local and wireguard sites as online for failover 2026-03-01 07:56:47 -08:00
Fizza-Mukhtar
7ce589c4f2 fix: exclude labels from container search to prevent false positives 2026-03-01 06:50:03 -08:00
Shreyas Papinwar
75a909784a fix: simplify path encoding per review — inline utils, use single key scheme
Address PR review comments:
- Remove pathUtils.ts and move sanitize/encodePath directly into utils.ts
- Simplify dual-key approach to single key using encodePath for map keys
- Remove backward-compat logic (not needed per reviewer)
- Update tests to match simplified approach
2026-03-01 15:48:26 +05:30
Shreyas
244f497a9c test: add comprehensive backward compatibility tests for path routing fix 2026-03-01 15:48:26 +05:30
Shreyas
e58f0c9f07 fix: preserve backward-compatible router names while fixing path collisions
Use encodePath only for internal map key grouping (collision-free) and
sanitize for Traefik-facing router/service names (unchanged for existing
users). Extract pure functions into pathUtils.ts so tests can run without
DB dependencies.
2026-03-01 15:48:26 +05:30
Shreyas
5f18c06e03 fix: use collision-free path encoding for Traefik router key generation 2026-03-01 15:48:26 +05:30
Fizza-Mukhtar
f36cf06e26 fix: fallback to local targets when newt targets are unhealthy 2026-03-01 01:43:15 -08:00
Owen Schwartz
27d52646a0 New translations en-us.json (Norwegian Bokmal) 2026-02-28 20:13:31 -08:00
Owen Schwartz
4dd8080c55 New translations en-us.json (Chinese Simplified) 2026-02-28 20:13:29 -08:00
Owen Schwartz
0b35d4f2e3 New translations en-us.json (Turkish) 2026-02-28 20:13:28 -08:00
Owen Schwartz
54a9fb9e54 New translations en-us.json (Russian) 2026-02-28 20:13:27 -08:00
Owen Schwartz
60a9e68f02 New translations en-us.json (Portuguese) 2026-02-28 20:13:25 -08:00
Owen Schwartz
ad374298e3 New translations en-us.json (Polish) 2026-02-28 20:13:24 -08:00
Owen Schwartz
c5dc4e6127 New translations en-us.json (Dutch) 2026-02-28 20:13:22 -08:00
Owen Schwartz
291ad831c5 New translations en-us.json (Korean) 2026-02-28 20:13:21 -08:00
Owen Schwartz
0a018f0ca8 New translations en-us.json (Italian) 2026-02-28 20:13:20 -08:00
Owen Schwartz
6673eeb1bb New translations en-us.json (German) 2026-02-28 20:13:18 -08:00
Owen Schwartz
4641f0b9ef New translations en-us.json (Czech) 2026-02-28 20:13:17 -08:00
Owen Schwartz
a4487964e5 New translations en-us.json (Bulgarian) 2026-02-28 20:13:15 -08:00
Owen Schwartz
fe42fdd1ec New translations en-us.json (Spanish) 2026-02-28 20:13:14 -08:00
Marc Schäfer
375211f184 feat(kubernetes): enable newtInstances by default and update installation instructions
Signed-off-by: Marc Schäfer <git@marcschaeferger.de>
2026-02-28 23:56:28 +01:00
Owen
66c377a5c9 Merge branch 'main' into dev 2026-02-28 12:14:41 -08:00
Owen
50c2aa0111 Add default memory limits 2026-02-28 12:14:27 -08:00
Owen
fdeb891137 Fix pagination effecting drop downs 2026-02-28 12:07:42 -08:00
Owen Schwartz
6a6e3a43b1 Merge pull request #2562 from LaurenceJJones/fix/zod-openapi-catch-error
fix(zod): Add openapi call after catch
2026-02-28 11:04:10 -08:00
Laurence
b0a34fa21b fix(openapi): Add openapi call after catch
fix: #2561
without making an explicit call to openapi a runtime error happens because it cannot infer the type, the call to openapi is the same across the codebase
2026-02-28 11:27:19 +00:00
Jacky Fong
5c4de03588 add reset bandwidth api for site
Change endpoint

update to reset all site in the organization

move the logic to organization

move the permission to organization
2026-02-28 15:47:03 +08:00
Owen
72bf6f3c41 Comma seperated 2026-02-27 17:53:44 -08:00
miloschwartz
ad9289e0c1 sort by name by default 2026-02-27 15:53:27 -08:00
Owen Schwartz
b0cb0e5a99 Merge pull request #2559 from fosrl/dev
1.16.1
2026-02-27 12:40:23 -08:00
miloschwartz
8347203bbe add sort to name col 2026-02-27 12:39:26 -08:00
miloschwartz
4aa1186aed fix machine client pagination 2026-02-27 11:59:55 -08:00
Owen
eed87af61d Use ecr base to build 2026-02-26 21:43:14 -08:00
Owen
daeea8e7ea Add alises to quieries
Fixes #2556
2026-02-26 21:37:47 -08:00
Owen
0d63a15715 Merge branch 'main' into dev 2026-02-26 20:14:41 -08:00
miloschwartz
fa2e229ada support authPath in device login 2026-02-26 14:59:34 -08:00
Laurence
81c1a1da9c enhance(sidebar): make mobile org selector sticky
Make org selector sticky on mobile sidebar

  Move OrgSelector outside the scrollable container so it stays fixed
  at the top while menu items scroll, matching the desktop sidebar
  behavior introduced in 9b2c0d0b.
2026-02-26 15:45:41 +00:00
Owen
5d9700d84c Update sum 2026-02-25 16:46:20 -08:00
Owen
f8a8cdaa5f Show enterprise tier 2026-02-25 16:45:35 -08:00
Owen
e23e446476 Fix rule violations of dynamic import 2026-02-25 16:35:57 -08:00
Owen
fa097df50b Dont maxmind on oss or enterprise 2026-02-25 16:26:33 -08:00
Owen
75f34ff127 Stub cache 2026-02-25 16:17:06 -08:00
Owen
c9586b4d93 Remove postgres logs from private config file 2026-02-25 16:09:26 -08:00
Owen
52937a6d90 Make sshCA GA 2026-02-25 16:04:47 -08:00
Owen Schwartz
186c131cce Merge pull request #2543 from fosrl/dev
1.16.0-rc.0
2026-02-25 15:51:42 -08:00
Owen Schwartz
8de3f9a440 Merge pull request #2542 from fosrl/crowdin_dev
New Crowdin updates
2026-02-25 15:49:34 -08:00
Owen Schwartz
ea49e179f9 New translations en-us.json (Norwegian Bokmal) 2026-02-25 15:48:16 -08:00
Owen Schwartz
485f4f1c8e New translations en-us.json (Chinese Simplified) 2026-02-25 15:48:15 -08:00
Owen Schwartz
5fb35d12d7 New translations en-us.json (Turkish) 2026-02-25 15:48:13 -08:00
Owen Schwartz
ec8a9fe3d2 New translations en-us.json (Russian) 2026-02-25 15:48:12 -08:00
Owen Schwartz
411a34e15e New translations en-us.json (Portuguese) 2026-02-25 15:48:11 -08:00
Owen Schwartz
3df71fd2bc New translations en-us.json (Polish) 2026-02-25 15:48:09 -08:00
Owen Schwartz
5e1f6085e3 New translations en-us.json (Dutch) 2026-02-25 15:48:08 -08:00
Owen Schwartz
53fc7ab6e3 New translations en-us.json (Korean) 2026-02-25 15:48:07 -08:00
Owen Schwartz
7779ed24fe New translations en-us.json (Italian) 2026-02-25 15:48:05 -08:00
Owen Schwartz
6e4193dae3 New translations en-us.json (German) 2026-02-25 15:48:04 -08:00
Owen Schwartz
f138609f48 New translations en-us.json (Czech) 2026-02-25 15:48:02 -08:00
Owen Schwartz
98154b5de3 New translations en-us.json (Bulgarian) 2026-02-25 15:48:01 -08:00
Owen Schwartz
6322fd9eef New translations en-us.json (Spanish) 2026-02-25 15:47:59 -08:00
Owen Schwartz
1c0949e957 New translations en-us.json (French) 2026-02-25 15:47:58 -08:00
ChanningHe
52f26396ac feat(integration): add domain CRUD endpoints to integration API 2026-02-26 08:44:55 +09:00
Owen
c3847e6001 Prefix usernames 2026-02-25 15:36:22 -08:00
Owen
5cf13a963d Add missing saving username 2026-02-25 15:30:34 -08:00
miloschwartz
b017877826 hide ssh access tab for cidr resources 2026-02-25 14:49:28 -08:00
Owen
959f68b520 Restrict cidr resource 2026-02-25 14:43:47 -08:00
Owen
14cab3fdb8 Update phrase 2026-02-25 14:37:52 -08:00
Owen
b8d468f6de Bump version 2026-02-25 14:22:24 -08:00
Owen
fc66394243 Merge branch 'main' into dev 2026-02-25 14:18:22 -08:00
miloschwartz
8fca243c9a update tierMatrix 2026-02-25 11:59:16 -08:00
miloschwartz
388f710379 add pg migration 2026-02-25 11:37:31 -08:00
Owen Schwartz
ba3ab4362b Merge pull request #2539 from fosrl/dependabot/npm_and_yarn/prod-minor-updates-22e7e52815
Bump the prod-minor-updates group across 1 directory with 3 updates
2026-02-25 11:32:19 -08:00
miloschwartz
e18c9afc2d add sqlite migration 2026-02-25 11:24:32 -08:00
Owen Schwartz
a9b4a86c4a Merge pull request #2470 from fosrl/dependabot/npm_and_yarn/qs-6.14.2
Bump qs from 6.14.1 to 6.14.2
2026-02-25 11:05:46 -08:00
Owen Schwartz
200ea502dd Merge pull request #2484 from fosrl/dependabot/go_modules/install/minor-updates-80eb8af454
Bump golang.org/x/term from 0.39.0 to 0.40.0 in /install in the minor-updates group
2026-02-25 11:05:34 -08:00
dependabot[bot]
de36db97eb Bump the prod-minor-updates group across 1 directory with 3 updates
Bumps the prod-minor-updates group with 3 updates in the / directory: [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg), [posthog-node](https://github.com/PostHog/posthog-js/tree/HEAD/packages/node) and [tailwind-merge](https://github.com/dcastil/tailwind-merge).


Updates `pg` from 8.18.0 to 8.19.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.19.0/packages/pg)

Updates `posthog-node` from 5.24.15 to 5.26.0
- [Release notes](https://github.com/PostHog/posthog-js/releases)
- [Changelog](https://github.com/PostHog/posthog-js/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js/commits/posthog-node@5.26.0/packages/node)

Updates `tailwind-merge` from 3.4.0 to 3.5.0
- [Release notes](https://github.com/dcastil/tailwind-merge/releases)
- [Commits](https://github.com/dcastil/tailwind-merge/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: pg
  dependency-version: 8.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: posthog-node
  dependency-version: 5.26.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: tailwind-merge
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-25 19:03:51 +00:00
dependabot[bot]
30283b044f Bump golang.org/x/term in /install in the minor-updates group
Bumps the minor-updates group in /install with 1 update: [golang.org/x/term](https://github.com/golang/term).


Updates `golang.org/x/term` from 0.39.0 to 0.40.0
- [Commits](https://github.com/golang/term/compare/v0.39.0...v0.40.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-25 19:01:31 +00:00
Owen Schwartz
055bed8a07 Merge pull request #2338 from fosrl/dependabot/github_actions/actions/checkout-6.0.2
Bump actions/checkout from 6.0.1 to 6.0.2
2026-02-25 11:00:55 -08:00
Owen Schwartz
12b5c2ab34 Merge pull request #2495 from fosrl/dependabot/npm_and_yarn/dev-patch-updates-ebb414287f
Bump @types/nodemailer from 7.0.9 to 7.0.10 in the dev-patch-updates group across 1 directory
2026-02-25 11:00:24 -08:00
Owen Schwartz
dd78674888 Merge pull request #2514 from fosrl/dependabot/npm_and_yarn/multi-60f2582fdd
Bump fast-xml-parser and @aws-sdk/xml-builder
2026-02-25 11:00:15 -08:00
Owen Schwartz
0d0df63847 Merge pull request #2518 from fosrl/dependabot/github_actions/actions/stale-10.2.0
Bump actions/stale from 10.1.1 to 10.2.0
2026-02-25 11:00:01 -08:00
Owen Schwartz
3ab00d9da8 Merge pull request #2527 from fosrl/dependabot/npm_and_yarn/prod-patch-updates-cbac17d765
Bump the prod-patch-updates group across 1 directory with 7 updates
2026-02-25 10:59:38 -08:00
Owen Schwartz
3e6e72c5c7 Merge pull request #2531 from fosrl/dependabot/npm_and_yarn/multi-40a89e2d0a
Bump minimatch
2026-02-25 10:59:08 -08:00
Owen Schwartz
5d8a55f08c Merge pull request #2415 from fosrl/dependabot/github_actions/aws-actions/configure-aws-credentials-6
Bump aws-actions/configure-aws-credentials from 5 to 6
2026-02-25 10:58:18 -08:00
Owen Schwartz
81c569aae4 Merge pull request #2393 from fosrl/dependabot/github_actions/docker/login-action-3.7.0
Bump docker/login-action from 3.6.0 to 3.7.0
2026-02-25 10:58:11 -08:00
Owen Schwartz
88fd3fc4da Merge pull request #2296 from fosrl/dependabot/npm_and_yarn/lodash-4.17.23
Bump lodash from 4.17.21 to 4.17.23
2026-02-25 10:58:02 -08:00
Owen
2282d3ae39 Fix formatting 2026-02-25 10:53:56 -08:00
Owen
c4dcec463a Merge branch 'dev' into LaurenceJJones-feature/installer-tui 2026-02-25 10:48:05 -08:00
Owen
5b7f893ad7 Merge branch 'main' into dev 2026-02-25 10:46:28 -08:00
Owen
2ede0d498a remove log 2026-02-25 10:46:20 -08:00
Owen
f518e8a0ff Merge branch 'feature/installer-tui' of github.com:LaurenceJJones/pangolin into LaurenceJJones-feature/installer-tui 2026-02-25 10:45:38 -08:00
Owen Schwartz
767284408a Merge pull request #2499 from LaurenceJJones/feature/build-variables
enhance(installer): use ldflags to inject versions
2026-02-25 10:42:04 -08:00
Owen Schwartz
eef51f3b84 Merge pull request #2491 from rodneyosodo/fix/install
fix(install): add error handling, code cleanups, and YAML type refactor
2026-02-25 10:41:03 -08:00
Owen Schwartz
69b7114a49 Merge pull request #2537 from Abhinav-kodes/fix/toggle-hydration-sync
fix: sync resource toggle states with context on initial load
2026-02-25 10:36:58 -08:00
Owen Schwartz
0ea38ea568 Merge pull request #2535 from Abhinav-kodes/fix-resource-session-delete-cookie
fix: correct session DELETE tautology and HTTP cookie domain interpolation
2026-02-25 10:35:09 -08:00
Abhinav-kodes
c600da71e3 fix: sync resource toggle states with context on initial load
- Replace defaultChecked with checked for controlled components
- Add useEffect to sync rulesEnabled, ssoEnabled, whitelistEnabled
  when resource context hydrates after mount
- Add nullish coalescing fallback to prevent undefined initial state
2026-02-25 22:07:08 +05:30
Abhinav-kodes
c64dd14b1a fix: correct session DELETE tautology and HTTP cookie domain interpolation 2026-02-25 17:24:27 +05:30
miloschwartz
8ea6d9fa67 add get user by username search endpoint to integration api 2026-02-24 22:04:15 -08:00
Owen
978ac8f53c Add logging 2026-02-24 20:51:27 -08:00
Owen
49a326cde7 Add trust proxy to the internal api
Fix access logs not having the right ip
2026-02-24 20:23:42 -08:00
Owen
63e208f4ec Use local cache in verify session 2026-02-24 19:56:16 -08:00
Owen
f50d1549b0 Update cache to use redis 2026-02-24 19:50:42 -08:00
Owen
55e24df671 Check and prefer user token if provided 2026-02-24 19:48:32 -08:00
Owen
b37e1d0cc0 Use debian slim; alpine broken? 2026-02-24 19:48:16 -08:00
Owen
afa26c0dd4 Exclude migrations? 2026-02-24 19:48:08 -08:00
miloschwartz
c71f46ede5 move copy button and fix translation 2026-02-24 19:44:08 -08:00
miloschwartz
20e547a0f6 first pass 2026-02-24 17:58:11 -08:00
dependabot[bot]
2edebaddc2 Bump the prod-patch-updates group across 1 directory with 7 updates
Bumps the prod-patch-updates group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@asteasolutions/zod-to-openapi](https://github.com/asteasolutions/zod-to-openapi) | `8.4.0` | `8.4.1` |
| [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components) | `1.0.7` | `1.0.8` |
| [@react-email/tailwind](https://github.com/resend/react-email/tree/HEAD/packages/tailwind) | `2.0.4` | `2.0.5` |
| [@simplewebauthn/server](https://github.com/MasterKale/SimpleWebAuthn/tree/HEAD/packages/server) | `13.2.2` | `13.2.3` |
| [glob](https://github.com/isaacs/node-glob) | `13.0.3` | `13.0.6` |
| [next-intl](https://github.com/amannn/next-intl) | `4.8.2` | `4.8.3` |
| [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.71.1` | `7.71.2` |



Updates `@asteasolutions/zod-to-openapi` from 8.4.0 to 8.4.1
- [Release notes](https://github.com/asteasolutions/zod-to-openapi/releases)
- [Commits](https://github.com/asteasolutions/zod-to-openapi/compare/v8.4.0...v8.4.1)

Updates `@react-email/components` from 1.0.7 to 1.0.8
- [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@1.0.8/packages/components)

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

Updates `@simplewebauthn/server` from 13.2.2 to 13.2.3
- [Release notes](https://github.com/MasterKale/SimpleWebAuthn/releases)
- [Changelog](https://github.com/MasterKale/SimpleWebAuthn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MasterKale/SimpleWebAuthn/commits/v13.2.3/packages/server)

Updates `glob` from 13.0.3 to 13.0.6
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v13.0.3...v13.0.6)

Updates `next-intl` from 4.8.2 to 4.8.3
- [Release notes](https://github.com/amannn/next-intl/releases)
- [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amannn/next-intl/compare/v4.8.2...v4.8.3)

Updates `react-hook-form` from 7.71.1 to 7.71.2
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.71.1...v7.71.2)

---
updated-dependencies:
- dependency-name: "@asteasolutions/zod-to-openapi"
  dependency-version: 8.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@react-email/components"
  dependency-version: 1.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@react-email/tailwind"
  dependency-version: 2.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@simplewebauthn/server"
  dependency-version: 13.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: glob
  dependency-version: 13.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: next-intl
  dependency-version: 4.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: react-hook-form
  dependency-version: 7.71.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-25 01:37:31 +00:00
dependabot[bot]
119e1d4867 Bump @types/nodemailer in the dev-patch-updates group across 1 directory
Bumps the dev-patch-updates group with 1 update in the / directory: [@types/nodemailer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/nodemailer).


Updates `@types/nodemailer` from 7.0.9 to 7.0.10
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/nodemailer)

---
updated-dependencies:
- dependency-name: "@types/nodemailer"
  dependency-version: 7.0.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-25 01:35:31 +00:00
dependabot[bot]
63e30d3378 Bump minimatch
Bumps  and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together.

Updates `minimatch` from 10.2.0 to 10.2.3
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.0...v10.2.3)

Updates `minimatch` from 3.1.2 to 3.1.4
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.0...v10.2.3)

Updates `minimatch` from 9.0.5 to 9.0.7
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.0...v10.2.3)

Updates `minimatch` from 10.1.1 to 10.2.3
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.0...v10.2.3)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 10.2.3
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 3.1.4
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 9.0.7
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 10.2.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-25 01:04:10 +00:00
Owen
d6fe04ec4e Fix orgid issue when regen credentials 2026-02-24 14:26:10 -08:00
Owen
b8a364af6a Fix log query 2026-02-23 22:01:11 -08:00
Owen
5ef808d4a2 Merge branch 'main' into logs-database 2026-02-23 16:39:39 -08:00
miloschwartz
848d4d91e6 fix sidebar 2026-02-23 13:40:08 -08:00
Owen
a502780c9b Fix sso username issue 2026-02-22 22:05:02 -08:00
Owen Schwartz
418e099804 Merge pull request #2521 from fosrl/dev
1.15.4-s.6
2026-02-22 21:13:51 -08:00
Owen Schwartz
06258aa386 Merge pull request #2513 from fosrl/crowdin_dev
New Crowdin updates
2026-02-22 21:13:17 -08:00
Owen Schwartz
d7608b1cc8 New translations en-us.json (Norwegian Bokmal) 2026-02-22 21:11:37 -08:00
Owen Schwartz
cb86ad4104 New translations en-us.json (Chinese Simplified) 2026-02-22 21:11:35 -08:00
Owen Schwartz
8cd51df1e1 New translations en-us.json (Turkish) 2026-02-22 21:11:34 -08:00
Owen Schwartz
8ef7220766 New translations en-us.json (Russian) 2026-02-22 21:11:33 -08:00
Owen Schwartz
b5333a3686 New translations en-us.json (Portuguese) 2026-02-22 21:11:31 -08:00
Owen Schwartz
e6e92dbc0f New translations en-us.json (Polish) 2026-02-22 21:11:30 -08:00
Owen Schwartz
01fdd41a10 New translations en-us.json (Dutch) 2026-02-22 21:11:28 -08:00
Owen Schwartz
6af06a38ae New translations en-us.json (Korean) 2026-02-22 21:11:27 -08:00
Owen Schwartz
5d9c66d22d New translations en-us.json (Italian) 2026-02-22 21:11:26 -08:00
Owen Schwartz
81f5a4b127 New translations en-us.json (German) 2026-02-22 21:11:24 -08:00
Owen Schwartz
da3e68a20b New translations en-us.json (Czech) 2026-02-22 21:11:23 -08:00
Owen Schwartz
8712c1719e New translations en-us.json (Bulgarian) 2026-02-22 21:11:22 -08:00
Owen Schwartz
593c5db0e8 New translations en-us.json (Spanish) 2026-02-22 21:11:20 -08:00
Owen Schwartz
b28391feae New translations en-us.json (French) 2026-02-22 21:11:19 -08:00
Owen
5f8df6d4cd Merge branch 'main' into dev 2026-02-22 21:02:58 -08:00
Owen
c36efe7f14 Add translations 2026-02-22 21:02:21 -08:00
Owen
cf97b6df9c Handle billing bad subs, remove exit node name from lock, prevent some
stuff on saas
2026-02-22 20:45:53 -08:00
dependabot[bot]
720d3a8135 Bump actions/stale from 10.1.1 to 10.2.0
Bumps [actions/stale](https://github.com/actions/stale) from 10.1.1 to 10.2.0.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](997185467f...b5d41d4e1d)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: 10.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 01:36:30 +00:00
dependabot[bot]
9c42458fa5 Bump actions/checkout from 6.0.1 to 6.0.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](8e8c483db8...de0fac2e45)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 01:31:48 +00:00
Owen
6d9b129ac9 Merge branch 'main' into k8s 2026-02-22 17:28:09 -08:00
miloschwartz
e17ec798d4 remove local fonts 2026-02-22 11:18:57 -08:00
miloschwartz
58ac499f30 add safeRead 2026-02-21 16:38:51 -08:00
miloschwartz
f07f0092ad testing with local font 2026-02-21 14:34:38 -08:00
dependabot[bot]
bcd3475d17 Bump fast-xml-parser and @aws-sdk/xml-builder
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and [@aws-sdk/xml-builder](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/xml-builder). These dependencies needed to be updated together.

Updates `fast-xml-parser` from 5.3.4 to 5.3.6
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.6)

Updates `@aws-sdk/xml-builder` from 3.972.4 to 3.972.5
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/xml-builder/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/xml-builder)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.3.6
  dependency-type: indirect
- dependency-name: "@aws-sdk/xml-builder"
  dependency-version: 3.972.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-21 20:18:36 +00:00
Owen Schwartz
7c04526088 New translations en-us.json (Czech) 2026-02-21 05:24:27 -08:00
Owen Schwartz
2d7ab68576 New translations en-us.json (Czech) 2026-02-21 04:14:18 -08:00
miloschwartz
218a4893b6 hide address on sites and clients 2026-02-20 22:47:56 -08:00
miloschwartz
266bf261aa update note in migration 2026-02-20 22:45:37 -08:00
Owen Schwartz
63694032e8 Merge pull request #2511 from fosrl/dev
1.15.4-s.4
2026-02-20 20:45:23 -08:00
Owen Schwartz
b77aaedb58 Merge pull request #2510 from fosrl/crowdin_dev
New Crowdin updates
2026-02-20 20:44:45 -08:00
Owen Schwartz
a316d0301f New translations en-us.json (Norwegian Bokmal) 2026-02-20 20:43:45 -08:00
Owen Schwartz
dcd499720e New translations en-us.json (Chinese Simplified) 2026-02-20 20:43:44 -08:00
Owen Schwartz
e18fe21eca New translations en-us.json (Turkish) 2026-02-20 20:43:43 -08:00
Owen Schwartz
2970b51fb8 New translations en-us.json (Russian) 2026-02-20 20:43:42 -08:00
Owen Schwartz
b9236ff52e New translations en-us.json (Portuguese) 2026-02-20 20:43:41 -08:00
Owen Schwartz
38eb0ec7ed New translations en-us.json (Polish) 2026-02-20 20:43:39 -08:00
Owen Schwartz
ecba4a0b80 New translations en-us.json (Dutch) 2026-02-20 20:43:38 -08:00
Owen Schwartz
e6da18c952 New translations en-us.json (Korean) 2026-02-20 20:43:37 -08:00
Owen Schwartz
12941ac5ae New translations en-us.json (Italian) 2026-02-20 20:43:36 -08:00
Owen Schwartz
11085bda63 New translations en-us.json (German) 2026-02-20 20:43:35 -08:00
Owen Schwartz
c03211cc53 New translations en-us.json (Czech) 2026-02-20 20:43:33 -08:00
Owen Schwartz
2867459600 New translations en-us.json (Bulgarian) 2026-02-20 20:43:32 -08:00
Owen Schwartz
32b24db9bf New translations en-us.json (Spanish) 2026-02-20 20:43:31 -08:00
Owen Schwartz
660bf9ff87 New translations en-us.json (French) 2026-02-20 20:43:30 -08:00
miloschwartz
78c4ddebba set defaults 2026-02-20 20:35:57 -08:00
miloschwartz
f2dfadb37b show disabled button for owner 2026-02-20 20:28:47 -08:00
miloschwartz
3f2bdf081f allow edit admin role ssh and support null authDaemonMode 2026-02-20 20:25:14 -08:00
miloschwartz
d6ba34aeea set auth daemon type on resource 2026-02-20 17:33:21 -08:00
Owen
b622aca221 Try to route logs requests to a different database 2026-02-20 17:20:01 -08:00
miloschwartz
6442eb12fb more visual adjustments 2026-02-20 10:43:25 -08:00
miloschwartz
01c15afa74 other visual adjustments 2026-02-19 23:41:04 -08:00
miloschwartz
4e88f1f38a more sidebar improvements 2026-02-19 22:41:14 -08:00
miloschwartz
13ab505f4d add ease to sidebar menu 2026-02-19 21:59:49 -08:00
miloschwartz
7d112aab27 improve alignment on sidebar 2026-02-19 21:52:47 -08:00
Owen
b786497299 Working on k8s 2026-02-19 17:55:49 -08:00
miloschwartz
eedf57af89 disable rybbit in saas 2026-02-19 17:54:40 -08:00
miloschwartz
7a01a4e090 ssh settings on a role 2026-02-19 17:53:11 -08:00
Owen
874794c996 Clean email 2026-02-18 14:07:50 -08:00
Owen
5e37c4e85f Resolve potential issues with processing roleIds 2026-02-18 13:55:04 -08:00
Owen
4e7eac368f Uniform ne check on niceId and dont reject clients 2026-02-18 11:56:01 -08:00
Laurence
e8398cb221 enhance(installer): use huh package to handle input
Instead of relying on stdin and stdout by default, using the huh package from charmbracelet allows us to handle user input more gracefully such as y/n instead of typing 'yes' or 'no'. If a user makes a mistake whilst typing in any text fields they cannot use left or right to edit a single character when using huh it can. This adds a dependancy and may increase the size of installer but overall improves user experience.
2026-02-18 11:05:48 +00:00
Laurence
9460e28c7b ehance(installer): use ldflags to inject versions
Instead of the CI/CD using sed to replace the 'replaceme' text we can instead use ldflags which can inject variables at build time to the versions. The makefile had a bunch of workarounds for dev so these have been removed to cleanup etc etc and fetchs versions from the gh api directly if the variables are not injected like the CI/CD does
2026-02-18 09:43:41 +00:00
dependabot[bot]
d8b45396e3 Bump qs from 6.14.1 to 6.14.2
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 05:08:23 +00:00
Rodney Osodo
952d0c74d0 refactor(install): use any for YAML map types instead of interface{}
Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
2026-02-17 10:45:06 +03:00
Rodney Osodo
ffbea7af59 fix(install): add error handling and minor code cleanups
Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
2026-02-17 10:45:06 +03:00
dependabot[bot]
971c375398 Bump docker/login-action from 3.6.0 to 3.7.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](5e57cd1181...c94ce9fb46)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 19:12:16 +00:00
dependabot[bot]
ac4439c5ae Bump aws-actions/configure-aws-credentials from 5 to 6
Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 5 to 6.
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/v5...v6)

---
updated-dependencies:
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 19:12:05 +00:00
dependabot[bot]
8c15855fc3 Bump lodash from 4.17.21 to 4.17.23
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-26 01:37:38 +00:00
Dennis
3d4df906cf Added missing translations 2025-12-22 18:43:43 +01:00
Dennis
e051142334 Add region-based resource rule 2025-12-22 17:44:56 +01:00
453 changed files with 32020 additions and 11994 deletions

View File

@@ -28,9 +28,9 @@ LICENSE
CONTRIBUTING.md
dist
.git
migrations/
server/migrations/
config/
build.ts
tsconfig.json
Dockerfile*
migrations/
drizzle.config.ts

View File

@@ -29,7 +29,7 @@ jobs:
permissions: write-all
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
role-duration-seconds: 3600
@@ -62,7 +62,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Monitor storage space
run: |
@@ -77,7 +77,7 @@ jobs:
fi
- name: Log in to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: docker.io
username: ${{ secrets.DOCKER_HUB_USERNAME }}
@@ -134,7 +134,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Monitor storage space
run: |
@@ -149,7 +149,7 @@ jobs:
fi
- name: Log in to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: docker.io
username: ${{ secrets.DOCKER_HUB_USERNAME }}
@@ -201,10 +201,10 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Log in to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: docker.io
username: ${{ secrets.DOCKER_HUB_USERNAME }}
@@ -256,7 +256,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Extract tag name
id: get-tag
@@ -264,7 +264,7 @@ jobs:
shell: bash
- name: Install Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: 1.24
@@ -289,25 +289,17 @@ jobs:
echo "LATEST_BADGER_TAG=$LATEST_TAG" >> $GITHUB_ENV
shell: bash
- name: Update install/main.go
run: |
PANGOLIN_VERSION=${{ env.TAG }}
GERBIL_VERSION=${{ env.LATEST_GERBIL_TAG }}
BADGER_VERSION=${{ env.LATEST_BADGER_TAG }}
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"$PANGOLIN_VERSION\"/" install/main.go
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"$GERBIL_VERSION\"/" install/main.go
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"$BADGER_VERSION\"/" install/main.go
echo "Updated install/main.go with Pangolin version $PANGOLIN_VERSION, Gerbil version $GERBIL_VERSION, and Badger version $BADGER_VERSION"
cat install/main.go
shell: bash
- name: Build installer
working-directory: install
run: |
make go-build-release
make go-build-release \
PANGOLIN_VERSION=${{ env.TAG }} \
GERBIL_VERSION=${{ env.LATEST_GERBIL_TAG }} \
BADGER_VERSION=${{ env.LATEST_BADGER_TAG }}
shell: bash
- name: Upload artifacts from /install/bin
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: install-bin
path: install/bin/
@@ -415,7 +407,7 @@ jobs:
shell: bash
- name: Login to GitHub Container Registry (for cosign)
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -423,7 +415,7 @@ jobs:
- name: Install cosign
# cosign is used to sign and verify container images (key and keyless)
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
- name: Dual-sign and verify (GHCR & Docker Hub)
# Sign each image by digest using keyless (OIDC) and key-based signing,
@@ -578,7 +570,7 @@ jobs:
permissions: write-all
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
role-duration-seconds: 3600

View File

@@ -21,10 +21,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '24'

View File

@@ -23,7 +23,7 @@ jobs:
skopeo --version
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
- name: Input check
run: |

View File

@@ -14,7 +14,7 @@ jobs:
permissions: write-all
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
role-duration-seconds: 3600

View File

@@ -23,7 +23,7 @@ jobs:
permissions: write-all
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
role-duration-seconds: 3600
@@ -54,7 +54,42 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download MaxMind GeoLite2 databases
env:
MAXMIND_LICENSE_KEY: ${{ secrets.MAXMIND_LICENSE_KEY }}
run: |
echo "Downloading MaxMind GeoLite2 databases..."
# Download GeoLite2-Country
curl -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" \
-o GeoLite2-Country.tar.gz
# Download GeoLite2-ASN
curl -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" \
-o GeoLite2-ASN.tar.gz
# Extract the .mmdb files
tar -xzf GeoLite2-Country.tar.gz --strip-components=1 --wildcards '*.mmdb'
tar -xzf GeoLite2-ASN.tar.gz --strip-components=1 --wildcards '*.mmdb'
# Verify files exist
if [ ! -f "GeoLite2-Country.mmdb" ]; then
echo "ERROR: Failed to download GeoLite2-Country.mmdb"
exit 1
fi
if [ ! -f "GeoLite2-ASN.mmdb" ]; then
echo "ERROR: Failed to download GeoLite2-ASN.mmdb"
exit 1
fi
# Clean up tar files
rm -f GeoLite2-Country.tar.gz GeoLite2-ASN.tar.gz
echo "MaxMind databases downloaded successfully"
ls -lh GeoLite2-*.mmdb
- name: Monitor storage space
run: |
@@ -69,7 +104,7 @@ jobs:
fi
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::${{ secrets.aws_account_id }}:role/${{ secrets.AWS_ROLE_NAME }}
role-duration-seconds: 3600
@@ -110,7 +145,7 @@ jobs:
permissions: write-all
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
role-duration-seconds: 3600

View File

@@ -14,7 +14,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 14
days-before-close: 14

View File

@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Node
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '24'
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Build Docker image sqlite
run: make dev-build-sqlite
@@ -71,7 +71,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Build Docker image pg
run: make dev-build-pg

View File

@@ -1,8 +1,9 @@
FROM node:24-alpine AS base
# FROM node:24-slim AS base
FROM public.ecr.aws/docker/library/node:24-slim AS base
WORKDIR /app
RUN apk add --no-cache python3 make g++
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
COPY package*.json ./
@@ -23,15 +24,20 @@ RUN if [ "$BUILD" = "oss" ]; then rm -rf server/private; fi && \
npm run build:cli && \
test -f dist/server.mjs
# Create placeholder files for MaxMind databases to avoid COPY errors
# Real files should be present for saas builds, placeholders for oss builds
RUN touch /app/GeoLite2-Country.mmdb /app/GeoLite2-ASN.mmdb
FROM base AS builder
RUN npm ci --omit=dev
FROM node:24-alpine AS runner
# FROM node:24-slim AS runner
FROM public.ecr.aws/docker/library/node:24-slim AS runner
WORKDIR /app
RUN apk add --no-cache curl tzdata
RUN apt-get update && apt-get install -y curl tzdata && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
@@ -49,6 +55,18 @@ COPY server/db/ios_models.json ./dist/ios_models.json
COPY server/db/mac_models.json ./dist/mac_models.json
COPY public ./public
# Copy MaxMind databases for SaaS builds
ARG BUILD=oss
RUN mkdir -p ./maxmind
# Copy MaxMind databases (placeholders exist for oss builds, real files for saas)
COPY --from=builder-dev /app/GeoLite2-Country.mmdb ./maxmind/GeoLite2-Country.mmdb
COPY --from=builder-dev /app/GeoLite2-ASN.mmdb ./maxmind/GeoLite2-ASN.mmdb
# Remove MaxMind databases for non-saas builds (keep only for saas)
RUN if [ "$BUILD" != "saas" ]; then rm -rf ./maxmind; fi
# OCI Image Labels - Build Args for dynamic values
ARG VERSION="dev"
ARG REVISION=""

View File

@@ -43,7 +43,7 @@
<p align="center">
<strong>
Start testing Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
Get started with Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
</strong>
</p>
@@ -60,9 +60,9 @@ Pangolin is an open-source, identity-based remote access platform built on WireG
| <img width=500 /> | Description |
|-----------------|--------------|
| **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/understanding-nodes) and connect to our control plane. |
| **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.pangolin.net/manage/remote-node/nodes) and connect to our control plane. |
## Key Features
@@ -85,17 +85,16 @@ Download the Pangolin client for your platform:
## Get Started
### Sign up now
Create an account at [app.pangolin.net](https://app.pangolin.net) to get started with Pangolin Cloud. A generous free tier is available.
### Check out the docs
We encourage everyone to read the full documentation first, which is
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
[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://pangolin.net/fcl.html). For inquiries about commercial licensing, please contact us at [contact@pangolin.net](mailto:contact@pangolin.net).

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@pangolin.net](mailto:security@pangolin.net) 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) with the following information:
- Description and location of the vulnerability.
- Potential impact of the vulnerability.

View File

@@ -0,0 +1,121 @@
import { CommandModule } from "yargs";
import { db, orgs } from "@server/db";
import { eq } from "drizzle-orm";
import { encrypt } from "@server/lib/crypto";
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
import { generateCA } from "@server/lib/sshCA";
import fs from "fs";
import yaml from "js-yaml";
type GenerateOrgCaKeysArgs = {
orgId: string;
secret?: string;
force?: boolean;
};
export const generateOrgCaKeys: CommandModule<{}, GenerateOrgCaKeysArgs> = {
command: "generate-org-ca-keys",
describe:
"Generate SSH CA public/private key pair for an organization and store them in the database (private key encrypted with server secret)",
builder: (yargs) => {
return yargs
.option("orgId", {
type: "string",
demandOption: true,
describe: "The organization ID"
})
.option("secret", {
type: "string",
describe:
"Server secret used to encrypt the CA private key. If omitted, read from config file (config.yml or config.yaml)."
})
.option("force", {
type: "boolean",
default: false,
describe:
"Overwrite existing CA keys for the org if they already exist"
});
},
handler: async (argv: {
orgId: string;
secret?: string;
force?: boolean;
}) => {
try {
const { orgId, force } = argv;
let secret = argv.secret;
if (!secret) {
const configPath = fs.existsSync(configFilePath1)
? configFilePath1
: fs.existsSync(configFilePath2)
? configFilePath2
: null;
if (!configPath) {
console.error(
"Error: No server secret provided and config file not found. " +
"Expected config.yml or config.yaml in the config directory, or pass --secret."
);
process.exit(1);
}
const configContent = fs.readFileSync(configPath, "utf8");
const config = yaml.load(configContent) as {
server?: { secret?: string };
};
if (!config?.server?.secret) {
console.error(
"Error: No server.secret in config file. Pass --secret or set server.secret in config."
);
process.exit(1);
}
secret = config.server.secret;
}
const [org] = await db
.select({
orgId: orgs.orgId,
sshCaPrivateKey: orgs.sshCaPrivateKey,
sshCaPublicKey: orgs.sshCaPublicKey
})
.from(orgs)
.where(eq(orgs.orgId, orgId))
.limit(1);
if (!org) {
console.error(`Error: Organization with orgId "${orgId}" not found.`);
process.exit(1);
}
if (org.sshCaPrivateKey != null || org.sshCaPublicKey != null) {
if (!force) {
console.error(
"Error: This organization already has CA keys. Use --force to overwrite."
);
process.exit(1);
}
}
const ca = generateCA(`pangolin-ssh-ca-${orgId}`);
const encryptedPrivateKey = encrypt(ca.privateKeyPem, secret);
await db
.update(orgs)
.set({
sshCaPrivateKey: encryptedPrivateKey,
sshCaPublicKey: ca.publicKeyOpenSSH
})
.where(eq(orgs.orgId, orgId));
console.log("SSH CA keys generated and stored for org:", orgId);
console.log("\nPublic key (OpenSSH format):");
console.log(ca.publicKeyOpenSSH);
process.exit(0);
} catch (error) {
console.error("Error generating org CA keys:", error);
process.exit(1);
}
}
};

View File

@@ -8,6 +8,7 @@ import { clearExitNodes } from "./commands/clearExitNodes";
import { rotateServerSecret } from "./commands/rotateServerSecret";
import { clearLicenseKeys } from "./commands/clearLicenseKeys";
import { deleteClient } from "./commands/deleteClient";
import { generateOrgCaKeys } from "./commands/generateOrgCaKeys";
yargs(hideBin(process.argv))
.scriptName("pangctl")
@@ -17,5 +18,6 @@ yargs(hideBin(process.argv))
.command(rotateServerSecret)
.command(clearLicenseKeys)
.command(deleteClient)
.command(generateOrgCaKeys)
.demandCommand()
.help().argv;

View File

@@ -4,6 +4,12 @@ services:
image: fosrl/pangolin:latest
container_name: pangolin
restart: unless-stopped
deploy:
resources:
limits:
memory: 1g
reservations:
memory: 256m
volumes:
- ./config:/app/config
healthcheck:

View File

@@ -1,41 +1,24 @@
all: update-versions go-build-release put-back
dev-all: dev-update-versions dev-build dev-clean
all: go-build-release
# Build with version injection via ldflags
# Versions can be passed via: make go-build-release PANGOLIN_VERSION=x.x.x GERBIL_VERSION=x.x.x BADGER_VERSION=x.x.x
# Or fetched automatically if not provided (requires curl and jq)
PANGOLIN_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/pangolin/tags | jq -r '.[0].name')
GERBIL_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name')
BADGER_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name')
LDFLAGS = -X main.pangolinVersion=$(PANGOLIN_VERSION) \
-X main.gerbilVersion=$(GERBIL_VERSION) \
-X main.badgerVersion=$(BADGER_VERSION)
go-build-release:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/installer_linux_amd64
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/installer_linux_arm64
@echo "Building with versions - Pangolin: $(PANGOLIN_VERSION), Gerbil: $(GERBIL_VERSION), Badger: $(BADGER_VERSION)"
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/installer_linux_amd64
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/installer_linux_arm64
clean:
rm -f bin/installer_linux_amd64
rm -f bin/installer_linux_arm64
update-versions:
@echo "Fetching latest versions..."
cp main.go main.go.bak && \
$(MAKE) dev-update-versions
put-back:
mv main.go.bak main.go
dev-update-versions:
if [ -z "$(tag)" ]; then \
PANGOLIN_VERSION=$$(curl -s https://api.github.com/repos/fosrl/pangolin/tags | jq -r '.[0].name'); \
else \
PANGOLIN_VERSION=$(tag); \
fi && \
GERBIL_VERSION=$$(curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name') && \
BADGER_VERSION=$$(curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name') && \
echo "Latest versions - Pangolin: $$PANGOLIN_VERSION, Gerbil: $$GERBIL_VERSION, Badger: $$BADGER_VERSION" && \
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"$$PANGOLIN_VERSION\"/" main.go && \
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"$$GERBIL_VERSION\"/" main.go && \
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"$$BADGER_VERSION\"/" main.go && \
echo "Updated main.go with latest versions"
dev-build: go-build-release
dev-clean:
@echo "Restoring version values ..."
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"replaceme\"/" main.go && \
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"replaceme\"/" main.go && \
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"replaceme\"/" main.go
@echo "Restored version strings in main.go"
.PHONY: all go-build-release clean

View File

@@ -99,11 +99,6 @@ func ReadAppConfig(configPath string) (*AppConfigValues, error) {
return values, nil
}
// findPattern finds the start of a pattern in a string
func findPattern(s, pattern string) int {
return bytes.Index([]byte(s), []byte(pattern))
}
func copyDockerService(sourceFile, destFile, serviceName string) error {
// Read source file
sourceData, err := os.ReadFile(sourceFile)
@@ -118,19 +113,19 @@ func copyDockerService(sourceFile, destFile, serviceName string) error {
}
// Parse source Docker Compose YAML
var sourceCompose map[string]interface{}
var sourceCompose map[string]any
if err := yaml.Unmarshal(sourceData, &sourceCompose); err != nil {
return fmt.Errorf("error parsing source Docker Compose file: %w", err)
}
// Parse destination Docker Compose YAML
var destCompose map[string]interface{}
var destCompose map[string]any
if err := yaml.Unmarshal(destData, &destCompose); err != nil {
return fmt.Errorf("error parsing destination Docker Compose file: %w", err)
}
// Get services section from source
sourceServices, ok := sourceCompose["services"].(map[string]interface{})
sourceServices, ok := sourceCompose["services"].(map[string]any)
if !ok {
return fmt.Errorf("services section not found in source file or has invalid format")
}
@@ -142,10 +137,10 @@ func copyDockerService(sourceFile, destFile, serviceName string) error {
}
// Get or create services section in destination
destServices, ok := destCompose["services"].(map[string]interface{})
destServices, ok := destCompose["services"].(map[string]any)
if !ok {
// If services section doesn't exist, create it
destServices = make(map[string]interface{})
destServices = make(map[string]any)
destCompose["services"] = destServices
}
@@ -187,17 +182,21 @@ func backupConfig() error {
return nil
}
func MarshalYAMLWithIndent(data interface{}, indent int) ([]byte, error) {
func MarshalYAMLWithIndent(data any, indent int) (resp []byte, err error) {
buffer := new(bytes.Buffer)
encoder := yaml.NewEncoder(buffer)
encoder.SetIndent(indent)
err := encoder.Encode(data)
if err != nil {
if err := encoder.Encode(data); err != nil {
return nil, err
}
defer encoder.Close()
defer func() {
if cerr := encoder.Close(); cerr != nil && err == nil {
err = cerr
}
}()
return buffer.Bytes(), nil
}
@@ -209,7 +208,7 @@ func replaceInFile(filepath, oldStr, newStr string) error {
}
// Replace the string
newContent := strings.Replace(string(content), oldStr, newStr, -1)
newContent := strings.ReplaceAll(string(content), oldStr, newStr)
// Write the modified content back to the file
err = os.WriteFile(filepath, []byte(newContent), 0644)
@@ -228,28 +227,28 @@ func CheckAndAddTraefikLogVolume(composePath string) error {
}
// Parse YAML into a generic map
var compose map[string]interface{}
var compose map[string]any
if err := yaml.Unmarshal(data, &compose); err != nil {
return fmt.Errorf("error parsing compose file: %w", err)
}
// Get services section
services, ok := compose["services"].(map[string]interface{})
services, ok := compose["services"].(map[string]any)
if !ok {
return fmt.Errorf("services section not found or invalid")
}
// Get traefik service
traefik, ok := services["traefik"].(map[string]interface{})
traefik, ok := services["traefik"].(map[string]any)
if !ok {
return fmt.Errorf("traefik service not found or invalid")
}
// Check volumes
logVolume := "./config/traefik/logs:/var/log/traefik"
var volumes []interface{}
var volumes []any
if existingVolumes, ok := traefik["volumes"].([]interface{}); ok {
if existingVolumes, ok := traefik["volumes"].([]any); ok {
// Check if volume already exists
for _, v := range existingVolumes {
if v.(string) == logVolume {
@@ -295,13 +294,13 @@ func MergeYAML(baseFile, overlayFile string) error {
}
// Parse base YAML into a map
var baseMap map[string]interface{}
var baseMap map[string]any
if err := yaml.Unmarshal(baseContent, &baseMap); err != nil {
return fmt.Errorf("error parsing base YAML: %v", err)
}
// Parse overlay YAML into a map
var overlayMap map[string]interface{}
var overlayMap map[string]any
if err := yaml.Unmarshal(overlayContent, &overlayMap); err != nil {
return fmt.Errorf("error parsing overlay YAML: %v", err)
}
@@ -324,8 +323,8 @@ func MergeYAML(baseFile, overlayFile string) error {
}
// mergeMap recursively merges two maps
func mergeMap(base, overlay map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
func mergeMap(base, overlay map[string]any) map[string]any {
result := make(map[string]any)
// Copy all key-values from base map
for k, v := range base {
@@ -336,8 +335,8 @@ func mergeMap(base, overlay map[string]interface{}) map[string]interface{} {
for k, v := range overlay {
// If both maps have the same key and both values are maps, merge recursively
if baseVal, ok := base[k]; ok {
if baseMap, isBaseMap := baseVal.(map[string]interface{}); isBaseMap {
if overlayMap, isOverlayMap := v.(map[string]interface{}); isOverlayMap {
if baseMap, isBaseMap := baseVal.(map[string]any); isBaseMap {
if overlayMap, isOverlayMap := v.(map[string]any); isOverlayMap {
result[k] = mergeMap(baseMap, overlayMap)
continue
}

View File

@@ -81,11 +81,17 @@ entryPoints:
transport:
respondingTimeouts:
readTimeout: "30m"
http3:
advertisedPort: 443
http:
tls:
certResolver: "letsencrypt"
middlewares:
- crowdsec@file
encodedCharacters:
allowEncodedSlash: true
allowEncodedQuestionMark: true
serversTransport:
insecureSkipVerify: true
insecureSkipVerify: true
ping:
entryPoint: "web"

View File

@@ -4,6 +4,12 @@ services:
image: docker.io/fosrl/pangolin:{{if .IsEnterprise}}ee-{{end}}{{.PangolinVersion}}
container_name: pangolin
restart: unless-stopped
deploy:
resources:
limits:
memory: 1g
reservations:
memory: 256m
volumes:
- ./config:/app/config
healthcheck:
@@ -32,15 +38,14 @@ services:
- 51820:51820/udp
- 21820:21820/udp
- 443:443
- 443:443/udp # For http3 QUIC if desired
- 80:80
{{end}}
traefik:
image: docker.io/traefik:v3.6
container_name: traefik
restart: unless-stopped
{{if .InstallGerbil}}
network_mode: service:gerbil # Ports appear on the gerbil service
{{end}}{{if not .InstallGerbil}}
{{if .InstallGerbil}} network_mode: service:gerbil # Ports appear on the gerbil service{{end}}{{if not .InstallGerbil}}
ports:
- 443:443
- 80:80

View File

@@ -40,6 +40,8 @@ entryPoints:
transport:
respondingTimeouts:
readTimeout: "30m"
http3:
advertisedPort: 443
http:
tls:
certResolver: "letsencrypt"

View File

@@ -144,12 +144,13 @@ func installDocker() error {
}
func startDockerService() error {
if runtime.GOOS == "linux" {
switch runtime.GOOS {
case "linux":
cmd := exec.Command("systemctl", "enable", "--now", "docker")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
} else if runtime.GOOS == "darwin" {
case "darwin":
// On macOS, Docker is usually started via the Docker Desktop application
fmt.Println("Please start Docker Desktop manually on macOS.")
return nil
@@ -302,7 +303,7 @@ func pullContainers(containerType SupportedContainer) error {
return nil
}
return fmt.Errorf("Unsupported container type: %s", containerType)
return fmt.Errorf("unsupported container type: %s", containerType)
}
// startContainers starts the containers using the appropriate command.
@@ -325,7 +326,7 @@ func startContainers(containerType SupportedContainer) error {
return nil
}
return fmt.Errorf("Unsupported container type: %s", containerType)
return fmt.Errorf("unsupported container type: %s", containerType)
}
// stopContainers stops the containers using the appropriate command.
@@ -347,7 +348,7 @@ func stopContainers(containerType SupportedContainer) error {
return nil
}
return fmt.Errorf("Unsupported container type: %s", containerType)
return fmt.Errorf("unsupported container type: %s", containerType)
}
// restartContainer restarts a specific container using the appropriate command.
@@ -369,5 +370,5 @@ func restartContainer(container string, containerType SupportedContainer) error
return nil
}
return fmt.Errorf("Unsupported container type: %s", containerType)
return fmt.Errorf("unsupported container type: %s", containerType)
}

View File

@@ -27,9 +27,18 @@ func installCrowdsec(config Config) error {
os.Exit(1)
}
os.MkdirAll("config/crowdsec/db", 0755)
os.MkdirAll("config/crowdsec/acquis.d", 0755)
os.MkdirAll("config/traefik/logs", 0755)
if err := os.MkdirAll("config/crowdsec/db", 0755); err != nil {
fmt.Printf("Error creating config files: %v\n", err)
os.Exit(1)
}
if err := os.MkdirAll("config/crowdsec/acquis.d", 0755); err != nil {
fmt.Printf("Error creating config files: %v\n", err)
os.Exit(1)
}
if err := os.MkdirAll("config/traefik/logs", 0755); err != nil {
fmt.Printf("Error creating config files: %v\n", err)
os.Exit(1)
}
if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil {
fmt.Printf("Error copying docker service: %v\n", err)
@@ -153,34 +162,34 @@ func CheckAndAddCrowdsecDependency(composePath string) error {
}
// Parse YAML into a generic map
var compose map[string]interface{}
var compose map[string]any
if err := yaml.Unmarshal(data, &compose); err != nil {
return fmt.Errorf("error parsing compose file: %w", err)
}
// Get services section
services, ok := compose["services"].(map[string]interface{})
services, ok := compose["services"].(map[string]any)
if !ok {
return fmt.Errorf("services section not found or invalid")
}
// Get traefik service
traefik, ok := services["traefik"].(map[string]interface{})
traefik, ok := services["traefik"].(map[string]any)
if !ok {
return fmt.Errorf("traefik service not found or invalid")
}
// Get dependencies
dependsOn, ok := traefik["depends_on"].(map[string]interface{})
dependsOn, ok := traefik["depends_on"].(map[string]any)
if ok {
// Append the new block for crowdsec
dependsOn["crowdsec"] = map[string]interface{}{
dependsOn["crowdsec"] = map[string]any{
"condition": "service_healthy",
}
} else {
// No dependencies exist, create it
traefik["depends_on"] = map[string]interface{}{
"crowdsec": map[string]interface{}{
traefik["depends_on"] = map[string]any{
"crowdsec": map[string]any{
"condition": "service_healthy",
},
}

View File

@@ -1,10 +1,38 @@
module installer
go 1.24.0
go 1.25.0
require (
golang.org/x/term v0.39.0
github.com/charmbracelet/huh v1.0.0
github.com/charmbracelet/lipgloss v1.1.0
golang.org/x/term v0.41.0
gopkg.in/yaml.v3 v3.0.1
)
require golang.org/x/sys v0.40.0 // indirect
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/catppuccin/go v0.3.0 // indirect
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect
github.com/charmbracelet/bubbletea v1.3.6 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.9.3 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.23.0 // indirect
)

View File

@@ -1,7 +1,80 @@
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws=
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw=
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/huh v1.0.0 h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw=
github.com/charmbracelet/huh v1.0.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=
github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -1,92 +1,208 @@
package main
import (
"bufio"
"errors"
"fmt"
"strings"
"syscall"
"os"
"strconv"
"github.com/charmbracelet/huh"
"golang.org/x/term"
)
func readString(reader *bufio.Reader, prompt string, defaultValue string) string {
// pangolinTheme is the custom theme using brand colors
var pangolinTheme = ThemePangolin()
// isAccessibleMode checks if we should use accessible mode (simple prompts)
// This is true for: non-TTY, TERM=dumb, or ACCESSIBLE env var set
func isAccessibleMode() bool {
// Check if stdin is not a terminal (piped input, CI, etc.)
if !term.IsTerminal(int(os.Stdin.Fd())) {
return true
}
// Check for dumb terminal
if os.Getenv("TERM") == "dumb" {
return true
}
// Check for explicit accessible mode request
if os.Getenv("ACCESSIBLE") != "" {
return true
}
return false
}
// handleAbort checks if the error is a user abort (Ctrl+C) and exits if so
func handleAbort(err error) {
if err != nil && errors.Is(err, huh.ErrUserAborted) {
fmt.Println("\nInstallation cancelled.")
os.Exit(0)
}
}
// runField runs a single field with the Pangolin theme, handling accessible mode
func runField(field huh.Field) error {
if isAccessibleMode() {
return field.RunAccessible(os.Stdout, os.Stdin)
}
form := huh.NewForm(huh.NewGroup(field)).WithTheme(pangolinTheme)
return form.Run()
}
func readString(prompt string, defaultValue string) string {
var value string
title := prompt
if defaultValue != "" {
fmt.Printf("%s (default: %s): ", prompt, defaultValue)
} else {
fmt.Print(prompt + ": ")
title = fmt.Sprintf("%s (default: %s)", prompt, defaultValue)
}
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "" {
return defaultValue
}
return input
}
func readStringNoDefault(reader *bufio.Reader, prompt string) string {
fmt.Print(prompt + ": ")
input, _ := reader.ReadString('\n')
return strings.TrimSpace(input)
}
input := huh.NewInput().
Title(title).
Value(&value)
func readPassword(prompt string, reader *bufio.Reader) string {
if term.IsTerminal(int(syscall.Stdin)) {
fmt.Print(prompt + ": ")
// Read password without echo if we're in a terminal
password, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println() // Add a newline since ReadPassword doesn't add one
if err != nil {
return ""
}
input := strings.TrimSpace(string(password))
if input == "" {
return readPassword(prompt, reader)
}
return input
} else {
// Fallback to reading from stdin if not in a terminal
return readString(reader, prompt, "")
// If no default value, this field is required
if defaultValue == "" {
input = input.Validate(func(s string) error {
if s == "" {
return fmt.Errorf("this field is required")
}
return nil
})
}
}
func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool {
defaultStr := "no"
if defaultValue {
defaultStr = "yes"
}
for {
input := readString(reader, prompt+" (yes/no)", defaultStr)
lower := strings.ToLower(input)
if lower == "yes" {
return true
} else if lower == "no" {
return false
} else {
fmt.Println("Please enter 'yes' or 'no'.")
}
}
}
err := runField(input)
handleAbort(err)
func readBoolNoDefault(reader *bufio.Reader, prompt string) bool {
for {
input := readStringNoDefault(reader, prompt+" (yes/no)")
lower := strings.ToLower(input)
if lower == "yes" {
return true
} else if lower == "no" {
return false
} else {
fmt.Println("Please enter 'yes' or 'no'.")
}
if value == "" {
value = defaultValue
}
}
func readInt(reader *bufio.Reader, prompt string, defaultValue int) int {
input := readString(reader, prompt, fmt.Sprintf("%d", defaultValue))
if input == "" {
return defaultValue
// Print the answer so it remains visible in terminal history (skip in accessible mode as it already shows)
if !isAccessibleMode() {
fmt.Printf("%s: %s\n", prompt, value)
}
value := defaultValue
fmt.Sscanf(input, "%d", &value)
return value
}
func readPassword(prompt string) string {
var value string
for {
input := huh.NewInput().
Title(prompt).
Value(&value).
EchoMode(huh.EchoModePassword).
Validate(func(s string) error {
if s == "" {
return fmt.Errorf("password is required")
}
return nil
})
err := runField(input)
handleAbort(err)
if value != "" {
// Print confirmation without revealing the password
if !isAccessibleMode() {
fmt.Printf("%s: %s\n", prompt, "********")
}
return value
}
}
}
func readBool(prompt string, defaultValue bool) bool {
var value = defaultValue
confirm := huh.NewConfirm().
Title(prompt).
Value(&value).
Affirmative("Yes").
Negative("No")
err := runField(confirm)
handleAbort(err)
// Print the answer so it remains visible in terminal history
if !isAccessibleMode() {
answer := "No"
if value {
answer = "Yes"
}
fmt.Printf("%s: %s\n", prompt, answer)
}
return value
}
func readBoolNoDefault(prompt string) bool {
var value bool
confirm := huh.NewConfirm().
Title(prompt).
Value(&value).
Affirmative("Yes").
Negative("No")
err := runField(confirm)
handleAbort(err)
// Print the answer so it remains visible in terminal history
if !isAccessibleMode() {
answer := "No"
if value {
answer = "Yes"
}
fmt.Printf("%s: %s\n", prompt, answer)
}
return value
}
func readInt(prompt string, defaultValue int) int {
var value string
title := fmt.Sprintf("%s (default: %d)", prompt, defaultValue)
input := huh.NewInput().
Title(title).
Value(&value).
Validate(func(s string) error {
if s == "" {
return nil
}
_, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("please enter a valid number")
}
return nil
})
err := runField(input)
handleAbort(err)
if value == "" {
// Print the answer so it remains visible in terminal history
if !isAccessibleMode() {
fmt.Printf("%s: %d\n", prompt, defaultValue)
}
return defaultValue
}
result, err := strconv.Atoi(value)
if err != nil {
if !isAccessibleMode() {
fmt.Printf("%s: %d\n", prompt, defaultValue)
}
return defaultValue
}
// Print the answer so it remains visible in terminal history
if !isAccessibleMode() {
fmt.Printf("%s: %d\n", prompt, result)
}
return result
}

View File

@@ -1,30 +1,35 @@
package main
import (
"bufio"
"crypto/rand"
"embed"
"encoding/base64"
"fmt"
"io"
"io/fs"
"crypto/rand"
"encoding/base64"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"text/template"
"time"
)
// DO NOT EDIT THIS FUNCTION; IT MATCHED BY REGEX IN CICD
// Version variables injected at build time via -ldflags
var (
pangolinVersion string
gerbilVersion string
badgerVersion string
)
func loadVersions(config *Config) {
config.PangolinVersion = "replaceme"
config.GerbilVersion = "replaceme"
config.BadgerVersion = "replaceme"
config.PangolinVersion = pangolinVersion
config.GerbilVersion = gerbilVersion
config.BadgerVersion = badgerVersion
}
//go:embed config/*
@@ -82,14 +87,19 @@ func main() {
}
}
reader := bufio.NewReader(os.Stdin)
var config Config
var alreadyInstalled = false
// Determine installation directory
installDir := findOrSelectInstallDirectory()
if err := os.Chdir(installDir); err != nil {
fmt.Printf("Error changing to installation directory: %v\n", err)
os.Exit(1)
}
// check if there is already a config file
if _, err := os.Stat("config/config.yml"); err != nil {
config = collectUserInput(reader)
config = collectUserInput()
loadVersions(&config)
config.DoCrowdsecInstall = false
@@ -102,7 +112,10 @@ func main() {
os.Exit(1)
}
moveFile("config/docker-compose.yml", "docker-compose.yml")
if err := moveFile("config/docker-compose.yml", "docker-compose.yml"); err != nil {
fmt.Printf("Error moving docker-compose.yml: %v\n", err)
os.Exit(1)
}
fmt.Println("\nConfiguration files created successfully!")
@@ -117,13 +130,17 @@ func main() {
fmt.Println("\n=== Starting installation ===")
if readBool(reader, "Would you like to install and start the containers?", true) {
if readBool("Would you like to install and start the containers?", true) {
config.InstallationContainerType = podmanOrDocker(reader)
config.InstallationContainerType = podmanOrDocker()
if !isDockerInstalled() && runtime.GOOS == "linux" && config.InstallationContainerType == Docker {
if readBool(reader, "Docker is not installed. Would you like to install it?", true) {
installDocker()
if readBool("Docker is not installed. Would you like to install it?", true) {
if err := installDocker(); err != nil {
fmt.Printf("Error installing Docker: %v\n", err)
return
}
// try to start docker service but ignore errors
if err := startDockerService(); err != nil {
fmt.Println("Error starting Docker service:", err)
@@ -132,7 +149,7 @@ func main() {
}
// wait 10 seconds for docker to start checking if docker is running every 2 seconds
fmt.Println("Waiting for Docker to start...")
for i := 0; i < 5; i++ {
for range 5 {
if isDockerRunning() {
fmt.Println("Docker is running!")
break
@@ -167,7 +184,7 @@ func main() {
fmt.Println("\n=== MaxMind Database Update ===")
if _, err := os.Stat("config/GeoLite2-Country.mmdb"); err == nil {
fmt.Println("MaxMind GeoLite2 Country database found.")
if readBool(reader, "Would you like to update the MaxMind database to the latest version?", false) {
if readBool("Would you like to update the MaxMind database to the latest version?", false) {
if err := downloadMaxMindDatabase(); err != nil {
fmt.Printf("Error updating MaxMind database: %v\n", err)
fmt.Println("You can try updating it manually later if needed.")
@@ -175,7 +192,7 @@ func main() {
}
} else {
fmt.Println("MaxMind GeoLite2 Country database not found.")
if readBool(reader, "Would you like to download the MaxMind GeoLite2 database for geoblocking functionality?", false) {
if readBool("Would you like to download the MaxMind GeoLite2 database for geoblocking functionality?", false) {
if err := downloadMaxMindDatabase(); err != nil {
fmt.Printf("Error downloading MaxMind database: %v\n", err)
fmt.Println("You can try downloading it manually later if needed.")
@@ -192,11 +209,11 @@ func main() {
if !checkIsCrowdsecInstalledInCompose() {
fmt.Println("\n=== CrowdSec Install ===")
// check if crowdsec is installed
if readBool(reader, "Would you like to install CrowdSec?", false) {
if readBool("Would you like to install CrowdSec?", false) {
fmt.Println("This installer constitutes a minimal viable CrowdSec deployment. CrowdSec will add extra complexity to your Pangolin installation and may not work to the best of its abilities out of the box. Users are expected to implement configuration adjustments on their own to achieve the best security posture. Consult the CrowdSec documentation for detailed configuration instructions.")
// BUG: crowdsec installation will be skipped if the user chooses to install on the first installation.
if readBool(reader, "Are you willing to manage CrowdSec?", false) {
if readBool("Are you willing to manage CrowdSec?", false) {
if config.DashboardDomain == "" {
traefikConfig, err := ReadTraefikConfig("config/traefik/traefik_config.yml")
if err != nil {
@@ -225,8 +242,8 @@ func main() {
fmt.Printf("Let's Encrypt Email: %s\n", config.LetsEncryptEmail)
fmt.Printf("Badger Version: %s\n", config.BadgerVersion)
if !readBool(reader, "Are these values correct?", true) {
config = collectUserInput(reader)
if !readBool("Are these values correct?", true) {
config = collectUserInput()
}
}
@@ -235,7 +252,7 @@ func main() {
if detectedType == Undefined {
// If detection fails, prompt the user
fmt.Println("Unable to detect container type from existing installation.")
config.InstallationContainerType = podmanOrDocker(reader)
config.InstallationContainerType = podmanOrDocker()
} else {
config.InstallationContainerType = detectedType
fmt.Printf("Detected container type: %s\n", config.InstallationContainerType)
@@ -277,8 +294,119 @@ func main() {
fmt.Printf("\nTo complete the initial setup, please visit:\nhttps://%s/auth/initial-setup\n", config.DashboardDomain)
}
func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
inputContainer := readString(reader, "Would you like to run Pangolin as Docker or Podman containers?", "docker")
func hasExistingInstall(dir string) bool {
configPath := filepath.Join(dir, "config", "config.yml")
_, err := os.Stat(configPath)
return err == nil
}
func findOrSelectInstallDirectory() string {
const defaultInstallDir = "/opt/pangolin"
// Get current working directory
cwd, err := os.Getwd()
if err != nil {
fmt.Printf("Error getting current directory: %v\n", err)
os.Exit(1)
}
// 1. Check current directory for existing install
if hasExistingInstall(cwd) {
fmt.Printf("Found existing Pangolin installation in current directory: %s\n", cwd)
return cwd
}
// 2. Check default location (/opt/pangolin) for existing install
if cwd != defaultInstallDir && hasExistingInstall(defaultInstallDir) {
fmt.Printf("\nFound existing Pangolin installation at: %s\n", defaultInstallDir)
if readBool(fmt.Sprintf("Would you like to use the existing installation at %s?", defaultInstallDir), true) {
return defaultInstallDir
}
}
// 3. No existing install found, prompt for installation directory
fmt.Println("\n=== Installation Directory ===")
fmt.Println("No existing Pangolin installation detected.")
installDir := readString("Enter the installation directory", defaultInstallDir)
// Expand ~ to home directory if present
if strings.HasPrefix(installDir, "~") {
home, err := os.UserHomeDir()
if err != nil {
fmt.Printf("Error getting home directory: %v\n", err)
os.Exit(1)
}
installDir = filepath.Join(home, installDir[1:])
}
// Convert to absolute path
absPath, err := filepath.Abs(installDir)
if err != nil {
fmt.Printf("Error resolving path: %v\n", err)
os.Exit(1)
}
installDir = absPath
// Check if directory exists
if _, err := os.Stat(installDir); os.IsNotExist(err) {
// Directory doesn't exist, create it
if readBool(fmt.Sprintf("Directory %s does not exist. Create it?", installDir), true) {
if err := os.MkdirAll(installDir, 0755); err != nil {
fmt.Printf("Error creating directory: %v\n", err)
os.Exit(1)
}
fmt.Printf("Created directory: %s\n", installDir)
// Offer to change ownership if running via sudo
changeDirectoryOwnership(installDir)
} else {
fmt.Println("Installation cancelled.")
os.Exit(0)
}
}
fmt.Printf("Installation directory: %s\n", installDir)
return installDir
}
func changeDirectoryOwnership(dir string) {
// Check if we're running via sudo by looking for SUDO_USER
sudoUser := os.Getenv("SUDO_USER")
if sudoUser == "" || os.Geteuid() != 0 {
return
}
sudoUID := os.Getenv("SUDO_UID")
sudoGID := os.Getenv("SUDO_GID")
if sudoUID == "" || sudoGID == "" {
return
}
fmt.Printf("\nRunning as root via sudo (original user: %s)\n", sudoUser)
if readBool(fmt.Sprintf("Would you like to change ownership of %s to user '%s'? This makes it easier to manage config files without sudo.", dir, sudoUser), true) {
uid, err := strconv.Atoi(sudoUID)
if err != nil {
fmt.Printf("Warning: Could not parse SUDO_UID: %v\n", err)
return
}
gid, err := strconv.Atoi(sudoGID)
if err != nil {
fmt.Printf("Warning: Could not parse SUDO_GID: %v\n", err)
return
}
if err := os.Chown(dir, uid, gid); err != nil {
fmt.Printf("Warning: Could not change ownership: %v\n", err)
} else {
fmt.Printf("Changed ownership of %s to %s\n", dir, sudoUser)
}
}
}
func podmanOrDocker() SupportedContainer {
inputContainer := readString("Would you like to run Pangolin as Docker or Podman containers?", "docker")
chosenContainer := Docker
if strings.EqualFold(inputContainer, "docker") {
@@ -290,7 +418,8 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
os.Exit(1)
}
if chosenContainer == Podman {
switch chosenContainer {
case Podman:
if !isPodmanInstalled() {
fmt.Println("Podman or podman-compose is not installed. Please install both manually. Automated installation will be available in a later release.")
os.Exit(1)
@@ -299,7 +428,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
if err := exec.Command("bash", "-c", "cat /etc/sysctl.d/99-podman.conf 2>/dev/null | grep 'net.ipv4.ip_unprivileged_port_start=' || cat /etc/sysctl.conf 2>/dev/null | grep 'net.ipv4.ip_unprivileged_port_start='").Run(); err != nil {
fmt.Println("Would you like to configure ports >= 80 as unprivileged ports? This enables podman containers to listen on low-range ports.")
fmt.Println("Pangolin will experience startup issues if this is not configured, because it needs to listen on port 80/443 by default.")
approved := readBool(reader, "The installer is about to execute \"echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system\". Approve?", true)
approved := readBool("The installer is about to execute \"echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system\". Approve?", true)
if approved {
if os.Geteuid() != 0 {
fmt.Println("You need to run the installer as root for such a configuration.")
@@ -311,7 +440,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
// Linux only.
if err := run("bash", "-c", "echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system"); err != nil {
fmt.Printf("Error configuring unprivileged ports: %v\n", err)
fmt.Printf("Error configuring unprivileged ports: %v\n", err)
os.Exit(1)
}
} else {
@@ -321,7 +450,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
fmt.Println("Unprivileged ports have been configured.")
}
} else if chosenContainer == Docker {
case Docker:
// check if docker is not installed and the user is root
if !isDockerInstalled() {
if os.Geteuid() != 0 {
@@ -336,7 +465,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
fmt.Println("The installer will not be able to run docker commands without running it as root.")
os.Exit(1)
}
} else {
default:
// This shouldn't happen unless there's a third container runtime.
os.Exit(1)
}
@@ -344,35 +473,35 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
return chosenContainer
}
func collectUserInput(reader *bufio.Reader) Config {
func collectUserInput() Config {
config := Config{}
// Basic configuration
fmt.Println("\n=== Basic Configuration ===")
config.IsEnterprise = readBoolNoDefault(reader, "Do you want to install the Enterprise version of Pangolin? The EE is free for personal use or for businesses making less than 100k USD annually.")
config.IsEnterprise = readBoolNoDefault("Do you want to install the Enterprise version of Pangolin? The EE is free for personal use or for businesses making less than 100k USD annually.")
config.BaseDomain = readString(reader, "Enter your base domain (no subdomain e.g. example.com)", "")
config.BaseDomain = readString("Enter your base domain (no subdomain e.g. example.com)", "")
// Set default dashboard domain after base domain is collected
defaultDashboardDomain := ""
if config.BaseDomain != "" {
defaultDashboardDomain = "pangolin." + config.BaseDomain
}
config.DashboardDomain = readString(reader, "Enter the domain for the Pangolin dashboard", defaultDashboardDomain)
config.LetsEncryptEmail = readString(reader, "Enter email for Let's Encrypt certificates", "")
config.InstallGerbil = readBool(reader, "Do you want to use Gerbil to allow tunneled connections", true)
config.DashboardDomain = readString("Enter the domain for the Pangolin dashboard", defaultDashboardDomain)
config.LetsEncryptEmail = readString("Enter email for Let's Encrypt certificates", "")
config.InstallGerbil = readBool("Do you want to use Gerbil to allow tunneled connections", true)
// Email configuration
fmt.Println("\n=== Email Configuration ===")
config.EnableEmail = readBool(reader, "Enable email functionality (SMTP)", false)
config.EnableEmail = readBool("Enable email functionality (SMTP)", false)
if config.EnableEmail {
config.EmailSMTPHost = readString(reader, "Enter SMTP host", "")
config.EmailSMTPPort = readInt(reader, "Enter SMTP port (default 587)", 587)
config.EmailSMTPUser = readString(reader, "Enter SMTP username", "")
config.EmailSMTPPass = readString(reader, "Enter SMTP password", "") // Should this be readPassword?
config.EmailNoReply = readString(reader, "Enter no-reply email address (often the same as SMTP username)", "")
config.EmailSMTPHost = readString("Enter SMTP host", "")
config.EmailSMTPPort = readInt("Enter SMTP port (default 587)", 587)
config.EmailSMTPUser = readString("Enter SMTP username", "")
config.EmailSMTPPass = readPassword("Enter SMTP password")
config.EmailNoReply = readString("Enter no-reply email address (often the same as SMTP username)", "")
}
// Validate required fields
@@ -393,8 +522,8 @@ func collectUserInput(reader *bufio.Reader) Config {
fmt.Println("\n=== Advanced Configuration ===")
config.EnableIPv6 = readBool(reader, "Is your server IPv6 capable?", true)
config.EnableGeoblocking = readBool(reader, "Do you want to download the MaxMind GeoLite2 database for geoblocking functionality?", true)
config.EnableIPv6 = readBool("Is your server IPv6 capable?", true)
config.EnableGeoblocking = readBool("Do you want to download the MaxMind GeoLite2 database for geoblocking functionality?", true)
if config.DashboardDomain == "" {
fmt.Println("Error: Dashboard Domain name is required")
@@ -405,15 +534,23 @@ func collectUserInput(reader *bufio.Reader) Config {
}
func createConfigFiles(config Config) error {
os.MkdirAll("config", 0755)
os.MkdirAll("config/letsencrypt", 0755)
os.MkdirAll("config/db", 0755)
os.MkdirAll("config/logs", 0755)
if err := os.MkdirAll("config", 0755); err != nil {
return fmt.Errorf("failed to create config directory: %v", err)
}
if err := os.MkdirAll("config/letsencrypt", 0755); err != nil {
return fmt.Errorf("failed to create letsencrypt directory: %v", err)
}
if err := os.MkdirAll("config/db", 0755); err != nil {
return fmt.Errorf("failed to create db directory: %v", err)
}
if err := os.MkdirAll("config/logs", 0755); err != nil {
return fmt.Errorf("failed to create logs directory: %v", err)
}
// Walk through all embedded files
err := fs.WalkDir(configFiles, "config", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
err := fs.WalkDir(configFiles, "config", func(path string, d fs.DirEntry, walkErr error) (err error) {
if walkErr != nil {
return walkErr
}
// Skip the root fs directory itself
@@ -464,7 +601,11 @@ func createConfigFiles(config Config) error {
if err != nil {
return fmt.Errorf("failed to create %s: %v", path, err)
}
defer outFile.Close()
defer func() {
if cerr := outFile.Close(); cerr != nil && err == nil {
err = cerr
}
}()
// Execute template
if err := tmpl.Execute(outFile, config); err != nil {
@@ -480,18 +621,26 @@ func createConfigFiles(config Config) error {
return nil
}
func copyFile(src, dst string) error {
func copyFile(src, dst string) (err error) {
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
defer func() {
if cerr := source.Close(); cerr != nil && err == nil {
err = cerr
}
}()
destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()
defer func() {
if cerr := destination.Close(); cerr != nil && err == nil {
err = cerr
}
}()
_, err = io.Copy(destination, source)
return err
@@ -562,22 +711,24 @@ func showSetupTokenInstructions(containerType SupportedContainer, dashboardDomai
fmt.Println("To get your setup token, you need to:")
fmt.Println("")
fmt.Println("1. Start the containers")
if containerType == Docker {
switch containerType {
case Docker:
fmt.Println(" docker compose up -d")
} else if containerType == Podman {
case Podman:
fmt.Println(" podman-compose up -d")
} else {
}
fmt.Println("")
fmt.Println("2. Wait for the Pangolin container to start and generate the token")
fmt.Println("")
fmt.Println("3. Check the container logs for the setup token")
if containerType == Docker {
switch containerType {
case Docker:
fmt.Println(" docker logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'")
} else if containerType == Podman {
case Podman:
fmt.Println(" podman logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'")
} else {
}
fmt.Println("")
fmt.Println("4. Look for output like")
fmt.Println(" === SETUP TOKEN GENERATED ===")
@@ -601,32 +752,6 @@ func generateRandomSecretKey() string {
return base64.StdEncoding.EncodeToString(secret)
}
func getPublicIP() string {
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Get("https://ifconfig.io/ip")
if err != nil {
return ""
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return ""
}
ip := strings.TrimSpace(string(body))
// Validate that it's a valid IP address
if net.ParseIP(ip) != nil {
return ip
}
return ""
}
// Run external commands with stdio/stderr attached.
func run(name string, args ...string) error {
cmd := exec.Command(name, args...)
@@ -639,10 +764,7 @@ func checkPortsAvailable(port int) error {
addr := fmt.Sprintf(":%d", port)
ln, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf(
"ERROR: port %d is occupied or cannot be bound: %w\n\n",
port, err,
)
return fmt.Errorf("ERROR: port %d is occupied or cannot be bound: %w", port, err)
}
if closeErr := ln.Close(); closeErr != nil {
fmt.Fprintf(os.Stderr,

51
install/theme.go Normal file
View File

@@ -0,0 +1,51 @@
package main
import (
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
)
// Pangolin brand colors (converted from oklch to hex)
var (
// Primary orange/amber - oklch(0.6717 0.1946 41.93)
primaryColor = lipgloss.AdaptiveColor{Light: "#D97706", Dark: "#F59E0B"}
// Muted foreground
mutedColor = lipgloss.AdaptiveColor{Light: "#737373", Dark: "#A3A3A3"}
// Success green
successColor = lipgloss.AdaptiveColor{Light: "#16A34A", Dark: "#22C55E"}
// Error red - oklch(0.577 0.245 27.325)
errorColor = lipgloss.AdaptiveColor{Light: "#DC2626", Dark: "#EF4444"}
// Normal text
normalFg = lipgloss.AdaptiveColor{Light: "#171717", Dark: "#FAFAFA"}
)
// ThemePangolin returns a huh theme using Pangolin brand colors
func ThemePangolin() *huh.Theme {
t := huh.ThemeBase()
// Focused state styles
t.Focused.Base = t.Focused.Base.BorderForeground(primaryColor)
t.Focused.Title = t.Focused.Title.Foreground(primaryColor).Bold(true)
t.Focused.Description = t.Focused.Description.Foreground(mutedColor)
t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(errorColor)
t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(errorColor)
t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(primaryColor)
t.Focused.NextIndicator = t.Focused.NextIndicator.Foreground(primaryColor)
t.Focused.PrevIndicator = t.Focused.PrevIndicator.Foreground(primaryColor)
t.Focused.Option = t.Focused.Option.Foreground(normalFg)
t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(primaryColor)
t.Focused.SelectedPrefix = lipgloss.NewStyle().Foreground(successColor).SetString("✓ ")
t.Focused.UnselectedPrefix = lipgloss.NewStyle().Foreground(mutedColor).SetString(" ")
t.Focused.FocusedButton = t.Focused.FocusedButton.Foreground(lipgloss.Color("#FFFFFF")).Background(primaryColor)
t.Focused.BlurredButton = t.Focused.BlurredButton.Foreground(normalFg).Background(lipgloss.AdaptiveColor{Light: "#E5E5E5", Dark: "#404040"})
t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(primaryColor)
t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(primaryColor)
// Blurred state inherits from focused but with hidden border
t.Blurred = t.Focused
t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder())
t.Blurred.Title = t.Blurred.Title.Foreground(mutedColor).Bold(false)
t.Blurred.TextInput.Prompt = t.Blurred.TextInput.Prompt.Foreground(mutedColor)
return t
}

115
license.py Normal file
View File

@@ -0,0 +1,115 @@
import os
import sys
# --- Configuration ---
# The header text to be added to the files.
HEADER_TEXT = """/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
"""
def should_add_header(file_path):
"""
Checks if a file should receive the commercial license header.
Returns True if 'private' is in the path or file content.
"""
# Check if 'private' is in the file path (case-insensitive)
if 'server/private' in file_path.lower():
return True
# Check if 'private' is in the file content (case-insensitive)
# try:
# with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
# content = f.read()
# if 'private' in content.lower():
# return True
# except Exception as e:
# print(f"Could not read file {file_path}: {e}")
return False
def process_directory(root_dir):
"""
Recursively scans a directory and adds headers to qualifying .ts or .tsx files,
skipping any 'node_modules' directories.
"""
print(f"Scanning directory: {root_dir}")
files_processed = 0
headers_added = 0
for root, dirs, files in os.walk(root_dir):
# --- MODIFICATION ---
# Exclude 'node_modules' directories from the scan to improve performance.
if 'node_modules' in dirs:
dirs.remove('node_modules')
for file in files:
if file.endswith('.ts') or file.endswith('.tsx'):
file_path = os.path.join(root, file)
files_processed += 1
try:
with open(file_path, 'r+', encoding='utf-8') as f:
original_content = f.read()
has_header = original_content.startswith(HEADER_TEXT.strip())
if should_add_header(file_path):
# Add header only if it's not already there
if not has_header:
f.seek(0, 0) # Go to the beginning of the file
f.write(HEADER_TEXT.strip() + '\n\n' + original_content)
print(f"Added header to: {file_path}")
headers_added += 1
else:
print(f"Header already exists in: {file_path}")
else:
# Remove header if it exists but shouldn't be there
if has_header:
# Find the end of the header and remove it (including following newlines)
header_with_newlines = HEADER_TEXT.strip() + '\n\n'
if original_content.startswith(header_with_newlines):
content_without_header = original_content[len(header_with_newlines):]
else:
# Handle case where there might be different newline patterns
header_end = len(HEADER_TEXT.strip())
# Skip any newlines after the header
while header_end < len(original_content) and original_content[header_end] in '\n\r':
header_end += 1
content_without_header = original_content[header_end:]
f.seek(0)
f.write(content_without_header)
f.truncate()
print(f"Removed header from: {file_path}")
headers_added += 1 # Reusing counter for modifications
except Exception as e:
print(f"Error processing file {file_path}: {e}")
print("\n--- Scan Complete ---")
print(f"Total .ts or .tsx files found: {files_processed}")
print(f"Files modified (headers added/removed): {headers_added}")
if __name__ == "__main__":
# Get the target directory from the command line arguments.
# If no directory is provided, it uses the current directory ('.').
if len(sys.argv) > 1:
target_directory = sys.argv[1]
else:
target_directory = '.' # Default to current directory
if not os.path.isdir(target_directory):
print(f"Error: Directory '{target_directory}' not found.")
sys.exit(1)
process_directory(os.path.abspath(target_directory))

View File

@@ -148,6 +148,11 @@
"createLink": "Създаване на връзка",
"resourcesNotFound": "Не са намерени ресурси",
"resourceSearch": "Търсене на ресурси",
"machineSearch": "Търсене на машини",
"machinesSearch": "Търсене на клиенти на машини...",
"machineNotFound": "Не са намерени машини",
"userDeviceSearch": "Търсене на устройства на потребителя",
"userDevicesSearch": "Търсене на устройства на потребителя...",
"openMenu": "Отваряне на менюто",
"resource": "Ресурс",
"title": "Заглавие",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Прокси заявки чрез HTTPS, използвайки напълно квалифицирано име на домейн.",
"resourceRaw": "Суров TCP/UDP ресурс",
"resourceRawDescription": "Прокси заявки чрез сурови TCP/UDP, използвайки порт номер.",
"resourceRawDescriptionCloud": "Получавайте заявки чрез суров TCP/UDP с използване на портен номер. Изисква се сайтовете да се свързват към отдалечен възел.",
"resourceCreate": "Създайте ресурс",
"resourceCreateDescription": "Следвайте стъпките по-долу, за да създадете нов ресурс",
"resourceSeeAll": "Вижте всички ресурси",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Изтрийте API ключа",
"apiKeysManage": "Управление на API ключове",
"apiKeysDescription": "API ключове се използват за удостоверяване с интеграционния API",
"provisioningKeysTitle": "Ключ за осигуряване",
"provisioningKeysManage": "Управление на ключове за осигуряване",
"provisioningKeysDescription": "Ключовете за осигуряване се използват за удостоверяване на автоматичното осигуряване на сайта за вашата организация.",
"provisioningManage": "Осигуряване",
"provisioningDescription": "Управление на ключовете за осигуряване и преглед на чаканещите сайтове за одобрение.",
"pendingSites": "Чаканещи сайтове",
"siteApproveSuccess": "Сайтът е одобрен успешно",
"siteApproveError": "Грешка при одобряването на сайта",
"provisioningKeys": "Ключове за осигуряване",
"searchProvisioningKeys": "Търсене на ключове за осигуряване...",
"provisioningKeysAdd": "Генериране на ключ за осигуряване",
"provisioningKeysErrorDelete": "Грешка при изтриване на ключ за осигуряване",
"provisioningKeysErrorDeleteMessage": "Грешка при изтриване на ключ за осигуряване",
"provisioningKeysQuestionRemove": "Сигурни ли сте, че искате да премахнете този ключ за осигуряване от организацията?",
"provisioningKeysMessageRemove": "След като бъде премахнат, ключът няма да бъде използван за осигуряване на сайтове.",
"provisioningKeysDeleteConfirm": "Потвърдете изтриването на ключ за осигуряване",
"provisioningKeysDelete": "Изтриване на ключ за осигуряване",
"provisioningKeysCreate": "Генериране на ключ за осигуряване",
"provisioningKeysCreateDescription": "Генерирайте нов ключ за осигуряване за организацията",
"provisioningKeysSeeAll": "Вижте всички ключове за осигуряване",
"provisioningKeysSave": "Запазете ключа за осигуряване",
"provisioningKeysSaveDescription": "Ще можете да видите това само веднъж. Копирайте го на сигурно място.",
"provisioningKeysErrorCreate": "Грешка при създаване на ключ за осигуряване",
"provisioningKeysList": "Нов ключ за осигуряване",
"provisioningKeysMaxBatchSize": "Максимален размер на пакет",
"provisioningKeysUnlimitedBatchSize": "Неограничен размер на партида (без лимит)",
"provisioningKeysMaxBatchUnlimited": "Неограничено",
"provisioningKeysMaxBatchSizeInvalid": "Въведете валиден максимален размер на партида (11,000,000).",
"provisioningKeysValidUntil": "Валиден до",
"provisioningKeysValidUntilHint": "Оставете празно за неограничено валидност.",
"provisioningKeysValidUntilInvalid": "Въведете валидна дата и час.",
"provisioningKeysNumUsed": "Брой използвания",
"provisioningKeysLastUsed": "Последно използван",
"provisioningKeysNoExpiry": "Без изтичане",
"provisioningKeysNeverUsed": "Никога",
"provisioningKeysEdit": "Редактиране на ключ за осигуряване",
"provisioningKeysEditDescription": "Актуализирайте максималния размер на партида и времето на изтичане за този ключ.",
"provisioningKeysApproveNewSites": "Одобрете нови сайтове",
"provisioningKeysApproveNewSitesDescription": "Автоматично одобряване на сайтове, които се регистрират с този ключ.",
"provisioningKeysUpdateError": "Грешка при актуализирането на ключа за осигуряване",
"provisioningKeysUpdated": "Ключът за осигуряване е актуализиран",
"provisioningKeysUpdatedDescription": "Вашите промени бяха запазени.",
"provisioningKeysBannerTitle": "Ключове за осигуряване на сайта",
"provisioningKeysBannerDescription": "Генерирайте ключ за осигуряване и го използвайте с Newt конектора за автоматично създаване на сайтове при първото стартиране — няма нужда от създаване на отделни идентификационни данни за всеки сайт.",
"provisioningKeysBannerButtonText": "Научете повече",
"pendingSitesBannerTitle": "Чакащи сайтове",
"pendingSitesBannerDescription": "Сайтовете, които се свързват чрез ключ за осигуряване, се появяват тук за преглед. Одобрете всеки сайт, преди да стане активен и да получи достъп до вашите ресурси.",
"pendingSitesBannerButtonText": "Научете повече",
"apiKeysSettings": "Настройки на {apiKeyName}",
"userTitle": "Управление на всички потребители",
"userDescription": "Преглед и управление на всички потребители в системата",
@@ -508,9 +562,12 @@
"userSaved": "Потребителят е запазен",
"userSavedDescription": "Потребителят беше актуализиран.",
"autoProvisioned": "Автоматично предоставено",
"autoProvisionSettings": "Настройки за автоматично осигуряване",
"autoProvisionedDescription": "Позволете този потребител да бъде автоматично управляван от доставчик на идентификационни данни",
"accessControlsDescription": "Управлявайте какво може да достъпва и прави този потребител в организацията",
"accessControlsSubmit": "Запазване на контролите за достъп",
"singleRolePerUserPlanNotice": "Вашият план поддържа само една роля на потребител.",
"singleRolePerUserEditionNotice": "Това издание поддържа само една роля на потребител.",
"roles": "Роли",
"accessUsersRoles": "Управление на потребители и роли",
"accessUsersRolesDescription": "Поканете потребители и ги добавете към роли, за да управлявате достъпа до организацията",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Неуспешно превключване на ресурса",
"resourcesErrorUpdateDescription": "Възникна грешка при актуализиране на ресурса",
"access": "Достъп",
"accessControl": "Контрол на достъпа",
"shareLink": "{resource} Сподели връзка",
"resourceSelect": "Изберете ресурс",
"shareLinks": "Споделени връзки",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Ролята е премахната",
"accessRoleRemovedDescription": "Ролята беше успешно премахната.",
"accessRoleRequiredRemove": "Преди да изтриете тази роля, моля изберете нова роля, към която да прехвърлите настоящите членове.",
"network": "Мрежа",
"manage": "Управление",
"sitesNotFound": "Няма намерени сайтове.",
"pangolinServerAdmin": "Администратор на сървър - Панголин",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "О, не! Страницата, която търсите, не съществува.",
"overview": "Общ преглед",
"home": "Начало",
"accessControl": "Контрол на достъпа",
"settings": "Настройки",
"usersAll": "Всички потребители",
"license": "Лиценз",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Получаване на потребител",
"actionGetOrgUser": "Вземете потребител на организация",
"actionListOrgDomains": "Изброяване на домейни на организация",
"actionGetDomain": "Вземи домейн",
"actionCreateOrgDomain": "Създай домейн",
"actionUpdateOrgDomain": "Актуализирай домейн",
"actionDeleteOrgDomain": "Изтрий домейн",
"actionGetDNSRecords": "Вземи DNS записи",
"actionRestartOrgDomain": "Рестартирай домейн",
"actionCreateSite": "Създаване на сайт",
"actionDeleteSite": "Изтриване на сайта",
"actionGetSite": "Вземете сайт",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Въведете конфигурационния токен от сървърната конзола.",
"setupTokenRequired": "Необходим е конфигурационен токен",
"actionUpdateSite": "Актуализиране на сайт",
"actionResetSiteBandwidth": "Нулиране на честотната лента на организацията",
"actionListSiteRoles": "Изброяване на позволените роли за сайта",
"actionCreateResource": "Създаване на ресурс",
"actionDeleteResource": "Изтриване на ресурс",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Изтрийте потребител",
"actionListUsers": "Изброяване на потребители",
"actionAddUserRole": "Добавяне на роля на потребител",
"actionSetUserOrgRoles": "Задайте роли на потребители",
"actionGenerateAccessToken": "Генериране на токен за достъп",
"actionDeleteAccessToken": "Изтриване на токен за достъп",
"actionListAccessTokens": "Изброяване на токени за достъп",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Частно",
"sidebarAccessControl": "Контрол на достъпа",
"sidebarLogsAndAnalytics": "Дневници и анализи",
"sidebarTeam": "Екип",
"sidebarUsers": "Потребители",
"sidebarAdmin": "Администратор",
"sidebarInvitations": "Покани",
"sidebarRoles": "Роли",
"sidebarShareableLinks": "Връзки",
"sidebarApiKeys": "API ключове",
"sidebarProvisioning": "Осигуряване",
"sidebarSettings": "Настройки",
"sidebarAllUsers": "Всички потребители",
"sidebarIdentityProviders": "Идентификационни доставчици",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Лог & Анализи",
"sidebarBluePrints": "Чертежи",
"sidebarOrganization": "Организация",
"sidebarManagement": "Управление",
"sidebarBillingAndLicenses": "Фактуриране & Лицензи",
"sidebarLogsAnalytics": "Анализи",
"blueprints": "Чертежи",
@@ -1289,7 +1358,6 @@
"parsedContents": "Парсирано съдържание (само за четене)",
"enableDockerSocket": "Активиране на Docker Чернова",
"enableDockerSocketDescription": "Активиране на Docker Socket маркировка за изтегляне на етикети на чернова. Пътят на гнездото трябва да бъде предоставен на Newt.",
"enableDockerSocketLink": "Научете повече",
"viewDockerContainers": "Преглед на Docker контейнери",
"containersIn": "Контейнери в {siteName}",
"selectContainerDescription": "Изберете контейнер, който да ползвате като име на хост за целта. Натиснете порт, за да ползвате порт",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Име на пространство: {namespace}",
"domainPickerShowMore": "Покажи повече",
"regionSelectorTitle": "Избор на регион",
"domainPickerRemoteExitNodeWarning": "Предоставените домейни не се поддържат, когато сайтовете се свързват към отдалечени крайни възли. За да бъдат ресурсите налични на отдалечени възли, използвайте персонализиран домейн вместо това.",
"regionSelectorInfo": "Изборът на регион ни помага да предоставим по-добра производителност за вашето местоположение. Не е необходимо да сте в същия регион като сървъра.",
"regionSelectorPlaceholder": "Изберете регион",
"regionSelectorComingSoon": "Очаква се скоро",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Уведомление за наличност на функциите",
"billingFeatureLossDescription": "Чрез понижението на плана, функциите, недостъпни в новия план, ще бъдат автоматично деактивирани. Някои настройки и конфигурации може да бъдат загубени. Моля, прегледайте ценовата матрица, за да разберете кои функции вече няма да са на разположение.",
"billingUsageExceedsLimit": "Текущото използване ({current}) надвишава ограничението ({limit})",
"billingPastDueTitle": "Плащането е просрочено",
"billingPastDueDescription": "Вашето плащане е просрочено. Моля, актуализирайте метода на плащане, за да продължите да използвате настоящия си план. Ако проблемът не бъде разрешен, абонаментът ви ще бъде прекратен и ще бъдете прехвърлени на безплатния план.",
"billingUnpaidTitle": "Абонаментът не е платен",
"billingUnpaidDescription": "Вашият абонамент не е платен и сте прехвърлени на безплатния план. Моля, актуализирайте метода на плащане, за да възстановите вашия абонамент.",
"billingIncompleteTitle": "Плащането е непълно",
"billingIncompleteDescription": "Вашето плащане е непълно. Моля, завършете процеса на плащане, за да активирате вашия абонамент.",
"billingIncompleteExpiredTitle": "Плащането е изтекло",
"billingIncompleteExpiredDescription": "Вашето плащане никога не е завършено и е изтекло. Прехвърлени сте на безплатния план. Моля, абонирайте се отново, за да възстановите достъпа до платените функции.",
"billingManageSubscription": "Управлявайте вашия абонамент",
"billingResolvePaymentIssue": "Моля, разрешете проблема с плащането преди да извършите надграждане или понижение",
"signUpTerms": {
"IAgreeToThe": "Съгласен съм с",
"termsOfService": "условията за ползване",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Времето е в секунди",
"requireDeviceApproval": "Изискват одобрение на устройства",
"requireDeviceApprovalDescription": "Потребители с тази роля трябва да имат нови устройства одобрени от администратор преди да могат да се свържат и да имат достъп до ресурси.",
"sshAccess": "SSH достъп",
"roleAllowSsh": "Разреши SSH",
"roleAllowSshAllow": "Разреши",
"roleAllowSshDisallow": "Забрани",
"roleAllowSshDescription": "Разреши на потребителите с тази роля да се свързват с ресурси чрез SSH. Когато е деактивирано, ролята не може да използва SSH достъп.",
"sshSudoMode": "Sudo достъп",
"sshSudoModeNone": "Няма",
"sshSudoModeNoneDescription": "Потребителят не може да изпълнява команди с sudo.",
"sshSudoModeFull": "Пълен Sudo",
"sshSudoModeFullDescription": "Потребителят може да изпълнява всяка команда с sudo.",
"sshSudoModeCommands": "Команди",
"sshSudoModeCommandsDescription": "Потребителят може да изпълнява само определени команди с sudo.",
"sshSudo": "Разреши sudo",
"sshSudoCommands": "Sudo команди",
"sshSudoCommandsDescription": "Списък, разделен със запетаи, с команди, които потребителят е позволено да изпълнява с sudo.",
"sshCreateHomeDir": "Създай начална директория",
"sshUnixGroups": "Unix групи",
"sshUnixGroupsDescription": "Списък, разделен със запетаи, с Unix групи, към които да се добави потребителят на целевия хост.",
"retryAttempts": "Опити за повторно",
"expectedResponseCodes": "Очаквани кодове за отговор",
"expectedResponseCodesDescription": "HTTP статус код, указващ здравословно състояние. Ако бъде оставено празно, между 200-300 се счита за здравословно.",
@@ -1851,6 +1948,40 @@
"exitNode": "Изходен възел",
"country": "Държава",
"rulesMatchCountry": "Понастоящем на базата на изходния IP",
"region": "Регион",
"selectRegion": "Изберете регион",
"searchRegions": "Търсене на региони...",
"noRegionFound": "Регионът не е намерен.",
"rulesMatchRegion": "Изберете регионална групировка на държави",
"rulesErrorInvalidRegion": "Невалиден регион",
"rulesErrorInvalidRegionDescription": "Моля, изберете валиден регион.",
"regionAfrica": "Африка",
"regionNorthernAfrica": "Северна Африка",
"regionEasternAfrica": "Източна Африка",
"regionMiddleAfrica": "Централна Африка",
"regionSouthernAfrica": "Южна Африка",
"regionWesternAfrica": "Западна Африка",
"regionAmericas": "Америките",
"regionCaribbean": "Карибите",
"regionCentralAmerica": "Централна Америка",
"regionSouthAmerica": "Южна Америка",
"regionNorthernAmerica": "Северна Америка",
"regionAsia": "Азия",
"regionCentralAsia": "Централна Азия",
"regionEasternAsia": "Източна Азия",
"regionSouthEasternAsia": "Югоизточна Азия",
"regionSouthernAsia": "Южна Азия",
"regionWesternAsia": "Западна Азия",
"regionEurope": "Европа",
"regionEasternEurope": "Източна Европа",
"regionNorthernEurope": "Северна Европа",
"regionSouthernEurope": "Южна Европа",
"regionWesternEurope": "Западна Европа",
"regionOceania": "Океания",
"regionAustraliaAndNewZealand": "Австралия и Нова Зеландия",
"regionMelanesia": "Меланезия",
"regionMicronesia": "Микронезия",
"regionPolynesia": "Полинезия",
"managedSelfHosted": {
"title": "Управлявано Самостоятелно-хоствано",
"description": "По-надежден и по-нисък поддръжка на Самостоятелно-хостван Панголиин сървър с допълнителни екстри",
@@ -1899,6 +2030,25 @@
"invalidValue": "Невалидна стойност",
"idpTypeLabel": "Тип на доставчика на идентичност",
"roleMappingExpressionPlaceholder": "напр.: contains(groups, 'admin') && 'Admin' || 'Member'",
"roleMappingModeFixedRoles": "Фиксирани роли",
"roleMappingModeMappingBuilder": "Строител на карти",
"roleMappingModeRawExpression": "Необработено израз",
"roleMappingFixedRolesPlaceholderSelect": "Изберете една или повече роли",
"roleMappingFixedRolesPlaceholderFreeform": "Въведете имена на роли (точно съвпадение на организацията)",
"roleMappingFixedRolesDescriptionSameForAll": "Присвойте същият набор от роли на всеки автоматично осигурен потребител.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "За стандартните политики въведете имена на роли, които съществуват във всяка организация, където е осигурен потребител. Имената трябва да съвпадат точно.",
"roleMappingClaimPath": "Път на иск",
"roleMappingClaimPathPlaceholder": "групи",
"roleMappingClaimPathDescription": "Път в съдържанието на маркера, който съдържа изходни стойности (например групи).",
"roleMappingMatchValue": "Съвпадение на стойност",
"roleMappingAssignRoles": "Присвояване на роли",
"roleMappingAddMappingRule": "Добавяне на правило за картироване",
"roleMappingRawExpressionResultDescription": "Изразът трябва да бъде оценен на низ или масив от низове.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Изразът трябва да бъде оценен на низ (едно име на роля).",
"roleMappingMatchValuePlaceholder": "Съвпадение на стойност (например: администратор)",
"roleMappingAssignRolesPlaceholderFreeform": "Въведете имена на роли (точно по организация)",
"roleMappingBuilderFreeformRowHint": "Имената на ролите трябва да съвпадат с роля във всяка целева организация.",
"roleMappingRemoveRule": "Премахни",
"idpGoogleConfiguration": "Конфигурация на Google",
"idpGoogleConfigurationDescription": "Конфигурирайте Google OAuth2 идентификационни данни",
"idpGoogleClientIdDescription": "Google OAuth2 идентификационен клиент",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Колко дълго да се задържат логовете за достъп",
"logRetentionActionLabel": "Задържане на логове за действия",
"logRetentionActionDescription": "Колко дълго да се задържат логовете за действия",
"logRetentionConnectionLabel": "Запазване на дневниците на връзките",
"logRetentionConnectionDescription": "Колко дълго да се съхраняват дневниците на връзките",
"logRetentionDisabled": "Деактивирано",
"logRetention3Days": "3 дни",
"logRetention7Days": "7 дни",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Край на следващата година",
"actionLogsDescription": "Прегледайте историята на действията, извършени в тази организация",
"accessLogsDescription": "Прегледайте заявките за удостоверяване на достъпа до ресурсите в тази организация",
"licenseRequiredToUse": "Изисква се лиценз за <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink>, за да използвате тази функция. Тази функция е също достъпна в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "Необходимо е <enterpriseEditionLink>изданието Enterprise</enterpriseEditionLink>, за да използвате тази функция. Тази функция е също достъпна в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Логове на връзката",
"connectionLogsDescription": "Вижте логовете на връзките за тунелите в тази организация",
"sidebarLogsConnection": "Логове на връзката",
"sidebarLogsStreaming": "Потоци",
"sourceAddress": "Източен адрес",
"destinationAddress": "Адрес на дестинация",
"duration": "Продължителност",
"licenseRequiredToUse": "Изисква се лиценз за <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> или <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> за използване на тази функция. <bookADemoLink>Резервирайте демонстрация или пробен POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> е необходим за използване на тази функция. Тази функция също е налична в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Резервирайте демонстрация или пробен POC</bookADemoLink>.",
"certResolver": "Решавач на сертификати",
"certResolverDescription": "Изберете решавач на сертификати за използване за този ресурс.",
"selectCertResolver": "Изберете решавач на сертификати",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Контрол на достъпа.",
"editInternalResourceDialogAccessControlDescription": "Контролирайте кои роли, потребители и клиентски машини имат достъп до този ресурс, когато са свързани. Администраторите винаги имат достъп.",
"editInternalResourceDialogPortRangeValidationError": "Обхватът на портовете трябва да е \"*\" за всички портове или списък от разделени със запетая портове и диапазони (например: \"80,443,8000-9000\"). Портовете трябва да са между 1 и 65535.",
"internalResourceAuthDaemonStrategy": "Локация на SSH Auth Daemon",
"internalResourceAuthDaemonStrategyDescription": "Изберете къде ще работи демонът за SSH удостоверение: на сайта (Newt) или на отдалечен хост.",
"internalResourceAuthDaemonDescription": "Демонът за SSH удостоверение управлява подписването на SSH ключове и PAM удостоверение за този ресурс. Изберете дали да работи на сайта (Newt) или на отделен отдалечен хост. Вижте <docsLink>документацията</docsLink> за повече информация.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Изберете стратегия",
"internalResourceAuthDaemonStrategyLabel": "Местоположение",
"internalResourceAuthDaemonSite": "На сайта",
"internalResourceAuthDaemonSiteDescription": "Демонът за удостоверение работи на сайта (Newt).",
"internalResourceAuthDaemonRemote": "Отдалечен хост",
"internalResourceAuthDaemonRemoteDescription": "Демонът за удостоверение работи на хост, който не е сайтът.",
"internalResourceAuthDaemonPort": "Порт на демона (незадължителен)",
"orgAuthWhatsThis": "Къде мога да намеря идентификатора на организацията си?",
"learnMore": "Научете повече.",
"backToHome": "Връщане към началната страница.",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Активирайте одобрения на устройства",
"approvalsEmptyStateStep2Description": "Редактирайте ролята и активирайте опцията 'Изискване на одобрения за устройства'. Потребители с тази роля ще трябва администраторско одобрение за нови устройства.",
"approvalsEmptyStatePreviewDescription": "Преглед: Когато е активирано, чакащите заявки за устройства ще се появят тук за преглед",
"approvalsEmptyStateButtonText": "Управлявайте роли"
"approvalsEmptyStateButtonText": "Управлявайте роли",
"domainErrorTitle": "Имаме проблем с проверката на вашия домейн",
"idpAdminAutoProvisionPoliciesTabHint": "Конфигурирайте картографирането на ролите и организационните политики на раздела <policiesTabLink>Настройки за автоматично осигуряване</policiesTabLink>.",
"streamingTitle": "Събитийни потоци",
"streamingDescription": "Предавайте събития от вашата организация до външни дестинации в реално време.",
"streamingUnnamedDestination": "Неименувана дестинация",
"streamingNoUrlConfigured": "Не е конфигуриран URL",
"streamingAddDestination": "Добавяне на дестинация",
"streamingHttpWebhookTitle": "HTTP Уеб хук",
"streamingHttpWebhookDescription": "Изпратете събития до всяка HTTP крайна точка с гъвкаво удостоверяване и шаблониране.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Предавайте събития на хранилище, съвместимо с S3. Очаквайте скоро.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Пресочвайте събития директно към вашият акаунт в Datadog. Очаквайте скоро.",
"streamingTypePickerDescription": "Изберете вид на дестинацията, за да започнете.",
"streamingFailedToLoad": "Неуспешно зареждане на дестинации",
"streamingUnexpectedError": "Възникна неочаквана грешка.",
"streamingFailedToUpdate": "Неуспешно актуализиране на дестинация",
"streamingDeletedSuccess": "Дестинацията беше изтрита успешно",
"streamingFailedToDelete": "Неуспешно изтриване на дестинацията",
"streamingDeleteTitle": "Изтриване на дестинация",
"streamingDeleteButtonText": "Изтриване на дестинация",
"streamingDeleteDialogAreYouSure": "Сигурни ли сте, че искате да изтриете",
"streamingDeleteDialogThisDestination": "тази дестинация",
"streamingDeleteDialogPermanentlyRemoved": "? Всички конфигурации ще бъдат премахнати завинаги.",
"httpDestEditTitle": "Редактиране на дестинация",
"httpDestAddTitle": "Добавяне на HTTP дестинация",
"httpDestEditDescription": "Актуализирайте конфигурацията за този HTTP събитий.",
"httpDestAddDescription": "Конфигурирайте нов HTTP крайна точка, за да получавате събития на вашата организация.",
"httpDestTabSettings": "Настройки",
"httpDestTabHeaders": "Заглавки",
"httpDestTabBody": "Тяло",
"httpDestTabLogs": "Логове",
"httpDestNamePlaceholder": "Моята HTTP дестинация",
"httpDestUrlLabel": "Дестинация URL",
"httpDestUrlErrorHttpRequired": "URL адресът трябва да използва http или https",
"httpDestUrlErrorHttpsRequired": "SSL е необходимо за облачни инсталации",
"httpDestUrlErrorInvalid": "Въведете валиден URL (напр. https://example.com/webhook)",
"httpDestAuthTitle": "Удостоверяване",
"httpDestAuthDescription": "Изберете как заявленията ви се удостоверяват.",
"httpDestAuthNoneTitle": "Без удостоверяване",
"httpDestAuthNoneDescription": "Изпращане на заявки без заглавие за удостоверяване.",
"httpDestAuthBearerTitle": "Bearer Токен",
"httpDestAuthBearerDescription": "Добавя заглавие за удостоверяване Bearer <token> към всяка заявка.",
"httpDestAuthBearerPlaceholder": "Вашият API ключ или токен",
"httpDestAuthBasicTitle": "Основно удостоверяване",
"httpDestAuthBasicDescription": "Добавя заглавие за удостоверяване Basic <credentials> към всяка заявка. Осигурете идентификационни данни като потребителско име:парола.",
"httpDestAuthBasicPlaceholder": "потребителско име:парола",
"httpDestAuthCustomTitle": "Персонализирано заглавие",
"httpDestAuthCustomDescription": "Посочете персонализирано име и стойност на заглавието за удостоверяване (например X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Имя на заглавието (напр. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Стойност на заглавието",
"httpDestCustomHeadersTitle": "Персонализирани заглавия за HTTP",
"httpDestCustomHeadersDescription": "Добавяне на персонализирани заглавия към всяка изходяща заявка. Полезно за статични токени или персонални Content-Type. По подразбиране се изпраща Content-Type: application/json.",
"httpDestNoHeadersConfigured": "Персонализирани заглавия не са конфигурирани. Кликнете \"Добавяне на заглавие\" да добавите такова.",
"httpDestHeaderNamePlaceholder": "Име на заглавието",
"httpDestHeaderValuePlaceholder": "Стойност на заглавието",
"httpDestAddHeader": "Добавяне на заглавие",
"httpDestBodyTemplateTitle": "Шаблон на персонализирано тяло",
"httpDestBodyTemplateDescription": "Управлявайте структурата на JSON съобщението, изпратено до вашата крайна точка. Ако е деактивирано, по подразбиране се изпраща JSON обект за всяко събитие.",
"httpDestEnableBodyTemplate": "Активиране на персонализиран шаблон на тяло",
"httpDestBodyTemplateLabel": "Шаблон за тяло (JSON)",
"httpDestBodyTemplateHint": "Използвайте шаблонни променливи за позоваване на полетата на събитията в съобщението си.",
"httpDestPayloadFormatTitle": "Формат на полезния товар",
"httpDestPayloadFormatDescription": "Как се сериализират събитията във всеки заявка.",
"httpDestFormatJsonArrayTitle": "JSON масив",
"httpDestFormatJsonArrayDescription": "Една заявка на партида, тялото е JSON масив. Съвместим с повечето общи уеб куки и Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Една заявка на партида, тялото е ново линии отделени JSON — един обект на ред, няма външен масив. Изисквано от Splunk HEC, Elastic / OpenSearch и Grafana.",
"httpDestFormatSingleTitle": "Едно събитие на заявка",
"httpDestFormatSingleDescription": "Изпращат се отделни HTTP POST за всяко индивидуално събитие. Използвайте само за крайни точки, които не могат да обработват партиди.",
"httpDestLogTypesTitle": "Видове логове",
"httpDestLogTypesDescription": "Изберете кои видове журнални записи ще се предават към тази дестинация. Предаването ще се прави само за активирани видове журнални записи.",
"httpDestAccessLogsTitle": "Логове за достъп",
"httpDestAccessLogsDescription": "Опити за достъп до ресурс, включително удостоверени и отказани заявки.",
"httpDestActionLogsTitle": "Логове на действия",
"httpDestActionLogsDescription": "Административни действия, извършени от потребители в организацията.",
"httpDestConnectionLogsTitle": "Логове на връзката",
"httpDestConnectionLogsDescription": "Събития на свързване и прекъсване на сайта и тунела, включително свръзки и прекъсвания.",
"httpDestRequestLogsTitle": "Заявки за логове",
"httpDestRequestLogsDescription": "Регистри за HTTP заявките към проксирани ресурси, включително метод, път и код на отговор.",
"httpDestSaveChanges": "Запази промените",
"httpDestCreateDestination": "Създаване на дестинация",
"httpDestUpdatedSuccess": "Дестинацията беше актуализирана успешно",
"httpDestCreatedSuccess": "Дестинацията беше създадена успешно",
"httpDestUpdateFailed": "Неуспешно актуализиране на дестинацията",
"httpDestCreateFailed": "Неуспешно създаване на дестинацията"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Vytvořit odkaz",
"resourcesNotFound": "Nebyly nalezeny žádné zdroje",
"resourceSearch": "Vyhledat zdroje",
"machineSearch": "Vyhledávací stroje",
"machinesSearch": "Hledat klienty stroje...",
"machineNotFound": "Nebyly nalezeny žádné stroje",
"userDeviceSearch": "Hledat uživatelská zařízení",
"userDevicesSearch": "Hledat uživatelská zařízení...",
"openMenu": "Otevřít nabídku",
"resource": "Zdroj",
"title": "Název",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy požadavky přes HTTPS pomocí plně kvalifikovaného názvu domény.",
"resourceRaw": "Surový TCP/UDP zdroj",
"resourceRawDescription": "Proxy požadavky přes nezpracovaný TCP/UDP pomocí čísla portu.",
"resourceRawDescriptionCloud": "Proxy požadavky na syrové TCP/UDP pomocí čísla portu. Vyžaduje připojení stránek ke vzdálenému uzlu.",
"resourceCreate": "Vytvořit zdroj",
"resourceCreateDescription": "Postupujte podle níže uvedených kroků, abyste vytvořili a připojili nový zdroj",
"resourceSeeAll": "Zobrazit všechny zdroje",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Odstranit klíč API",
"apiKeysManage": "Správa API klíčů",
"apiKeysDescription": "API klíče se používají k ověření s integračním API",
"provisioningKeysTitle": "Zajišťovací klíč",
"provisioningKeysManage": "Spravovat zajišťovací klíče",
"provisioningKeysDescription": "Zajišťovací klíče slouží k ověření automatického poskytování služeb vaší organizaci.",
"provisioningManage": "Zajištění",
"provisioningDescription": "Spravovat klíče pro nastavení a zkontrolovat čekající stránky čekající na schválení.",
"pendingSites": "Nevyřízené weby",
"siteApproveSuccess": "Web byl úspěšně schválen",
"siteApproveError": "Chyba při schvalování webu",
"provisioningKeys": "Poskytovací klíče",
"searchProvisioningKeys": "Hledat klíče k zajišťování...",
"provisioningKeysAdd": "Generovat zajišťovací klíč",
"provisioningKeysErrorDelete": "Chyba při odstraňování klíče pro úpravu",
"provisioningKeysErrorDeleteMessage": "Chyba při odstraňování klíče pro úpravu",
"provisioningKeysQuestionRemove": "Jste si jisti, že chcete odstranit tento konfigurační klíč z organizace?",
"provisioningKeysMessageRemove": "Jakmile je klíč odstraněn, nelze již použít pro poskytování služeb.",
"provisioningKeysDeleteConfirm": "Potvrdit odstranění zajišťovacího klíče",
"provisioningKeysDelete": "Odstranit zajišťovací klíč",
"provisioningKeysCreate": "Generovat zajišťovací klíč",
"provisioningKeysCreateDescription": "Vygenerovat nový klíč pro organizaci",
"provisioningKeysSeeAll": "Zobrazit všechny doplňovací klíče",
"provisioningKeysSave": "Uložit konfigurační klíč",
"provisioningKeysSaveDescription": "Toto můžete vidět pouze jednou. Zkopírujte ho na bezpečné místo.",
"provisioningKeysErrorCreate": "Chyba při vytváření doplňovacího klíče",
"provisioningKeysList": "Nový klíč pro poskytování informací",
"provisioningKeysMaxBatchSize": "Maximální velikost dávky",
"provisioningKeysUnlimitedBatchSize": "Neomezená velikost šarže (bez omezení)",
"provisioningKeysMaxBatchUnlimited": "Bez omezení",
"provisioningKeysMaxBatchSizeInvalid": "Zadejte platnou maximální velikost šarže (11,000,000).",
"provisioningKeysValidUntil": "Platné do",
"provisioningKeysValidUntilHint": "Ponechte prázdné, pokud vyprší platnost.",
"provisioningKeysValidUntilInvalid": "Zadejte platné datum a čas.",
"provisioningKeysNumUsed": "Časy použití",
"provisioningKeysLastUsed": "Naposledy použito",
"provisioningKeysNoExpiry": "Bez vypršení platnosti",
"provisioningKeysNeverUsed": "Nikdy",
"provisioningKeysEdit": "Upravit zajišťovací klíč",
"provisioningKeysEditDescription": "Aktualizujte maximální velikost dávky a dobu vypršení platnosti tohoto klíče.",
"provisioningKeysApproveNewSites": "Schválit nové stránky",
"provisioningKeysApproveNewSitesDescription": "Automaticky schvalovat weby, které se registrují pomocí tohoto klíče.",
"provisioningKeysUpdateError": "Chyba při aktualizaci klíče",
"provisioningKeysUpdated": "Zajišťovací klíč byl aktualizován",
"provisioningKeysUpdatedDescription": "Vaše změny byly uloženy.",
"provisioningKeysBannerTitle": "Klíče pro poskytování webu",
"provisioningKeysBannerDescription": "Vygenerujte konfigurační klíč a používejte jej pomocí nového konektoru k automatickému vytváření stránek při prvním startu není třeba nastavovat samostatné přihlašovací údaje pro každý web.",
"provisioningKeysBannerButtonText": "Zjistit více",
"pendingSitesBannerTitle": "Nevyřízené weby",
"pendingSitesBannerDescription": "Zde se zobrazují stránky, které se připojují pomocí doplňovacího klíče. Schválte každý web předtím, než bude aktivní, a získejte přístup k vašim zdrojům.",
"pendingSitesBannerButtonText": "Zjistit více",
"apiKeysSettings": "Nastavení {apiKeyName}",
"userTitle": "Spravovat všechny uživatele",
"userDescription": "Zobrazit a spravovat všechny uživatele v systému",
@@ -336,7 +390,7 @@
"userQuestionRemove": "Jste si jisti, že chcete trvale odstranit uživatele ze serveru?",
"licenseKey": "Licenční klíč",
"valid": "Valid",
"numberOfSites": "Počet stránek",
"numberOfSites": "Počet lokalit",
"licenseKeySearch": "Hledat licenční klíče...",
"licenseKeyAdd": "Přidat licenční klíč",
"type": "Typ",
@@ -508,9 +562,12 @@
"userSaved": "Uživatel uložen",
"userSavedDescription": "Uživatel byl aktualizován.",
"autoProvisioned": "Automaticky poskytnuto",
"autoProvisionSettings": "Automatická nastavení",
"autoProvisionedDescription": "Povolit tomuto uživateli automaticky spravovat poskytovatel identity",
"accessControlsDescription": "Spravovat co může tento uživatel přistupovat a dělat v organizaci",
"accessControlsSubmit": "Uložit kontroly přístupu",
"singleRolePerUserPlanNotice": "Váš plán podporuje pouze jednu roli na uživatele.",
"singleRolePerUserEditionNotice": "Tato verze podporuje pouze jednu roli na uživatele.",
"roles": "Role",
"accessUsersRoles": "Spravovat uživatele a role",
"accessUsersRolesDescription": "Pozvěte uživatele a přidejte je do rolí pro správu přístupu k organizaci",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Nepodařilo se přepnout zdroj",
"resourcesErrorUpdateDescription": "Došlo k chybě při aktualizaci zdroje",
"access": "Přístup",
"accessControl": "Kontrola přístupu",
"shareLink": "{resource} Sdílet odkaz",
"resourceSelect": "Vyberte zdroj",
"shareLinks": "Sdílet odkazy",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Role odstraněna",
"accessRoleRemovedDescription": "Role byla úspěšně odstraněna.",
"accessRoleRequiredRemove": "Před odstraněním této role vyberte novou roli, do které chcete převést existující členy.",
"network": "Síť",
"manage": "Spravovat",
"sitesNotFound": "Nebyly nalezeny žádné stránky.",
"pangolinServerAdmin": "Správce serveru - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Jejda! Stránka, kterou hledáte, neexistuje.",
"overview": "Přehled",
"home": "Domů",
"accessControl": "Kontrola přístupu",
"settings": "Nastavení",
"usersAll": "Všichni uživatelé",
"license": "Licence",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Získat uživatele",
"actionGetOrgUser": "Získat uživatele organizace",
"actionListOrgDomains": "Seznam domén organizace",
"actionGetDomain": "Získat doménu",
"actionCreateOrgDomain": "Vytvořit doménu",
"actionUpdateOrgDomain": "Aktualizovat doménu",
"actionDeleteOrgDomain": "Odstranit doménu",
"actionGetDNSRecords": "Získat záznamy DNS",
"actionRestartOrgDomain": "Restartovat doménu",
"actionCreateSite": "Vytvořit lokalitu",
"actionDeleteSite": "Odstranění lokality",
"actionGetSite": "Získat web",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Zadejte nastavovací token z konzole serveru.",
"setupTokenRequired": "Je vyžadován token nastavení",
"actionUpdateSite": "Aktualizovat stránku",
"actionResetSiteBandwidth": "Resetovat šířku pásma organizace",
"actionListSiteRoles": "Seznam povolených rolí webu",
"actionCreateResource": "Vytvořit zdroj",
"actionDeleteResource": "Odstranit dokument",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Odstranit uživatele",
"actionListUsers": "Seznam uživatelů",
"actionAddUserRole": "Přidat uživatelskou roli",
"actionSetUserOrgRoles": "Nastavit uživatelské role",
"actionGenerateAccessToken": "Generovat přístupový token",
"actionDeleteAccessToken": "Odstranit přístupový token",
"actionListAccessTokens": "Seznam přístupových tokenů",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Soukromé",
"sidebarAccessControl": "Kontrola přístupu",
"sidebarLogsAndAnalytics": "Logy & Analytika",
"sidebarTeam": "Tým",
"sidebarUsers": "Uživatelé",
"sidebarAdmin": "Admin",
"sidebarInvitations": "Pozvánky",
"sidebarRoles": "Role",
"sidebarShareableLinks": "Odkazy",
"sidebarApiKeys": "API klíče",
"sidebarProvisioning": "Zajištění",
"sidebarSettings": "Nastavení",
"sidebarAllUsers": "Všichni uživatelé",
"sidebarIdentityProviders": "Poskytovatelé identity",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Plány",
"sidebarOrganization": "Organizace",
"sidebarManagement": "Správa",
"sidebarBillingAndLicenses": "Fakturace a licence",
"sidebarLogsAnalytics": "Analytici",
"blueprints": "Plány",
@@ -1289,7 +1358,6 @@
"parsedContents": "Parsed content (Pouze pro čtení)",
"enableDockerSocket": "Povolit Docker plán",
"enableDockerSocketDescription": "Povolte seškrábání štítků na Docker Socket pro popisky plánů. Nová cesta musí být k dispozici.",
"enableDockerSocketLink": "Zjistit více",
"viewDockerContainers": "Zobrazit kontejnery Dockeru",
"containersIn": "Kontejnery v {siteName}",
"selectContainerDescription": "Vyberte jakýkoli kontejner pro použití jako název hostitele pro tento cíl. Klikněte na port pro použití portu.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Jmenný prostor: {namespace}",
"domainPickerShowMore": "Zobrazit více",
"regionSelectorTitle": "Vybrat region",
"domainPickerRemoteExitNodeWarning": "Poskytnuté domény nejsou podporovány, když se stránky připojují k vzdáleným výstupním uzlům. Pro dostupné zdroje na vzdálených uzlech použijte vlastní doménu.",
"regionSelectorInfo": "Výběr regionu nám pomáhá poskytovat lepší výkon pro vaši polohu. Nemusíte být ve stejném regionu jako váš server.",
"regionSelectorPlaceholder": "Vyberte region",
"regionSelectorComingSoon": "Již brzy",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Upozornění na dostupnost funkce",
"billingFeatureLossDescription": "Po pomenutí budou funkce v novém plánu automaticky zakázány. Některá nastavení a konfigurace mohou být ztraceny. Zkontrolujte cenovou matrici, abyste pochopili, které funkce již nebudou k dispozici.",
"billingUsageExceedsLimit": "Aktuální využití ({current}) překračuje limit ({limit})",
"billingPastDueTitle": "Poslední splatnost platby",
"billingPastDueDescription": "Vaše platba je již splatná. Chcete-li pokračovat v používání aktuálních tarifů, aktualizujte prosím způsob platby. Pokud nebude vyřešeno, Vaše předplatné bude zrušeno a budete vráceno na úroveň zdarma.",
"billingUnpaidTitle": "Předplatné nezaplaceno",
"billingUnpaidDescription": "Vaše předplatné není zaplaceno a byli jste vráceni do bezplatné úrovně. Aktualizujte prosím svou platební metodu pro obnovení předplatného.",
"billingIncompleteTitle": "Platba nedokončena",
"billingIncompleteDescription": "Vaše platba je neúplná. Pro aktivaci předplatného prosím dokončete platební proces.",
"billingIncompleteExpiredTitle": "Platba vypršela",
"billingIncompleteExpiredDescription": "Vaše platba nebyla nikdy dokončena a vypršela. Byli jste vráceni na úroveň zdarma. Prosím, přihlašte se znovu pro obnovení přístupu k placeným funkcím.",
"billingManageSubscription": "Spravujte své předplatné",
"billingResolvePaymentIssue": "Vyřešte prosím problém s platbou před upgradem nebo upgradem",
"signUpTerms": {
"IAgreeToThe": "Souhlasím s",
"termsOfService": "podmínky služby",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Čas je v sekundách",
"requireDeviceApproval": "Vyžadovat schválení zařízení",
"requireDeviceApprovalDescription": "Uživatelé s touto rolí potřebují nová zařízení schválená správcem, než se mohou připojit a přistupovat ke zdrojům.",
"sshAccess": "SSH přístup",
"roleAllowSsh": "Povolit SSH",
"roleAllowSshAllow": "Povolit",
"roleAllowSshDisallow": "Zakázat",
"roleAllowSshDescription": "Povolit uživatelům s touto rolí připojení k zdrojům přes SSH. Je-li zakázáno, role nemůže používat přístup SSH.",
"sshSudoMode": "Súdánský přístup",
"sshSudoModeNone": "Nic",
"sshSudoModeNoneDescription": "Uživatel nemůže spouštět příkazy se sudo.",
"sshSudoModeFull": "Úplný Súdán",
"sshSudoModeFullDescription": "Uživatel může spustit libovolný příkaz se sudo.",
"sshSudoModeCommands": "Příkazy",
"sshSudoModeCommandsDescription": "Uživatel může spustit pouze zadané příkazy s sudo.",
"sshSudo": "Povolit sudo",
"sshSudoCommands": "Sudo příkazy",
"sshSudoCommandsDescription": "Čárkami oddělený seznam příkazů, které může uživatel spouštět s sudo.",
"sshCreateHomeDir": "Vytvořit domovský adresář",
"sshUnixGroups": "Unixové skupiny",
"sshUnixGroupsDescription": "Čárkou oddělené skupiny Unix přidají uživatele do cílového hostitele.",
"retryAttempts": "Opakovat pokusy",
"expectedResponseCodes": "Očekávané kódy odezvy",
"expectedResponseCodesDescription": "HTTP kód stavu, který označuje zdravý stav. Ponecháte-li prázdné, 200-300 je považováno za zdravé.",
@@ -1851,6 +1948,40 @@
"exitNode": "Ukončit uzel",
"country": "L 343, 22.12.2009, s. 1).",
"rulesMatchCountry": "Aktuálně založené na zdrojové IP adrese",
"region": "Oblasti",
"selectRegion": "Vyberte region",
"searchRegions": "Hledat regiony...",
"noRegionFound": "Nebyl nalezen žádný region.",
"rulesMatchRegion": "Vyberte regionální seskupení zemí",
"rulesErrorInvalidRegion": "Neplatný region",
"rulesErrorInvalidRegionDescription": "Vyberte prosím platný region.",
"regionAfrica": "Afrika",
"regionNorthernAfrica": "Severní Afrika",
"regionEasternAfrica": "Východní Afrika",
"regionMiddleAfrica": "Střední Afrika",
"regionSouthernAfrica": "Jižní Afrika",
"regionWesternAfrica": "Západní Afrika",
"regionAmericas": "Ameriky",
"regionCaribbean": "Karibské",
"regionCentralAmerica": "Střední Amerika",
"regionSouthAmerica": "Jižní Amerika",
"regionNorthernAmerica": "Severní Amerika",
"regionAsia": "Asie",
"regionCentralAsia": "Střední Asie",
"regionEasternAsia": "Východní Asie",
"regionSouthEasternAsia": "jihovýchodní Asie",
"regionSouthernAsia": "Jižní Asie",
"regionWesternAsia": "Západní Asie",
"regionEurope": "L 347, 20.12.2013, s. 965).",
"regionEasternEurope": "Východní Evropa",
"regionNorthernEurope": "Severní Evropa",
"regionSouthernEurope": "Jižní Evropa",
"regionWesternEurope": "Západní Evropa",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Austrálie a Nový Zéland",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Spravované vlastní hostování",
"description": "Spolehlivější a nízko udržovaný Pangolinův server s dalšími zvony a bičkami",
@@ -1899,6 +2030,25 @@
"invalidValue": "Neplatná hodnota",
"idpTypeLabel": "Typ poskytovatele identity",
"roleMappingExpressionPlaceholder": "např. obsahuje(skupiny, 'admin') && 'Admin' || 'Member'",
"roleMappingModeFixedRoles": "Pevné role",
"roleMappingModeMappingBuilder": "Tvorba mapování",
"roleMappingModeRawExpression": "Surový výraz",
"roleMappingFixedRolesPlaceholderSelect": "Vyberte jednu nebo více rolí",
"roleMappingFixedRolesPlaceholderFreeform": "Napište názvy rolí (shoda podle organizace)",
"roleMappingFixedRolesDescriptionSameForAll": "Přiřadit stejnou roli nastavenou každému uživateli automatického poskytování.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Pro výchozí zásady zadejte názvy rolí, které existují v každé organizaci, kde jsou uživatelé poskytováni. Jména musí přesně odpovídat.",
"roleMappingClaimPath": "Cesta k žádosti",
"roleMappingClaimPathPlaceholder": "skupiny",
"roleMappingClaimPathDescription": "Cesta k užitečnému zatížení tokenu, která obsahuje zdrojové hodnoty (například skupiny).",
"roleMappingMatchValue": "Hodnota zápasu",
"roleMappingAssignRoles": "Přiřadit role",
"roleMappingAddMappingRule": "Přidat pravidlo pro mapování",
"roleMappingRawExpressionResultDescription": "Výraz se musí vyhodnotit do pole řetězce nebo řetězce.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Výraz musí být vyhodnocen na řetězec (jediný název role).",
"roleMappingMatchValuePlaceholder": "Hodnota zápasu (například: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Napište názvy rolí (exact per org)",
"roleMappingBuilderFreeformRowHint": "Názvy rolí musí odpovídat roli v každé cílové organizaci.",
"roleMappingRemoveRule": "Odstranit",
"idpGoogleConfiguration": "Konfigurace Google",
"idpGoogleConfigurationDescription": "Konfigurace přihlašovacích údajů Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Jak dlouho uchovávat přístupové záznamy",
"logRetentionActionLabel": "Uchovávání protokolu akcí",
"logRetentionActionDescription": "Jak dlouho uchovávat záznamy akcí",
"logRetentionConnectionLabel": "Uchovávání protokolu připojení",
"logRetentionConnectionDescription": "Jak dlouho uchovávat protokoly připojení",
"logRetentionDisabled": "Zakázáno",
"logRetention3Days": "3 dny",
"logRetention7Days": "7 dní",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Konec následujícího roku",
"actionLogsDescription": "Zobrazit historii akcí provedených v této organizaci",
"accessLogsDescription": "Zobrazit žádosti o ověření přístupu pro zdroje v této organizaci",
"licenseRequiredToUse": "Pro použití této funkce je vyžadována licence <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> . Tato funkce je také dostupná v <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> je vyžadována pro použití této funkce. Tato funkce je také k dispozici v <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Protokoly připojení",
"connectionLogsDescription": "Zobrazit protokoly připojení pro tunely v této organizaci",
"sidebarLogsConnection": "Protokoly připojení",
"sidebarLogsStreaming": "Streamování",
"sourceAddress": "Zdrojová adresa",
"destinationAddress": "Cílová adresa",
"duration": "Doba trvání",
"licenseRequiredToUse": "Pro použití této funkce je vyžadována licence <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> nebo <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> . <bookADemoLink>Zarezervujte si demo nebo POC zkušební verzi</bookADemoLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> je vyžadována pro použití této funkce. Tato funkce je také k dispozici v <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Rezervujte si demo nebo POC zkušební verzi</bookADemoLink>.",
"certResolver": "Oddělovač certifikátů",
"certResolverDescription": "Vyberte řešitele certifikátů pro tento dokument.",
"selectCertResolver": "Vyberte řešič certifikátů",
@@ -2362,7 +2521,7 @@
"terms": "Výrazy",
"privacy": "Soukromí",
"security": "Zabezpečení",
"docs": "Dokumenty",
"docs": "Dokumentace",
"deviceActivation": "Aktivace zařízení",
"deviceCodeInvalidFormat": "Kód musí být 9 znaků (např. A1AJ-N5JD)",
"deviceCodeInvalidOrExpired": "Neplatný nebo prošlý kód",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Řízení přístupu",
"editInternalResourceDialogAccessControlDescription": "Kontrolujte, které role, uživatelé a klienti mohou přistupovat k tomuto prostředku, když jsou připojeni. Admini mají vždy přístup.",
"editInternalResourceDialogPortRangeValidationError": "Rozsah portů musí být \"*\" pro všechny porty, nebo seznam portů a rozsahů oddělených čárkou (např. \"80,443,8000-9000\"). Porty musí být mezi 1 a 65535.",
"internalResourceAuthDaemonStrategy": "SSH Auth Démon umístění",
"internalResourceAuthDaemonStrategyDescription": "Zvolte, kde běží SSH autentizační démon: na stránce (Newt) nebo na vzdáleném serveru.",
"internalResourceAuthDaemonDescription": "SSH autentizační daemon zpracovává podpis SSH klíče a PAM autentizaci tohoto zdroje. Vyberte si, zda běží na webu (Newt) nebo na samostatném vzdáleném serveru. Více informací najdete v <docsLink>dokumentaci</docsLink>.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Vybrat strategii",
"internalResourceAuthDaemonStrategyLabel": "Poloha",
"internalResourceAuthDaemonSite": "Na stránce",
"internalResourceAuthDaemonSiteDescription": "Auth daemon běží na webu (Newt).",
"internalResourceAuthDaemonRemote": "Vzdálený server",
"internalResourceAuthDaemonRemoteDescription": "Auth daemon běží na hostitele, který není web.",
"internalResourceAuthDaemonPort": "Daemon port (volitelné)",
"orgAuthWhatsThis": "Kde najdu ID mé organizace?",
"learnMore": "Zjistit více",
"backToHome": "Zpět na domovskou stránku",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Povolit schválení zařízení",
"approvalsEmptyStateStep2Description": "Upravte roli a povolte možnost 'Vyžadovat schválení zařízení'. Uživatelé s touto rolí budou potřebovat schválení pro nová zařízení správce.",
"approvalsEmptyStatePreviewDescription": "Náhled: Pokud je povoleno, čekající na zařízení se zde zobrazí žádosti o recenzi",
"approvalsEmptyStateButtonText": "Spravovat role"
"approvalsEmptyStateButtonText": "Spravovat role",
"domainErrorTitle": "Máme problém s ověřením tvé domény",
"idpAdminAutoProvisionPoliciesTabHint": "Nastavte pravidla mapování rolí a organizace na kartě <policiesTabLink>Automatická úprava nastavení</policiesTabLink>.",
"streamingTitle": "Streamování událostí",
"streamingDescription": "Streamujte události z vaší organizace do externích destinací v reálném čase.",
"streamingUnnamedDestination": "Nepojmenovaný cíl",
"streamingNoUrlConfigured": "Není nakonfigurována žádná URL",
"streamingAddDestination": "Přidat cíl",
"streamingHttpWebhookTitle": "HTTP webový háček",
"streamingHttpWebhookDescription": "Odeslat události na libovolný HTTP koncový bod s pružnou autentizací a šablonou.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Streamujte události do úložiště, které je kompatibilní se S3. Brzy přijde.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Přeposlat události přímo do vašeho účtu Datadog účtu. Brzy přijde.",
"streamingTypePickerDescription": "Vyberte cílový typ pro začátek.",
"streamingFailedToLoad": "Nepodařilo se načíst destinace",
"streamingUnexpectedError": "Došlo k neočekávané chybě.",
"streamingFailedToUpdate": "Nepodařilo se aktualizovat cíl",
"streamingDeletedSuccess": "Cíl byl úspěšně odstraněn",
"streamingFailedToDelete": "Nepodařilo se odstranit cíl",
"streamingDeleteTitle": "Odstranit cíl",
"streamingDeleteButtonText": "Odstranit cíl",
"streamingDeleteDialogAreYouSure": "Jste si jisti, že chcete odstranit",
"streamingDeleteDialogThisDestination": "tato destinace",
"streamingDeleteDialogPermanentlyRemoved": "? Všechny konfigurace budou trvale odstraněny.",
"httpDestEditTitle": "Upravit cíl",
"httpDestAddTitle": "Přidat cíl HTTP",
"httpDestEditDescription": "Aktualizovat konfiguraci pro tuto destinaci HTTP události",
"httpDestAddDescription": "Konfigurace nového koncového bodu HTTP pro příjem událostí vaší organizace.",
"httpDestTabSettings": "Nastavení",
"httpDestTabHeaders": "Záhlaví",
"httpDestTabBody": "Tělo",
"httpDestTabLogs": "Logy",
"httpDestNamePlaceholder": "Moje HTTP cíl",
"httpDestUrlLabel": "Cílová adresa URL",
"httpDestUrlErrorHttpRequired": "URL musí používat http nebo https",
"httpDestUrlErrorHttpsRequired": "HTTPS je vyžadován při nasazení do cloudu",
"httpDestUrlErrorInvalid": "Zadejte platnou URL (např. https://example.com/webhook)",
"httpDestAuthTitle": "Autentifikace",
"httpDestAuthDescription": "Zvolte, jak jsou požadavky na tvůj koncový bod ověřeny.",
"httpDestAuthNoneTitle": "Žádné ověření",
"httpDestAuthNoneDescription": "Odešle žádosti bez záhlaví autorizace.",
"httpDestAuthBearerTitle": "Token na doručitele",
"httpDestAuthBearerDescription": "Přidá autorizaci: Hlavička Bearer <token> ke každému požadavku.",
"httpDestAuthBearerPlaceholder": "Váš API klíč nebo token",
"httpDestAuthBasicTitle": "Základní ověření",
"httpDestAuthBasicDescription": "Přidá autorizaci: Základní <credentials> hlavička. Poskytněte přihlašovací údaje jako uživatelské jméno:password.",
"httpDestAuthBasicPlaceholder": "uživatelské jméno:heslo",
"httpDestAuthCustomTitle": "Vlastní záhlaví",
"httpDestAuthCustomDescription": "Zadejte název a hodnotu vlastního HTTP hlavičky pro ověření (např. X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Název záhlaví (např. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Hodnota záhlaví",
"httpDestCustomHeadersTitle": "Vlastní HTTP hlavičky",
"httpDestCustomHeadersDescription": "Přidat vlastní hlavičky ke každému odchozímu požadavku. Užitečné pro statické tokeny nebo vlastní Typ obsahu. Ve výchozím nastavení je typ obsahu: application/json.",
"httpDestNoHeadersConfigured": "Nejsou nakonfigurovány žádné vlastní záhlaví. Pro přidání klikněte na \"Přidat záhlaví\".",
"httpDestHeaderNamePlaceholder": "Název záhlaví",
"httpDestHeaderValuePlaceholder": "Hodnota",
"httpDestAddHeader": "Přidat záhlaví",
"httpDestBodyTemplateTitle": "Vlastní šablona těla",
"httpDestBodyTemplateDescription": "Ovládá strukturu užitečného zatížení JSON odeslanou na váš koncový bod. Pokud je vypnuto, je pro každou událost zaslán výchozí objekt JSON.",
"httpDestEnableBodyTemplate": "Povolit vlastní šablonu těla",
"httpDestBodyTemplateLabel": "Šablona těla (JSON)",
"httpDestBodyTemplateHint": "Použijte šablonové proměnné pro referenční pole události ve vašem užitečném zatížení.",
"httpDestPayloadFormatTitle": "Formát datového zatížení",
"httpDestPayloadFormatDescription": "Jak jsou události serializovány v každém žádajícím subjektu.",
"httpDestFormatJsonArrayTitle": "JSON pole",
"httpDestFormatJsonArrayDescription": "Jeden požadavek na každou šarži, tělo je pole JSON. Kompatibilní s většinou generických webových háčků a Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Jeden požadavek na každou šarži, tělo je nově ohraničené JSON jeden objekt na jednu čáru, bez vnějšího pole. Vyžaduje Splunk HEC, Elastic / OpenSearch, a Grafana Loki.",
"httpDestFormatSingleTitle": "Jedna událost na požadavek",
"httpDestFormatSingleDescription": "Odešle samostatnou HTTP POST pro každou jednotlivou událost. Používejte pouze pro koncové body, které nemohou zpracovávat dávky.",
"httpDestLogTypesTitle": "Typy protokolů",
"httpDestLogTypesDescription": "Vyberte, které typy logů jsou přesměrovány do této destinace. Budou streamovány pouze povolené typy logů.",
"httpDestAccessLogsTitle": "Protokoly přístupu",
"httpDestAccessLogsDescription": "Pokusy o přístup k dokumentům, včetně ověřených a zamítnutých požadavků.",
"httpDestActionLogsTitle": "Záznamy akcí",
"httpDestActionLogsDescription": "Správní opatření prováděná uživateli v rámci organizace.",
"httpDestConnectionLogsTitle": "Protokoly připojení",
"httpDestConnectionLogsDescription": "Události týkající se připojení lokality a tunelu, včetně připojení a odpojení.",
"httpDestRequestLogsTitle": "Záznamy požadavků",
"httpDestRequestLogsDescription": "HTTP záznamy požadavků pro proxy zdroje, včetně metod, cesty a kódu odpovědi.",
"httpDestSaveChanges": "Uložit změny",
"httpDestCreateDestination": "Vytvořit cíl",
"httpDestUpdatedSuccess": "Cíl byl úspěšně aktualizován",
"httpDestCreatedSuccess": "Cíl byl úspěšně vytvořen",
"httpDestUpdateFailed": "Nepodařilo se aktualizovat cíl",
"httpDestCreateFailed": "Nepodařilo se vytvořit cíl"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Link erstellen",
"resourcesNotFound": "Keine Ressourcen gefunden",
"resourceSearch": "Suche Ressourcen",
"machineSearch": "Maschinen suchen",
"machinesSearch": "Suche Maschinen-Klienten...",
"machineNotFound": "Keine Maschinen gefunden",
"userDeviceSearch": "Benutzergeräte durchsuchen",
"userDevicesSearch": "Benutzergeräte durchsuchen...",
"openMenu": "Menü öffnen",
"resource": "Ressource",
"title": "Titel",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy-Anfragen über HTTPS mit einem voll qualifizierten Domain-Namen.",
"resourceRaw": "Direkte TCP/UDP Ressource (raw)",
"resourceRawDescription": "Proxy-Anfragen über rohes TCP/UDP mit einer Portnummer.",
"resourceRawDescriptionCloud": "Proxy-Anfragen über rohe TCP/UDP mit Portnummer. Benötigt Sites, um sich mit einem entfernten Knoten zu verbinden.",
"resourceCreate": "Ressource erstellen",
"resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen",
"resourceSeeAll": "Alle Ressourcen anzeigen",
@@ -322,6 +328,54 @@
"apiKeysDelete": "API-Schlüssel löschen",
"apiKeysManage": "API-Schlüssel verwalten",
"apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet",
"provisioningKeysTitle": "Bereitstellungsschlüssel",
"provisioningKeysManage": "Bereitstellungsschlüssel verwalten",
"provisioningKeysDescription": "Bereitstellungsschlüssel werden verwendet, um die automatisierte Bereitstellung von Seiten für Ihr Unternehmen zu authentifizieren.",
"provisioningManage": "Bereitstellung",
"provisioningDescription": "Bereitstellungsschlüssel verwalten und ausstehende Seiten prüfen, die noch auf Genehmigung warten.",
"pendingSites": "Ausstehende Seiten",
"siteApproveSuccess": "Site erfolgreich freigegeben",
"siteApproveError": "Fehler beim Bestätigen der Seite",
"provisioningKeys": "Bereitstellungsschlüssel",
"searchProvisioningKeys": "Bereitstellungsschlüssel suchen...",
"provisioningKeysAdd": "Bereitstellungsschlüssel generieren",
"provisioningKeysErrorDelete": "Fehler beim Löschen des Bereitstellungsschlüssels",
"provisioningKeysErrorDeleteMessage": "Fehler beim Löschen des Bereitstellungsschlüssels",
"provisioningKeysQuestionRemove": "Sind Sie sicher, dass Sie diesen Bereitstellungsschlüssel aus der Organisation entfernen möchten?",
"provisioningKeysMessageRemove": "Einmal entfernt, kann der Schlüssel nicht mehr für die Bereitstellung der Site verwendet werden.",
"provisioningKeysDeleteConfirm": "Bereitstellungsschlüssel löschen bestätigen",
"provisioningKeysDelete": "Bereitstellungsschlüssel löschen",
"provisioningKeysCreate": "Bereitstellungsschlüssel generieren",
"provisioningKeysCreateDescription": "Einen neuen Bereitstellungsschlüssel für die Organisation generieren",
"provisioningKeysSeeAll": "Alle Bereitstellungsschlüssel anzeigen",
"provisioningKeysSave": "Bereitstellungsschlüssel speichern",
"provisioningKeysSaveDescription": "Sie können dies nur einmal sehen. Kopieren Sie es an einen sicheren Ort.",
"provisioningKeysErrorCreate": "Fehler beim Erstellen des Bereitstellungsschlüssels",
"provisioningKeysList": "Neuer Bereitstellungsschlüssel",
"provisioningKeysMaxBatchSize": "Max. Batch-Größe",
"provisioningKeysUnlimitedBatchSize": "Unbegrenzte Batch-Größe (kein Limit)",
"provisioningKeysMaxBatchUnlimited": "Unbegrenzt",
"provisioningKeysMaxBatchSizeInvalid": "Geben Sie eine gültige maximale Batchgröße ein (11.000.000).",
"provisioningKeysValidUntil": "Gültig bis",
"provisioningKeysValidUntilHint": "Leer lassen für keine Verjährung.",
"provisioningKeysValidUntilInvalid": "Geben Sie ein gültiges Datum und Zeit ein.",
"provisioningKeysNumUsed": "Verwendete Zeiten",
"provisioningKeysLastUsed": "Zuletzt verwendet",
"provisioningKeysNoExpiry": "Kein Ablauf",
"provisioningKeysNeverUsed": "Nie",
"provisioningKeysEdit": "Bereitstellungsschlüssel bearbeiten",
"provisioningKeysEditDescription": "Aktualisieren Sie die maximale Batch-Größe und Ablaufzeit für diesen Schlüssel.",
"provisioningKeysApproveNewSites": "Neue Seiten genehmigen",
"provisioningKeysApproveNewSitesDescription": "Sites, die sich mit diesem Schlüssel registrieren, automatisch freigeben.",
"provisioningKeysUpdateError": "Fehler beim Aktualisieren des Bereitstellungsschlüssels",
"provisioningKeysUpdated": "Bereitstellungsschlüssel aktualisiert",
"provisioningKeysUpdatedDescription": "Ihre Änderungen wurden gespeichert.",
"provisioningKeysBannerTitle": "Website-Bereitstellungsschlüssel",
"provisioningKeysBannerDescription": "Generieren Sie einen Bereitstellungsschlüssel und verwenden Sie ihn mit dem Newt-Konnektor, um beim ersten Start automatisch Sites zu erstellen keine Notwendigkeit, separate Anmeldeinformationen für jede Seite einzurichten.",
"provisioningKeysBannerButtonText": "Mehr erfahren",
"pendingSitesBannerTitle": "Ausstehende Seiten",
"pendingSitesBannerDescription": "Sites, die sich mit einem Bereitstellungsschlüssel verbinden, erscheinen hier zur Überprüfung. Bestätigen Sie jede Site, bevor sie aktiv wird und erhalten Zugriff auf Ihre Ressourcen.",
"pendingSitesBannerButtonText": "Mehr erfahren",
"apiKeysSettings": "{apiKeyName} Einstellungen",
"userTitle": "Alle Benutzer verwalten",
"userDescription": "Alle Benutzer im System anzeigen und verwalten",
@@ -508,9 +562,12 @@
"userSaved": "Benutzer gespeichert",
"userSavedDescription": "Der Benutzer wurde aktualisiert.",
"autoProvisioned": "Automatisch bereitgestellt",
"autoProvisionSettings": "Auto-Bereitstellungseinstellungen",
"autoProvisionedDescription": "Erlaube diesem Benutzer die automatische Verwaltung durch Identitätsanbieter",
"accessControlsDescription": "Verwalten Sie, worauf dieser Benutzer in der Organisation zugreifen und was er tun kann",
"accessControlsSubmit": "Zugriffskontrollen speichern",
"singleRolePerUserPlanNotice": "Ihr Plan unterstützt nur eine Rolle pro Benutzer.",
"singleRolePerUserEditionNotice": "Diese Ausgabe unterstützt nur eine Rolle pro Benutzer.",
"roles": "Rollen",
"accessUsersRoles": "Benutzer & Rollen verwalten",
"accessUsersRolesDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf die Organisation zu verwalten",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Fehler beim Umschalten der Ressource",
"resourcesErrorUpdateDescription": "Beim Aktualisieren der Ressource ist ein Fehler aufgetreten",
"access": "Zugriff",
"accessControl": "Zugriffskontrolle",
"shareLink": "{resource} Freigabe-Link",
"resourceSelect": "Ressource auswählen",
"shareLinks": "Freigabe-Links",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rolle entfernt",
"accessRoleRemovedDescription": "Die Rolle wurde erfolgreich entfernt.",
"accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.",
"network": "Netzwerk",
"manage": "Verwalten",
"sitesNotFound": "Keine Standorte gefunden.",
"pangolinServerAdmin": "Server-Admin - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Hoppla! Die gesuchte Seite existiert nicht.",
"overview": "Übersicht",
"home": "Startseite",
"accessControl": "Zugriffskontrolle",
"settings": "Einstellungen",
"usersAll": "Alle Benutzer",
"license": "Lizenz",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Benutzer abrufen",
"actionGetOrgUser": "Organisationsbenutzer abrufen",
"actionListOrgDomains": "Organisationsdomains auflisten",
"actionGetDomain": "Domain abrufen",
"actionCreateOrgDomain": "Domain erstellen",
"actionUpdateOrgDomain": "Domain aktualisieren",
"actionDeleteOrgDomain": "Domain löschen",
"actionGetDNSRecords": "DNS-Einträge abrufen",
"actionRestartOrgDomain": "Domain neu starten",
"actionCreateSite": "Standort erstellen",
"actionDeleteSite": "Standort löschen",
"actionGetSite": "Standort abrufen",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Geben Sie das Setup-Token von der Serverkonsole ein.",
"setupTokenRequired": "Setup-Token ist erforderlich",
"actionUpdateSite": "Standorte aktualisieren",
"actionResetSiteBandwidth": "Organisations-Bandbreite zurücksetzen",
"actionListSiteRoles": "Erlaubte Standort-Rollen auflisten",
"actionCreateResource": "Ressource erstellen",
"actionDeleteResource": "Ressource löschen",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Benutzer entfernen",
"actionListUsers": "Benutzer auflisten",
"actionAddUserRole": "Benutzerrolle hinzufügen",
"actionSetUserOrgRoles": "Benutzerrollen festlegen",
"actionGenerateAccessToken": "Zugriffstoken generieren",
"actionDeleteAccessToken": "Zugriffstoken löschen",
"actionListAccessTokens": "Zugriffstoken auflisten",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privat",
"sidebarAccessControl": "Zugriffskontrolle",
"sidebarLogsAndAnalytics": "Protokolle & Analysen",
"sidebarTeam": "Team",
"sidebarUsers": "Benutzer",
"sidebarAdmin": "Admin",
"sidebarInvitations": "Einladungen",
"sidebarRoles": "Rollen",
"sidebarShareableLinks": "Links",
"sidebarApiKeys": "API-Schlüssel",
"sidebarProvisioning": "Bereitstellung",
"sidebarSettings": "Einstellungen",
"sidebarAllUsers": "Alle Benutzer",
"sidebarIdentityProviders": "Identitätsanbieter",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Log & Analytik",
"sidebarBluePrints": "Blaupausen",
"sidebarOrganization": "Organisation",
"sidebarManagement": "Management",
"sidebarBillingAndLicenses": "Abrechnung & Lizenzen",
"sidebarLogsAnalytics": "Analytik",
"blueprints": "Blaupausen",
@@ -1289,7 +1358,6 @@
"parsedContents": "Analysierte Inhalte (Nur lesen)",
"enableDockerSocket": "Docker Blueprint aktivieren",
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blueprintbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
"enableDockerSocketLink": "Mehr erfahren",
"viewDockerContainers": "Docker Container anzeigen",
"containersIn": "Container in {siteName}",
"selectContainerDescription": "Wählen Sie einen Container, der als Hostname für dieses Ziel verwendet werden soll. Klicken Sie auf einen Port, um einen Port zu verwenden.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Namespace: {namespace}",
"domainPickerShowMore": "Mehr anzeigen",
"regionSelectorTitle": "Region auswählen",
"domainPickerRemoteExitNodeWarning": "Angegebene Domains werden nicht unterstützt, wenn sich Websites mit externen Exit-Knoten verbinden. Damit Ressourcen auf entfernten Knoten verfügbar sind, verwenden Sie stattdessen eine eigene Domain.",
"regionSelectorInfo": "Das Auswählen einer Region hilft uns, eine bessere Leistung für Ihren Standort bereitzustellen. Sie müssen sich nicht in derselben Region wie Ihr Server befinden.",
"regionSelectorPlaceholder": "Wähle eine Region",
"regionSelectorComingSoon": "Kommt bald",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Verfügbarkeitshinweis",
"billingFeatureLossDescription": "Durch Herabstufung werden Funktionen, die im neuen Paket nicht verfügbar sind, automatisch deaktiviert. Einige Einstellungen und Konfigurationen können verloren gehen. Bitte überprüfen Sie die Preismatrix um zu verstehen, welche Funktionen nicht mehr verfügbar sein werden.",
"billingUsageExceedsLimit": "Aktuelle Nutzung ({current}) überschreitet das Limit ({limit})",
"billingPastDueTitle": "Zahlung vergangene Fälligkeit",
"billingPastDueDescription": "Ihre Zahlung ist abgelaufen. Bitte aktualisieren Sie Ihre Zahlungsmethode, um die aktuellen Funktionen Ihres Pakets weiter zu nutzen. Wenn nicht geklärt, wird Ihr Abonnement abgebrochen und Sie werden auf die kostenlose Stufe zurückgekehrt.",
"billingUnpaidTitle": "Unbezahltes Abonnement",
"billingUnpaidDescription": "Dein Abonnement ist unbezahlt und du wurdest auf die kostenlose Stufe zurückgekehrt. Bitte aktualisiere deine Zahlungsmethode, um dein Abonnement wiederherzustellen.",
"billingIncompleteTitle": "Zahlung unvollständig",
"billingIncompleteDescription": "Ihre Zahlung ist unvollständig. Bitte schließen Sie den Zahlungsvorgang ab, um Ihr Abonnement zu aktivieren.",
"billingIncompleteExpiredTitle": "Zahlung abgelaufen",
"billingIncompleteExpiredDescription": "Deine Zahlung wurde nie abgeschlossen und ist abgelaufen. Du wurdest zur kostenlosen Stufe zurückgekehrt. Bitte melde dich erneut an, um den Zugriff auf kostenpflichtige Funktionen wiederherzustellen.",
"billingManageSubscription": "Verwalten Sie Ihr Abonnement",
"billingResolvePaymentIssue": "Bitte beheben Sie Ihr Zahlungsproblem vor dem Upgrade oder Herabstufen",
"signUpTerms": {
"IAgreeToThe": "Ich stimme den",
"termsOfService": "Nutzungsbedingungen zu",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Zeit ist in Sekunden",
"requireDeviceApproval": "Gerätegenehmigungen erforderlich",
"requireDeviceApprovalDescription": "Benutzer mit dieser Rolle benötigen neue Geräte, die von einem Administrator genehmigt wurden, bevor sie sich verbinden und auf Ressourcen zugreifen können.",
"sshAccess": "SSH-Zugriff",
"roleAllowSsh": "SSH erlauben",
"roleAllowSshAllow": "Erlauben",
"roleAllowSshDisallow": "Nicht zulassen",
"roleAllowSshDescription": "Benutzern mit dieser Rolle erlauben, sich über SSH mit Ressourcen zu verbinden. Wenn deaktiviert, kann die Rolle keinen SSH-Zugriff verwenden.",
"sshSudoMode": "Sudo-Zugriff",
"sshSudoModeNone": "Keine",
"sshSudoModeNoneDescription": "Benutzer kann keine Befehle mit sudo ausführen.",
"sshSudoModeFull": "Volles Sudo",
"sshSudoModeFullDescription": "Benutzer kann jeden Befehl mit sudo ausführen.",
"sshSudoModeCommands": "Befehle",
"sshSudoModeCommandsDescription": "Benutzer kann nur die angegebenen Befehle mit sudo ausführen.",
"sshSudo": "sudo erlauben",
"sshSudoCommands": "Sudo-Befehle",
"sshSudoCommandsDescription": "Kommagetrennte Liste von Befehlen, die der Benutzer mit sudo ausführen darf.",
"sshCreateHomeDir": "Home-Verzeichnis erstellen",
"sshUnixGroups": "Unix-Gruppen",
"sshUnixGroupsDescription": "Durch Komma getrennte Unix-Gruppen, um den Benutzer auf dem Zielhost hinzuzufügen.",
"retryAttempts": "Wiederholungsversuche",
"expectedResponseCodes": "Erwartete Antwortcodes",
"expectedResponseCodesDescription": "HTTP-Statuscode, der einen gesunden Zustand anzeigt. Wenn leer gelassen, wird 200-300 als gesund angesehen.",
@@ -1851,6 +1948,40 @@
"exitNode": "Exit-Node",
"country": "Land",
"rulesMatchCountry": "Derzeit basierend auf der Quell-IP",
"region": "Region",
"selectRegion": "Region wählen...",
"searchRegions": "Regionen suchen...",
"noRegionFound": "Keine Region gefunden.",
"rulesMatchRegion": "Wählen Sie eine Regionalgruppe von Ländern",
"rulesErrorInvalidRegion": "Ungültige Region",
"rulesErrorInvalidRegionDescription": "Bitte wählen Sie eine gültige Region aus.",
"regionAfrica": "Afrika",
"regionNorthernAfrica": "Nordafrika",
"regionEasternAfrica": "Ostafrika",
"regionMiddleAfrica": "Zentralafrika",
"regionSouthernAfrica": "Südliches Afrika",
"regionWesternAfrica": "Westafrika",
"regionAmericas": "Amerika",
"regionCaribbean": "Karibik",
"regionCentralAmerica": "Mittelamerika",
"regionSouthAmerica": "Südamerika",
"regionNorthernAmerica": "Nordamerika",
"regionAsia": "Asien",
"regionCentralAsia": "Zentralasien",
"regionEasternAsia": "Ostasien",
"regionSouthEasternAsia": "Südostasien",
"regionSouthernAsia": "Südasien",
"regionWesternAsia": "Westasien",
"regionEurope": "Europa",
"regionEasternEurope": "Osteuropa",
"regionNorthernEurope": "Nordeuropa",
"regionSouthernEurope": "Südeuropa",
"regionWesternEurope": "Westeuropa",
"regionOceania": "Ozeanien",
"regionAustraliaAndNewZealand": "Australien und Neuseeland",
"regionMelanesia": "Melanesien",
"regionMicronesia": "Mikronesien",
"regionPolynesia": "Polynesien",
"managedSelfHosted": {
"title": "Verwaltetes Selbsthosted",
"description": "Zuverlässiger und wartungsarmer Pangolin Server mit zusätzlichen Glocken und Pfeifen",
@@ -1899,6 +2030,25 @@
"invalidValue": "Ungültiger Wert",
"idpTypeLabel": "Identitätsanbietertyp",
"roleMappingExpressionPlaceholder": "z. B. enthalten(Gruppen, 'admin') && 'Admin' || 'Mitglied'",
"roleMappingModeFixedRoles": "Feste Rollen",
"roleMappingModeMappingBuilder": "Mapping Builder",
"roleMappingModeRawExpression": "Roher Ausdruck",
"roleMappingFixedRolesPlaceholderSelect": "Wählen Sie eine oder mehrere Rollen",
"roleMappingFixedRolesPlaceholderFreeform": "Rollennamen eingeben (exakte Übereinstimmung pro Organisation)",
"roleMappingFixedRolesDescriptionSameForAll": "Weisen Sie jedem auto-provisionierten Benutzer die gleiche Rolle zu.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Für Standardrichtlinien geben Sie Rollennamen ein, die in jeder Organisation existieren, in der Benutzer angegeben sind. Namen müssen exakt übereinstimmen.",
"roleMappingClaimPath": "Pfad einfordern",
"roleMappingClaimPathPlaceholder": "gruppen",
"roleMappingClaimPathDescription": "Pfad in der Token Payload mit Quellwerten (zum Beispiel Gruppen).",
"roleMappingMatchValue": "Match-Wert",
"roleMappingAssignRoles": "Rollen zuweisen",
"roleMappingAddMappingRule": "Zuordnungsregel hinzufügen",
"roleMappingRawExpressionResultDescription": "Ausdruck muss zu einem String oder String Array ausgewertet werden.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Ausdruck muss zu einem String (einem einzigen Rollennamen) ausgewertet werden.",
"roleMappingMatchValuePlaceholder": "Match-Wert (z. B.: Admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Rollennamen eingeben (exakt pro Ort)",
"roleMappingBuilderFreeformRowHint": "Rollennamen müssen mit einer Rolle in jeder Zielorganisation übereinstimmen.",
"roleMappingRemoveRule": "Entfernen",
"idpGoogleConfiguration": "Google-Konfiguration",
"idpGoogleConfigurationDescription": "Google OAuth2 Zugangsdaten konfigurieren",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Wie lange Zugriffsprotokolle beibehalten werden sollen",
"logRetentionActionLabel": "Aktionsprotokoll-Speicherung",
"logRetentionActionDescription": "Dauer des Action-Logs",
"logRetentionConnectionLabel": "Verbindungsprotokoll-Speicherung",
"logRetentionConnectionDescription": "Wie lange Verbindungsprotokolle gespeichert werden sollen",
"logRetentionDisabled": "Deaktiviert",
"logRetention3Days": "3 Tage",
"logRetention7Days": "7 Tage",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Ende des folgenden Jahres",
"actionLogsDescription": "Verlauf der in dieser Organisation durchgeführten Aktionen anzeigen",
"accessLogsDescription": "Zugriffsauth-Anfragen für Ressourcen in dieser Organisation anzeigen",
"licenseRequiredToUse": "Um diese Funktion nutzen zu können, ist eine <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> Lizenz erforderlich. Diese Funktion ist auch in der <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> verfügbar.",
"ossEnterpriseEditionRequired": "Um diese Funktion nutzen zu können, ist die <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> erforderlich. Diese Funktion ist auch in der <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> verfügbar.",
"connectionLogs": "Verbindungsprotokolle",
"connectionLogsDescription": "Verbindungsprotokolle für Tunnel in dieser Organisation anzeigen",
"sidebarLogsConnection": "Verbindungsprotokolle",
"sidebarLogsStreaming": "Streaming",
"sourceAddress": "Quelladresse",
"destinationAddress": "Zieladresse",
"duration": "Dauer",
"licenseRequiredToUse": "Eine <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> Lizenz oder <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> wird benötigt, um diese Funktion nutzen zu können. <bookADemoLink>Buchen Sie eine Demo oder POC Testversion</bookADemoLink>.",
"ossEnterpriseEditionRequired": "Die <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> wird benötigt, um diese Funktion nutzen zu können. Diese Funktion ist auch in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>verfügbar. <bookADemoLink>Buchen Sie eine Demo oder POC Testversion</bookADemoLink>.",
"certResolver": "Zertifikatsauflöser",
"certResolverDescription": "Wählen Sie den Zertifikatslöser aus, der für diese Ressource verwendet werden soll.",
"selectCertResolver": "Zertifikatsauflöser auswählen",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Zugriffskontrolle",
"editInternalResourceDialogAccessControlDescription": "Kontrollieren Sie, welche Rollen, Benutzer und Maschinen-Clients Zugriff auf diese Ressource haben, wenn sie verbunden sind. Admins haben immer Zugriff.",
"editInternalResourceDialogPortRangeValidationError": "Der Port-Bereich muss \"*\" für alle Ports sein, oder eine kommaseparierte Liste von Ports und Bereichen (z.B. \"80,443.8000-9000\"). Ports müssen zwischen 1 und 65535 liegen.",
"internalResourceAuthDaemonStrategy": "SSH Auth-Daemon Standort",
"internalResourceAuthDaemonStrategyDescription": "Wählen Sie aus, wo der SSH-Authentifizierungs-Daemon läuft: auf der Site (Newt) oder auf einem entfernten Host.",
"internalResourceAuthDaemonDescription": "Der SSH-Authentifizierungs-Daemon verarbeitet SSH-Schlüsselsignaturen und PAM-Authentifizierung für diese Ressource. Wählen Sie, ob sie auf der Website (Newt) oder auf einem separaten entfernten Host ausgeführt wird. Siehe <docsLink>die Dokumentation</docsLink> für mehr.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Strategie auswählen",
"internalResourceAuthDaemonStrategyLabel": "Standort",
"internalResourceAuthDaemonSite": "Vor Ort",
"internalResourceAuthDaemonSiteDescription": "Der Auth Daemon läuft auf der Seite (Newt).",
"internalResourceAuthDaemonRemote": "Entfernter Host",
"internalResourceAuthDaemonRemoteDescription": "Der Auth Daemon läuft auf einem Host, der nicht die Site ist.",
"internalResourceAuthDaemonPort": "Daemon-Port (optional)",
"orgAuthWhatsThis": "Wo finde ich meine Organisations-ID?",
"learnMore": "Mehr erfahren",
"backToHome": "Zurück zur Startseite",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Gerätegenehmigungen aktivieren",
"approvalsEmptyStateStep2Description": "Bearbeite eine Rolle und aktiviere die Option 'Gerätegenehmigung erforderlich'. Benutzer mit dieser Rolle benötigen Administrator-Genehmigung für neue Geräte.",
"approvalsEmptyStatePreviewDescription": "Vorschau: Wenn aktiviert, werden ausstehende Geräteanfragen hier zur Überprüfung angezeigt",
"approvalsEmptyStateButtonText": "Rollen verwalten"
"approvalsEmptyStateButtonText": "Rollen verwalten",
"domainErrorTitle": "Wir haben Probleme mit der Überprüfung deiner Domain",
"idpAdminAutoProvisionPoliciesTabHint": "Konfigurieren Sie Rollenzuordnungs- und Organisationsrichtlinien auf der Registerkarte <policiesTabLink>Auto-Bereitstellungseinstellungen</policiesTabLink>.",
"streamingTitle": "Event Streaming",
"streamingDescription": "Streamen Sie Events aus Ihrem Unternehmen in Echtzeit zu externen Zielen.",
"streamingUnnamedDestination": "Unbenanntes Ziel",
"streamingNoUrlConfigured": "Keine URL konfiguriert",
"streamingAddDestination": "Ziel hinzufügen",
"streamingHttpWebhookTitle": "HTTP Webhook",
"streamingHttpWebhookDescription": "Sende Ereignisse an jeden HTTP-Endpunkt mit flexibler Authentifizierung und Vorlage.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Streame Ereignisse in eine S3-kompatible Objekt-Speicher-Eimer. Kommt bald.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Events direkt an Ihr Datadog Konto weiterleiten. Kommen Sie bald.",
"streamingTypePickerDescription": "Wählen Sie einen Zieltyp aus, um loszulegen.",
"streamingFailedToLoad": "Fehler beim Laden der Ziele",
"streamingUnexpectedError": "Ein unerwarteter Fehler ist aufgetreten.",
"streamingFailedToUpdate": "Fehler beim Aktualisieren des Ziels",
"streamingDeletedSuccess": "Ziel erfolgreich gelöscht",
"streamingFailedToDelete": "Fehler beim Löschen des Ziels",
"streamingDeleteTitle": "Ziel löschen",
"streamingDeleteButtonText": "Ziel löschen",
"streamingDeleteDialogAreYouSure": "Sind Sie sicher, dass Sie löschen möchten",
"streamingDeleteDialogThisDestination": "dieses Ziel",
"streamingDeleteDialogPermanentlyRemoved": "? Alle Konfiguration wird dauerhaft entfernt.",
"httpDestEditTitle": "Ziel bearbeiten",
"httpDestAddTitle": "HTTP-Ziel hinzufügen",
"httpDestEditDescription": "Aktualisiere die Konfiguration für dieses HTTP-Streaming-Ziel.",
"httpDestAddDescription": "Konfigurieren Sie einen neuen HTTP-Endpunkt, um die Ereignisse Ihrer Organisation zu empfangen.",
"httpDestTabSettings": "Einstellungen",
"httpDestTabHeaders": "Kopfzeilen",
"httpDestTabBody": "Körper",
"httpDestTabLogs": "Logs",
"httpDestNamePlaceholder": "Mein HTTP-Ziel",
"httpDestUrlLabel": "Ziel-URL",
"httpDestUrlErrorHttpRequired": "URL muss http oder https verwenden",
"httpDestUrlErrorHttpsRequired": "HTTPS wird für Cloud-Deployment benötigt",
"httpDestUrlErrorInvalid": "Geben Sie eine gültige URL ein (z.B. https://example.com/webhook)",
"httpDestAuthTitle": "Authentifizierung",
"httpDestAuthDescription": "Legen Sie fest, wie Anfragen an Ihren Endpunkt authentifiziert werden.",
"httpDestAuthNoneTitle": "Keine Authentifizierung",
"httpDestAuthNoneDescription": "Sendet Anfragen ohne Autorisierungs-Header.",
"httpDestAuthBearerTitle": "Bären-Token",
"httpDestAuthBearerDescription": "Fügt eine Berechtigung hinzu: Bearer <token> Header zu jeder Anfrage.",
"httpDestAuthBearerPlaceholder": "Ihr API-Schlüssel oder Token",
"httpDestAuthBasicTitle": "Einfacher Auth",
"httpDestAuthBasicDescription": "Fügt eine Autorisierung hinzu: Basic <credentials> Kopfzeile hinzu. Geben Sie Anmeldedaten als Benutzername:password an.",
"httpDestAuthBasicPlaceholder": "benutzername:password",
"httpDestAuthCustomTitle": "Eigene Kopfzeile",
"httpDestAuthCustomDescription": "Geben Sie einen eigenen HTTP-Header-Namen und einen Wert für die Authentifizierung an (z.B. X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Headername (z.B. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Header-Wert",
"httpDestCustomHeadersTitle": "Eigene HTTP-Header",
"httpDestCustomHeadersDescription": "Fügen Sie jeder ausgehenden Anfrage benutzerdefinierte Kopfzeilen hinzu. Nützlich für statische Tokens oder einen benutzerdefinierten Content-Typ. Standardmäßig wird Content-Type: application/json gesendet.",
"httpDestNoHeadersConfigured": "Keine benutzerdefinierten Header konfiguriert. Klicken Sie auf \"Header hinzufügen\", um einen hinzuzufügen.",
"httpDestHeaderNamePlaceholder": "Header-Name",
"httpDestHeaderValuePlaceholder": "Wert",
"httpDestAddHeader": "Header hinzufügen",
"httpDestBodyTemplateTitle": "Eigene Body-Vorlage",
"httpDestBodyTemplateDescription": "Steuere die JSON-Payload-Struktur, die an deinen Endpunkt gesendet wurde. Wenn deaktiviert, wird für jede Veranstaltung ein Standard-JSON-Objekt gesendet.",
"httpDestEnableBodyTemplate": "Eigene Körpervorlage aktivieren",
"httpDestBodyTemplateLabel": "Body-Vorlage (JSON)",
"httpDestBodyTemplateHint": "Verwenden Sie Template-Variablen, um Ereignisfelder in Ihrer Payload zu referenzieren.",
"httpDestPayloadFormatTitle": "Payload-Format",
"httpDestPayloadFormatDescription": "Wie Ereignisse in jedes Anfragegremium serialisiert werden.",
"httpDestFormatJsonArrayTitle": "JSON Array",
"httpDestFormatJsonArrayDescription": "Eine Anfrage pro Stapel ist ein JSON-Array. Kompatibel mit den meisten generischen Webhooks und Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Eine Anfrage pro Batch, der Körper ist newline-getrenntes JSON — ein Objekt pro Zeile, kein äußeres Array. Benötigt von Splunk HEC, Elastic / OpenSearch, und Grafana Loki.",
"httpDestFormatSingleTitle": "Ein Ereignis pro Anfrage",
"httpDestFormatSingleDescription": "Sendet eine separate HTTP-POST für jedes einzelne Ereignis. Nur für Endpunkte, die Batches nicht handhaben können.",
"httpDestLogTypesTitle": "Log-Typen",
"httpDestLogTypesDescription": "Wählen Sie, welche Log-Typen an dieses Ziel weitergeleitet werden. Nur aktivierte Log-Typen werden gestreamt.",
"httpDestAccessLogsTitle": "Zugriffsprotokolle",
"httpDestAccessLogsDescription": "Ressourcenzugriffe, einschließlich authentifizierter und abgelehnter Anfragen.",
"httpDestActionLogsTitle": "Aktionsprotokolle",
"httpDestActionLogsDescription": "Administrative Maßnahmen, die von Benutzern innerhalb der Organisation durchgeführt werden.",
"httpDestConnectionLogsTitle": "Verbindungsprotokolle",
"httpDestConnectionLogsDescription": "Site- und Tunnelverbindungen, einschließlich Verbindungen und Trennungen.",
"httpDestRequestLogsTitle": "Logs anfordern",
"httpDestRequestLogsDescription": "HTTP-Request-Protokolle für proxiierte Ressourcen, einschließlich Methode, Pfad und Antwort-Code.",
"httpDestSaveChanges": "Änderungen speichern",
"httpDestCreateDestination": "Ziel erstellen",
"httpDestUpdatedSuccess": "Ziel erfolgreich aktualisiert",
"httpDestCreatedSuccess": "Ziel erfolgreich erstellt",
"httpDestUpdateFailed": "Fehler beim Aktualisieren des Ziels",
"httpDestCreateFailed": "Fehler beim Erstellen des Ziels"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Create Link",
"resourcesNotFound": "No resources found",
"resourceSearch": "Search resources",
"machineSearch": "Search machines",
"machinesSearch": "Search machine clients...",
"machineNotFound": "No machines found",
"userDeviceSearch": "Search user devices",
"userDevicesSearch": "Search user devices...",
"openMenu": "Open menu",
"resource": "Resource",
"title": "Title",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy requests over HTTPS using a fully qualified domain name.",
"resourceRaw": "Raw TCP/UDP Resource",
"resourceRawDescription": "Proxy requests over raw TCP/UDP using a port number.",
"resourceRawDescriptionCloud": "Proxy requests over raw TCP/UDP using a port number. Requires sites to connect to a remote node.",
"resourceCreate": "Create Resource",
"resourceCreateDescription": "Follow the steps below to create a new resource",
"resourceSeeAll": "See All Resources",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Delete API Key",
"apiKeysManage": "Manage API Keys",
"apiKeysDescription": "API keys are used to authenticate with the integration API",
"provisioningKeysTitle": "Provisioning Key",
"provisioningKeysManage": "Manage Provisioning Keys",
"provisioningKeysDescription": "Provisioning keys are used to authenticate automated site provisioning for your organization.",
"provisioningManage": "Provisioning",
"provisioningDescription": "Manage provisioning keys and review pending sites awaiting approval.",
"pendingSites": "Pending Sites",
"siteApproveSuccess": "Site approved successfully",
"siteApproveError": "Error approving site",
"provisioningKeys": "Provisioning Keys",
"searchProvisioningKeys": "Search provisioning keys...",
"provisioningKeysAdd": "Generate Provisioning Key",
"provisioningKeysErrorDelete": "Error deleting provisioning key",
"provisioningKeysErrorDeleteMessage": "Error deleting provisioning key",
"provisioningKeysQuestionRemove": "Are you sure you want to remove this provisioning key from the organization?",
"provisioningKeysMessageRemove": "Once removed, the key can no longer be used for site provisioning.",
"provisioningKeysDeleteConfirm": "Confirm Delete Provisioning Key",
"provisioningKeysDelete": "Delete Provisioning key",
"provisioningKeysCreate": "Generate Provisioning Key",
"provisioningKeysCreateDescription": "Generate a new provisioning key for the organization",
"provisioningKeysSeeAll": "See all provisioning keys",
"provisioningKeysSave": "Save the provisioning key",
"provisioningKeysSaveDescription": "You will only be able to see this once. Copy it to a secure place.",
"provisioningKeysErrorCreate": "Error creating provisioning key",
"provisioningKeysList": "New provisioning key",
"provisioningKeysMaxBatchSize": "Max batch size",
"provisioningKeysUnlimitedBatchSize": "Unlimited batch size (no limit)",
"provisioningKeysMaxBatchUnlimited": "Unlimited",
"provisioningKeysMaxBatchSizeInvalid": "Enter a valid max batch size (11,000,000).",
"provisioningKeysValidUntil": "Valid until",
"provisioningKeysValidUntilHint": "Leave empty for no expiration.",
"provisioningKeysValidUntilInvalid": "Enter a valid date and time.",
"provisioningKeysNumUsed": "Times used",
"provisioningKeysLastUsed": "Last used",
"provisioningKeysNoExpiry": "No expiration",
"provisioningKeysNeverUsed": "Never",
"provisioningKeysEdit": "Edit Provisioning Key",
"provisioningKeysEditDescription": "Update the max batch size and expiration time for this key.",
"provisioningKeysApproveNewSites": "Approve new sites",
"provisioningKeysApproveNewSitesDescription": "Automatically approve sites that register with this key.",
"provisioningKeysUpdateError": "Error updating provisioning key",
"provisioningKeysUpdated": "Provisioning key updated",
"provisioningKeysUpdatedDescription": "Your changes have been saved.",
"provisioningKeysBannerTitle": "Site Provisioning Keys",
"provisioningKeysBannerDescription": "Generate a provisioning key and use it with the Newt connector to automatically create sites on first startup — no need to set up separate credentials for each site.",
"provisioningKeysBannerButtonText": "Learn More",
"pendingSitesBannerTitle": "Pending Sites",
"pendingSitesBannerDescription": "Sites that connect using a provisioning key appear here for review. Approve each site before it becomes active and gains access to your resources.",
"pendingSitesBannerButtonText": "Learn More",
"apiKeysSettings": "{apiKeyName} Settings",
"userTitle": "Manage All Users",
"userDescription": "View and manage all users in the system",
@@ -508,9 +562,12 @@
"userSaved": "User saved",
"userSavedDescription": "The user has been updated.",
"autoProvisioned": "Auto Provisioned",
"autoProvisionSettings": "Auto Provision Settings",
"autoProvisionedDescription": "Allow this user to be automatically managed by identity provider",
"accessControlsDescription": "Manage what this user can access and do in the organization",
"accessControlsSubmit": "Save Access Controls",
"singleRolePerUserPlanNotice": "Your plan only supports one role per user.",
"singleRolePerUserEditionNotice": "This edition only supports one role per user.",
"roles": "Roles",
"accessUsersRoles": "Manage Users & Roles",
"accessUsersRolesDescription": "Invite users and add them to roles to manage access to the organization",
@@ -567,6 +624,8 @@
"targetErrorInvalidPortDescription": "Please enter a valid port number",
"targetErrorNoSite": "No site selected",
"targetErrorNoSiteDescription": "Please select a site for the target",
"targetTargetsCleared": "Targets cleared",
"targetTargetsClearedDescription": "All targets have been removed from this resource",
"targetCreated": "Target created",
"targetCreatedDescription": "Target has been created successfully",
"targetErrorCreate": "Failed to create target",
@@ -650,6 +709,7 @@
"resourcesErrorUpdate": "Failed to toggle resource",
"resourcesErrorUpdateDescription": "An error occurred while updating the resource",
"access": "Access",
"accessControl": "Access Control",
"shareLink": "{resource} Share Link",
"resourceSelect": "Select resource",
"shareLinks": "Share Links",
@@ -790,6 +850,7 @@
"accessRoleRemoved": "Role removed",
"accessRoleRemovedDescription": "The role has been successfully removed.",
"accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.",
"network": "Network",
"manage": "Manage",
"sitesNotFound": "No sites found.",
"pangolinServerAdmin": "Server Admin - Pangolin",
@@ -884,7 +945,7 @@
"defaultMappingsRole": "Default Role Mapping",
"defaultMappingsRoleDescription": "The result of this expression must return the role name as defined in the organization as a string.",
"defaultMappingsOrg": "Default Organization Mapping",
"defaultMappingsOrgDescription": "This expression must return the org ID or true for the user to be allowed to access the organization.",
"defaultMappingsOrgDescription": "When set, this expression must return the organization ID or true for the user to access that organization. When unset, defining an organization policy for that org is enough: the user is allowed in as long as a valid role mapping can be resolved for them within the organization.",
"defaultMappingsSubmit": "Save Default Mappings",
"orgPoliciesEdit": "Edit Organization Policy",
"org": "Organization",
@@ -1037,7 +1098,6 @@
"pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.",
"overview": "Overview",
"home": "Home",
"accessControl": "Access Control",
"settings": "Settings",
"usersAll": "All Users",
"license": "License",
@@ -1100,6 +1160,12 @@
"actionGetUser": "Get User",
"actionGetOrgUser": "Get Organization User",
"actionListOrgDomains": "List Organization Domains",
"actionGetDomain": "Get Domain",
"actionCreateOrgDomain": "Create Domain",
"actionUpdateOrgDomain": "Update Domain",
"actionDeleteOrgDomain": "Delete Domain",
"actionGetDNSRecords": "Get DNS Records",
"actionRestartOrgDomain": "Restart Domain",
"actionCreateSite": "Create Site",
"actionDeleteSite": "Delete Site",
"actionGetSite": "Get Site",
@@ -1111,6 +1177,7 @@
"setupTokenDescription": "Enter the setup token from the server console.",
"setupTokenRequired": "Setup token is required",
"actionUpdateSite": "Update Site",
"actionResetSiteBandwidth": "Reset Organization Bandwidth",
"actionListSiteRoles": "List Allowed Site Roles",
"actionCreateResource": "Create Resource",
"actionDeleteResource": "Delete Resource",
@@ -1140,6 +1207,7 @@
"actionRemoveUser": "Remove User",
"actionListUsers": "List Users",
"actionAddUserRole": "Add User Role",
"actionSetUserOrgRoles": "Set User Roles",
"actionGenerateAccessToken": "Generate Access Token",
"actionDeleteAccessToken": "Delete Access Token",
"actionListAccessTokens": "List Access Tokens",
@@ -1249,12 +1317,14 @@
"sidebarClientResources": "Private",
"sidebarAccessControl": "Access Control",
"sidebarLogsAndAnalytics": "Logs & Analytics",
"sidebarTeam": "Team",
"sidebarUsers": "Users",
"sidebarAdmin": "Admin",
"sidebarInvitations": "Invitations",
"sidebarRoles": "Roles",
"sidebarShareableLinks": "Links",
"sidebarApiKeys": "API Keys",
"sidebarProvisioning": "Provisioning",
"sidebarSettings": "Settings",
"sidebarAllUsers": "All Users",
"sidebarIdentityProviders": "Identity Providers",
@@ -1267,6 +1337,7 @@
"sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Blueprints",
"sidebarOrganization": "Organization",
"sidebarManagement": "Management",
"sidebarBillingAndLicenses": "Billing & Licenses",
"sidebarLogsAnalytics": "Analytics",
"blueprints": "Blueprints",
@@ -1288,8 +1359,7 @@
"contents": "Contents",
"parsedContents": "Parsed Contents (Read Only)",
"enableDockerSocket": "Enable Docker Blueprint",
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
"enableDockerSocketLink": "Learn More",
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt. Read about how this works in <docsLink>the documentation</docsLink>.",
"viewDockerContainers": "View Docker Containers",
"containersIn": "Containers in {siteName}",
"selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.",
@@ -1417,6 +1487,7 @@
"domainPickerNamespace": "Namespace: {namespace}",
"domainPickerShowMore": "Show More",
"regionSelectorTitle": "Select Region",
"domainPickerRemoteExitNodeWarning": "Provided domains are not supported when sites connect to remote exit nodes. For resources to be available on remote nodes, use a custom domain instead.",
"regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.",
"regionSelectorPlaceholder": "Choose a region",
"regionSelectorComingSoon": "Coming Soon",
@@ -1570,6 +1641,16 @@
"billingFeatureLossWarning": "Feature Availability Notice",
"billingFeatureLossDescription": "By downgrading, features not available in the new plan will be automatically disabled. Some settings and configurations may be lost. Please review the pricing matrix to understand which features will no longer be available.",
"billingUsageExceedsLimit": "Current usage ({current}) exceeds limit ({limit})",
"billingPastDueTitle": "Payment Past Due",
"billingPastDueDescription": "Your payment is past due. Please update your payment method to continue using your current plan features. If not resolved, your subscription will be canceled and you'll be reverted to the free tier.",
"billingUnpaidTitle": "Subscription Unpaid",
"billingUnpaidDescription": "Your subscription is unpaid and you have been reverted to the free tier. Please update your payment method to restore your subscription.",
"billingIncompleteTitle": "Payment Incomplete",
"billingIncompleteDescription": "Your payment is incomplete. Please complete the payment process to activate your subscription.",
"billingIncompleteExpiredTitle": "Payment Expired",
"billingIncompleteExpiredDescription": "Your payment was never completed and has expired. You have been reverted to the free tier. Please subscribe again to restore access to paid features.",
"billingManageSubscription": "Manage your subscription",
"billingResolvePaymentIssue": "Please resolve your payment issue before upgrading or downgrading",
"signUpTerms": {
"IAgreeToThe": "I agree to the",
"termsOfService": "terms of service",
@@ -1643,6 +1724,24 @@
"timeIsInSeconds": "Time is in seconds",
"requireDeviceApproval": "Require Device Approvals",
"requireDeviceApprovalDescription": "Users with this role need new devices approved by an admin before they can connect and access resources.",
"sshAccess": "SSH Access",
"roleAllowSsh": "Allow SSH",
"roleAllowSshAllow": "Allow",
"roleAllowSshDisallow": "Disallow",
"roleAllowSshDescription": "Allow users with this role to connect to resources via SSH. When disabled, the role cannot use SSH access.",
"sshSudoMode": "Sudo Access",
"sshSudoModeNone": "None",
"sshSudoModeNoneDescription": "User cannot run commands with sudo.",
"sshSudoModeFull": "Full Sudo",
"sshSudoModeFullDescription": "User can run any command with sudo.",
"sshSudoModeCommands": "Commands",
"sshSudoModeCommandsDescription": "User can run only the specified commands with sudo.",
"sshSudo": "Allow sudo",
"sshSudoCommands": "Sudo Commands",
"sshSudoCommandsDescription": "Comma separated list of commands the user is allowed to run with sudo.",
"sshCreateHomeDir": "Create Home Directory",
"sshUnixGroups": "Unix Groups",
"sshUnixGroupsDescription": "Comma separated Unix groups to add the user to on the target host.",
"retryAttempts": "Retry Attempts",
"expectedResponseCodes": "Expected Response Codes",
"expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.",
@@ -1851,6 +1950,40 @@
"exitNode": "Exit Node",
"country": "Country",
"rulesMatchCountry": "Currently based on source IP",
"region": "Region",
"selectRegion": "Select region",
"searchRegions": "Search regions...",
"noRegionFound": "No region found.",
"rulesMatchRegion": "Select a regional grouping of countries",
"rulesErrorInvalidRegion": "Invalid region",
"rulesErrorInvalidRegionDescription": "Please select a valid region.",
"regionAfrica": "Africa",
"regionNorthernAfrica": "Northern Africa",
"regionEasternAfrica": "Eastern Africa",
"regionMiddleAfrica": "Middle Africa",
"regionSouthernAfrica": "Southern Africa",
"regionWesternAfrica": "Western Africa",
"regionAmericas": "Americas",
"regionCaribbean": "Caribbean",
"regionCentralAmerica": "Central America",
"regionSouthAmerica": "South America",
"regionNorthernAmerica": "Northern America",
"regionAsia": "Asia",
"regionCentralAsia": "Central Asia",
"regionEasternAsia": "Eastern Asia",
"regionSouthEasternAsia": "South-Eastern Asia",
"regionSouthernAsia": "Southern Asia",
"regionWesternAsia": "Western Asia",
"regionEurope": "Europe",
"regionEasternEurope": "Eastern Europe",
"regionNorthernEurope": "Northern Europe",
"regionSouthernEurope": "Southern Europe",
"regionWesternEurope": "Western Europe",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australia and New Zealand",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Managed Self-Hosted",
"description": "More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles",
@@ -1899,6 +2032,25 @@
"invalidValue": "Invalid value",
"idpTypeLabel": "Identity Provider Type",
"roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'",
"roleMappingModeFixedRoles": "Fixed Roles",
"roleMappingModeMappingBuilder": "Mapping Builder",
"roleMappingModeRawExpression": "Raw Expression",
"roleMappingFixedRolesPlaceholderSelect": "Select one or more roles",
"roleMappingFixedRolesPlaceholderFreeform": "Type role names (exact match per organization)",
"roleMappingFixedRolesDescriptionSameForAll": "Assign the same role set to every auto-provisioned user.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "For default policies, type role names that exist in each organization where users are provisioned. Names must match exactly.",
"roleMappingClaimPath": "Claim Path",
"roleMappingClaimPathPlaceholder": "groups",
"roleMappingClaimPathDescription": "Path in the token payload that contains source values (for example, groups).",
"roleMappingMatchValue": "Match Value",
"roleMappingAssignRoles": "Assign Roles",
"roleMappingAddMappingRule": "Add Mapping Rule",
"roleMappingRawExpressionResultDescription": "Expression must evaluate to a string or string array.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Expression must evaluate to a string (a single role name).",
"roleMappingMatchValuePlaceholder": "Match value (for example: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Type role names (exact per org)",
"roleMappingBuilderFreeformRowHint": "Role names must match a role in each target organization.",
"roleMappingRemoveRule": "Remove",
"idpGoogleConfiguration": "Google Configuration",
"idpGoogleConfigurationDescription": "Configure the Google OAuth2 credentials",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -1988,8 +2140,8 @@
"orgAuthNoAccount": "Don't have an account?",
"subscriptionRequiredToUse": "A subscription is required to use this feature.",
"mustUpgradeToUse": "You must upgrade your subscription to use this feature.",
"subscriptionRequiredTierToUse": "This feature requires <tierLink>{tier}</tierLink> or higher.",
"upgradeToTierToUse": "Upgrade to <tierLink>{tier}</tierLink> or higher to use this feature.",
"subscriptionRequiredTierToUse": "This feature requires <tierLink>{tier}</tierLink>.",
"upgradeToTierToUse": "Upgrade to <tierLink>{tier}</tierLink> to use this feature.",
"subscriptionTierTier1": "Home",
"subscriptionTierTier2": "Team",
"subscriptionTierTier3": "Business",
@@ -2079,7 +2231,7 @@
"manageMachineClients": "Manage Machine Clients",
"manageMachineClientsDescription": "Create and manage clients that servers and systems use to privately connect to resources",
"machineClientsBannerTitle": "Servers & Automated Systems",
"machineClientsBannerDescription": "Machine clients are for servers and automated systems that are not associated with a specific user. They authenticate with an ID and secret, and can run with Pangolin CLI, Olm CLI, or Olm as a container.",
"machineClientsBannerDescription": "Machine clients are for servers and automated systems that are not associated with a specific user. They authenticate with an ID and secret, and can be deployed as a CLI or a container.",
"machineClientsBannerPangolinCLI": "Pangolin CLI",
"machineClientsBannerOlmCLI": "Olm CLI",
"machineClientsBannerOlmContainer": "Container",
@@ -2295,6 +2447,8 @@
"logRetentionAccessDescription": "How long to retain access logs",
"logRetentionActionLabel": "Action Log Retention",
"logRetentionActionDescription": "How long to retain action logs",
"logRetentionConnectionLabel": "Connection Log Retention",
"logRetentionConnectionDescription": "How long to retain connection logs",
"logRetentionDisabled": "Disabled",
"logRetention3Days": "3 days",
"logRetention7Days": "7 days",
@@ -2305,8 +2459,15 @@
"logRetentionEndOfFollowingYear": "End of following year",
"actionLogsDescription": "View a history of actions performed in this organization",
"accessLogsDescription": "View access auth requests for resources in this organization",
"licenseRequiredToUse": "An <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> license is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "The <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Connection Logs",
"connectionLogsDescription": "View connection logs for tunnels in this organization",
"sidebarLogsConnection": "Connection Logs",
"sidebarLogsStreaming": "Streaming",
"sourceAddress": "Source Address",
"destinationAddress": "Destination Address",
"duration": "Duration",
"licenseRequiredToUse": "An <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> license or <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is required to use this feature. <bookADemoLink>Book a free demo or POC trial to learn more</bookADemoLink>.",
"ossEnterpriseEditionRequired": "The <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Book a free demo or POC trial to learn more</bookADemoLink>.",
"certResolver": "Certificate Resolver",
"certResolverDescription": "Select the certificate resolver to use for this resource.",
"selectCertResolver": "Select Certificate Resolver",
@@ -2471,9 +2632,9 @@
"remoteExitNodeRegenerateCredentialsConfirmation": "Are you sure you want to regenerate the credentials for this remote exit node?",
"remoteExitNodeRegenerateCredentialsWarning": "This will regenerate the credentials. The remote exit node will stay connected until you manually restart it and use the new credentials.",
"agent": "Agent",
"personalUseOnly": "Personal Use Only",
"loginPageLicenseWatermark": "This instance is licensed for personal use only.",
"instanceIsUnlicensed": "This instance is unlicensed.",
"personalUseOnly": "Personal Use Only",
"loginPageLicenseWatermark": "This instance is licensed for personal use only.",
"instanceIsUnlicensed": "This instance is unlicensed.",
"portRestrictions": "Port Restrictions",
"allPorts": "All",
"custom": "Custom",
@@ -2503,6 +2664,17 @@
"editInternalResourceDialogAccessControl": "Access Control",
"editInternalResourceDialogAccessControlDescription": "Control which roles, users, and machine clients have access to this resource when connected. Admins always have access.",
"editInternalResourceDialogPortRangeValidationError": "Port range must be \"*\" for all ports, or a comma-separated list of ports and ranges (e.g., \"80,443,8000-9000\"). Ports must be between 1 and 65535.",
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon Location",
"internalResourceAuthDaemonStrategyDescription": "Choose where the SSH authentication daemon runs: on the site (Newt) or on a remote host.",
"internalResourceAuthDaemonDescription": "The SSH authentication daemon handles SSH key signing and PAM authentication for this resource. Choose whether it runs on the site (Newt) or on a separate remote host. See <docsLink>the documentation</docsLink> for more.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Select Strategy",
"internalResourceAuthDaemonStrategyLabel": "Location",
"internalResourceAuthDaemonSite": "On Site",
"internalResourceAuthDaemonSiteDescription": "Auth daemon runs on the site (Newt).",
"internalResourceAuthDaemonRemote": "Remote Host",
"internalResourceAuthDaemonRemoteDescription": "Auth daemon runs on this resource's destination - not the site.",
"internalResourceAuthDaemonPort": "Daemon Port (optional)",
"orgAuthWhatsThis": "Where can I find my organization ID?",
"learnMore": "Learn more",
"backToHome": "Go back to home",
@@ -2516,7 +2688,7 @@
"automaticModeDescription": " Show maintenance page only when all backend targets are down or unhealthy. Your resource continues working normally as long as at least one target is healthy.",
"forced": "Forced",
"forcedModeDescription": "Always show the maintenance page regardless of backend health. Use this for planned maintenance when you want to prevent all access.",
"warning:" : "Warning:",
"warning:": "Warning:",
"forcedeModeWarning": "All traffic will be directed to the maintenance page. Your backend resources will not receive any requests.",
"pageTitle": "Page Title",
"pageTitleDescription": "The main heading displayed on the maintenance page",
@@ -2632,5 +2804,91 @@
"approvalsEmptyStateStep2Title": "Enable Device Approvals",
"approvalsEmptyStateStep2Description": "Edit a role and enable the 'Require Device Approvals' option. Users with this role will need admin approval for new devices.",
"approvalsEmptyStatePreviewDescription": "Preview: When enabled, pending device requests will appear here for review",
"approvalsEmptyStateButtonText": "Manage Roles"
"approvalsEmptyStateButtonText": "Manage Roles",
"domainErrorTitle": "We are having trouble verifying your domain",
"idpAdminAutoProvisionPoliciesTabHint": "Configure role mapping and organization policies on the <policiesTabLink>Auto Provision Settings</policiesTabLink> tab.",
"streamingTitle": "Event Streaming",
"streamingDescription": "Stream events from your organization to external destinations in real time.",
"streamingUnnamedDestination": "Unnamed destination",
"streamingNoUrlConfigured": "No URL configured",
"streamingAddDestination": "Add Destination",
"streamingHttpWebhookTitle": "HTTP Webhook",
"streamingHttpWebhookDescription": "Send events to any HTTP endpoint with flexible authentication and templating.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Stream events to an S3-compatible object storage bucket. Coming soon.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Forward events directly to your Datadog account. Coming soon.",
"streamingTypePickerDescription": "Choose a destination type to get started.",
"streamingFailedToLoad": "Failed to load destinations",
"streamingUnexpectedError": "An unexpected error occurred.",
"streamingFailedToUpdate": "Failed to update destination",
"streamingDeletedSuccess": "Destination deleted successfully",
"streamingFailedToDelete": "Failed to delete destination",
"streamingDeleteTitle": "Delete Destination",
"streamingDeleteButtonText": "Delete Destination",
"streamingDeleteDialogAreYouSure": "Are you sure you want to delete",
"streamingDeleteDialogThisDestination": "this destination",
"streamingDeleteDialogPermanentlyRemoved": "? All configuration will be permanently removed.",
"httpDestEditTitle": "Edit Destination",
"httpDestAddTitle": "Add HTTP Destination",
"httpDestEditDescription": "Update the configuration for this HTTP event streaming destination.",
"httpDestAddDescription": "Configure a new HTTP endpoint to receive your organization's events.",
"httpDestTabSettings": "Settings",
"httpDestTabHeaders": "Headers",
"httpDestTabBody": "Body",
"httpDestTabLogs": "Logs",
"httpDestNamePlaceholder": "My HTTP destination",
"httpDestUrlLabel": "Destination URL",
"httpDestUrlErrorHttpRequired": "URL must use http or https",
"httpDestUrlErrorHttpsRequired": "HTTPS is required on cloud deployments",
"httpDestUrlErrorInvalid": "Enter a valid URL (e.g. https://example.com/webhook)",
"httpDestAuthTitle": "Authentication",
"httpDestAuthDescription": "Choose how requests to your endpoint are authenticated.",
"httpDestAuthNoneTitle": "No Authentication",
"httpDestAuthNoneDescription": "Sends requests without an Authorization header.",
"httpDestAuthBearerTitle": "Bearer Token",
"httpDestAuthBearerDescription": "Adds an Authorization: Bearer <token> header to each request.",
"httpDestAuthBearerPlaceholder": "Your API key or token",
"httpDestAuthBasicTitle": "Basic Auth",
"httpDestAuthBasicDescription": "Adds an Authorization: Basic <credentials> header. Provide credentials as username:password.",
"httpDestAuthBasicPlaceholder": "username:password",
"httpDestAuthCustomTitle": "Custom Header",
"httpDestAuthCustomDescription": "Specify a custom HTTP header name and value for authentication (e.g. X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Header name (e.g. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Header value",
"httpDestCustomHeadersTitle": "Custom HTTP Headers",
"httpDestCustomHeadersDescription": "Add custom headers to every outgoing request. Useful for static tokens or a custom Content-Type. By default, Content-Type: application/json is sent.",
"httpDestNoHeadersConfigured": "No custom headers configured. Click \"Add Header\" to add one.",
"httpDestHeaderNamePlaceholder": "Header name",
"httpDestHeaderValuePlaceholder": "Value",
"httpDestAddHeader": "Add Header",
"httpDestBodyTemplateTitle": "Custom Body Template",
"httpDestBodyTemplateDescription": "Control the JSON payload structure sent to your endpoint. If disabled, a default JSON object is sent for each event.",
"httpDestEnableBodyTemplate": "Enable custom body template",
"httpDestBodyTemplateLabel": "Body Template (JSON)",
"httpDestBodyTemplateHint": "Use template variables to reference event fields in your payload.",
"httpDestPayloadFormatTitle": "Payload Format",
"httpDestPayloadFormatDescription": "How events are serialised into each request body.",
"httpDestFormatJsonArrayTitle": "JSON Array",
"httpDestFormatJsonArrayDescription": "One request per batch, body is a JSON array. Compatible with most generic webhooks and Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "One request per batch, body is newline-delimited JSON — one object per line, no outer array. Required by Splunk HEC, Elastic / OpenSearch, and Grafana Loki.",
"httpDestFormatSingleTitle": "One Event Per Request",
"httpDestFormatSingleDescription": "Sends a separate HTTP POST for each individual event. Use only for endpoints that cannot handle batches.",
"httpDestLogTypesTitle": "Log Types",
"httpDestLogTypesDescription": "Choose which log types are forwarded to this destination. Only enabled log types will be streamed.",
"httpDestAccessLogsTitle": "Access Logs",
"httpDestAccessLogsDescription": "Resource access attempts, including authenticated and denied requests.",
"httpDestActionLogsTitle": "Action Logs",
"httpDestActionLogsDescription": "Administrative actions performed by users within the organization.",
"httpDestConnectionLogsTitle": "Connection Logs",
"httpDestConnectionLogsDescription": "Site and tunnel connection events, including connects and disconnects.",
"httpDestRequestLogsTitle": "Request Logs",
"httpDestRequestLogsDescription": "HTTP request logs for proxied resources, including method, path, and response code.",
"httpDestSaveChanges": "Save Changes",
"httpDestCreateDestination": "Create Destination",
"httpDestUpdatedSuccess": "Destination updated successfully",
"httpDestCreatedSuccess": "Destination created successfully",
"httpDestUpdateFailed": "Failed to update destination",
"httpDestCreateFailed": "Failed to create destination"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Crear enlace",
"resourcesNotFound": "No se encontraron recursos",
"resourceSearch": "Buscar recursos",
"machineSearch": "Buscar máquinas",
"machinesSearch": "Buscar clientes...",
"machineNotFound": "No hay máquinas",
"userDeviceSearch": "Buscar dispositivos de usuario",
"userDevicesSearch": "Buscar dispositivos de usuario...",
"openMenu": "Abrir menú",
"resource": "Recurso",
"title": "Título",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy proporciona solicitudes sobre HTTPS usando un nombre de dominio completamente calificado.",
"resourceRaw": "Recurso TCP/UDP sin procesar",
"resourceRawDescription": "Proxy proporciona solicitudes sobre TCP/UDP usando un número de puerto.",
"resourceRawDescriptionCloud": "Las peticiones de proxy sobre TCP/UDP crudas usando un número de puerto. Requiere que los sitios se conecten a un nodo remoto.",
"resourceCreate": "Crear Recurso",
"resourceCreateDescription": "Siga los siguientes pasos para crear un nuevo recurso",
"resourceSeeAll": "Ver todos los recursos",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Borrar Clave API",
"apiKeysManage": "Administrar claves API",
"apiKeysDescription": "Las claves API se utilizan para autenticar con la API de integración",
"provisioningKeysTitle": "Clave de aprovisionamiento",
"provisioningKeysManage": "Administrar Claves de Aprovisionamiento",
"provisioningKeysDescription": "Las claves de aprovisionamiento se utilizan para autenticar la provisión automatizada del sitio para su organización.",
"provisioningManage": "Aprovisionamiento",
"provisioningDescription": "Administrar las claves de aprovisionamiento y revisar los sitios pendientes de aprobación.",
"pendingSites": "Sitios pendientes",
"siteApproveSuccess": "Sitio aprobado con éxito",
"siteApproveError": "Error al aprobar el sitio",
"provisioningKeys": "Claves de aprovisionamiento",
"searchProvisioningKeys": "Buscar claves de suministro...",
"provisioningKeysAdd": "Generar clave de aprovisionamiento",
"provisioningKeysErrorDelete": "Error al eliminar la clave de aprovisionamiento",
"provisioningKeysErrorDeleteMessage": "Error al eliminar la clave de aprovisionamiento",
"provisioningKeysQuestionRemove": "¿Está seguro que desea eliminar esta clave de aprovisionamiento de la organización?",
"provisioningKeysMessageRemove": "Una vez eliminada, la clave ya no se puede utilizar para la disposición del sitio.",
"provisioningKeysDeleteConfirm": "Confirmar Eliminar Clave de Aprovisionamiento",
"provisioningKeysDelete": "Eliminar clave de aprovisionamiento",
"provisioningKeysCreate": "Generar clave de aprovisionamiento",
"provisioningKeysCreateDescription": "Generar una nueva clave de aprovisionamiento para la organización",
"provisioningKeysSeeAll": "Ver todas las claves de aprovisionamiento",
"provisioningKeysSave": "Guardar la clave de aprovisionamiento",
"provisioningKeysSaveDescription": "Sólo podrás verlo una vez. Copítalo a un lugar seguro.",
"provisioningKeysErrorCreate": "Error al crear la clave de provisioning",
"provisioningKeysList": "Nueva clave de aprovisionamiento",
"provisioningKeysMaxBatchSize": "Tamaño máximo de lote",
"provisioningKeysUnlimitedBatchSize": "Tamaño ilimitado del lote (sin límite)",
"provisioningKeysMaxBatchUnlimited": "Ilimitado",
"provisioningKeysMaxBatchSizeInvalid": "Introduzca un tamaño máximo de lote válido (11,000,000).",
"provisioningKeysValidUntil": "Válido hasta",
"provisioningKeysValidUntilHint": "Dejar vacío para no expirar.",
"provisioningKeysValidUntilInvalid": "Introduzca una fecha y hora válidas.",
"provisioningKeysNumUsed": "Tiempos usados",
"provisioningKeysLastUsed": "Último uso",
"provisioningKeysNoExpiry": "No expiración",
"provisioningKeysNeverUsed": "Nunca",
"provisioningKeysEdit": "Editar clave de aprovisionamiento",
"provisioningKeysEditDescription": "Actualizar el tamaño máximo de lote y el tiempo de caducidad para esta clave.",
"provisioningKeysApproveNewSites": "Aprobar nuevos sitios",
"provisioningKeysApproveNewSitesDescription": "Aprobar automáticamente los sitios que se registran con esta clave.",
"provisioningKeysUpdateError": "Error al actualizar la clave de aprovisionamiento",
"provisioningKeysUpdated": "Clave de aprovisionamiento actualizada",
"provisioningKeysUpdatedDescription": "Sus cambios han sido guardados.",
"provisioningKeysBannerTitle": "Claves de aprovisionamiento del sitio",
"provisioningKeysBannerDescription": "Generar una clave de aprovisionamiento y usarla con el conector Newt para crear automáticamente sitios en el primer inicio — no es necesario configurar credenciales separadas para cada sitio.",
"provisioningKeysBannerButtonText": "Saber más",
"pendingSitesBannerTitle": "Sitios pendientes",
"pendingSitesBannerDescription": "Los sitios que se conectan usando una clave de aprovisionamiento aparecen aquí para su revisión. Aprobar cada sitio antes de que se active y obtenga acceso a sus recursos.",
"pendingSitesBannerButtonText": "Saber más",
"apiKeysSettings": "Ajustes {apiKeyName}",
"userTitle": "Administrar todos los usuarios",
"userDescription": "Ver y administrar todos los usuarios en el sistema",
@@ -508,9 +562,12 @@
"userSaved": "Usuario guardado",
"userSavedDescription": "El usuario ha sido actualizado.",
"autoProvisioned": "Auto asegurado",
"autoProvisionSettings": "Configuración de Auto Provision",
"autoProvisionedDescription": "Permitir a este usuario ser administrado automáticamente por el proveedor de identidad",
"accessControlsDescription": "Administrar lo que este usuario puede acceder y hacer en la organización",
"accessControlsSubmit": "Guardar controles de acceso",
"singleRolePerUserPlanNotice": "Tu plan sólo soporta un rol por usuario.",
"singleRolePerUserEditionNotice": "Esta edición sólo soporta un rol por usuario.",
"roles": "Roles",
"accessUsersRoles": "Administrar usuarios y roles",
"accessUsersRolesDescription": "Invitar usuarios y añadirlos a roles para administrar el acceso a la organización",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Error al cambiar el recurso",
"resourcesErrorUpdateDescription": "Se ha producido un error al actualizar el recurso",
"access": "Acceder",
"accessControl": "Control de acceso",
"shareLink": "{resource} Compartir Enlace",
"resourceSelect": "Seleccionar recurso",
"shareLinks": "Compartir enlaces",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rol eliminado",
"accessRoleRemovedDescription": "El rol se ha eliminado correctamente.",
"accessRoleRequiredRemove": "Antes de eliminar este rol, seleccione un nuevo rol al que transferir miembros existentes.",
"network": "Red",
"manage": "Gestionar",
"sitesNotFound": "Sitios no encontrados.",
"pangolinServerAdmin": "Admin Servidor - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "¡Vaya! La página que estás buscando no existe.",
"overview": "Resumen",
"home": "Inicio",
"accessControl": "Control de acceso",
"settings": "Ajustes",
"usersAll": "Todos los usuarios",
"license": "Licencia",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Obtener usuario",
"actionGetOrgUser": "Obtener usuario de la organización",
"actionListOrgDomains": "Listar dominios de la organización",
"actionGetDomain": "Obtener dominio",
"actionCreateOrgDomain": "Crear dominio",
"actionUpdateOrgDomain": "Actualizar dominio",
"actionDeleteOrgDomain": "Eliminar dominio",
"actionGetDNSRecords": "Obtener registros DNS",
"actionRestartOrgDomain": "Reiniciar dominio",
"actionCreateSite": "Crear sitio",
"actionDeleteSite": "Eliminar sitio",
"actionGetSite": "Obtener sitio",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Ingrese el token de configuración desde la consola del servidor.",
"setupTokenRequired": "Se requiere el token de configuración",
"actionUpdateSite": "Actualizar sitio",
"actionResetSiteBandwidth": "Restablecer ancho de banda de la organización",
"actionListSiteRoles": "Lista de roles permitidos del sitio",
"actionCreateResource": "Crear Recurso",
"actionDeleteResource": "Eliminar Recurso",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Eliminar usuario",
"actionListUsers": "Listar usuarios",
"actionAddUserRole": "Añadir rol de usuario",
"actionSetUserOrgRoles": "Establecer roles de usuario",
"actionGenerateAccessToken": "Generar token de acceso",
"actionDeleteAccessToken": "Eliminar token de acceso",
"actionListAccessTokens": "Lista de Tokens de Acceso",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privado",
"sidebarAccessControl": "Control de acceso",
"sidebarLogsAndAnalytics": "Registros y análisis",
"sidebarTeam": "Equipo",
"sidebarUsers": "Usuarios",
"sidebarAdmin": "Admin",
"sidebarInvitations": "Invitaciones",
"sidebarRoles": "Roles",
"sidebarShareableLinks": "Enlaces",
"sidebarApiKeys": "Claves API",
"sidebarProvisioning": "Aprovisionamiento",
"sidebarSettings": "Ajustes",
"sidebarAllUsers": "Todos los usuarios",
"sidebarIdentityProviders": "Proveedores de identidad",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Registro y análisis",
"sidebarBluePrints": "Planos",
"sidebarOrganization": "Organización",
"sidebarManagement": "Gestión",
"sidebarBillingAndLicenses": "Facturación y licencias",
"sidebarLogsAnalytics": "Analíticas",
"blueprints": "Planos",
@@ -1289,7 +1358,6 @@
"parsedContents": "Contenido analizado (Sólo lectura)",
"enableDockerSocket": "Habilitar Plano Docker",
"enableDockerSocketDescription": "Activar el raspado de etiquetas de Socket Docker para etiquetas de planos. La ruta del Socket debe proporcionarse a Newt.",
"enableDockerSocketLink": "Saber más",
"viewDockerContainers": "Ver contenedores Docker",
"containersIn": "Contenedores en {siteName}",
"selectContainerDescription": "Seleccione cualquier contenedor para usar como nombre de host para este objetivo. Haga clic en un puerto para usar un puerto.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Espacio de nombres: {namespace}",
"domainPickerShowMore": "Mostrar más",
"regionSelectorTitle": "Seleccionar Región",
"domainPickerRemoteExitNodeWarning": "Los dominios suministrados no son compatibles cuando los sitios se conectan a nodos de salida remotos. Para que los recursos estén disponibles en nodos remotos, utilice un dominio personalizado en su lugar.",
"regionSelectorInfo": "Seleccionar una región nos ayuda a brindar un mejor rendimiento para tu ubicación. No tienes que estar en la misma región que tu servidor.",
"regionSelectorPlaceholder": "Elige una región",
"regionSelectorComingSoon": "Próximamente",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Aviso de disponibilidad de funcionalidad",
"billingFeatureLossDescription": "Al degradar, las características no disponibles en el nuevo plan se desactivarán automáticamente. Algunas configuraciones y configuraciones pueden perderse. Por favor, revise la matriz de precios para entender qué características ya no estarán disponibles.",
"billingUsageExceedsLimit": "El uso actual ({current}) supera el límite ({limit})",
"billingPastDueTitle": "Pago vencido",
"billingPastDueDescription": "Su pago ha vencido. Por favor, actualice su método de pago para seguir utilizando las características actuales de su plan. Si no se resuelve, tu suscripción se cancelará y serás revertido al nivel gratuito.",
"billingUnpaidTitle": "Suscripción no pagada",
"billingUnpaidDescription": "Tu suscripción no está pagada y has sido revertido al nivel gratuito. Por favor, actualiza tu método de pago para restaurar tu suscripción.",
"billingIncompleteTitle": "Pago incompleto",
"billingIncompleteDescription": "Su pago está incompleto. Por favor, complete el proceso de pago para activar su suscripción.",
"billingIncompleteExpiredTitle": "Pago expirado",
"billingIncompleteExpiredDescription": "Tu pago nunca se completó y ha expirado. Has sido revertido al nivel gratuito. Suscríbete de nuevo para restaurar el acceso a las funciones de pago.",
"billingManageSubscription": "Administra tu suscripción",
"billingResolvePaymentIssue": "Por favor resuelva su problema de pago antes de actualizar o bajar de calificación",
"signUpTerms": {
"IAgreeToThe": "Estoy de acuerdo con los",
"termsOfService": "términos del servicio",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "El tiempo está en segundos",
"requireDeviceApproval": "Requiere aprobaciones del dispositivo",
"requireDeviceApprovalDescription": "Los usuarios con este rol necesitan nuevos dispositivos aprobados por un administrador antes de poder conectarse y acceder a los recursos.",
"sshAccess": "Acceso a SSH",
"roleAllowSsh": "Permitir SSH",
"roleAllowSshAllow": "Permitir",
"roleAllowSshDisallow": "Rechazar",
"roleAllowSshDescription": "Permitir a los usuarios con este rol conectarse a recursos a través de SSH. Cuando está desactivado, el rol no puede usar acceso SSH.",
"sshSudoMode": "Acceso Sudo",
"sshSudoModeNone": "Ninguna",
"sshSudoModeNoneDescription": "El usuario no puede ejecutar comandos con sudo.",
"sshSudoModeFull": "Sudo completo",
"sshSudoModeFullDescription": "El usuario puede ejecutar cualquier comando con sudo.",
"sshSudoModeCommands": "Comandos",
"sshSudoModeCommandsDescription": "El usuario sólo puede ejecutar los comandos especificados con sudo.",
"sshSudo": "Permitir sudo",
"sshSudoCommands": "Comandos Sudo",
"sshSudoCommandsDescription": "Lista separada por comas de comandos que el usuario puede ejecutar con sudo.",
"sshCreateHomeDir": "Crear directorio principal",
"sshUnixGroups": "Grupos Unix",
"sshUnixGroupsDescription": "Grupos Unix separados por comas para agregar el usuario en el host de destino.",
"retryAttempts": "Intentos de Reintento",
"expectedResponseCodes": "Códigos de respuesta esperados",
"expectedResponseCodesDescription": "Código de estado HTTP que indica un estado saludable. Si se deja en blanco, se considera saludable de 200 a 300.",
@@ -1851,6 +1948,40 @@
"exitNode": "Nodo de Salida",
"country": "País",
"rulesMatchCountry": "Actualmente basado en IP de origen",
"region": "Región",
"selectRegion": "Seleccionar región",
"searchRegions": "Buscar regiones...",
"noRegionFound": "Región no encontrada.",
"rulesMatchRegion": "Seleccione una agrupación regional de países",
"rulesErrorInvalidRegion": "Región no válida",
"rulesErrorInvalidRegionDescription": "Por favor, seleccione una región válida.",
"regionAfrica": "Africa",
"regionNorthernAfrica": "África septentrional",
"regionEasternAfrica": "África oriental",
"regionMiddleAfrica": "África central",
"regionSouthernAfrica": "África del Sur",
"regionWesternAfrica": "África Occidental",
"regionAmericas": "Américas",
"regionCaribbean": "Caribe",
"regionCentralAmerica": "América Central",
"regionSouthAmerica": "América del Sur",
"regionNorthernAmerica": "América del Norte",
"regionAsia": "Asia",
"regionCentralAsia": "Asia Central",
"regionEasternAsia": "Asia oriental",
"regionSouthEasternAsia": "Asia sudoriental",
"regionSouthernAsia": "Asia meridional",
"regionWesternAsia": "Asia Occidental",
"regionEurope": "Europa",
"regionEasternEurope": "Europa del Este",
"regionNorthernEurope": "Europa septentrional",
"regionSouthernEurope": "Europa meridional",
"regionWesternEurope": "Europa Occidental",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australia y Nueva Zelanda",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Autogestionado",
"description": "Servidor Pangolin autoalojado más fiable y de bajo mantenimiento con campanas y silbidos extra",
@@ -1899,6 +2030,25 @@
"invalidValue": "Valor inválido",
"idpTypeLabel": "Tipo de proveedor de identidad",
"roleMappingExpressionPlaceholder": "e.g., contiene(grupos, 'administrador') && 'administrador' || 'miembro'",
"roleMappingModeFixedRoles": "Roles fijos",
"roleMappingModeMappingBuilder": "Constructor de mapeo",
"roleMappingModeRawExpression": "Expresión sin procesar",
"roleMappingFixedRolesPlaceholderSelect": "Seleccione uno o más roles",
"roleMappingFixedRolesPlaceholderFreeform": "Nombre de rol de tipo (coincidencia exacta por organización)",
"roleMappingFixedRolesDescriptionSameForAll": "Asignar el mismo rol establecido a cada usuario auto-provisionado.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Para las políticas predeterminadas, escriba nombres de roles que existen en cada organización donde los usuarios son proporcionados. Los nombres deben coincidir exactamente.",
"roleMappingClaimPath": "Reclamar ruta",
"roleMappingClaimPathPlaceholder": "grupos",
"roleMappingClaimPathDescription": "Ruta en el payload del token que contiene valores de origen (por ejemplo, grupos).",
"roleMappingMatchValue": "Valor de partida",
"roleMappingAssignRoles": "Asignar roles",
"roleMappingAddMappingRule": "Añadir regla de mapeo",
"roleMappingRawExpressionResultDescription": "La expresión debe evaluar a un array de cadenas o cadenas.",
"roleMappingRawExpressionResultDescriptionSingleRole": "La expresión debe evaluar una cadena (un solo nombre de rol).",
"roleMappingMatchValuePlaceholder": "Valor coincidente (por ejemplo: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Escriba nombres de rol (exacto por org)",
"roleMappingBuilderFreeformRowHint": "Los nombres de rol deben coincidir con un rol en cada organización objetivo.",
"roleMappingRemoveRule": "Eliminar",
"idpGoogleConfiguration": "Configuración de Google",
"idpGoogleConfigurationDescription": "Configurar las credenciales de Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Cuánto tiempo retener los registros de acceso",
"logRetentionActionLabel": "Retención de registro de acción",
"logRetentionActionDescription": "Cuánto tiempo retener los registros de acción",
"logRetentionConnectionLabel": "Retención de Registro de Conexión",
"logRetentionConnectionDescription": "Cuánto tiempo conservar los registros de conexión",
"logRetentionDisabled": "Deshabilitado",
"logRetention3Days": "3 días",
"logRetention7Days": "7 días",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Fin del año siguiente",
"actionLogsDescription": "Ver un historial de acciones realizadas en esta organización",
"accessLogsDescription": "Ver solicitudes de acceso a los recursos de esta organización",
"licenseRequiredToUse": "Se requiere una licencia <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> para utilizar esta función. Esta característica también está disponible en <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "La <enterpriseEditionLink>versión Enterprise</enterpriseEditionLink> es necesaria para utilizar esta función. Esta función también está disponible en <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Registros de conexión",
"connectionLogsDescription": "Ver registros de conexión para túneles en esta organización",
"sidebarLogsConnection": "Registros de conexión",
"sidebarLogsStreaming": "Transmisión",
"sourceAddress": "Dirección de origen",
"destinationAddress": "Dirección de destino",
"duration": "Duración",
"licenseRequiredToUse": "Se requiere una licencia <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> o <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> para usar esta función. <bookADemoLink>Reserve una demostración o prueba POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "La <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> es necesaria para utilizar esta función. Esta función también está disponible en <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Reserva una demostración o prueba POC</bookADemoLink>.",
"certResolver": "Resolver certificado",
"certResolverDescription": "Seleccione la resolución de certificados a utilizar para este recurso.",
"selectCertResolver": "Seleccionar Resolver Certificado",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Control de acceso",
"editInternalResourceDialogAccessControlDescription": "Controla qué roles, usuarios y clientes de máquinas tienen acceso a este recurso cuando están conectados. Los administradores siempre tienen acceso.",
"editInternalResourceDialogPortRangeValidationError": "El rango de puertos debe ser \"*\" para todos los puertos, o una lista separada por comas de puertos y rangos (por ejemplo, \"80,443,8000-9000\"). Los puertos deben estar entre 1 y 65535.",
"internalResourceAuthDaemonStrategy": "Ubicación del demonio de autenticación SSSH",
"internalResourceAuthDaemonStrategyDescription": "Elija dónde se ejecuta el daemon de autenticación SSH: en el sitio (Newt) o en un host remoto.",
"internalResourceAuthDaemonDescription": "El daemon de autenticación SSSH maneja la firma de claves SSH y autenticación PAM para este recurso. Elija si se ejecuta en el sitio (Newt) o en un host remoto separado. Vea <docsLink>la documentación</docsLink> para más.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Seleccionar estrategia",
"internalResourceAuthDaemonStrategyLabel": "Ubicación",
"internalResourceAuthDaemonSite": "En el sitio",
"internalResourceAuthDaemonSiteDescription": "Auth daemon corre en el sitio (Newt).",
"internalResourceAuthDaemonRemote": "Host remoto",
"internalResourceAuthDaemonRemoteDescription": "El daemon Auth corre en un host que no es el sitio.",
"internalResourceAuthDaemonPort": "Puerto de demonio (opcional)",
"orgAuthWhatsThis": "¿Dónde puedo encontrar el ID de mi organización?",
"learnMore": "Más información",
"backToHome": "Volver a inicio",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Habilitar aprobaciones de dispositivo",
"approvalsEmptyStateStep2Description": "Editar un rol y habilitar la opción 'Requerir aprobaciones de dispositivos'. Los usuarios con este rol necesitarán la aprobación del administrador para nuevos dispositivos.",
"approvalsEmptyStatePreviewDescription": "Vista previa: Cuando está habilitado, las solicitudes de dispositivo pendientes aparecerán aquí para su revisión",
"approvalsEmptyStateButtonText": "Administrar roles"
"approvalsEmptyStateButtonText": "Administrar roles",
"domainErrorTitle": "Estamos teniendo problemas para verificar su dominio",
"idpAdminAutoProvisionPoliciesTabHint": "Configure el mapeo de roles y las políticas de organización en la pestaña <policiesTabLink>Configuración de provisión automática</policiesTabLink>.",
"streamingTitle": "Transmisión de Eventos",
"streamingDescription": "Transmita eventos desde su organización a destinos externos en tiempo real.",
"streamingUnnamedDestination": "Destino sin nombre",
"streamingNoUrlConfigured": "No hay URL configurada",
"streamingAddDestination": "Añadir destino",
"streamingHttpWebhookTitle": "Webhook HTTP",
"streamingHttpWebhookDescription": "Enviar eventos a cualquier extremo HTTP con autenticación flexible y plantilla.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Transmite eventos a un bucket de almacenamiento de objetos compatible con S3. Próximamente.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Reenviar eventos directamente a tu cuenta de Datadog. Próximamente.",
"streamingTypePickerDescription": "Elija un tipo de destino para empezar.",
"streamingFailedToLoad": "Error al cargar destinos",
"streamingUnexpectedError": "Se ha producido un error inesperado.",
"streamingFailedToUpdate": "Error al actualizar destino",
"streamingDeletedSuccess": "Destino eliminado correctamente",
"streamingFailedToDelete": "Error al eliminar destino",
"streamingDeleteTitle": "Eliminar destino",
"streamingDeleteButtonText": "Eliminar destino",
"streamingDeleteDialogAreYouSure": "¿Está seguro que desea eliminar",
"streamingDeleteDialogThisDestination": "este destino",
"streamingDeleteDialogPermanentlyRemoved": "? Toda la configuración se eliminará permanentemente.",
"httpDestEditTitle": "Editar destino",
"httpDestAddTitle": "Añadir destino HTTP",
"httpDestEditDescription": "Actualizar la configuración para este destino de transmisión de eventos HTTP.",
"httpDestAddDescription": "Configure un nuevo extremo HTTP para recibir los eventos de su organización.",
"httpDestTabSettings": "Ajustes",
"httpDestTabHeaders": "Encabezados",
"httpDestTabBody": "Cuerpo",
"httpDestTabLogs": "Registros",
"httpDestNamePlaceholder": "Mi destino HTTP",
"httpDestUrlLabel": "URL de destino",
"httpDestUrlErrorHttpRequired": "URL debe usar http o https",
"httpDestUrlErrorHttpsRequired": "HTTPS es necesario en implementaciones en la nube",
"httpDestUrlErrorInvalid": "Introduzca una URL válida (ej. https://example.com/webhook)",
"httpDestAuthTitle": "Autenticación",
"httpDestAuthDescription": "Elija cómo están autenticadas las solicitudes en su punto final.",
"httpDestAuthNoneTitle": "Sin autenticación",
"httpDestAuthNoneDescription": "Envía solicitudes sin un encabezado de autorización.",
"httpDestAuthBearerTitle": "Tóken de portador",
"httpDestAuthBearerDescription": "Añade una autorización: portador <token> encabezado a cada solicitud.",
"httpDestAuthBearerPlaceholder": "Tu clave o token API",
"httpDestAuthBasicTitle": "Auth Básica",
"httpDestAuthBasicDescription": "Añade una Autorización: encabezado básico <credentials> . Proporcione credenciales como nombre de usuario: contraseña.",
"httpDestAuthBasicPlaceholder": "usuario:contraseña",
"httpDestAuthCustomTitle": "Cabecera personalizada",
"httpDestAuthCustomDescription": "Especifique un nombre de cabecera HTTP personalizado y un valor para la autenticación (por ejemplo, X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Nombre de cabecera (ej. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Valor de cabecera",
"httpDestCustomHeadersTitle": "Cabeceras HTTP personalizadas",
"httpDestCustomHeadersDescription": "Añadir cabeceras personalizadas a cada petición saliente. Útil para tokens estáticos o un tipo de contenido personalizado. De forma predeterminada, Content Type: application/json es enviado.",
"httpDestNoHeadersConfigured": "No hay cabeceras personalizadas. Haga clic en \"Añadir cabecera\" para añadir una.",
"httpDestHeaderNamePlaceholder": "Nombre de cabecera",
"httpDestHeaderValuePlaceholder": "Valor",
"httpDestAddHeader": "Añadir cabecera",
"httpDestBodyTemplateTitle": "Plantilla de cuerpo personalizada",
"httpDestBodyTemplateDescription": "Controla la estructura de carga de JSON enviada a tu punto final. Si está desactivado, se envía un objeto JSON por defecto para cada evento.",
"httpDestEnableBodyTemplate": "Activar plantilla de cuerpo personalizado",
"httpDestBodyTemplateLabel": "Plantilla de cuerpo (JSON)",
"httpDestBodyTemplateHint": "Utilice variables de plantilla para referenciar los campos del evento en su carga útil.",
"httpDestPayloadFormatTitle": "Formato de carga",
"httpDestPayloadFormatDescription": "Cómo se serializan los eventos en cada cuerpo de solicitud.",
"httpDestFormatJsonArrayTitle": "Matriz JSON",
"httpDestFormatJsonArrayDescription": "Una petición por lote, cuerpo es una matriz JSON. Compatible con la mayoría de los webhooks y Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Una petición por lote, el cuerpo es JSON delimitado por línea — un objeto por línea, sin arrays externos. Requerido por Splunk HEC, Elastic / OpenSearch, y Grafana Loki.",
"httpDestFormatSingleTitle": "Un evento por solicitud",
"httpDestFormatSingleDescription": "Envía un HTTP POST separado para cada evento individual. Úsalo sólo para los extremos que no pueden manejar lotes.",
"httpDestLogTypesTitle": "Tipos de Log",
"httpDestLogTypesDescription": "Elija qué tipos de registro son reenviados a este destino. Sólo los tipos de registro habilitados serán transmitidos.",
"httpDestAccessLogsTitle": "Registros de acceso",
"httpDestAccessLogsDescription": "Intentos de acceso a recursos, incluyendo solicitudes autenticadas y denegadas.",
"httpDestActionLogsTitle": "Registros de acción",
"httpDestActionLogsDescription": "Acciones administrativas realizadas por los usuarios dentro de la organización.",
"httpDestConnectionLogsTitle": "Registros de conexión",
"httpDestConnectionLogsDescription": "Eventos de conexión de sitios y túneles, incluyendo conexiones y desconexiones.",
"httpDestRequestLogsTitle": "Registros de Solicitud",
"httpDestRequestLogsDescription": "Registros de peticiones HTTP para recursos proxyficados, incluyendo método, ruta y código de respuesta.",
"httpDestSaveChanges": "Guardar Cambios",
"httpDestCreateDestination": "Crear destino",
"httpDestUpdatedSuccess": "Destino actualizado correctamente",
"httpDestCreatedSuccess": "Destino creado correctamente",
"httpDestUpdateFailed": "Error al actualizar destino",
"httpDestCreateFailed": "Error al crear el destino"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Créer un lien",
"resourcesNotFound": "Aucune ressource trouvée",
"resourceSearch": "Rechercher des ressources",
"machineSearch": "Rechercher des machines",
"machinesSearch": "Rechercher des clients de la machine...",
"machineNotFound": "Aucune machine trouvée",
"userDeviceSearch": "Rechercher des périphériques utilisateur",
"userDevicesSearch": "Rechercher des appareils utilisateurs...",
"openMenu": "Ouvrir le menu",
"resource": "Ressource",
"title": "Titre de la page",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy les demandes sur HTTPS en utilisant un nom de domaine entièrement qualifié.",
"resourceRaw": "Ressource TCP/UDP brute",
"resourceRawDescription": "Proxy les demandes sur TCP/UDP brut en utilisant un numéro de port.",
"resourceRawDescriptionCloud": "Requêtes de proxy sur TCP/UDP brute en utilisant un numéro de port. Nécessite des sites pour se connecter à un noeud distant.",
"resourceCreate": "Créer une ressource",
"resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource",
"resourceSeeAll": "Voir toutes les ressources",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Supprimer la clé d'API",
"apiKeysManage": "Gérer les clés d'API",
"apiKeysDescription": "Les clés d'API sont utilisées pour s'authentifier avec l'API d'intégration",
"provisioningKeysTitle": "Clé de provisioning",
"provisioningKeysManage": "Gérer les clés de provisioning",
"provisioningKeysDescription": "Les clés de provisioning sont utilisées pour authentifier la fourniture automatique de sites pour votre organisation.",
"provisioningManage": "Mise en place",
"provisioningDescription": "Gérer les clés de provisioning et examiner les sites en attente d'approbation.",
"pendingSites": "Sites en attente",
"siteApproveSuccess": "Site approuvé avec succès",
"siteApproveError": "Erreur lors de l'approbation du site",
"provisioningKeys": "Clés de provisionnement",
"searchProvisioningKeys": "Recherche des clés de provision...",
"provisioningKeysAdd": "Générer une clé de provisioning",
"provisioningKeysErrorDelete": "Erreur lors de la suppression de la clé de provisioning",
"provisioningKeysErrorDeleteMessage": "Erreur lors de la suppression de la clé de provisioning",
"provisioningKeysQuestionRemove": "Êtes-vous sûr de vouloir supprimer cette clé de provisioning de l'organisation ?",
"provisioningKeysMessageRemove": "Une fois supprimée, la clé ne peut plus être utilisée pour le provisionnement du site.",
"provisioningKeysDeleteConfirm": "Confirmer la suppression de la clé de provisioning",
"provisioningKeysDelete": "Supprimer la clé de provisioning",
"provisioningKeysCreate": "Générer une clé de provisioning",
"provisioningKeysCreateDescription": "Générer une nouvelle clé de provisioning pour l'organisation",
"provisioningKeysSeeAll": "Voir toutes les clés de provisioning",
"provisioningKeysSave": "Enregistrer la clé de provisioning",
"provisioningKeysSaveDescription": "Vous ne pourrez voir cela qu'une seule fois. Copiez-le dans un endroit sécurisé.",
"provisioningKeysErrorCreate": "Erreur lors de la création de la clé de provisioning",
"provisioningKeysList": "Nouvelle clé de provisioning",
"provisioningKeysMaxBatchSize": "Taille maximale du lot",
"provisioningKeysUnlimitedBatchSize": "Taille de lot illimitée (sans limite)",
"provisioningKeysMaxBatchUnlimited": "Illimité",
"provisioningKeysMaxBatchSizeInvalid": "Entrez une taille de lot maximale valide (11 000 000).",
"provisioningKeysValidUntil": "Valable jusqu'au",
"provisioningKeysValidUntilHint": "Laisser vide pour ne pas expirer.",
"provisioningKeysValidUntilInvalid": "Entrez une date et une heure valides.",
"provisioningKeysNumUsed": "Nombre de fois utilisées",
"provisioningKeysLastUsed": "Dernière utilisation",
"provisioningKeysNoExpiry": "Pas d'expiration",
"provisioningKeysNeverUsed": "Jamais",
"provisioningKeysEdit": "Modifier la clé de provisioning",
"provisioningKeysEditDescription": "Mettre à jour la taille maximale du lot et la durée d'expiration de cette clé.",
"provisioningKeysApproveNewSites": "Approuver les nouveaux sites",
"provisioningKeysApproveNewSitesDescription": "Approuver automatiquement les sites qui s'inscrivent avec cette clé.",
"provisioningKeysUpdateError": "Erreur lors de la mise à jour de la clé de provisioning",
"provisioningKeysUpdated": "Clé de provisioning mise à jour",
"provisioningKeysUpdatedDescription": "Vos modifications ont été enregistrées.",
"provisioningKeysBannerTitle": "Clés de provisioning du site",
"provisioningKeysBannerDescription": "Générez une clé de provisioning et utilisez-la avec le connecteur Newt pour créer automatiquement des sites au premier démarrage — pas besoin de configurer des identifiants distincts pour chaque site.",
"provisioningKeysBannerButtonText": "En savoir plus",
"pendingSitesBannerTitle": "Sites en attente",
"pendingSitesBannerDescription": "Les sites qui se connectent à l'aide d'une clé de provisioning apparaissent ici pour être revus. Approuver chaque site avant qu'il ne devienne actif et qu'il accède à vos ressources.",
"pendingSitesBannerButtonText": "En savoir plus",
"apiKeysSettings": "Paramètres de {apiKeyName}",
"userTitle": "Gérer tous les utilisateurs",
"userDescription": "Voir et gérer tous les utilisateurs du système",
@@ -508,9 +562,12 @@
"userSaved": "Utilisateur enregistré",
"userSavedDescription": "L'utilisateur a été mis à jour.",
"autoProvisioned": "Auto-provisionné",
"autoProvisionSettings": "Paramètres de la fourniture automatique",
"autoProvisionedDescription": "Permettre à cet utilisateur d'être géré automatiquement par le fournisseur d'identité",
"accessControlsDescription": "Gérer ce que cet utilisateur peut accéder et faire dans l'organisation",
"accessControlsSubmit": "Enregistrer les contrôles d'accès",
"singleRolePerUserPlanNotice": "Votre plan ne prend en charge qu'un seul rôle par utilisateur.",
"singleRolePerUserEditionNotice": "Cette édition ne prend en charge qu'un rôle par utilisateur.",
"roles": "Rôles",
"accessUsersRoles": "Gérer les utilisateurs et les rôles",
"accessUsersRolesDescription": "Invitez des utilisateurs et ajoutez-les aux rôles pour gérer l'accès à l'organisation",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Échec de la bascule de la ressource",
"resourcesErrorUpdateDescription": "Une erreur s'est produite lors de la mise à jour de la ressource",
"access": "Accès",
"accessControl": "Contrôle d'accès",
"shareLink": "Lien de partage {resource}",
"resourceSelect": "Sélectionner une ressource",
"shareLinks": "Liens de partage",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rôle supprimé",
"accessRoleRemovedDescription": "Le rôle a été supprimé avec succès.",
"accessRoleRequiredRemove": "Avant de supprimer ce rôle, veuillez sélectionner un nouveau rôle pour transférer les membres existants.",
"network": "Réseau",
"manage": "Gérer",
"sitesNotFound": "Aucun site trouvé.",
"pangolinServerAdmin": "Admin Serveur - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Oups! La page que vous recherchez n'existe pas.",
"overview": "Vue d'ensemble",
"home": "Accueil",
"accessControl": "Contrôle d'accès",
"settings": "Paramètres",
"usersAll": "Tous les utilisateurs",
"license": "Licence",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Obtenir l'utilisateur",
"actionGetOrgUser": "Obtenir l'utilisateur de l'organisation",
"actionListOrgDomains": "Lister les domaines de l'organisation",
"actionGetDomain": "Obtenir un domaine",
"actionCreateOrgDomain": "Créer un domaine",
"actionUpdateOrgDomain": "Mettre à jour le domaine",
"actionDeleteOrgDomain": "Supprimer le domaine",
"actionGetDNSRecords": "Récupérer les enregistrements DNS",
"actionRestartOrgDomain": "Redémarrer le domaine",
"actionCreateSite": "Créer un site",
"actionDeleteSite": "Supprimer un site",
"actionGetSite": "Obtenir un site",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Entrez le jeton de configuration depuis la console du serveur.",
"setupTokenRequired": "Le jeton de configuration est requis.",
"actionUpdateSite": "Mettre à jour un site",
"actionResetSiteBandwidth": "Réinitialiser la bande passante de l'organisation",
"actionListSiteRoles": "Lister les rôles autorisés du site",
"actionCreateResource": "Créer une ressource",
"actionDeleteResource": "Supprimer une ressource",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Supprimer un utilisateur",
"actionListUsers": "Lister les utilisateurs",
"actionAddUserRole": "Ajouter un rôle utilisateur",
"actionSetUserOrgRoles": "Définir les rôles de l'utilisateur",
"actionGenerateAccessToken": "Générer un jeton d'accès",
"actionDeleteAccessToken": "Supprimer un jeton d'accès",
"actionListAccessTokens": "Lister les jetons d'accès",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privé",
"sidebarAccessControl": "Contrôle d'accès",
"sidebarLogsAndAnalytics": "Journaux & Analytiques",
"sidebarTeam": "Equipe",
"sidebarUsers": "Utilisateurs",
"sidebarAdmin": "Administrateur",
"sidebarInvitations": "Invitations",
"sidebarRoles": "Rôles",
"sidebarShareableLinks": "Liens",
"sidebarApiKeys": "Clés API",
"sidebarProvisioning": "Mise en place",
"sidebarSettings": "Réglages",
"sidebarAllUsers": "Tous les utilisateurs",
"sidebarIdentityProviders": "Fournisseurs d'identité",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Journaux & Analytiques",
"sidebarBluePrints": "Configs",
"sidebarOrganization": "Organisation",
"sidebarManagement": "Gestion",
"sidebarBillingAndLicenses": "Facturation & Licences",
"sidebarLogsAnalytics": "Analyses",
"blueprints": "Configs",
@@ -1289,7 +1358,6 @@
"parsedContents": "Contenu analysé (lecture seule)",
"enableDockerSocket": "Activer la Config Docker",
"enableDockerSocketDescription": "Activer le ramassage d'étiquettes de socket Docker pour les étiquettes de plan. Le chemin de socket doit être fourni à Newt.",
"enableDockerSocketLink": "En savoir plus",
"viewDockerContainers": "Voir les conteneurs Docker",
"containersIn": "Conteneurs en {siteName}",
"selectContainerDescription": "Sélectionnez n'importe quel conteneur à utiliser comme nom d'hôte pour cette cible. Cliquez sur un port pour utiliser un port.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Espace de noms : {namespace}",
"domainPickerShowMore": "Afficher plus",
"regionSelectorTitle": "Sélectionner Région",
"domainPickerRemoteExitNodeWarning": "Les domaines fournis ne sont pas pris en charge lorsque les sites se connectent à des nœuds de sortie distants. Pour que les ressources soient disponibles sur des nœuds distants, utilisez un domaine personnalisé à la place.",
"regionSelectorInfo": "Sélectionner une région nous aide à offrir de meilleures performances pour votre localisation. Vous n'avez pas besoin d'être dans la même région que votre serveur.",
"regionSelectorPlaceholder": "Choisissez une région",
"regionSelectorComingSoon": "Bientôt disponible",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Avis de disponibilité des fonctionnalités",
"billingFeatureLossDescription": "En rétrogradant, les fonctionnalités non disponibles dans le nouveau plan seront automatiquement désactivées. Certains paramètres et configurations peuvent être perdus. Veuillez consulter la matrice de prix pour comprendre quelles fonctionnalités ne seront plus disponibles.",
"billingUsageExceedsLimit": "L'utilisation actuelle ({current}) dépasse la limite ({limit})",
"billingPastDueTitle": "Paiement en retard",
"billingPastDueDescription": "Votre paiement est échu. Veuillez mettre à jour votre méthode de paiement pour continuer à utiliser les fonctionnalités de votre plan actuel. Si non résolu, votre abonnement sera annulé et vous serez remis au niveau gratuit.",
"billingUnpaidTitle": "Abonnement impayé",
"billingUnpaidDescription": "Votre abonnement est impayé et vous avez été reversé au niveau gratuit. Veuillez mettre à jour votre méthode de paiement pour restaurer votre abonnement.",
"billingIncompleteTitle": "Paiement incomplet",
"billingIncompleteDescription": "Votre paiement est incomplet. Veuillez compléter le processus de paiement pour activer votre abonnement.",
"billingIncompleteExpiredTitle": "Paiement expiré",
"billingIncompleteExpiredDescription": "Votre paiement n'a jamais été complété et a expiré. Vous avez été restauré au niveau gratuit. Veuillez vous abonner à nouveau pour restaurer l'accès aux fonctionnalités payantes.",
"billingManageSubscription": "Gérer votre abonnement",
"billingResolvePaymentIssue": "Veuillez résoudre votre problème de paiement avant de procéder à la mise à niveau ou à la rétrogradation",
"signUpTerms": {
"IAgreeToThe": "Je suis d'accord avec",
"termsOfService": "les conditions d'utilisation",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Le temps est exprimé en secondes",
"requireDeviceApproval": "Exiger les autorisations de l'appareil",
"requireDeviceApprovalDescription": "Les utilisateurs ayant ce rôle ont besoin de nouveaux périphériques approuvés par un administrateur avant de pouvoir se connecter et accéder aux ressources.",
"sshAccess": "Accès SSH",
"roleAllowSsh": "Autoriser SSH",
"roleAllowSshAllow": "Autoriser",
"roleAllowSshDisallow": "Interdire",
"roleAllowSshDescription": "Autoriser les utilisateurs avec ce rôle à se connecter aux ressources via SSH. Lorsque désactivé, le rôle ne peut pas utiliser les accès SSH.",
"sshSudoMode": "Accès Sudo",
"sshSudoModeNone": "Aucun",
"sshSudoModeNoneDescription": "L'utilisateur ne peut pas exécuter de commandes avec sudo.",
"sshSudoModeFull": "Sudo complet",
"sshSudoModeFullDescription": "L'utilisateur peut exécuter n'importe quelle commande avec sudo.",
"sshSudoModeCommands": "Commandes",
"sshSudoModeCommandsDescription": "L'utilisateur ne peut exécuter que les commandes spécifiées avec sudo.",
"sshSudo": "Autoriser sudo",
"sshSudoCommands": "Commandes Sudo",
"sshSudoCommandsDescription": "Liste des commandes séparées par des virgules que l'utilisateur est autorisé à exécuter avec sudo.",
"sshCreateHomeDir": "Créer un répertoire personnel",
"sshUnixGroups": "Groupes Unix",
"sshUnixGroupsDescription": "Groupes Unix séparés par des virgules pour ajouter l'utilisateur sur l'hôte cible.",
"retryAttempts": "Tentatives de réessai",
"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.",
@@ -1851,6 +1948,40 @@
"exitNode": "Nœud de sortie",
"country": "Pays",
"rulesMatchCountry": "Actuellement basé sur l'IP source",
"region": "Région",
"selectRegion": "Sélectionner une région",
"searchRegions": "Rechercher des régions...",
"noRegionFound": "Aucune région trouvée.",
"rulesMatchRegion": "Sélectionnez un groupement régional de pays",
"rulesErrorInvalidRegion": "Région invalide",
"rulesErrorInvalidRegionDescription": "Veuillez sélectionner une région valide.",
"regionAfrica": "L'Afrique",
"regionNorthernAfrica": "Afrique du Nord",
"regionEasternAfrica": "Afrique de l'Est",
"regionMiddleAfrica": "Afrique Moyenne",
"regionSouthernAfrica": "Afrique australe",
"regionWesternAfrica": "Afrique de l'Ouest",
"regionAmericas": "Amériques",
"regionCaribbean": "Caraïbes",
"regionCentralAmerica": "Amérique centrale",
"regionSouthAmerica": "Amérique du Sud",
"regionNorthernAmerica": "Amérique du Nord",
"regionAsia": "L'Asie",
"regionCentralAsia": "Asie centrale",
"regionEasternAsia": "Asie de l'Est",
"regionSouthEasternAsia": "Asie du Sud-Est",
"regionSouthernAsia": "Asie du Sud",
"regionWesternAsia": "Asie de l'Ouest",
"regionEurope": "LEurope",
"regionEasternEurope": "Europe de l'Est",
"regionNorthernEurope": "Europe du Nord",
"regionSouthernEurope": "Europe du Sud",
"regionWesternEurope": "Europe occidentale",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australie et Nouvelle-Zélande",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Gestion autonome",
"description": "Serveur Pangolin auto-hébergé avec des cloches et des sifflets supplémentaires",
@@ -1899,6 +2030,25 @@
"invalidValue": "Valeur non valide",
"idpTypeLabel": "Type de fournisseur d'identité",
"roleMappingExpressionPlaceholder": "ex: contenu(groupes) && 'admin' || 'membre'",
"roleMappingModeFixedRoles": "Rôles fixes",
"roleMappingModeMappingBuilder": "Constructeur de cartographie",
"roleMappingModeRawExpression": "Expression brute",
"roleMappingFixedRolesPlaceholderSelect": "Sélectionnez un ou plusieurs rôles",
"roleMappingFixedRolesPlaceholderFreeform": "Tapez les noms des rôles (correspondance exacte par organisation)",
"roleMappingFixedRolesDescriptionSameForAll": "Assigner le même jeu de rôles à chaque utilisateur auto-provisionné.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Pour les politiques par défaut, les noms de rôles de type qui existent dans chaque organisation où les utilisateurs sont fournis. Les noms doivent correspondre exactement.",
"roleMappingClaimPath": "Chemin de revendication",
"roleMappingClaimPathPlaceholder": "Groupes",
"roleMappingClaimPathDescription": "Chemin dans le bloc de jeton qui contient les valeurs source (par exemple, les groupes).",
"roleMappingMatchValue": "Valeur de la correspondance",
"roleMappingAssignRoles": "Assigner des rôles",
"roleMappingAddMappingRule": "Ajouter une règle de mappage",
"roleMappingRawExpressionResultDescription": "L'expression doit être évaluée à une chaîne ou un tableau de chaînes.",
"roleMappingRawExpressionResultDescriptionSingleRole": "L'expression doit être évaluée à une chaîne (un seul nom de rôle).",
"roleMappingMatchValuePlaceholder": "Valeur de la correspondance (par exemple: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Tapez les noms des rôles (exact par org)",
"roleMappingBuilderFreeformRowHint": "Les noms de rôle doivent correspondre à un rôle dans chaque organisation cible.",
"roleMappingRemoveRule": "Supprimer",
"idpGoogleConfiguration": "Configuration Google",
"idpGoogleConfigurationDescription": "Configurer les identifiants Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Durée de conservation des journaux d'accès",
"logRetentionActionLabel": "Retention du journal des actions",
"logRetentionActionDescription": "Durée de conservation du journal des actions",
"logRetentionConnectionLabel": "Rétention du journal de connexion",
"logRetentionConnectionDescription": "Durée de conservation des logs de connexion",
"logRetentionDisabled": "Désactivé",
"logRetention3Days": "3 jours",
"logRetention7Days": "7 jours",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Fin de l'année suivante",
"actionLogsDescription": "Voir l'historique des actions effectuées dans cette organisation",
"accessLogsDescription": "Voir les demandes d'authentification d'accès aux ressources de cette organisation",
"licenseRequiredToUse": "Une licence <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> est nécessaire pour utiliser cette fonctionnalité. Cette fonctionnalité est également disponible dans <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "La version <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> est requise pour utiliser cette fonctionnalité. Cette fonctionnalité est également disponible dans <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Journaux de connexion",
"connectionLogsDescription": "Voir les journaux de connexion pour les tunnels de cette organisation",
"sidebarLogsConnection": "Journaux de connexion",
"sidebarLogsStreaming": "Streaming en cours",
"sourceAddress": "Adresse source",
"destinationAddress": "Adresse de destination",
"duration": "Durée",
"licenseRequiredToUse": "Une <enterpriseLicenseLink>licence Enterprise Edition</enterpriseLicenseLink> ou <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> est requise pour utiliser cette fonctionnalité. <bookADemoLink>Réservez une démonstration ou une évaluation de POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "La version <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> est requise pour utiliser cette fonctionnalité. Cette fonctionnalité est également disponible dans <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Réservez une démo ou un essai POC</bookADemoLink>.",
"certResolver": "Résolveur de certificat",
"certResolverDescription": "Sélectionnez le solveur de certificat à utiliser pour cette ressource.",
"selectCertResolver": "Sélectionnez le résolveur de certificat",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Contrôle d'accès",
"editInternalResourceDialogAccessControlDescription": "Contrôlez quels rôles, utilisateurs et clients de machine ont accès à cette ressource lorsqu'ils sont connectés. Les administrateurs ont toujours accès.",
"editInternalResourceDialogPortRangeValidationError": "La plage de ports doit être \"*\" pour tous les ports, ou une liste de ports et de plages séparés par des virgules (par exemple, \"80,443,8000-9000\"). Les ports doivent être compris entre 1 et 65535.",
"internalResourceAuthDaemonStrategy": "Emplacement du démon d'authentification SSH",
"internalResourceAuthDaemonStrategyDescription": "Choisissez où le démon d'authentification SSH s'exécute : sur le site (Newt) ou sur un hôte distant.",
"internalResourceAuthDaemonDescription": "Le démon d'authentification SSH gère la signature des clés SSH et l'authentification PAM pour cette ressource. Choisissez s'il fonctionne sur le site (Newt) ou sur un hôte distant séparé. Voir <docsLink>la documentation</docsLink> pour plus d'informations.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Choisir une stratégie",
"internalResourceAuthDaemonStrategyLabel": "Localisation",
"internalResourceAuthDaemonSite": "Sur le site",
"internalResourceAuthDaemonSiteDescription": "Le démon Auth fonctionne sur le site (Newt).",
"internalResourceAuthDaemonRemote": "Hôte distant",
"internalResourceAuthDaemonRemoteDescription": "Le démon Auth fonctionne sur un hôte qui n'est pas le site.",
"internalResourceAuthDaemonPort": "Port du démon (optionnel)",
"orgAuthWhatsThis": "Où puis-je trouver mon identifiant d'organisation ?",
"learnMore": "En savoir plus",
"backToHome": "Retour à l'accueil",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Activer les autorisations de l'appareil",
"approvalsEmptyStateStep2Description": "Modifier un rôle et activer l'option 'Exiger les autorisations de l'appareil'. Les utilisateurs avec ce rôle auront besoin de l'approbation de l'administrateur pour les nouveaux appareils.",
"approvalsEmptyStatePreviewDescription": "Aperçu: Lorsque cette option est activée, les demandes de périphérique en attente apparaîtront ici pour vérification",
"approvalsEmptyStateButtonText": "Gérer les rôles"
"approvalsEmptyStateButtonText": "Gérer les rôles",
"domainErrorTitle": "Nous avons des difficultés à vérifier votre domaine",
"idpAdminAutoProvisionPoliciesTabHint": "Configurer les politiques de mappage des rôles et de l'organisation dans l'onglet <policiesTabLink>Paramètres de la fourniture automatique</policiesTabLink>.",
"streamingTitle": "Streaming d'événements",
"streamingDescription": "Diffusez en temps réel des événements de votre organisation vers des destinations externes.",
"streamingUnnamedDestination": "Destination sans nom",
"streamingNoUrlConfigured": "Aucune URL configurée",
"streamingAddDestination": "Ajouter une destination",
"streamingHttpWebhookTitle": "Webhook HTTP",
"streamingHttpWebhookDescription": "Envoyez des événements à n'importe quel point de terminaison HTTP avec une authentification flexible et un template.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Flux d'événements vers un compartiment de stockage d'objet compatible S3. Bientôt.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Transférer des événements directement sur votre compte Datadog. Prochainement.",
"streamingTypePickerDescription": "Choisissez un type de destination pour commencer.",
"streamingFailedToLoad": "Impossible de charger les destinations",
"streamingUnexpectedError": "Une erreur inattendue s'est produite.",
"streamingFailedToUpdate": "Impossible de mettre à jour la destination",
"streamingDeletedSuccess": "Destination supprimée avec succès",
"streamingFailedToDelete": "Impossible de supprimer la destination",
"streamingDeleteTitle": "Supprimer la destination",
"streamingDeleteButtonText": "Supprimer la destination",
"streamingDeleteDialogAreYouSure": "Êtes-vous sûr de vouloir supprimer",
"streamingDeleteDialogThisDestination": "cette destination",
"streamingDeleteDialogPermanentlyRemoved": "? Toutes les configurations seront définitivement supprimées.",
"httpDestEditTitle": "Modifier la destination",
"httpDestAddTitle": "Ajouter une destination HTTP",
"httpDestEditDescription": "Mettre à jour la configuration pour cette destination de streaming d'événements HTTP.",
"httpDestAddDescription": "Configurez un nouveau point de terminaison HTTP pour recevoir les événements de votre organisation.",
"httpDestTabSettings": "Réglages",
"httpDestTabHeaders": "En-têtes",
"httpDestTabBody": "Corps",
"httpDestTabLogs": "Journaux",
"httpDestNamePlaceholder": "Ma destination HTTP",
"httpDestUrlLabel": "URL de destination",
"httpDestUrlErrorHttpRequired": "L'URL doit utiliser http ou https",
"httpDestUrlErrorHttpsRequired": "HTTPS est requis pour les déploiements du cloud",
"httpDestUrlErrorInvalid": "Entrez une URL valide (par exemple https://example.com/webhook)",
"httpDestAuthTitle": "Authentification",
"httpDestAuthDescription": "Choisissez comment les requêtes à votre terminaison sont authentifiées.",
"httpDestAuthNoneTitle": "Aucune authentification",
"httpDestAuthNoneDescription": "Envoie des requêtes sans en-tête d'autorisation.",
"httpDestAuthBearerTitle": "Jeton de Porteur",
"httpDestAuthBearerDescription": "Ajoute un en-tête Authorization: Bearer <token> à chaque requête.",
"httpDestAuthBearerPlaceholder": "Votre clé API ou votre jeton",
"httpDestAuthBasicTitle": "Authentification basique",
"httpDestAuthBasicDescription": "Ajoute une autorisation : en-tête de base <credentials> . Fournissez des informations d'identification comme nom d'utilisateur:mot de passe.",
"httpDestAuthBasicPlaceholder": "nom d'utilisateur:mot de passe",
"httpDestAuthCustomTitle": "En-tête personnalisé",
"httpDestAuthCustomDescription": "Spécifiez un nom d'en-tête HTTP personnalisé et une valeur pour l'authentification (par exemple X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Nom de l'en-tête (par exemple X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Valeur de l'en-tête",
"httpDestCustomHeadersTitle": "En-têtes HTTP personnalisés",
"httpDestCustomHeadersDescription": "Ajouter des en-têtes personnalisés à chaque requête sortante. Utile pour les jetons statiques ou un type de contenu personnalisé. Par défaut, Content-Type: application/json est envoyé.",
"httpDestNoHeadersConfigured": "Aucun en-tête personnalisé configuré. Cliquez sur \"Ajouter un en-tête\" pour en ajouter un.",
"httpDestHeaderNamePlaceholder": "Nom de l'en-tête",
"httpDestHeaderValuePlaceholder": "Valeur",
"httpDestAddHeader": "Ajouter un en-tête",
"httpDestBodyTemplateTitle": "Modèle de corps personnalisé",
"httpDestBodyTemplateDescription": "Contrôle la structure de charge utile JSON envoyée à votre terminal. Si désactivé, un objet JSON par défaut est envoyé pour chaque événement.",
"httpDestEnableBodyTemplate": "Activer le modèle de corps personnalisé",
"httpDestBodyTemplateLabel": "Modèle de corps (JSON)",
"httpDestBodyTemplateHint": "Utilisez les variables de modèle pour référencer les champs d'événement dans votre charge utile.",
"httpDestPayloadFormatTitle": "Format de la charge utile",
"httpDestPayloadFormatDescription": "Comment les événements sont sérialisés dans chaque corps de requête.",
"httpDestFormatJsonArrayTitle": "Tableau JSON",
"httpDestFormatJsonArrayDescription": "Une requête par lot, le corps est un tableau JSON. Compatible avec la plupart des webhooks génériques et des datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Une requête par lot, body est un JSON délimité par une nouvelle ligne — un objet par ligne, pas de tableau extérieur. Requis par Splunk HEC, Elastic / OpenSearch, et Grafana Loki.",
"httpDestFormatSingleTitle": "Un événement par demande",
"httpDestFormatSingleDescription": "Envoie un POST HTTP séparé pour chaque événement individuel. Utilisé uniquement pour les terminaux qui ne peuvent pas gérer des lots.",
"httpDestLogTypesTitle": "Types de logs",
"httpDestLogTypesDescription": "Choisissez quels types de journaux sont envoyés à cette destination. Seuls les types de journaux activés seront diffusés.",
"httpDestAccessLogsTitle": "Journaux d'accès",
"httpDestAccessLogsDescription": "Tentatives d'accès aux ressources, y compris les demandes authentifiées et refusées.",
"httpDestActionLogsTitle": "Journaux des actions",
"httpDestActionLogsDescription": "Actions administratives effectuées par les utilisateurs au sein de l'organisation.",
"httpDestConnectionLogsTitle": "Journaux de connexion",
"httpDestConnectionLogsDescription": "Événements de connexion du site et du tunnel, y compris les connexions et les déconnexions.",
"httpDestRequestLogsTitle": "Journal des requêtes",
"httpDestRequestLogsDescription": "Journaux des requêtes HTTP pour les ressources proxiées, y compris la méthode, le chemin et le code de réponse.",
"httpDestSaveChanges": "Enregistrer les modifications",
"httpDestCreateDestination": "Créer une destination",
"httpDestUpdatedSuccess": "Destination mise à jour avec succès",
"httpDestCreatedSuccess": "Destination créée avec succès",
"httpDestUpdateFailed": "Impossible de mettre à jour la destination",
"httpDestCreateFailed": "Impossible de créer la destination"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Crea Collegamento",
"resourcesNotFound": "Nessuna risorsa trovata",
"resourceSearch": "Cerca risorse",
"machineSearch": "Ricerca macchine",
"machinesSearch": "Cerca client macchina...",
"machineNotFound": "Nessuna macchina trovata",
"userDeviceSearch": "Cerca dispositivi utente",
"userDevicesSearch": "Cerca dispositivi utente...",
"openMenu": "Apri menu",
"resource": "Risorsa",
"title": "Titolo",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Richieste proxy su HTTPS usando un nome di dominio completo.",
"resourceRaw": "Risorsa Raw TCP/UDP",
"resourceRawDescription": "Richieste proxy su TCP/UDP grezzo utilizzando un numero di porta.",
"resourceRawDescriptionCloud": "Richiesta proxy su TCP/UDP grezzo utilizzando un numero di porta. Richiede siti per connettersi a un nodo remoto.",
"resourceCreate": "Crea Risorsa",
"resourceCreateDescription": "Segui i passaggi seguenti per creare una nuova risorsa",
"resourceSeeAll": "Vedi Tutte Le Risorse",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Elimina Chiave API",
"apiKeysManage": "Gestisci Chiavi API",
"apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione",
"provisioningKeysTitle": "Chiave Di Provvedimento",
"provisioningKeysManage": "Gestisci Chiavi Di Provvedimento",
"provisioningKeysDescription": "Le chiavi di provisioning vengono utilizzate per autenticare il provisioning automatico del sito per la tua organizzazione.",
"provisioningManage": "Accantonamento",
"provisioningDescription": "Gestire le chiavi di provisioning e rivedere i siti in attesa di approvazione.",
"pendingSites": "Siti In Attesa",
"siteApproveSuccess": "Sito approvato con successo",
"siteApproveError": "Errore nell'approvazione del sito",
"provisioningKeys": "Chiavi Di Provvedimento",
"searchProvisioningKeys": "Cerca i tasti di provisioning ...",
"provisioningKeysAdd": "Genera Chiave Di Provvedimento",
"provisioningKeysErrorDelete": "Errore nell'eliminare la chiave di provisioning",
"provisioningKeysErrorDeleteMessage": "Errore nell'eliminare la chiave di provisioning",
"provisioningKeysQuestionRemove": "Sei sicuro di voler rimuovere questa chiave di provisioning dall'organizzazione?",
"provisioningKeysMessageRemove": "Una volta rimossa, la chiave non può più essere utilizzata per il provisioning.",
"provisioningKeysDeleteConfirm": "Conferma Elimina Chiave Provvisoria",
"provisioningKeysDelete": "Elimina chiave di provisioning",
"provisioningKeysCreate": "Genera Chiave Di Provvedimento",
"provisioningKeysCreateDescription": "Genera una nuova chiave di provisioning per l'organizzazione",
"provisioningKeysSeeAll": "Vedi tutte le chiavi di provisioning",
"provisioningKeysSave": "Salva la chiave di provisioning",
"provisioningKeysSaveDescription": "Sarai in grado di vedere solo una volta. Copiarlo in un posto sicuro.",
"provisioningKeysErrorCreate": "Errore nella creazione della chiave di provisioning",
"provisioningKeysList": "Nuova chiave di provisioning",
"provisioningKeysMaxBatchSize": "Dimensione massima lotto",
"provisioningKeysUnlimitedBatchSize": "Dimensione illimitata del lotto (nessun limite)",
"provisioningKeysMaxBatchUnlimited": "Illimitato",
"provisioningKeysMaxBatchSizeInvalid": "Inserisci un lotto massimo valido (11.000.000).",
"provisioningKeysValidUntil": "Valido fino al",
"provisioningKeysValidUntilHint": "Lasciare vuoto per nessuna scadenza.",
"provisioningKeysValidUntilInvalid": "Inserisci una data e ora valide.",
"provisioningKeysNumUsed": "Volte usate",
"provisioningKeysLastUsed": "Ultimo utilizzo",
"provisioningKeysNoExpiry": "Nessuna scadenza",
"provisioningKeysNeverUsed": "Mai",
"provisioningKeysEdit": "Modifica Chiave Di Provvedimento",
"provisioningKeysEditDescription": "Aggiorna la dimensione massima del lotto e il tempo di scadenza per questa chiave.",
"provisioningKeysApproveNewSites": "Approva nuovi siti",
"provisioningKeysApproveNewSitesDescription": "Approvare automaticamente i siti che si registrano con questa chiave.",
"provisioningKeysUpdateError": "Errore nell'aggiornamento della chiave di provisioning",
"provisioningKeysUpdated": "Chiave di accantonamento aggiornata",
"provisioningKeysUpdatedDescription": "Le tue modifiche sono state salvate.",
"provisioningKeysBannerTitle": "Chiavi Di Provvedimento Sito",
"provisioningKeysBannerDescription": "Generare una chiave di provisioning e usarla con il connettore Newt per creare automaticamente siti al primo avvio — non è necessario impostare credenziali separate per ogni sito.",
"provisioningKeysBannerButtonText": "Scopri di più",
"pendingSitesBannerTitle": "Siti In Attesa",
"pendingSitesBannerDescription": "I siti che si connettono utilizzando una chiave di provisioning appaiono qui per la revisione. Approva ogni sito prima che diventi attivo e ottenga l'accesso alle tue risorse.",
"pendingSitesBannerButtonText": "Scopri di più",
"apiKeysSettings": "Impostazioni {apiKeyName}",
"userTitle": "Gestisci Tutti Gli Utenti",
"userDescription": "Visualizza e gestisci tutti gli utenti del sistema",
@@ -508,9 +562,12 @@
"userSaved": "Utente salvato",
"userSavedDescription": "L'utente è stato aggiornato.",
"autoProvisioned": "Auto Provisioned",
"autoProvisionSettings": "Impostazioni Automatiche Di Fornitura",
"autoProvisionedDescription": "Permetti a questo utente di essere gestito automaticamente dal provider di identità",
"accessControlsDescription": "Gestisci cosa questo utente può accedere e fare nell'organizzazione",
"accessControlsSubmit": "Salva Controlli di Accesso",
"singleRolePerUserPlanNotice": "Il tuo piano supporta solo un ruolo per utente.",
"singleRolePerUserEditionNotice": "Questa edizione supporta solo un ruolo per utente.",
"roles": "Ruoli",
"accessUsersRoles": "Gestisci Utenti e Ruoli",
"accessUsersRolesDescription": "Invita gli utenti e aggiungili ai ruoli per gestire l'accesso all'organizzazione",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Impossibile attivare/disattivare la risorsa",
"resourcesErrorUpdateDescription": "Si è verificato un errore durante l'aggiornamento della risorsa",
"access": "Accesso",
"accessControl": "Controllo Accessi",
"shareLink": "Link di Condivisione {resource}",
"resourceSelect": "Seleziona risorsa",
"shareLinks": "Link di Condivisione",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Ruolo rimosso",
"accessRoleRemovedDescription": "Il ruolo è stato rimosso con successo.",
"accessRoleRequiredRemove": "Prima di eliminare questo ruolo, seleziona un nuovo ruolo a cui trasferire i membri esistenti.",
"network": "Rete",
"manage": "Gestisci",
"sitesNotFound": "Nessun sito trovato.",
"pangolinServerAdmin": "Server Admin - Pangolina",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Oops! La pagina che stai cercando non esiste.",
"overview": "Panoramica",
"home": "Home",
"accessControl": "Controllo Accessi",
"settings": "Impostazioni",
"usersAll": "Tutti Gli Utenti",
"license": "Licenza",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Ottieni Utente",
"actionGetOrgUser": "Ottieni Utente Organizzazione",
"actionListOrgDomains": "Elenca Domini Organizzazione",
"actionGetDomain": "Ottieni Dominio",
"actionCreateOrgDomain": "Crea Dominio",
"actionUpdateOrgDomain": "Aggiorna Dominio",
"actionDeleteOrgDomain": "Elimina Dominio",
"actionGetDNSRecords": "Ottieni Record DNS",
"actionRestartOrgDomain": "Riavvia Dominio",
"actionCreateSite": "Crea Sito",
"actionDeleteSite": "Elimina Sito",
"actionGetSite": "Ottieni Sito",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Inserisci il token di configurazione dalla console del server.",
"setupTokenRequired": "Il token di configurazione è richiesto",
"actionUpdateSite": "Aggiorna Sito",
"actionResetSiteBandwidth": "Reimposta Larghezza Banda Dell'Organizzazione",
"actionListSiteRoles": "Elenca Ruoli Sito Consentiti",
"actionCreateResource": "Crea Risorsa",
"actionDeleteResource": "Elimina Risorsa",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Rimuovi Utente",
"actionListUsers": "Elenca Utenti",
"actionAddUserRole": "Aggiungi Ruolo Utente",
"actionSetUserOrgRoles": "Imposta Ruoli Utente",
"actionGenerateAccessToken": "Genera Token di Accesso",
"actionDeleteAccessToken": "Elimina Token di Accesso",
"actionListAccessTokens": "Elenca Token di Accesso",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privato",
"sidebarAccessControl": "Controllo Accesso",
"sidebarLogsAndAnalytics": "Registri E Analisi",
"sidebarTeam": "Squadra",
"sidebarUsers": "Utenti",
"sidebarAdmin": "Amministratore",
"sidebarInvitations": "Inviti",
"sidebarRoles": "Ruoli",
"sidebarShareableLinks": "Collegamenti",
"sidebarApiKeys": "Chiavi API",
"sidebarProvisioning": "Accantonamento",
"sidebarSettings": "Impostazioni",
"sidebarAllUsers": "Tutti Gli Utenti",
"sidebarIdentityProviders": "Fornitori Di Identità",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Progetti",
"sidebarOrganization": "Organizzazione",
"sidebarManagement": "Gestione",
"sidebarBillingAndLicenses": "Fatturazione E Licenze",
"sidebarLogsAnalytics": "Analisi",
"blueprints": "Progetti",
@@ -1289,7 +1358,6 @@
"parsedContents": "Sommario Analizzato (Solo Lettura)",
"enableDockerSocket": "Abilita Progetto Docker",
"enableDockerSocketDescription": "Abilita la raschiatura dell'etichetta Docker Socket per le etichette dei progetti. Il percorso del socket deve essere fornito a Newt.",
"enableDockerSocketLink": "Scopri di più",
"viewDockerContainers": "Visualizza Contenitori Docker",
"containersIn": "Contenitori in {siteName}",
"selectContainerDescription": "Seleziona qualsiasi contenitore da usare come hostname per questo obiettivo. Fai clic su una porta per usare una porta.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Namespace: {namespace}",
"domainPickerShowMore": "Mostra Altro",
"regionSelectorTitle": "Seleziona regione",
"domainPickerRemoteExitNodeWarning": "I domini forniti non sono supportati quando i siti si connettono a nodi di uscita remoti. Affinché le risorse siano disponibili su nodi remoti, utilizza invece un dominio personalizzato.",
"regionSelectorInfo": "Selezionare una regione ci aiuta a fornire migliori performance per la tua posizione. Non devi necessariamente essere nella stessa regione del tuo server.",
"regionSelectorPlaceholder": "Scegli una regione",
"regionSelectorComingSoon": "Prossimamente",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Avviso Di Disponibilità Caratteristica",
"billingFeatureLossDescription": "Con il downgrading, le funzioni non disponibili nel nuovo piano saranno disattivate automaticamente. Alcune impostazioni e configurazioni potrebbero andare perse. Controlla la matrice dei prezzi per capire quali funzioni non saranno più disponibili.",
"billingUsageExceedsLimit": "L'utilizzo corrente ({current}) supera il limite ({limit})",
"billingPastDueTitle": "Pagamento Scaduto",
"billingPastDueDescription": "Il pagamento è scaduto. Si prega di aggiornare il metodo di pagamento per continuare a utilizzare le funzioni del piano corrente. Se non risolto, il tuo abbonamento verrà annullato e verrai ripristinato al livello gratuito.",
"billingUnpaidTitle": "Abbonamento Non Pagato",
"billingUnpaidDescription": "Il tuo abbonamento non è pagato e sei stato restituito al livello gratuito. Per favore aggiorna il metodo di pagamento per ripristinare l'abbonamento.",
"billingIncompleteTitle": "Pagamento Incompleto",
"billingIncompleteDescription": "Il pagamento è incompleto. Si prega di completare il processo di pagamento per attivare il tuo abbonamento.",
"billingIncompleteExpiredTitle": "Pagamento Scaduto",
"billingIncompleteExpiredDescription": "Il tuo pagamento non è mai stato completato ed è scaduto. Sei stato ripristinato al livello gratuito. Si prega di iscriversi nuovamente per ripristinare l'accesso alle funzionalità a pagamento.",
"billingManageSubscription": "Gestisci il tuo abbonamento",
"billingResolvePaymentIssue": "Si prega di risolvere il problema di pagamento prima di aggiornare o declassare",
"signUpTerms": {
"IAgreeToThe": "Accetto i",
"termsOfService": "termini di servizio",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Il tempo è in secondi",
"requireDeviceApproval": "Richiede Approvazioni Dispositivo",
"requireDeviceApprovalDescription": "Gli utenti con questo ruolo hanno bisogno di nuovi dispositivi approvati da un amministratore prima di poter connettersi e accedere alle risorse.",
"sshAccess": "Accesso SSH",
"roleAllowSsh": "Consenti SSH",
"roleAllowSshAllow": "Consenti",
"roleAllowSshDisallow": "Non Consentire",
"roleAllowSshDescription": "Consenti agli utenti con questo ruolo di connettersi alle risorse tramite SSH. Quando disabilitato, il ruolo non può utilizzare l'accesso SSH.",
"sshSudoMode": "Accesso Sudo",
"sshSudoModeNone": "Nessuno",
"sshSudoModeNoneDescription": "L'utente non può eseguire comandi con sudo.",
"sshSudoModeFull": "Sudo Completo",
"sshSudoModeFullDescription": "L'utente può eseguire qualsiasi comando con sudo.",
"sshSudoModeCommands": "Comandi",
"sshSudoModeCommandsDescription": "L'utente può eseguire solo i comandi specificati con sudo.",
"sshSudo": "Consenti sudo",
"sshSudoCommands": "Comandi Sudo",
"sshSudoCommandsDescription": "Elenco di comandi separati da virgole che l'utente può eseguire con sudo.",
"sshCreateHomeDir": "Crea Cartella Home",
"sshUnixGroups": "Gruppi Unix",
"sshUnixGroupsDescription": "Gruppi Unix separati da virgole per aggiungere l'utente sull'host di destinazione.",
"retryAttempts": "Tentativi di Riprova",
"expectedResponseCodes": "Codici di Risposta Attesi",
"expectedResponseCodesDescription": "Codice di stato HTTP che indica lo stato di salute. Se lasciato vuoto, considerato sano è compreso tra 200-300.",
@@ -1851,6 +1948,40 @@
"exitNode": "Nodo di Uscita",
"country": "Paese",
"rulesMatchCountry": "Attualmente basato sull'IP di origine",
"region": "Regione",
"selectRegion": "Seleziona regione",
"searchRegions": "Cerca regioni...",
"noRegionFound": "Nessuna regione trovata.",
"rulesMatchRegion": "Seleziona un raggruppamento regionale di paesi",
"rulesErrorInvalidRegion": "Regione non valida",
"rulesErrorInvalidRegionDescription": "Seleziona una regione valida.",
"regionAfrica": "Africa",
"regionNorthernAfrica": "Africa Settentrionale",
"regionEasternAfrica": "Africa Orientale",
"regionMiddleAfrica": "Africa Centrale",
"regionSouthernAfrica": "Africa Meridionale",
"regionWesternAfrica": "Africa Occidentale",
"regionAmericas": "Americhe",
"regionCaribbean": "Caraibi",
"regionCentralAmerica": "America Centrale",
"regionSouthAmerica": "America Del Sud",
"regionNorthernAmerica": "America Del Nord",
"regionAsia": "Asia",
"regionCentralAsia": "Asia Centrale",
"regionEasternAsia": "Asia Orientale",
"regionSouthEasternAsia": "Asia Sudorientale",
"regionSouthernAsia": "Asia Meridionale",
"regionWesternAsia": "Asia Occidentale",
"regionEurope": "Europa",
"regionEasternEurope": "Europa Orientale",
"regionNorthernEurope": "Europa Settentrionale",
"regionSouthernEurope": "Europa Meridionale",
"regionWesternEurope": "Europa Occidentale",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australia e Nuova Zelanda",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Gestito Auto-Ospitato",
"description": "Server Pangolin self-hosted più affidabile e a bassa manutenzione con campanelli e fischietti extra",
@@ -1899,6 +2030,25 @@
"invalidValue": "Valore non valido",
"idpTypeLabel": "Tipo Provider Identità",
"roleMappingExpressionPlaceholder": "es. contiene(gruppi, 'admin') && 'Admin' <unk> <unk> 'Membro'",
"roleMappingModeFixedRoles": "Ruoli Fissi",
"roleMappingModeMappingBuilder": "Mapping Builder",
"roleMappingModeRawExpression": "Espressione Raw",
"roleMappingFixedRolesPlaceholderSelect": "Seleziona uno o più ruoli",
"roleMappingFixedRolesPlaceholderFreeform": "Digita nomi dei ruoli (corrispondenza esatta per organizzazione)",
"roleMappingFixedRolesDescriptionSameForAll": "Assegna lo stesso ruolo impostato a ogni utente auto-provisioned.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Per i criteri predefiniti, digita i nomi dei ruoli che esistono in ogni organizzazione in cui gli utenti sono forniti. I nomi devono corrispondere esattamente.",
"roleMappingClaimPath": "Richiedi Percorso",
"roleMappingClaimPathPlaceholder": "gruppi",
"roleMappingClaimPathDescription": "Percorso nel payload del token che contiene valori sorgente (ad esempio, gruppi).",
"roleMappingMatchValue": "Valore Della Partita",
"roleMappingAssignRoles": "Assegna Ruoli",
"roleMappingAddMappingRule": "Aggiungi Regola Mappatura",
"roleMappingRawExpressionResultDescription": "Espressione deve essere valutata in una stringa o array di stringhe.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Espressione deve valutare in una stringa (un singolo nome ruolo).",
"roleMappingMatchValuePlaceholder": "Valore della corrispondenza (per esempio: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Digita i nomi dei ruoli (esatto per org)",
"roleMappingBuilderFreeformRowHint": "I nomi dei ruoli devono corrispondere a un ruolo in ogni organizzazione di destinazione.",
"roleMappingRemoveRule": "Rimuovi",
"idpGoogleConfiguration": "Configurazione Google",
"idpGoogleConfigurationDescription": "Configura le credenziali di Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Per quanto tempo conservare i log di accesso",
"logRetentionActionLabel": "Ritenzione Registro Azioni",
"logRetentionActionDescription": "Per quanto tempo conservare i log delle azioni",
"logRetentionConnectionLabel": "Ritenzione Registro Di Connessione",
"logRetentionConnectionDescription": "Per quanto tempo conservare i log di connessione",
"logRetentionDisabled": "Disabilitato",
"logRetention3Days": "3 giorni",
"logRetention7Days": "7 giorni",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Fine dell'anno successivo",
"actionLogsDescription": "Visualizza una cronologia delle azioni eseguite in questa organizzazione",
"accessLogsDescription": "Visualizza le richieste di autenticazione di accesso per le risorse in questa organizzazione",
"licenseRequiredToUse": "Per utilizzare questa funzione è necessaria una licenza <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> . Questa funzionalità è disponibile anche in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "L' <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> è necessaria per utilizzare questa funzione. Questa funzionalità è disponibile anche in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Log Di Connessione",
"connectionLogsDescription": "Visualizza i log di connessione per i tunnel in questa organizzazione",
"sidebarLogsConnection": "Log Di Connessione",
"sidebarLogsStreaming": "Streaming",
"sourceAddress": "Indirizzo Di Origine",
"destinationAddress": "Indirizzo Di Destinazione",
"duration": "Durata",
"licenseRequiredToUse": "Per utilizzare questa funzione è necessaria una licenza <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> o <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> . <bookADemoLink>Prenota una demo o una prova POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "L' <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> è necessaria per utilizzare questa funzione. Questa funzione è disponibile anche in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Prenota una demo o una prova POC</bookADemoLink>.",
"certResolver": "Risolutore Di Certificato",
"certResolverDescription": "Selezionare il risolutore di certificati da usare per questa risorsa.",
"selectCertResolver": "Seleziona Risolutore Di Certificato",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Controllo Accesso",
"editInternalResourceDialogAccessControlDescription": "Controlla quali ruoli, utenti e client macchina hanno accesso a questa risorsa quando connessi. Gli amministratori hanno sempre accesso.",
"editInternalResourceDialogPortRangeValidationError": "Il range delle porte deve essere \"*\" per tutte le porte, o un elenco di porte e intervalli separato da virgole (ad es. \"80,443,8000-9000\"). Le porte devono essere tra 1 e 65535.",
"internalResourceAuthDaemonStrategy": "Posizione Demone Autenticazione SSH",
"internalResourceAuthDaemonStrategyDescription": "Scegli dove funziona il demone di autenticazione SSH: sul sito (Newt) o su un host remoto.",
"internalResourceAuthDaemonDescription": "Il demone di autenticazione SSH gestisce la firma della chiave SSH e l'autenticazione PAM per questa risorsa. Scegli se viene eseguito sul sito (Newt) o su un host remoto separato. Vedi <docsLink>la documentazione</docsLink> per ulteriori informazioni.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Seleziona Strategia",
"internalResourceAuthDaemonStrategyLabel": "Posizione",
"internalResourceAuthDaemonSite": "Sul Sito",
"internalResourceAuthDaemonSiteDescription": "Il demone Auth viene eseguito sul sito (Nuovo).",
"internalResourceAuthDaemonRemote": "Host Remoto",
"internalResourceAuthDaemonRemoteDescription": "Il demone di autenticazione viene eseguito su un host che non è il sito.",
"internalResourceAuthDaemonPort": "Porta Demone (facoltativa)",
"orgAuthWhatsThis": "Dove posso trovare l'ID della mia organizzazione?",
"learnMore": "Scopri di più",
"backToHome": "Torna alla home",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Abilita Approvazioni Dispositivo",
"approvalsEmptyStateStep2Description": "Modifica un ruolo e abilita l'opzione 'Richiedi l'approvazione del dispositivo'. Gli utenti con questo ruolo avranno bisogno dell'approvazione dell'amministratore per i nuovi dispositivi.",
"approvalsEmptyStatePreviewDescription": "Anteprima: quando abilitato, le richieste di dispositivo in attesa appariranno qui per la revisione",
"approvalsEmptyStateButtonText": "Gestisci Ruoli"
"approvalsEmptyStateButtonText": "Gestisci Ruoli",
"domainErrorTitle": "Stiamo avendo problemi a verificare il tuo dominio",
"idpAdminAutoProvisionPoliciesTabHint": "Configura la mappatura dei ruoli e le politiche di organizzazione nella scheda <policiesTabLink>Auto Provision Settings</policiesTabLink>.",
"streamingTitle": "Streaming Eventi",
"streamingDescription": "Trasmetti eventi dalla tua organizzazione a destinazioni esterne in tempo reale.",
"streamingUnnamedDestination": "Destinazione senza nome",
"streamingNoUrlConfigured": "Nessun URL configurato",
"streamingAddDestination": "Aggiungi Destinazione",
"streamingHttpWebhookTitle": "Webhook HTTP",
"streamingHttpWebhookDescription": "Invia eventi a qualsiasi endpoint HTTP con autenticazione e template flessibili.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Trasmetti eventi su un contenitore di archiviazione per oggetti compatibile con S3. Presto in arrivo.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Inoltra gli eventi direttamente al tuo account Datadog. In arrivo.",
"streamingTypePickerDescription": "Scegli un tipo di destinazione per iniziare.",
"streamingFailedToLoad": "Impossibile caricare le destinazioni",
"streamingUnexpectedError": "Si è verificato un errore imprevisto.",
"streamingFailedToUpdate": "Impossibile aggiornare la destinazione",
"streamingDeletedSuccess": "Destinazione eliminata con successo",
"streamingFailedToDelete": "Impossibile eliminare la destinazione",
"streamingDeleteTitle": "Elimina Destinazione",
"streamingDeleteButtonText": "Elimina Destinazione",
"streamingDeleteDialogAreYouSure": "Sei sicuro di voler eliminare",
"streamingDeleteDialogThisDestination": "questa destinazione",
"streamingDeleteDialogPermanentlyRemoved": "? Tutta la configurazione verrà definitivamente rimossa.",
"httpDestEditTitle": "Modifica Destinazione",
"httpDestAddTitle": "Aggiungi Destinazione HTTP",
"httpDestEditDescription": "Aggiorna la configurazione per questa destinazione di streaming di eventi HTTP.",
"httpDestAddDescription": "Configura un nuovo endpoint HTTP per ricevere gli eventi della tua organizzazione.",
"httpDestTabSettings": "Impostazioni",
"httpDestTabHeaders": "Intestazioni",
"httpDestTabBody": "Corpo",
"httpDestTabLogs": "Registri",
"httpDestNamePlaceholder": "La mia destinazione HTTP",
"httpDestUrlLabel": "Url Di Destinazione",
"httpDestUrlErrorHttpRequired": "L'URL deve usare http o https",
"httpDestUrlErrorHttpsRequired": "HTTPS è richiesto sulle distribuzioni cloud",
"httpDestUrlErrorInvalid": "Inserisci un URL valido (es. https://example.com/webhook)",
"httpDestAuthTitle": "Autenticazione",
"httpDestAuthDescription": "Scegli come vengono autenticate le richieste al tuo endpoint.",
"httpDestAuthNoneTitle": "Nessuna Autenticazione",
"httpDestAuthNoneDescription": "Invia richieste senza intestazione autorizzazione.",
"httpDestAuthBearerTitle": "Token Del Portatore",
"httpDestAuthBearerDescription": "Aggiunge un'intestazione Autorizzazione: Bearer <token> ad ogni richiesta.",
"httpDestAuthBearerPlaceholder": "La tua chiave API o token",
"httpDestAuthBasicTitle": "Autenticazione Base",
"httpDestAuthBasicDescription": "Aggiunge un'autorizzazione: intestazione di base <credentials> . Fornisce le credenziali come username:password.",
"httpDestAuthBasicPlaceholder": "username:password",
"httpDestAuthCustomTitle": "Intestazione Personalizzata",
"httpDestAuthCustomDescription": "Specifica un nome e un valore di intestazione HTTP personalizzati per l'autenticazione (ad esempio X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Nome intestazione (es. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Valore intestazione",
"httpDestCustomHeadersTitle": "Intestazioni Http Personalizzate",
"httpDestCustomHeadersDescription": "Aggiungi intestazioni personalizzate ad ogni richiesta in uscita. Utile per token statici o un tipo di contenuto personalizzato. Come impostazione predefinita, viene inviato il tipo di contenuto/json.",
"httpDestNoHeadersConfigured": "Nessuna intestazione personalizzata configurata. Fare clic su \"Aggiungi intestazione\" per aggiungerne una.",
"httpDestHeaderNamePlaceholder": "Nome intestazione",
"httpDestHeaderValuePlaceholder": "Valore",
"httpDestAddHeader": "Aggiungi Intestazione",
"httpDestBodyTemplateTitle": "Modello Corpo Personalizzato",
"httpDestBodyTemplateDescription": "Controlla la struttura JSON payload inviata al tuo endpoint. Se disabilitata, viene inviato un oggetto JSON predefinito per ogni evento.",
"httpDestEnableBodyTemplate": "Abilita modello corpo personalizzato",
"httpDestBodyTemplateLabel": "Modello Corpo (JSON)",
"httpDestBodyTemplateHint": "Usa le variabili del modello per fare riferimento ai campi dell'evento nel tuo payload.",
"httpDestPayloadFormatTitle": "Formato Payload",
"httpDestPayloadFormatDescription": "Come gli eventi sono serializzati in ogni organismo di richiesta.",
"httpDestFormatJsonArrayTitle": "JSON Array",
"httpDestFormatJsonArrayDescription": "Una richiesta per lotto, corpo è un array JSON. Compatibile con la maggior parte dei webhooks generici e Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Una richiesta per lotto, corpo è newline-delimited JSON — un oggetto per linea, nessun array esterno. Richiesto da Splunk HEC, Elastic / OpenSearch, e Grafana Loki.",
"httpDestFormatSingleTitle": "Un Evento Per Richiesta",
"httpDestFormatSingleDescription": "Invia un HTTP POST separato per ogni singolo evento. Usa solo per gli endpoint che non possono gestire i batch.",
"httpDestLogTypesTitle": "Tipi Di Log",
"httpDestLogTypesDescription": "Scegli quali tipi di log vengono inoltrati a questa destinazione. Verranno trasmessi solo i tipi di log abilitati.",
"httpDestAccessLogsTitle": "Log Accesso",
"httpDestAccessLogsDescription": "Tentativi di accesso alle risorse, comprese le richieste autenticate e negate.",
"httpDestActionLogsTitle": "Log Azioni",
"httpDestActionLogsDescription": "Azioni amministrative eseguite dagli utenti all'interno dell'organizzazione.",
"httpDestConnectionLogsTitle": "Log Di Connessione",
"httpDestConnectionLogsDescription": "Eventi di connessione al sito e al tunnel, inclusi collegamenti e disconnessioni.",
"httpDestRequestLogsTitle": "Log Richiesta",
"httpDestRequestLogsDescription": "Registri di richiesta HTTP per le risorse proxy, inclusi metodo, percorso e codice di risposta.",
"httpDestSaveChanges": "Salva Modifiche",
"httpDestCreateDestination": "Crea Destinazione",
"httpDestUpdatedSuccess": "Destinazione aggiornata con successo",
"httpDestCreatedSuccess": "Destinazione creata con successo",
"httpDestUpdateFailed": "Impossibile aggiornare la destinazione",
"httpDestCreateFailed": "Impossibile creare la destinazione"
}

View File

@@ -148,6 +148,11 @@
"createLink": "링크 생성",
"resourcesNotFound": "리소스가 발견되지 않았습니다.",
"resourceSearch": "리소스 검색",
"machineSearch": "기계 검색",
"machinesSearch": "기계 클라이언트 검색...",
"machineNotFound": "기계를 찾을 수 없습니다",
"userDeviceSearch": "사용자 장치 검색",
"userDevicesSearch": "사용자 장치 검색...",
"openMenu": "메뉴 열기",
"resource": "리소스",
"title": "제목",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "완전한 도메인 이름을 사용해 RAW 또는 HTTPS로 프록시 요청을 수행합니다.",
"resourceRaw": "원시 TCP/UDP 리소스",
"resourceRawDescription": "포트 번호를 사용하여 RAW TCP/UDP로 요청을 프록시합니다.",
"resourceRawDescriptionCloud": "포트 번호를 사용하여 원격 노드에 연결해야 합니다. 원격 노드에서 리소스를 사용하려면 사용자 지정 도메인을 사용하십시오.",
"resourceCreate": "리소스 생성",
"resourceCreateDescription": "아래 단계를 따라 새 리소스를 생성하세요.",
"resourceSeeAll": "모든 리소스 보기",
@@ -322,6 +328,54 @@
"apiKeysDelete": "API 키 삭제",
"apiKeysManage": "API 키 관리",
"apiKeysDescription": "API 키는 통합 API와 인증하는 데 사용됩니다.",
"provisioningKeysTitle": "프로비저닝 키",
"provisioningKeysManage": "프로비저닝 키 관리",
"provisioningKeysDescription": "프로비저닝 키는 조직의 자동 사이트 프로비저닝 인증에 사용됩니다.",
"provisioningManage": "프로비저닝",
"provisioningDescription": "프로비저닝 키를 관리하고 승인을 기다리는 사이트를 검토합니다.",
"pendingSites": "대기중인 사이트",
"siteApproveSuccess": "사이트가 성공적으로 승인되었습니다",
"siteApproveError": "사이트 승인 오류",
"provisioningKeys": "프로비저닝 키",
"searchProvisioningKeys": "프로비저닝 키 검색...",
"provisioningKeysAdd": "프로비저닝 키 생성",
"provisioningKeysErrorDelete": "프로비저닝 키 삭제 오류",
"provisioningKeysErrorDeleteMessage": "프로비저닝 키 삭제 오류",
"provisioningKeysQuestionRemove": "이 프로비저닝 키를 조직에서 제거하시겠습니까?",
"provisioningKeysMessageRemove": "제거 후에는 이 키를 사이트 프로비저닝에 사용할 수 없습니다.",
"provisioningKeysDeleteConfirm": "프로비저닝 키 삭제 확인",
"provisioningKeysDelete": "프로비저닝 키 삭제",
"provisioningKeysCreate": "프로비저닝 키 생성",
"provisioningKeysCreateDescription": "조직을 위한 새로운 프로비저닝 키 생성",
"provisioningKeysSeeAll": "모든 프로비저닝 키 보기",
"provisioningKeysSave": "프로비저닝 키 저장",
"provisioningKeysSaveDescription": "이것은 한 번만 볼 수 있습니다. 안전한 장소에 복사해 두세요.",
"provisioningKeysErrorCreate": "프로비저닝 키 생성 오류",
"provisioningKeysList": "새 프로비저닝 키",
"provisioningKeysMaxBatchSize": "최대 배치 크기",
"provisioningKeysUnlimitedBatchSize": "무제한 배치 크기 (제한 없음)",
"provisioningKeysMaxBatchUnlimited": "무제한",
"provisioningKeysMaxBatchSizeInvalid": "유효한 최대 배치 크기를 입력하세요 (11,000,000).",
"provisioningKeysValidUntil": "유효 기간",
"provisioningKeysValidUntilHint": "만료 날짜를 설정하지 않을 경우 빈칸으로 남겨 두세요.",
"provisioningKeysValidUntilInvalid": "유효한 날짜와 시간을 입력하세요.",
"provisioningKeysNumUsed": "사용 횟수",
"provisioningKeysLastUsed": "마지막 사용",
"provisioningKeysNoExpiry": "만료 없음",
"provisioningKeysNeverUsed": "절대",
"provisioningKeysEdit": "프로비저닝 키 수정",
"provisioningKeysEditDescription": "이 키의 최대 배치 크기 및 만료 시간을 업데이트하세요.",
"provisioningKeysApproveNewSites": "새로운 사이트 승인",
"provisioningKeysApproveNewSitesDescription": "이 키를 등록하는 사이트를 자동으로 승인합니다.",
"provisioningKeysUpdateError": "프로비저닝 키 업데이트 오류",
"provisioningKeysUpdated": "프로비저닝 키가 업데이트되었습니다",
"provisioningKeysUpdatedDescription": "변경 사항이 저장되었습니다.",
"provisioningKeysBannerTitle": "사이트 프로비저닝 키",
"provisioningKeysBannerDescription": "프로비저닝 키를 생성하여 Newt 커넥터와 함께 사용해 첫 실행 시 자동으로 사이트를 생성하세요 — 각 사이트마다 별도의 인증을 설정할 필요가 없습니다.",
"provisioningKeysBannerButtonText": "자세히 알아보기",
"pendingSitesBannerTitle": "대기중인 사이트",
"pendingSitesBannerDescription": "프로비저닝 키를 사용하여 연결하는 사이트는 검토 대기 중입니다. 사이트가 활성화되어 리소스에 액세스하기 전에 각 사이트를 승인하세요.",
"pendingSitesBannerButtonText": "자세히 알아보기",
"apiKeysSettings": "{apiKeyName} 설정",
"userTitle": "모든 사용자 관리",
"userDescription": "시스템의 모든 사용자를 보고 관리합니다",
@@ -508,9 +562,12 @@
"userSaved": "사용자 저장됨",
"userSavedDescription": "사용자가 업데이트되었습니다.",
"autoProvisioned": "자동 프로비저닝됨",
"autoProvisionSettings": "자동 프로비저닝 설정",
"autoProvisionedDescription": "이 사용자가 ID 공급자에 의해 자동으로 관리될 수 있도록 허용합니다",
"accessControlsDescription": "이 사용자가 조직에서 접근하고 수행할 수 있는 작업을 관리하세요",
"accessControlsSubmit": "접근 제어 저장",
"singleRolePerUserPlanNotice": "계획에는 사용자당 한 가지 역할만 지원됩니다.",
"singleRolePerUserEditionNotice": "이 판에는 사용자당 한 가지 역할만 지원됩니다.",
"roles": "역할",
"accessUsersRoles": "사용자 및 역할 관리",
"accessUsersRolesDescription": "사용자를 초대하고 역할에 추가하여 조직에 대한 접근을 관리하세요",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "리소스를 전환하는 데 실패했습니다.",
"resourcesErrorUpdateDescription": "리소스를 업데이트하는 동안 오류가 발생했습니다.",
"access": "접속",
"accessControl": "액세스 제어",
"shareLink": "{resource} 공유 링크",
"resourceSelect": "리소스 선택",
"shareLinks": "공유 링크",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "역할이 제거되었습니다",
"accessRoleRemovedDescription": "역할이 성공적으로 제거되었습니다.",
"accessRoleRequiredRemove": "이 역할을 삭제하기 전에 기존 구성원을 전송할 새 역할을 선택하세요.",
"network": "네트워크",
"manage": "관리",
"sitesNotFound": "사이트를 찾을 수 없습니다.",
"pangolinServerAdmin": "서버 관리자 - 판골린",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "앗! 찾고 있는 페이지가 존재하지 않습니다.",
"overview": "개요",
"home": "홈",
"accessControl": "액세스 제어",
"settings": "설정",
"usersAll": "모든 사용자",
"license": "라이선스",
@@ -1100,6 +1158,12 @@
"actionGetUser": "사용자 조회",
"actionGetOrgUser": "조직 사용자 가져오기",
"actionListOrgDomains": "조직 도메인 목록",
"actionGetDomain": "도메인 가져오기",
"actionCreateOrgDomain": "도메인 생성",
"actionUpdateOrgDomain": "도메인 업데이트",
"actionDeleteOrgDomain": "도메인 삭제",
"actionGetDNSRecords": "DNS 레코드 가져오기",
"actionRestartOrgDomain": "도메인 재시작",
"actionCreateSite": "사이트 생성",
"actionDeleteSite": "사이트 삭제",
"actionGetSite": "사이트 가져오기",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "서버 콘솔에서 설정 토큰 입력.",
"setupTokenRequired": "설정 토큰이 필요합니다",
"actionUpdateSite": "사이트 업데이트",
"actionResetSiteBandwidth": "조직 대역폭 재설정",
"actionListSiteRoles": "허용된 사이트 역할 목록",
"actionCreateResource": "리소스 생성",
"actionDeleteResource": "리소스 삭제",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "사용자 제거",
"actionListUsers": "사용자 목록",
"actionAddUserRole": "사용자 역할 추가",
"actionSetUserOrgRoles": "사용자 역할 설정",
"actionGenerateAccessToken": "액세스 토큰 생성",
"actionDeleteAccessToken": "액세스 토큰 삭제",
"actionListAccessTokens": "액세스 토큰 목록",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "비공개",
"sidebarAccessControl": "액세스 제어",
"sidebarLogsAndAnalytics": "로그 및 분석",
"sidebarTeam": "팀",
"sidebarUsers": "사용자",
"sidebarAdmin": "관리자",
"sidebarInvitations": "초대",
"sidebarRoles": "역할",
"sidebarShareableLinks": "링크",
"sidebarApiKeys": "API 키",
"sidebarProvisioning": "프로비저닝",
"sidebarSettings": "설정",
"sidebarAllUsers": "모든 사용자",
"sidebarIdentityProviders": "신원 공급자",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "로그 & 통계",
"sidebarBluePrints": "청사진",
"sidebarOrganization": "조직",
"sidebarManagement": "관리",
"sidebarBillingAndLicenses": "결제 및 라이선스",
"sidebarLogsAnalytics": "분석",
"blueprints": "청사진",
@@ -1289,7 +1358,6 @@
"parsedContents": "구문 분석된 콘텐츠 (읽기 전용)",
"enableDockerSocket": "Docker 청사진 활성화",
"enableDockerSocketDescription": "블루프린트 레이블을 위한 Docker 소켓 레이블 수집을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
"enableDockerSocketLink": "자세히 알아보기",
"viewDockerContainers": "도커 컨테이너 보기",
"containersIn": "{siteName}의 컨테이너",
"selectContainerDescription": "이 대상을 위한 호스트 이름으로 사용할 컨테이너를 선택하세요. 포트를 사용하려면 포트를 클릭하세요.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "이름 공간: {namespace}",
"domainPickerShowMore": "더보기",
"regionSelectorTitle": "지역 선택",
"domainPickerRemoteExitNodeWarning": "제공된 도메인은 원격 종료 노드에 연결된 사이트에서 지원되지 않습니다. 원격 노드에서 리소스를 사용하려면 사용자 지정 도메인을 사용하십시오.",
"regionSelectorInfo": "지역을 선택하면 위치에 따라 더 나은 성능이 제공됩니다. 서버와 같은 지역에 있을 필요는 없습니다.",
"regionSelectorPlaceholder": "지역 선택",
"regionSelectorComingSoon": "곧 출시 예정",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "기능 가용성 알림",
"billingFeatureLossDescription": "다운그레이드함으로써 새 계획에서 사용할 수 없는 기능은 자동으로 비활성화됩니다. 일부 설정 및 구성은 손실될 수 있습니다. 어떤 기능들이 더 이상 사용 불가능한지 이해하기 위해 가격표를 검토하세요.",
"billingUsageExceedsLimit": "현재 사용량 ({current})이 제한 ({limit})을 초과합니다",
"billingPastDueTitle": "연체된 결제",
"billingPastDueDescription": "결제가 연체되었습니다. 현재 이용 중인 플랜 기능을 계속 사용하기 위해 결제 수단을 업데이트해 주세요. 해결되지 않으면 구독이 취소되고 무료 요금제로 전환됩니다.",
"billingUnpaidTitle": "결제되지 않은 구독",
"billingUnpaidDescription": "구독 결제가 완료되지 않아 무료 요금제로 전환되었습니다. 구독을 복원하려면 결제 수단을 업데이트해 주세요.",
"billingIncompleteTitle": "불완전한 결제",
"billingIncompleteDescription": "결제가 불완전합니다. 구독을 활성화하기 위해 결제 과정을 완료해 주세요.",
"billingIncompleteExpiredTitle": "만료된 결제",
"billingIncompleteExpiredDescription": "결제가 완료되지 않아 만료되었습니다. 무료 요금제로 전환되었습니다. 유료 기능에 대한 액세스를 복원하려면 다시 구독해 주세요.",
"billingManageSubscription": "구독을 관리하십시오",
"billingResolvePaymentIssue": "업그레이드 또는 다운그레이드하기 전에 결제 문제를 해결해 주세요.",
"signUpTerms": {
"IAgreeToThe": "동의합니다",
"termsOfService": "서비스 약관",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "시간은 초 단위입니다",
"requireDeviceApproval": "장치 승인 요구",
"requireDeviceApprovalDescription": "이 역할을 가진 사용자는 장치가 연결되기 전에 관리자의 승인이 필요합니다.",
"sshAccess": "SSH 접속",
"roleAllowSsh": "SSH 허용",
"roleAllowSshAllow": "허용",
"roleAllowSshDisallow": "허용 안 함",
"roleAllowSshDescription": "이 역할을 가진 사용자가 SSH를 통해 리소스에 연결할 수 있도록 허용합니다. 비활성화되면 역할은 SSH 접속을 사용할 수 없습니다.",
"sshSudoMode": "Sudo 접속",
"sshSudoModeNone": "없음",
"sshSudoModeNoneDescription": "사용자는 sudo로 명령을 실행할 수 없습니다.",
"sshSudoModeFull": "전체 Sudo",
"sshSudoModeFullDescription": "사용자는 모든 명령을 sudo로 실행할 수 있습니다.",
"sshSudoModeCommands": "명령",
"sshSudoModeCommandsDescription": "사용자는 sudo로 지정된 명령만 실행할 수 있습니다.",
"sshSudo": "Sudo 허용",
"sshSudoCommands": "Sudo 명령",
"sshSudoCommandsDescription": "사용자가 sudo로 실행할 수 있는 명령어의 쉼표로 구분된 목록입니다.",
"sshCreateHomeDir": "홈 디렉터리 생성",
"sshUnixGroups": "유닉스 그룹",
"sshUnixGroupsDescription": "대상 호스트에서 사용자에게 추가할 유닉스 그룹의 쉼표로 구분된 목록입니다.",
"retryAttempts": "재시도 횟수",
"expectedResponseCodes": "예상 응답 코드",
"expectedResponseCodesDescription": "정상 상태를 나타내는 HTTP 상태 코드입니다. 비워 두면 200-300이 정상으로 간주됩니다.",
@@ -1851,6 +1948,40 @@
"exitNode": "종단 노드",
"country": "국가",
"rulesMatchCountry": "현재 소스 IP를 기반으로 합니다",
"region": "지역",
"selectRegion": "지역 선택",
"searchRegions": "지역 검색...",
"noRegionFound": "지역을 찾을 수 없습니다.",
"rulesMatchRegion": "국가의 지역 구성을 선택합니다",
"rulesErrorInvalidRegion": "잘못된 지역",
"rulesErrorInvalidRegionDescription": "유효한 지역을 선택하세요.",
"regionAfrica": "아프리카",
"regionNorthernAfrica": "북부 아프리카",
"regionEasternAfrica": "동부 아프리카",
"regionMiddleAfrica": "중부 아프리카",
"regionSouthernAfrica": "남부 아프리카",
"regionWesternAfrica": "서부 아프리카",
"regionAmericas": "아메리카",
"regionCaribbean": "카리브",
"regionCentralAmerica": "중앙 아메리카",
"regionSouthAmerica": "남아메리카",
"regionNorthernAmerica": "북미",
"regionAsia": "아시아",
"regionCentralAsia": "중앙 아시아",
"regionEasternAsia": "동아시아",
"regionSouthEasternAsia": "동남아시아",
"regionSouthernAsia": "남아시아",
"regionWesternAsia": "서아시아",
"regionEurope": "유럽",
"regionEasternEurope": "동부 유럽",
"regionNorthernEurope": "북부 유럽",
"regionSouthernEurope": "남부 유럽",
"regionWesternEurope": "서부 유럽",
"regionOceania": "오세아니아",
"regionAustraliaAndNewZealand": "호주와 뉴질랜드",
"regionMelanesia": "멜라네시아",
"regionMicronesia": "미크로네시아",
"regionPolynesia": "폴리네시아",
"managedSelfHosted": {
"title": "관리 자체 호스팅",
"description": "더 신뢰할 수 있고 낮은 유지보수의 자체 호스팅 팡골린 서버, 추가 기능 포함",
@@ -1899,6 +2030,25 @@
"invalidValue": "잘못된 값",
"idpTypeLabel": "신원 공급자 유형",
"roleMappingExpressionPlaceholder": "예: contains(groups, 'admin') && 'Admin' || 'Member'",
"roleMappingModeFixedRoles": "고정 역할",
"roleMappingModeMappingBuilder": "매핑 빌더",
"roleMappingModeRawExpression": "원시 표현식",
"roleMappingFixedRolesPlaceholderSelect": "하나 이상의 역할을 선택하세요",
"roleMappingFixedRolesPlaceholderFreeform": "역할 이름 입력 (조직마다 정확히 일치)",
"roleMappingFixedRolesDescriptionSameForAll": "모든 자동 프로비전 사용자에게 동일한 역할 세트를 할당합니다.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "기본 정책의 경우 사용자가 프로비저닝된 조직의 역할 이름을 입력하세요. 이름은 정확히 일치해야 합니다.",
"roleMappingClaimPath": "클레임 경로",
"roleMappingClaimPathPlaceholder": "그룹",
"roleMappingClaimPathDescription": "토큰 페이로드에서 소스 값을 포함하는 경로 (예: 그룹).",
"roleMappingMatchValue": "매치 값",
"roleMappingAssignRoles": "역할 할당",
"roleMappingAddMappingRule": "매핑 규칙 추가",
"roleMappingRawExpressionResultDescription": "표현식은 문자열 또는 문자열 배열로 평가되어야 합니다.",
"roleMappingRawExpressionResultDescriptionSingleRole": "표현식은 문자열 (단일 역할 이름)로 평가되어야 합니다.",
"roleMappingMatchValuePlaceholder": "매치 값 (예: 관리자)",
"roleMappingAssignRolesPlaceholderFreeform": "역할 이름 입력 (조직마다 정확히)",
"roleMappingBuilderFreeformRowHint": "역할 이름은 각 대상 조직의 역할과 일치해야 합니다.",
"roleMappingRemoveRule": "제거",
"idpGoogleConfiguration": "Google 구성",
"idpGoogleConfigurationDescription": "Google OAuth2 자격 증명을 구성합니다.",
"idpGoogleClientIdDescription": "Google OAuth2 클라이언트 ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "접근 로그를 얼마나 오래 보관할지",
"logRetentionActionLabel": "작업 로그 보관",
"logRetentionActionDescription": "작업 로그를 얼마나 오래 보관할지",
"logRetentionConnectionLabel": "연결 로그 보유 기간",
"logRetentionConnectionDescription": "연결 로그를 얼마나 오래 보유할지",
"logRetentionDisabled": "비활성화됨",
"logRetention3Days": "3 일",
"logRetention7Days": "7 일",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "다음 연도 말",
"actionLogsDescription": "이 조직에서 수행된 작업의 기록을 봅니다",
"accessLogsDescription": "이 조직의 자원에 대한 접근 인증 요청을 확인합니다",
"licenseRequiredToUse": "이 기능을 사용하려면 <enterpriseLicenseLink>엔터프라이즈 에디션</enterpriseLicenseLink> 라이선스가 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다.",
"ossEnterpriseEditionRequired": "이 기능을 사용하려면 <enterpriseEditionLink>엔터프라이즈 에디션</enterpriseEditionLink>이 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다.",
"connectionLogs": "연결 로그",
"connectionLogsDescription": "이 조직의 터널 연결 로그 보기",
"sidebarLogsConnection": "연결 로그",
"sidebarLogsStreaming": "스트리밍",
"sourceAddress": "소스 주소",
"destinationAddress": "대상 주소",
"duration": "지속 시간",
"licenseRequiredToUse": "이 기능을 사용하려면 <enterpriseLicenseLink>엔터프라이즈 에디션</enterpriseLicenseLink> 라이선스가 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다. <bookADemoLink>데모 또는 POC 체험을 예약하세요</bookADemoLink>.",
"ossEnterpriseEditionRequired": "이 기능을 사용하려면 <enterpriseEditionLink>엔터프라이즈 에디션</enterpriseEditionLink>이(가) 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다. <bookADemoLink>데모 또는 POC 체험을 예약하세요</bookADemoLink>.",
"certResolver": "인증서 해결사",
"certResolverDescription": "이 리소스에 사용할 인증서 해결사를 선택하세요.",
"selectCertResolver": "인증서 해결사 선택",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "액세스 제어",
"editInternalResourceDialogAccessControlDescription": "연결 시 이 리소스에 대한 액세스 권한을 가지는 역할, 사용자, 그리고 머신 클라이언트를 제어합니다. 관리자는 항상 접근할 수 있습니다.",
"editInternalResourceDialogPortRangeValidationError": "모든 포트에 대해서는 \"*\"로, 아니면 쉼표로 구분된 포트 및 범위 목록(예: \"80,443,8000-9000\")을 설정해야 합니다. 포트는 1에서 65535 사이여야 합니다.",
"internalResourceAuthDaemonStrategy": "SSH 인증 데몬 위치",
"internalResourceAuthDaemonStrategyDescription": "SSH 인증 데몬이 작동하는 위치를 선택하세요: 사이트(Newt)에서 또는 원격 호스트에서.",
"internalResourceAuthDaemonDescription": "SSH 인증 데몬은 이 리소스를 위한 SSH 키 서명과 PAM 인증을 처리합니다. 사이트(Newt)에서 나 별도의 원격 호스트에서 실행할 것인지를 선택하세요. 자세한 내용은 <docsLink>문서</docsLink>를 참조하세요.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "전략 선택",
"internalResourceAuthDaemonStrategyLabel": "위치",
"internalResourceAuthDaemonSite": "사이트에서 인증 데몬이 실행됩니다(Newt).",
"internalResourceAuthDaemonSiteDescription": "인증 데몬이 사이트(Newt)에서 실행됩니다.",
"internalResourceAuthDaemonRemote": "원격 호스트",
"internalResourceAuthDaemonRemoteDescription": "인증 데몬이 사이트가 아닌 다른 호스트에서 실행됩니다.",
"internalResourceAuthDaemonPort": "데몬 포트 (선택 사항)",
"orgAuthWhatsThis": "조직 ID를 어디에서 찾을 수 있습니까?",
"learnMore": "자세히 알아보기",
"backToHome": "홈으로 돌아가기",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "장치 승인 활성화",
"approvalsEmptyStateStep2Description": "역할을 편집하고 '장치 승인 요구' 옵션을 활성화하세요. 이 역할을 가진 사용자는 새 장치에 대해 관리자의 승인이 필요합니다.",
"approvalsEmptyStatePreviewDescription": "미리 보기: 활성화된 경우, 승인 대기 중인 장치 요청이 검토용으로 여기에 표시됩니다.",
"approvalsEmptyStateButtonText": "역할 관리"
"approvalsEmptyStateButtonText": "역할 관리",
"domainErrorTitle": "도메인 확인에 문제가 발생했습니다.",
"idpAdminAutoProvisionPoliciesTabHint": "<policiesTabLink>자동 프로비저닝 설정</policiesTabLink> 탭에서 역할 매핑 및 조직 정책을 구성합니다.",
"streamingTitle": "이벤트 스트리밍",
"streamingDescription": "조직의 이벤트를 외부 목적지로 실시간 전송합니다.",
"streamingUnnamedDestination": "이름이 없는 대상지",
"streamingNoUrlConfigured": "설정된 URL이 없습니다",
"streamingAddDestination": "대상지 추가",
"streamingHttpWebhookTitle": "HTTP 웹훅",
"streamingHttpWebhookDescription": "유연한 인증 및 템플릿 작성 기능을 갖춘 HTTP 엔드포인트에 이벤트를 전송합니다.",
"streamingS3Title": "아마존 S3",
"streamingS3Description": "S3 호환 객체 스토리지 버킷에 이벤트를 스트리밍합니다. 곧 제공됩니다.",
"streamingDatadogTitle": "데이터독",
"streamingDatadogDescription": "이벤트를 직접 Datadog 계정으로 전달합니다. 곧 제공됩니다.",
"streamingTypePickerDescription": "목표 유형을 선택하여 시작합니다.",
"streamingFailedToLoad": "대상 로드에 실패했습니다",
"streamingUnexpectedError": "예기치 않은 오류가 발생했습니다.",
"streamingFailedToUpdate": "대상지를 업데이트하는 데 실패했습니다",
"streamingDeletedSuccess": "대상지가 성공적으로 삭제되었습니다",
"streamingFailedToDelete": "대상지 삭제 실패",
"streamingDeleteTitle": "대상지 삭제",
"streamingDeleteButtonText": "대상지 삭제",
"streamingDeleteDialogAreYouSure": "삭제하시겠습니까",
"streamingDeleteDialogThisDestination": "이 대상지",
"streamingDeleteDialogPermanentlyRemoved": "? 모든 구성은 영구적으로 제거됩니다.",
"httpDestEditTitle": "대상지 수정",
"httpDestAddTitle": "HTTP 대상지 추가",
"httpDestEditDescription": "이 HTTP 이벤트 스트리밍 대상지의 구성을 업데이트하세요.",
"httpDestAddDescription": "조직의 이벤트 수신을 위한 새로운 HTTP 엔드포인트를 구성하세요.",
"httpDestTabSettings": "설정",
"httpDestTabHeaders": "헤더",
"httpDestTabBody": "본문",
"httpDestTabLogs": "로그",
"httpDestNamePlaceholder": "내 HTTP 대상",
"httpDestUrlLabel": "대상 URL",
"httpDestUrlErrorHttpRequired": "URL은 http 또는 https를 사용해야 합니다",
"httpDestUrlErrorHttpsRequired": "클라우드 배포에는 HTTPS가 필요합니다",
"httpDestUrlErrorInvalid": "유효한 URL을 입력하세요 (예: https://example.com/webhook)",
"httpDestAuthTitle": "인증",
"httpDestAuthDescription": "엔드포인트에 대한 요청 인증 방법을 선택하세요.",
"httpDestAuthNoneTitle": "인증 없음",
"httpDestAuthNoneDescription": "Authorization 헤더 없이 요청을 보냅니다.",
"httpDestAuthBearerTitle": "Bearer 토큰",
"httpDestAuthBearerDescription": "모든 요청에 Authorization: Bearer <token> 헤더를 추가합니다.",
"httpDestAuthBearerPlaceholder": "API 키 또는 토큰",
"httpDestAuthBasicTitle": "기본 인증",
"httpDestAuthBasicDescription": "Authorization: Basic <credentials> 헤더를 추가합니다. 자격 증명은 username:password 형식으로 제공하세요.",
"httpDestAuthBasicPlaceholder": "사용자 이름:비밀번호",
"httpDestAuthCustomTitle": "사용자 정의 헤더",
"httpDestAuthCustomDescription": "인증을 위한 사용자 정의 HTTP 헤더 이름 및 값을 지정하세요 (예: X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "헤더 이름 (예: X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "헤더 값",
"httpDestCustomHeadersTitle": "사용자 정의 HTTP 헤더",
"httpDestCustomHeadersDescription": "모든 발신 요청에 사용자 정의 헤더를 추가합니다. 정적 토큰 또는 사용자 정의 Content-Type에 유용합니다. 기본적으로 Content-Type: application/json이 전송됩니다.",
"httpDestNoHeadersConfigured": "구성된 사용자 정의 헤더가 없습니다. \"헤더 추가\"를 클릭하여 추가하세요.",
"httpDestHeaderNamePlaceholder": "헤더 이름",
"httpDestHeaderValuePlaceholder": "값",
"httpDestAddHeader": "헤더 추가",
"httpDestBodyTemplateTitle": "사용자 정의 본문 템플릿",
"httpDestBodyTemplateDescription": "엔드포인트에 전송되는 JSON 페이로드 구조를 제어합니다. 비활성화된 경우 각 이벤트에 대해 기본 JSON 객체가 전송됩니다.",
"httpDestEnableBodyTemplate": "사용자 정의 본문 템플릿 활성화",
"httpDestBodyTemplateLabel": "본문 템플릿 (JSON)",
"httpDestBodyTemplateHint": "템플릿 변수를 사용하여 페이로드에서 이벤트 필드를 참조하세요.",
"httpDestPayloadFormatTitle": "페이로드 형식",
"httpDestPayloadFormatDescription": "각 요청 본문에 이벤트가 시리얼라이즈되는 방식입니다.",
"httpDestFormatJsonArrayTitle": "JSON 배열",
"httpDestFormatJsonArrayDescription": "각 배치마다 요청 하나씩, 본문은 JSON 배열입니다. 대부분의 일반 웹훅 및 Datadog과 호환됩니다.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "각 배치마다 요청 하나씩, 본문은 줄 구분 JSON — 한 라인에 하나의 객체가 있으며 외부 배열이 없습니다. Splunk HEC, Elastic / OpenSearch, Grafana Loki에 필요합니다.",
"httpDestFormatSingleTitle": "각 요청 당 하나의 이벤트",
"httpDestFormatSingleDescription": "각 개별 이벤트에 대해 별도의 HTTP POST를 전송합니다. 배치를 처리할 수 없는 엔드포인트에만 사용하세요.",
"httpDestLogTypesTitle": "로그 유형",
"httpDestLogTypesDescription": "이 대상지에 전달될 로그 유형을 선택하세요. 활성화된 로그 유형만 스트리밍 됩니다.",
"httpDestAccessLogsTitle": "접근 로그",
"httpDestAccessLogsDescription": "인증 및 거부된 요청을 포함한 리소스 접근 시도.",
"httpDestActionLogsTitle": "작업 로그",
"httpDestActionLogsDescription": "조직 내에서 사용자가 수행한 관리 작업.",
"httpDestConnectionLogsTitle": "연결 로그",
"httpDestConnectionLogsDescription": "사이트 및 터널 연결 이벤트, 연결 및 연결 끊기를 포함합니다.",
"httpDestRequestLogsTitle": "요청 로그",
"httpDestRequestLogsDescription": "프록시된 리소스에 대한 HTTP 요청 로그, 메서드, 경로 및 응답 코드를 포함합니다.",
"httpDestSaveChanges": "변경 사항 저장",
"httpDestCreateDestination": "대상지 생성",
"httpDestUpdatedSuccess": "대상지가 성공적으로 업데이트되었습니다",
"httpDestCreatedSuccess": "대상지가 성공적으로 생성되었습니다",
"httpDestUpdateFailed": "대상지를 업데이트하는 데 실패했습니다",
"httpDestCreateFailed": "대상지를 생성하는 데 실패했습니다"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Opprett lenke",
"resourcesNotFound": "Ingen ressurser funnet",
"resourceSearch": "Søk i ressurser",
"machineSearch": "Søk etter maskiner",
"machinesSearch": "Søk etter maskinklienter...",
"machineNotFound": "Ingen maskiner funnet",
"userDeviceSearch": "Søk etter brukerenheter",
"userDevicesSearch": "Søk etter brukerenheter...",
"openMenu": "Åpne meny",
"resource": "Ressurs",
"title": "Tittel",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy forespørsler over HTTPS ved å bruke et fullstendig kvalifisert domenenavn.",
"resourceRaw": "Rå TCP/UDP-ressurs",
"resourceRawDescription": "Proxy forespørsler over rå TCP/UDP ved å bruke et portnummer.",
"resourceRawDescriptionCloud": "Proxy forespørsler om rå TCP/UDP ved hjelp av et portnummer. Krever sider for å koble til en ekstern node.",
"resourceCreate": "Opprett ressurs",
"resourceCreateDescription": "Følg trinnene nedenfor for å opprette en ny ressurs",
"resourceSeeAll": "Se alle ressurser",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Slett API-nøkkel",
"apiKeysManage": "Administrer API-nøkler",
"apiKeysDescription": "API-nøkler brukes for å autentisere med integrasjons-API",
"provisioningKeysTitle": "Foreløpig nøkkel",
"provisioningKeysManage": "Behandle bestemmende nøkler",
"provisioningKeysDescription": "Bestemmelsesnøkler brukes til å godkjenne automatisert nettstedsløsning for din organisasjon.",
"provisioningManage": "Levering",
"provisioningDescription": "Administrer foreløpig nøkler og gjennomgå ventende nettsteder som venter på godkjenning.",
"pendingSites": "Ventende nettsteder",
"siteApproveSuccess": "Vellykket godkjenning av nettsted",
"siteApproveError": "Feil ved godkjenning av side",
"provisioningKeys": "Foreløpig nøkler",
"searchProvisioningKeys": "Søk varer i lagrings nøkler...",
"provisioningKeysAdd": "Generer fremvisende nøkkel",
"provisioningKeysErrorDelete": "Feil under sletting av foreløpig nøkkel",
"provisioningKeysErrorDeleteMessage": "Feil under sletting av foreløpig nøkkel",
"provisioningKeysQuestionRemove": "Er du sikker på at du vil fjerne denne midlertidig nøkkelen fra organisasjonen?",
"provisioningKeysMessageRemove": "Når nøkkelen er fjernet, kan den ikke lenger brukes til anleggsavsetning.",
"provisioningKeysDeleteConfirm": "Bekreft sletting av bestemmelsesnøkkel",
"provisioningKeysDelete": "Slett bestemmelsesnøkkel",
"provisioningKeysCreate": "Generer fremvisende nøkkel",
"provisioningKeysCreateDescription": "Generer en ny foreløpig nøkkel til organisasjonen",
"provisioningKeysSeeAll": "Se alle foreløpig nøkler",
"provisioningKeysSave": "Lagre den midlertidig nøkkelen",
"provisioningKeysSaveDescription": "Du kan bare se denne én gang. Kopier det til et sikkert sted.",
"provisioningKeysErrorCreate": "Feil under oppretting av foreløpig nøkkel",
"provisioningKeysList": "Ny provisorisk nøkkel",
"provisioningKeysMaxBatchSize": "Maks størrelse på bunt",
"provisioningKeysUnlimitedBatchSize": "Ubegrenset mengde bunt (ingen begrensning)",
"provisioningKeysMaxBatchUnlimited": "Ubegrenset",
"provisioningKeysMaxBatchSizeInvalid": "Angi en gyldig sjakkstørrelse (11 000.000).",
"provisioningKeysValidUntil": "Gyldig til",
"provisioningKeysValidUntilHint": "La stå tomt for ingen utløp.",
"provisioningKeysValidUntilInvalid": "Angi en gyldig dato og klokkeslett.",
"provisioningKeysNumUsed": "Antall ganger brukt",
"provisioningKeysLastUsed": "Sist brukt",
"provisioningKeysNoExpiry": "Ingen utløpsdato",
"provisioningKeysNeverUsed": "Aldri",
"provisioningKeysEdit": "Rediger bestemmelsesnøkkel",
"provisioningKeysEditDescription": "Oppdater maksimal størrelse for bunt og utløpstid for denne nøkkelen.",
"provisioningKeysApproveNewSites": "Godkjenn nye nettsteder",
"provisioningKeysApproveNewSitesDescription": "Godkjenn automatisk nettsteder som registrerer deg med denne nøkkelen.",
"provisioningKeysUpdateError": "Feil under oppdatering av foreløpig nøkkel",
"provisioningKeysUpdated": "Foreslå nøkkel oppdatert",
"provisioningKeysUpdatedDescription": "Dine endringer er lagret.",
"provisioningKeysBannerTitle": "Sidens bestemmende nøkler",
"provisioningKeysBannerDescription": "Generer en foreløpig nøkkel og bruk den med Nyhetskontakten for å automatisk opprette sider ved første oppstart — trenger ikke å sette opp separat innloggingsinformasjon for hver side.",
"provisioningKeysBannerButtonText": "Lær mer",
"pendingSitesBannerTitle": "Ventende nettsteder",
"pendingSitesBannerDescription": "Nettsteder som kobler deg til ved hjelp av en bestemmelsestekst, vises her for gjennomgang. Godkjenn hvert nettsted før det blir aktivt og får tilgang til ressursene dine.",
"pendingSitesBannerButtonText": "Lær mer",
"apiKeysSettings": "{apiKeyName} Innstillinger",
"userTitle": "Administrer alle brukere",
"userDescription": "Vis og administrer alle brukere i systemet",
@@ -508,9 +562,12 @@
"userSaved": "Bruker lagret",
"userSavedDescription": "Brukeren har blitt oppdatert.",
"autoProvisioned": "Auto avlyst",
"autoProvisionSettings": "Auto leveringsinnstillinger",
"autoProvisionedDescription": "Tillat denne brukeren å bli automatisk administrert av en identitetsleverandør",
"accessControlsDescription": "Administrer hva denne brukeren kan få tilgang til og gjøre i organisasjonen",
"accessControlsSubmit": "Lagre tilgangskontroller",
"singleRolePerUserPlanNotice": "Din plan støtter bare én rolle per bruker.",
"singleRolePerUserEditionNotice": "Denne utgaven støtter bare én rolle per bruker.",
"roles": "Roller",
"accessUsersRoles": "Administrer brukere og roller",
"accessUsersRolesDescription": "Inviter brukere og legg dem til roller for å administrere tilgang til organisasjonen",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Feilet å slå av/på ressurs",
"resourcesErrorUpdateDescription": "En feil oppstod under oppdatering av ressursen",
"access": "Tilgang",
"accessControl": "Tilgangskontroll",
"shareLink": "{resource} Del Lenke",
"resourceSelect": "Velg ressurs",
"shareLinks": "Del lenker",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rolle fjernet",
"accessRoleRemovedDescription": "Rollen er vellykket fjernet.",
"accessRoleRequiredRemove": "Før du sletter denne rollen, vennligst velg en ny rolle å overføre eksisterende medlemmer til.",
"network": "Nettverk",
"manage": "Administrer",
"sitesNotFound": "Ingen områder funnet.",
"pangolinServerAdmin": "Server Admin - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Oops! Siden du leter etter finnes ikke.",
"overview": "Oversikt",
"home": "Hjem",
"accessControl": "Tilgangskontroll",
"settings": "Innstillinger",
"usersAll": "Alle brukere",
"license": "Lisens",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Hent bruker",
"actionGetOrgUser": "Hent organisasjonsbruker",
"actionListOrgDomains": "List opp organisasjonsdomener",
"actionGetDomain": "Få Domene",
"actionCreateOrgDomain": "Opprett domene",
"actionUpdateOrgDomain": "Oppdater domene",
"actionDeleteOrgDomain": "Slett domene",
"actionGetDNSRecords": "Hent DNS-oppføringer",
"actionRestartOrgDomain": "Omstart Domene",
"actionCreateSite": "Opprett område",
"actionDeleteSite": "Slett område",
"actionGetSite": "Hent område",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Skriv inn oppsetttoken fra serverkonsollen.",
"setupTokenRequired": "Oppsetttoken er nødvendig",
"actionUpdateSite": "Oppdater område",
"actionResetSiteBandwidth": "Tilbakestill organisasjons-båndbredde",
"actionListSiteRoles": "List opp tillatte områderoller",
"actionCreateResource": "Opprett ressurs",
"actionDeleteResource": "Slett ressurs",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Fjern bruker",
"actionListUsers": "List opp brukere",
"actionAddUserRole": "Legg til brukerrolle",
"actionSetUserOrgRoles": "Angi brukerroller",
"actionGenerateAccessToken": "Generer tilgangstoken",
"actionDeleteAccessToken": "Slett tilgangstoken",
"actionListAccessTokens": "List opp tilgangstokener",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privat",
"sidebarAccessControl": "Tilgangskontroll",
"sidebarLogsAndAnalytics": "Logger og analyser",
"sidebarTeam": "Lag",
"sidebarUsers": "Brukere",
"sidebarAdmin": "Administrator",
"sidebarInvitations": "Invitasjoner",
"sidebarRoles": "Roller",
"sidebarShareableLinks": "Lenker",
"sidebarApiKeys": "API-nøkler",
"sidebarProvisioning": "Levering",
"sidebarSettings": "Innstillinger",
"sidebarAllUsers": "Alle brukere",
"sidebarIdentityProviders": "Identitetsleverandører",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Logg og analyser",
"sidebarBluePrints": "Tegninger",
"sidebarOrganization": "Organisasjon",
"sidebarManagement": "Administrasjon",
"sidebarBillingAndLicenses": "Fakturering & lisenser",
"sidebarLogsAnalytics": "Analyser",
"blueprints": "Tegninger",
@@ -1289,7 +1358,6 @@
"parsedContents": "Parastinnhold (kun lese)",
"enableDockerSocket": "Aktiver Docker blåkopi",
"enableDockerSocketDescription": "Aktiver skraping av Docker Socket for blueprint Etiketter. Socket bane må brukes for nye.",
"enableDockerSocketLink": "Lær mer",
"viewDockerContainers": "Vis Docker-containere",
"containersIn": "Containere i {siteName}",
"selectContainerDescription": "Velg en hvilken som helst container for å bruke som vertsnavn for dette målet. Klikk på en port for å bruke en port.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Navnerom: {namespace}",
"domainPickerShowMore": "Vis mer",
"regionSelectorTitle": "Velg Region",
"domainPickerRemoteExitNodeWarning": "Tilbudte domener støttes ikke når sider kobles til eksterne avkjøringsnoder. For ressurser som skal være tilgjengelige på eksterne noder, brukes et egendefinert domene i stedet.",
"regionSelectorInfo": "Å velge en region hjelper oss med å gi bedre ytelse for din lokasjon. Du trenger ikke være i samme region som serveren.",
"regionSelectorPlaceholder": "Velg en region",
"regionSelectorComingSoon": "Kommer snart",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Fremhev tilgjengelig varsel",
"billingFeatureLossDescription": "Ved å nedgradere vil funksjoner som ikke er tilgjengelige i den nye planen automatisk bli deaktivert. Noen innstillinger og konfigurasjoner kan gå tapt. Vennligst gjennomgå prismatrisen for å forstå hvilke funksjoner som ikke lenger vil være tilgjengelige.",
"billingUsageExceedsLimit": "Gjeldende bruk ({current}) overskrider grensen ({limit})",
"billingPastDueTitle": "Betalingen har forfalt",
"billingPastDueDescription": "Betalingen er forfalt. Vennligst oppdater betalingsmetoden din for å fortsette å bruke den gjeldende funksjonsplanen din. Hvis du ikke har løst deg, vil abonnementet ditt avbrytes, og du vil bli tilbakestilt til gratistiden.",
"billingUnpaidTitle": "Abonnement ubetalt",
"billingUnpaidDescription": "Ditt abonnement er ubetalt og du har blitt tilbakestilt til gratis kasse. Vennligst oppdater din betalingsmetode for å gjenopprette abonnementet.",
"billingIncompleteTitle": "Betaling ufullstendig",
"billingIncompleteDescription": "Betalingen er ufullstendig. Vennligst fullfør betalingsprosessen for å aktivere abonnementet.",
"billingIncompleteExpiredTitle": "Betaling utløpt",
"billingIncompleteExpiredDescription": "Din betaling ble aldri fullført, og har utløpt. Du har blitt tilbakestilt til gratis dekk. Vennligst abonner på nytt for å gjenopprette tilgangen til betalte funksjoner.",
"billingManageSubscription": "Administrere ditt abonnement",
"billingResolvePaymentIssue": "Vennligst løs ditt betalingsproblem før du oppgraderer eller nedgraderer betalingen",
"signUpTerms": {
"IAgreeToThe": "Jeg godtar",
"termsOfService": "brukervilkårene",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Tid er i sekunder",
"requireDeviceApproval": "Krev enhetsgodkjenning",
"requireDeviceApprovalDescription": "Brukere med denne rollen trenger nye enheter godkjent av en admin før de kan koble seg og få tilgang til ressurser.",
"sshAccess": "SSH tilgang",
"roleAllowSsh": "Tillat SSH",
"roleAllowSshAllow": "Tillat",
"roleAllowSshDisallow": "Forby",
"roleAllowSshDescription": "Tillat brukere med denne rollen å koble til ressurser via SSH. Når deaktivert får rollen ikke tilgang til SSH.",
"sshSudoMode": "Sudo tilgang",
"sshSudoModeNone": "Ingen",
"sshSudoModeNoneDescription": "Brukeren kan ikke kjøre kommandoer med sudo.",
"sshSudoModeFull": "Full Sudo",
"sshSudoModeFullDescription": "Brukeren kan kjøre hvilken som helst kommando med sudo.",
"sshSudoModeCommands": "Kommandoer",
"sshSudoModeCommandsDescription": "Brukeren kan bare kjøre de angitte kommandoene med sudo.",
"sshSudo": "Tillat sudo",
"sshSudoCommands": "Sudo kommandoer",
"sshSudoCommandsDescription": "Kommaseparert liste med kommandoer brukeren kan kjøre med sudo.",
"sshCreateHomeDir": "Opprett hjemmappe",
"sshUnixGroups": "Unix grupper",
"sshUnixGroupsDescription": "Kommaseparerte Unix grupper for å legge brukeren til på mål-verten.",
"retryAttempts": "Forsøk på nytt",
"expectedResponseCodes": "Forventede svarkoder",
"expectedResponseCodesDescription": "HTTP-statuskode som indikerer sunn status. Hvis den blir stående tom, regnes 200-300 som sunn.",
@@ -1851,6 +1948,40 @@
"exitNode": "Utgangsnode",
"country": "Land",
"rulesMatchCountry": "For tiden basert på kilde IP",
"region": "Fylke",
"selectRegion": "Velg region",
"searchRegions": "Søk etter områder...",
"noRegionFound": "Ingen region funnet.",
"rulesMatchRegion": "Velg en regional gruppering av land",
"rulesErrorInvalidRegion": "Ugyldig område",
"rulesErrorInvalidRegionDescription": "Vennligst velg et gyldig område.",
"regionAfrica": "Afrika",
"regionNorthernAfrica": "[country name] Nord-Afrika",
"regionEasternAfrica": "Øst-Afrika",
"regionMiddleAfrica": "Middle Africa",
"regionSouthernAfrica": "Sør-Afrika",
"regionWesternAfrica": "[country name] Vest-Afrika",
"regionAmericas": "Amerika",
"regionCaribbean": "Karibia",
"regionCentralAmerica": "Sentral-Amerika",
"regionSouthAmerica": "Sør-Amerika",
"regionNorthernAmerica": "Nord-Amerika",
"regionAsia": "Asia",
"regionCentralAsia": "Sentral-Asia",
"regionEasternAsia": "Øst-Asia",
"regionSouthEasternAsia": "Sørøst-Asia",
"regionSouthernAsia": "Sørlige Asia",
"regionWesternAsia": "Vest-Asia",
"regionEurope": "Europa",
"regionEasternEurope": "Øst-Europa",
"regionNorthernEurope": "Nord-Europa",
"regionSouthernEurope": "Sørlige Europa",
"regionWesternEurope": "Vest-Europa",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australia og New Zealand",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Administrert selv-hostet",
"description": "Sikre og lavvedlikeholdsservere, selvbetjente Pangolin med ekstra klokker, og understell",
@@ -1899,6 +2030,25 @@
"invalidValue": "Ugyldig verdi",
"idpTypeLabel": "Identitet leverandør type",
"roleMappingExpressionPlaceholder": "F.eks. inneholder(grupper, 'admin') && 'Admin' ⋅'Medlem'",
"roleMappingModeFixedRoles": "Fast roller",
"roleMappingModeMappingBuilder": "Kartlegger bygger",
"roleMappingModeRawExpression": "Rå uttrykk",
"roleMappingFixedRolesPlaceholderSelect": "Velg en eller flere roller",
"roleMappingFixedRolesPlaceholderFreeform": "Skriv inn rollenavn (eksakt treff per organisasjon)",
"roleMappingFixedRolesDescriptionSameForAll": "Tilordne den samme rollen som er satt til hver automatisk midlertidig bruker.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "For standard policyer, type rollenavn som eksisterer i hver organisasjon der brukerne tilbys. Navn må stemmer nøyaktig.",
"roleMappingClaimPath": "Krev sti",
"roleMappingClaimPathPlaceholder": "grupper",
"roleMappingClaimPathDescription": "Sti i i token nyttelast som inneholder kildeverdier (for eksempel grupper).",
"roleMappingMatchValue": "Treff verdi",
"roleMappingAssignRoles": "Tilordne roller",
"roleMappingAddMappingRule": "Legg til tilordningsregel",
"roleMappingRawExpressionResultDescription": "Uttrykk skal vurderes til en streng eller en tekststreng.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Uttrykk må evaluere til en streng (en rollenavn).",
"roleMappingMatchValuePlaceholder": "Match verdi (for eksempel: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Angi rollenavn (eksakt per org)",
"roleMappingBuilderFreeformRowHint": "Rollenavn må samsvare med en rolle i hver målorganisasjon.",
"roleMappingRemoveRule": "Fjern",
"idpGoogleConfiguration": "Google Konfigurasjon",
"idpGoogleConfigurationDescription": "Konfigurer Google OAuth2 legitimasjonen",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Hvor lenge du vil beholde adgangslogger",
"logRetentionActionLabel": "Handlings logg nytt",
"logRetentionActionDescription": "Hvor lenge handlingen skal lagres",
"logRetentionConnectionLabel": "Logg nyhet",
"logRetentionConnectionDescription": "Hvor lenge du vil beholde tilkoblingslogger",
"logRetentionDisabled": "Deaktivert",
"logRetention3Days": "3 dager",
"logRetention7Days": "7 dager",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Slutt på neste år",
"actionLogsDescription": "Vis historikk for handlinger som er utført i denne organisasjonen",
"accessLogsDescription": "Vis autoriseringsforespørsler for ressurser i denne organisasjonen",
"licenseRequiredToUse": "En <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisens er påkrevd for å bruke denne funksjonen. Denne funksjonen er også tilgjengelig i <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> er nødvendig for å bruke denne funksjonen. Denne funksjonen er også tilgjengelig i <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Loggfiler for tilkobling",
"connectionLogsDescription": "Vis tilkoblingslogger for tunneler i denne organisasjonen",
"sidebarLogsConnection": "Loggfiler for tilkobling",
"sidebarLogsStreaming": "Strømming",
"sourceAddress": "Kilde adresse",
"destinationAddress": "Måladresse (Automatic Translation)",
"duration": "Varighet",
"licenseRequiredToUse": "En <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisens eller <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> er påkrevd for å bruke denne funksjonen. <bookADemoLink>Bestill en demo eller POC prøveversjon</bookADemoLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> er nødvendig for å bruke denne funksjonen. Denne funksjonen er også tilgjengelig i <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Bestill en demo eller POC studie</bookADemoLink>.",
"certResolver": "Sertifikat løser",
"certResolverDescription": "Velg sertifikatløser som skal brukes for denne ressursen.",
"selectCertResolver": "Velg sertifikatløser",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Tilgangskontroll",
"editInternalResourceDialogAccessControlDescription": "Kontroller hvilke roller, brukere og maskinklienter som har tilgang til denne ressursen når den er koblet til. Administratorer har alltid tilgang.",
"editInternalResourceDialogPortRangeValidationError": "Portsjiktet må være \"*\" for alle porter, eller en kommaseparert liste med porter og sjikt (f.eks. \"80,443,8000-9000\"). Porter må være mellom 1 og 65535.",
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon Sted",
"internalResourceAuthDaemonStrategyDescription": "Velg hvor SSH-autentisering daemon kjører: på nettstedet (Newt) eller på en ekstern vert.",
"internalResourceAuthDaemonDescription": "SSH-godkjenning daemon håndterer SSH-nøkkel signering og PAM autentisering for denne ressursen. Velg om den kjører på nettstedet (Newt) eller på en separat ekstern vert. Se <docsLink>dokumentasjonen</docsLink> for mer.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Velg strategi",
"internalResourceAuthDaemonStrategyLabel": "Sted",
"internalResourceAuthDaemonSite": "På nettsted",
"internalResourceAuthDaemonSiteDescription": "Autentiser daemon kjører på nettstedet (Newt).",
"internalResourceAuthDaemonRemote": "Ekstern vert",
"internalResourceAuthDaemonRemoteDescription": "Autentiser daemon kjører på en vert som ikke er nettstedet.",
"internalResourceAuthDaemonPort": "Daemon Port (valgfritt)",
"orgAuthWhatsThis": "Hvor kan jeg finne min organisasjons-ID?",
"learnMore": "Lær mer",
"backToHome": "Gå tilbake til start",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Aktiver enhetsgodkjenninger",
"approvalsEmptyStateStep2Description": "Rediger en rolle og aktiver alternativet 'Kreve enhetsgodkjenninger'. Brukere med denne rollen vil trenge administratorgodkjenning for nye enheter.",
"approvalsEmptyStatePreviewDescription": "Forhåndsvisning: Når aktivert, ventende enhets forespørsler vil vises her for vurdering",
"approvalsEmptyStateButtonText": "Administrer Roller"
"approvalsEmptyStateButtonText": "Administrer Roller",
"domainErrorTitle": "Vi har problemer med å verifisere domenet ditt",
"idpAdminAutoProvisionPoliciesTabHint": "Konfigurer rollegartlegging og organisasjonspolicyer på <policiesTabLink>Auto leveringsinnstillinger</policiesTabLink> fanen.",
"streamingTitle": "Hendelse Strømming",
"streamingDescription": "Stream hendelser fra din organisasjon til eksterne destinasjoner i sanntid.",
"streamingUnnamedDestination": "Plassering uten navn",
"streamingNoUrlConfigured": "Ingen URL konfigurert",
"streamingAddDestination": "Legg til mål",
"streamingHttpWebhookTitle": "HTTP Webhook",
"streamingHttpWebhookDescription": "Send hendelser til alle HTTP-endepunkter med fleksibel autentisering og maling.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Strøm hendelser til en S3-kompatibel objektlagringskjøt. Kommer snart.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Videresend arrangementer direkte til din Datadog-konto. Kommer snart.",
"streamingTypePickerDescription": "Velg en måltype for å komme i gang.",
"streamingFailedToLoad": "Kan ikke laste inn destinasjoner",
"streamingUnexpectedError": "En uventet feil oppstod.",
"streamingFailedToUpdate": "Kunne ikke oppdatere destinasjon",
"streamingDeletedSuccess": "Målet ble slettet",
"streamingFailedToDelete": "Kunne ikke slette destinasjon",
"streamingDeleteTitle": "Slett mål",
"streamingDeleteButtonText": "Slett mål",
"streamingDeleteDialogAreYouSure": "Er du sikker på at du vil slette",
"streamingDeleteDialogThisDestination": "denne destinasjonen",
"streamingDeleteDialogPermanentlyRemoved": "? Alle konfigurasjoner vil bli slettet permanent.",
"httpDestEditTitle": "Rediger mål",
"httpDestAddTitle": "Legg til HTTP-destinasjon",
"httpDestEditDescription": "Oppdater konfigurasjonen for denne HTTP-hendelsesstrømmedestinasjonen.",
"httpDestAddDescription": "Konfigurer et nytt HTTP endepunkt for å motta organisasjonens hendelser.",
"httpDestTabSettings": "Innstillinger",
"httpDestTabHeaders": "Overskrifter",
"httpDestTabBody": "Innhold",
"httpDestTabLogs": "Logger",
"httpDestNamePlaceholder": "Min HTTP destinasjon",
"httpDestUrlLabel": "Destinasjons URL",
"httpDestUrlErrorHttpRequired": "URL-adressen må bruke httpp eller https",
"httpDestUrlErrorHttpsRequired": "HTTPS er nødvendig for distribusjon av sky",
"httpDestUrlErrorInvalid": "Skriv inn en gyldig nettadresse (f.eks. https://eksempel.com/webhook)",
"httpDestAuthTitle": "Autentisering",
"httpDestAuthDescription": "Velg hvordan ønsker til sluttpunktet ditt er autentisert.",
"httpDestAuthNoneTitle": "Ingen godkjenning",
"httpDestAuthNoneDescription": "Sender forespørsler uten autorisasjonsoverskrift.",
"httpDestAuthBearerTitle": "Bærer Symbol",
"httpDestAuthBearerDescription": "Legger til en autorisasjon: Bearer <token> header til hver forespørsel.",
"httpDestAuthBearerPlaceholder": "Din API-nøkkel eller token",
"httpDestAuthBasicTitle": "Standard Auth",
"httpDestAuthBasicDescription": "Legger til en godkjenning: Grunnleggende <credentials> overskrift. Angi legitimasjon som brukernavn:passord.",
"httpDestAuthBasicPlaceholder": "brukernavn:passord",
"httpDestAuthCustomTitle": "Egendefinert topptekst",
"httpDestAuthCustomDescription": "Angi et egendefinert HTTP headers navn og verdi for autentisering (f.eks X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Topptekst navn (f.eks X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Header verdi",
"httpDestCustomHeadersTitle": "Egendefinerte HTTP-overskrifter",
"httpDestCustomHeadersDescription": "Legg til egendefinerte overskrifter til hver utgående forespørsel. Nyttig for statisk tokens eller en egendefinert innholdstype. Som standard blir innholdstype: applikasjon/json sendt.",
"httpDestNoHeadersConfigured": "Ingen egendefinerte overskrifter konfigurert. Klikk \"Legg til topptekst\" for å legge til en.",
"httpDestHeaderNamePlaceholder": "Navn på topptekst",
"httpDestHeaderValuePlaceholder": "Verdi",
"httpDestAddHeader": "Legg til topptekst",
"httpDestBodyTemplateTitle": "Egendefinert hovedmal",
"httpDestBodyTemplateDescription": "Kontroller JSON nyttelaststrukturen sendt til ditt endepunkt. Hvis deaktivert, sendes et standard JSON-objekt for hver hendelse.",
"httpDestEnableBodyTemplate": "Aktiver egendefinert meldingsmal",
"httpDestBodyTemplateLabel": "Kroppsmal (JSON)",
"httpDestBodyTemplateHint": "Bruk designmal variabler for å referere til eventfelt i din betaling.",
"httpDestPayloadFormatTitle": "Mål format",
"httpDestPayloadFormatDescription": "Hvordan blir hendelser serialisert inn i hver forespørselsorgan.",
"httpDestFormatJsonArrayTitle": "JSON liste",
"httpDestFormatJsonArrayDescription": "Én forespørsel per batch, innholdet er en JSON-liste. Kompatibel med de mest generiske webhooks og Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Én forespørsel per sats, innholdet er nytt avgrenset JSON — et objekt per linje, ingen ytterarray. Kreves av Splunk HEC, Elastisk/OpenSearch, og Grafana Loki.",
"httpDestFormatSingleTitle": "En hendelse per forespørsel",
"httpDestFormatSingleDescription": "Sender en separat HTTP POST for hver enkelt hendelse. Bruk bare for endepunkter som ikke kan håndtere batcher.",
"httpDestLogTypesTitle": "Logg typer",
"httpDestLogTypesDescription": "Velg hvilke loggtyper som blir videresendt til dette målet. Bare aktiverte loggtyper vil bli strømmet.",
"httpDestAccessLogsTitle": "Tilgangslogger (Automatic Translation)",
"httpDestAccessLogsDescription": "Adgangsforsøk for ressurser, inkludert godkjente og nektet forespørsler.",
"httpDestActionLogsTitle": "Handlingslogger",
"httpDestActionLogsDescription": "Administrative tiltak som utføres av brukere innenfor organisasjonen.",
"httpDestConnectionLogsTitle": "Loggfiler for tilkobling",
"httpDestConnectionLogsDescription": "Utstyrs- og tunneltilkoblingshendelser, inkludert forbindelser og frakobling.",
"httpDestRequestLogsTitle": "Forespørselslogger (Automatic Translation)",
"httpDestRequestLogsDescription": "HTTP-forespørsel logger for bekreftede ressurser, inkludert metode, bane og responskode.",
"httpDestSaveChanges": "Lagre endringer",
"httpDestCreateDestination": "Opprett mål",
"httpDestUpdatedSuccess": "Målet er oppdatert",
"httpDestCreatedSuccess": "Målet er opprettet",
"httpDestUpdateFailed": "Kunne ikke oppdatere destinasjon",
"httpDestCreateFailed": "Kan ikke opprette mål"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Koppeling aanmaken",
"resourcesNotFound": "Geen bronnen gevonden",
"resourceSearch": "Zoek bronnen",
"machineSearch": "Zoek machines",
"machinesSearch": "Zoek machine-clients...",
"machineNotFound": "Geen machines gevonden",
"userDeviceSearch": "Gebruikersapparaten zoeken",
"userDevicesSearch": "Gebruikersapparaten zoeken...",
"openMenu": "Menu openen",
"resource": "Bron",
"title": "Aanspreektitel",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxyverzoeken via HTTPS met een volledig gekwalificeerde domeinnaam.",
"resourceRaw": "TCP/UDP bron",
"resourceRawDescription": "Proxyverzoeken via ruwe TCP/UDP met een poortnummer.",
"resourceRawDescriptionCloud": "Proxy verzoeken over rauwe TCP/UDP met behulp van een poortnummer. Vereist sites om verbinding te maken met een remote node.",
"resourceCreate": "Bron maken",
"resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken",
"resourceSeeAll": "Alle bronnen bekijken",
@@ -322,6 +328,54 @@
"apiKeysDelete": "API-sleutel verwijderen",
"apiKeysManage": "API-sleutels beheren",
"apiKeysDescription": "API-sleutels worden gebruikt om te verifiëren met de integratie-API",
"provisioningKeysTitle": "Vertrekkende sleutel",
"provisioningKeysManage": "Beheren van Provisioning Sleutels",
"provisioningKeysDescription": "Provisionerende sleutels worden gebruikt om geautomatiseerde sitebepaling voor uw organisatie te verifiëren.",
"provisioningManage": "Provisie",
"provisioningDescription": "Voorzieningssleutels beheren en sites beoordelen in afwachting van goedkeuring.",
"pendingSites": "Openstaande sites",
"siteApproveSuccess": "Site succesvol goedgekeurd",
"siteApproveError": "Fout bij goedkeuren website",
"provisioningKeys": "Verhelderende sleutels",
"searchProvisioningKeys": "Zoek provisioningsleutels ...",
"provisioningKeysAdd": "Genereer Provisioning Sleutel",
"provisioningKeysErrorDelete": "Fout bij verwijderen provisioning sleutel",
"provisioningKeysErrorDeleteMessage": "Fout bij verwijderen provisioning sleutel",
"provisioningKeysQuestionRemove": "Weet u zeker dat u deze proefsleutel van de organisatie wilt verwijderen?",
"provisioningKeysMessageRemove": "Eenmaal verwijderd, kan de sleutel niet meer worden gebruikt voor site-instructie.",
"provisioningKeysDeleteConfirm": "Bevestig Verwijderen Provisione-sleutel",
"provisioningKeysDelete": "Provisione-sleutel verwijderen",
"provisioningKeysCreate": "Genereer Provisioning Sleutel",
"provisioningKeysCreateDescription": "Een nieuwe provisioningsleutel voor de organisatie genereren",
"provisioningKeysSeeAll": "Bekijk alle provisioning sleutels",
"provisioningKeysSave": "Sla de provisioning sleutel op",
"provisioningKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plaats.",
"provisioningKeysErrorCreate": "Fout bij aanmaken provisioning sleutel",
"provisioningKeysList": "Nieuwe provisioning sleutel",
"provisioningKeysMaxBatchSize": "Maximale batchgrootte",
"provisioningKeysUnlimitedBatchSize": "Onbeperkte batchgrootte (geen limiet)",
"provisioningKeysMaxBatchUnlimited": "Onbeperkt",
"provisioningKeysMaxBatchSizeInvalid": "Voer een geldige maximale batchgrootte in (11.000,000).",
"provisioningKeysValidUntil": "Geldig tot",
"provisioningKeysValidUntilHint": "Laat leeg voor geen vervaldatum.",
"provisioningKeysValidUntilInvalid": "Voer een geldige datum en tijd in.",
"provisioningKeysNumUsed": "Aantal keer gebruikt",
"provisioningKeysLastUsed": "Laatst gebruikt",
"provisioningKeysNoExpiry": "Geen vervaldatum",
"provisioningKeysNeverUsed": "Nooit",
"provisioningKeysEdit": "Wijzig Provisioning Sleutel",
"provisioningKeysEditDescription": "Werk de maximale batchgrootte en verlooptijd voor deze sleutel bij.",
"provisioningKeysApproveNewSites": "Goedkeuren van nieuwe sites",
"provisioningKeysApproveNewSitesDescription": "Automatisch sites goedkeuren die zich registreren met deze sleutel.",
"provisioningKeysUpdateError": "Fout tijdens bijwerken provisioning sleutel",
"provisioningKeysUpdated": "Provisie sleutel bijgewerkt",
"provisioningKeysUpdatedDescription": "Uw wijzigingen zijn opgeslagen.",
"provisioningKeysBannerTitle": "Bewerkingssleutels voor websites",
"provisioningKeysBannerDescription": "Genereer een provisioning-sleutel en gebruik deze met de Newt-connector om automatisch sites aan te maken bij het opstarten van de eerste opstart- het is niet nodig om afzonderlijke inloggegevens in te stellen voor elke site.",
"provisioningKeysBannerButtonText": "Meer informatie",
"pendingSitesBannerTitle": "Openstaande sites",
"pendingSitesBannerDescription": "Sites die met elkaar verbinden met behulp van een provisioning-sleutel verschijnen hier voor beoordeling. Accepteer elke site voordat deze actief wordt en krijgt toegang tot uw bronnen.",
"pendingSitesBannerButtonText": "Meer informatie",
"apiKeysSettings": "{apiKeyName} instellingen",
"userTitle": "Alle gebruikers beheren",
"userDescription": "Bekijk en beheer alle gebruikers in het systeem",
@@ -508,9 +562,12 @@
"userSaved": "Gebruiker opgeslagen",
"userSavedDescription": "De gebruiker is bijgewerkt.",
"autoProvisioned": "Automatisch bevestigen",
"autoProvisionSettings": "Auto Provisie Instellingen",
"autoProvisionedDescription": "Toestaan dat deze gebruiker automatisch wordt beheerd door een identiteitsprovider",
"accessControlsDescription": "Beheer wat deze gebruiker toegang heeft tot en doet in de organisatie",
"accessControlsSubmit": "Bewaar Toegangsbesturing",
"singleRolePerUserPlanNotice": "Uw plan ondersteunt slechts één rol per gebruiker.",
"singleRolePerUserEditionNotice": "Deze editie ondersteunt slechts één rol per gebruiker.",
"roles": "Rollen",
"accessUsersRoles": "Beheer Gebruikers & Rollen",
"accessUsersRolesDescription": "Nodig gebruikers uit en voeg ze toe aan de rollen om toegang tot de organisatie te beheren",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Bron wisselen mislukt",
"resourcesErrorUpdateDescription": "Er is een fout opgetreden tijdens het bijwerken van het document",
"access": "Toegangsrechten",
"accessControl": "Toegangs controle",
"shareLink": "{resource} Share link",
"resourceSelect": "Selecteer resource",
"shareLinks": "Links delen",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rol verwijderd",
"accessRoleRemovedDescription": "De rol is succesvol verwijderd.",
"accessRoleRequiredRemove": "Voordat u deze rol verwijdert, selecteer een nieuwe rol om bestaande leden aan te dragen.",
"network": "Netwerk",
"manage": "Beheren",
"sitesNotFound": "Geen sites gevonden.",
"pangolinServerAdmin": "Serverbeheer - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Oeps! De pagina die je zoekt bestaat niet.",
"overview": "Overzicht.",
"home": "Startpagina",
"accessControl": "Toegangs controle",
"settings": "Instellingen",
"usersAll": "Alle gebruikers",
"license": "Licentie",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Gebruiker ophalen",
"actionGetOrgUser": "Krijg organisatie-gebruiker",
"actionListOrgDomains": "Lijst organisatie domeinen",
"actionGetDomain": "Domein verkrijgen",
"actionCreateOrgDomain": "Domein aanmaken",
"actionUpdateOrgDomain": "Domein bijwerken",
"actionDeleteOrgDomain": "Domein verwijderen",
"actionGetDNSRecords": "Krijg DNS Records",
"actionRestartOrgDomain": "Domein opnieuw starten",
"actionCreateSite": "Site aanmaken",
"actionDeleteSite": "Site verwijderen",
"actionGetSite": "Site ophalen",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Voer het setup-token in vanaf de serverconsole.",
"setupTokenRequired": "Setup-token is vereist",
"actionUpdateSite": "Site bijwerken",
"actionResetSiteBandwidth": "Reset organisatieschandbreedte",
"actionListSiteRoles": "Toon toegestane sitenollen",
"actionCreateResource": "Bron maken",
"actionDeleteResource": "Document verwijderen",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Gebruiker verwijderen",
"actionListUsers": "Gebruikers weergeven",
"actionAddUserRole": "Gebruikersrol toevoegen",
"actionSetUserOrgRoles": "Stel gebruikersrollen in",
"actionGenerateAccessToken": "Genereer Toegangstoken",
"actionDeleteAccessToken": "Verwijder toegangstoken",
"actionListAccessTokens": "Lijst toegangstokens",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privé",
"sidebarAccessControl": "Toegangs controle",
"sidebarLogsAndAnalytics": "Logs & Analytics",
"sidebarTeam": "Team",
"sidebarUsers": "Gebruikers",
"sidebarAdmin": "Beheerder",
"sidebarInvitations": "Uitnodigingen",
"sidebarRoles": "Rollen",
"sidebarShareableLinks": "Koppelingen",
"sidebarApiKeys": "API sleutels",
"sidebarProvisioning": "Provisie",
"sidebarSettings": "Instellingen",
"sidebarAllUsers": "Alle gebruikers",
"sidebarIdentityProviders": "Identiteit aanbieders",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Blauwdrukken",
"sidebarOrganization": "Organisatie",
"sidebarManagement": "Beheer",
"sidebarBillingAndLicenses": "Facturatie & Licenties",
"sidebarLogsAnalytics": "Analyses",
"blueprints": "Blauwdrukken",
@@ -1289,7 +1358,6 @@
"parsedContents": "Geparseerde inhoud (alleen lezen)",
"enableDockerSocket": "Schakel Docker Blauwdruk in",
"enableDockerSocketDescription": "Schakel Docker Socket label in voor blauwdruk labels. Pad naar Nieuw.",
"enableDockerSocketLink": "Meer informatie",
"viewDockerContainers": "Bekijk Docker containers",
"containersIn": "Containers in {siteName}",
"selectContainerDescription": "Selecteer een container om als hostnaam voor dit doel te gebruiken. Klik op een poort om een poort te gebruiken.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Naamruimte: {namespace}",
"domainPickerShowMore": "Meer weergeven",
"regionSelectorTitle": "Selecteer Regio",
"domainPickerRemoteExitNodeWarning": "Opgegeven domeinen worden niet ondersteund wanneer websites verbinding maken met externe sluitnodes. Gebruik in plaats daarvan een aangepast domein. Om bronnen beschikbaar te maken op externe nodes.",
"regionSelectorInfo": "Het selecteren van een regio helpt ons om betere prestaties te leveren voor uw locatie. U hoeft niet in dezelfde regio als uw server te zijn.",
"regionSelectorPlaceholder": "Kies een regio",
"regionSelectorComingSoon": "Komt binnenkort",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Kennisgeving beschikbaarheid",
"billingFeatureLossDescription": "Door downgraden worden functies die niet beschikbaar zijn in het nieuwe abonnement automatisch uitgeschakeld. Sommige instellingen en configuraties kunnen verloren gaan. Raadpleeg de prijsmatrix om te begrijpen welke functies niet langer beschikbaar zijn.",
"billingUsageExceedsLimit": "Huidig gebruik ({current}) overschrijdt limiet ({limit})",
"billingPastDueTitle": "Vervaldatum betaling",
"billingPastDueDescription": "Uw betaling is verlopen. Werk uw betaalmethode bij om uw huidige abonnementsfuncties te blijven gebruiken. Als dit niet is opgelost, zal je abonnement worden geannuleerd en zal je worden teruggezet naar de vrije rang.",
"billingUnpaidTitle": "Abonnement Onbetaald",
"billingUnpaidDescription": "Uw abonnement is niet betaald en u bent teruggekeerd naar het gratis niveau. Update uw betalingsmethode om uw abonnement te herstellen.",
"billingIncompleteTitle": "Betaling onvolledig",
"billingIncompleteDescription": "Uw betaling is onvolledig. Voltooi alstublieft het betalingsproces om uw abonnement te activeren.",
"billingIncompleteExpiredTitle": "Betaling verlopen",
"billingIncompleteExpiredDescription": "Uw betaling is nooit voltooid en verlopen. U bent teruggekeerd naar de gratis niveaus. Abonneer u opnieuw om de toegang tot betaalde functies te herstellen.",
"billingManageSubscription": "Beheer uw abonnement",
"billingResolvePaymentIssue": "Gelieve uw betalingsprobleem op te lossen voor het upgraden of downgraden",
"signUpTerms": {
"IAgreeToThe": "Ik ga akkoord met de",
"termsOfService": "servicevoorwaarden",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Tijd is in seconden",
"requireDeviceApproval": "Vereist goedkeuring van apparaat",
"requireDeviceApprovalDescription": "Gebruikers met deze rol hebben nieuwe apparaten nodig die door een beheerder zijn goedgekeurd voordat ze verbinding kunnen maken met bronnen en deze kunnen gebruiken.",
"sshAccess": "SSH toegang",
"roleAllowSsh": "SSH toestaan",
"roleAllowSshAllow": "Toestaan",
"roleAllowSshDisallow": "Weigeren",
"roleAllowSshDescription": "Sta gebruikers met deze rol toe om verbinding te maken met bronnen via SSH. Indien uitgeschakeld kan de rol geen gebruik maken van SSH toegang.",
"sshSudoMode": "Sudo toegang",
"sshSudoModeNone": "geen",
"sshSudoModeNoneDescription": "Gebruiker kan geen commando's uitvoeren met sudo.",
"sshSudoModeFull": "Volledige Sudo",
"sshSudoModeFullDescription": "Gebruiker kan elk commando uitvoeren met een sudo.",
"sshSudoModeCommands": "Opdrachten",
"sshSudoModeCommandsDescription": "Gebruiker kan alleen de opgegeven commando's uitvoeren met de sudo.",
"sshSudo": "sudo toestaan",
"sshSudoCommands": "Sudo Commando's",
"sshSudoCommandsDescription": "Komma's gescheiden lijst van commando's waar de gebruiker een sudo mee mag uitvoeren.",
"sshCreateHomeDir": "Maak Home Directory",
"sshUnixGroups": "Unix groepen",
"sshUnixGroupsDescription": "Door komma's gescheiden Unix-groepen om de gebruiker toe te voegen aan de doelhost.",
"retryAttempts": "Herhaal Pogingen",
"expectedResponseCodes": "Verwachte Reactiecodes",
"expectedResponseCodesDescription": "HTTP-statuscode die gezonde status aangeeft. Indien leeg wordt 200-300 als gezond beschouwd.",
@@ -1851,6 +1948,40 @@
"exitNode": "Exit Node",
"country": "Land",
"rulesMatchCountry": "Momenteel gebaseerd op bron IP",
"region": "Regio",
"selectRegion": "Selecteer regio",
"searchRegions": "Zoek regio's...",
"noRegionFound": "Geen regio gevonden.",
"rulesMatchRegion": "Selecteer een regionale groepering van landen",
"rulesErrorInvalidRegion": "Ongeldige regio",
"rulesErrorInvalidRegionDescription": "Selecteer een geldige regio.",
"regionAfrica": "Afrika",
"regionNorthernAfrica": "Noord-Afrika",
"regionEasternAfrica": "Oost Afrika",
"regionMiddleAfrica": "Midden Afrika",
"regionSouthernAfrica": "Zuidelijk Afrika",
"regionWesternAfrica": "Westelijk Afrika",
"regionAmericas": "Amerika's",
"regionCaribbean": "Caraïben",
"regionCentralAmerica": "Midden-Amerika",
"regionSouthAmerica": "Zuid Amerika",
"regionNorthernAmerica": "Noord-Amerika",
"regionAsia": "Azië",
"regionCentralAsia": "Centraal-Azië",
"regionEasternAsia": "Oost-Azië",
"regionSouthEasternAsia": "Zuid-Oost-Azië",
"regionSouthernAsia": "Zuid-Azië",
"regionWesternAsia": "Westelijk Azië",
"regionEurope": "Europa",
"regionEasternEurope": "Oost-Europa",
"regionNorthernEurope": "Noord-Europa",
"regionSouthernEurope": "Zuid-Europa",
"regionWesternEurope": "West-Europa",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australië en Nieuw-Zeeland",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Beheerde Self-Hosted",
"description": "betrouwbaardere en slecht onderhouden Pangolin server met extra klokken en klokkenluiders",
@@ -1899,6 +2030,25 @@
"invalidValue": "Ongeldige waarde",
"idpTypeLabel": "Identiteit provider type",
"roleMappingExpressionPlaceholder": "bijvoorbeeld bevat (groepen, 'admin') && 'Admin' ½ 'Member'",
"roleMappingModeFixedRoles": "Vaste rollen",
"roleMappingModeMappingBuilder": "Toewijzing Bouwer",
"roleMappingModeRawExpression": "Ruwe expressie",
"roleMappingFixedRolesPlaceholderSelect": "Selecteer één of meer rollen",
"roleMappingFixedRolesPlaceholderFreeform": "Typ rolnamen (exacte overeenkomst per organisatie)",
"roleMappingFixedRolesDescriptionSameForAll": "Wijs dezelfde rolset toe aan elke auto-provisioned gebruiker.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Voor standaardbeleid, typ rolnamen die bestaan in elke organisatie waar gebruikers worden opgegeven. Namen moeten exact overeenkomen.",
"roleMappingClaimPath": "Claim pad",
"roleMappingClaimPathPlaceholder": "Groepen",
"roleMappingClaimPathDescription": "Pad in de token payload die bronwaarden bevat (bijvoorbeeld groepen).",
"roleMappingMatchValue": "Kies een waarde",
"roleMappingAssignRoles": "Rollen toewijzen",
"roleMappingAddMappingRule": "Toewijzingsregel toevoegen",
"roleMappingRawExpressionResultDescription": "Expressie moet een tekenreeks of tekenreeks evalueren.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Expressie moet evalueren naar een tekenreeks (een naam met één rol).",
"roleMappingMatchValuePlaceholder": "Overeenkomende waarde (bijvoorbeeld: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Typ rolnamen (exact per org)",
"roleMappingBuilderFreeformRowHint": "Rol namen moeten overeenkomen met een rol in elke doelorganisatie.",
"roleMappingRemoveRule": "Verwijderen",
"idpGoogleConfiguration": "Google Configuratie",
"idpGoogleConfigurationDescription": "Configureer de Google OAuth2-referenties",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Hoe lang de toegangslogboeken behouden blijven",
"logRetentionActionLabel": "Actie log bewaring",
"logRetentionActionDescription": "Hoe lang de action logs behouden moeten blijven",
"logRetentionConnectionLabel": "Connectie log bewaring",
"logRetentionConnectionDescription": "Hoe lang de verbindingslogs onderhouden",
"logRetentionDisabled": "Uitgeschakeld",
"logRetention3Days": "3 dagen",
"logRetention7Days": "7 dagen",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Einde van volgend jaar",
"actionLogsDescription": "Bekijk een geschiedenis van acties die worden uitgevoerd in deze organisatie",
"accessLogsDescription": "Toegangsverificatieverzoeken voor resources in deze organisatie bekijken",
"licenseRequiredToUse": "Een <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> licentie is vereist om deze functie te gebruiken. Deze functie is ook beschikbaar in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "De <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is vereist om deze functie te gebruiken. Deze functie is ook beschikbaar in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Connectie Logs",
"connectionLogsDescription": "Toon verbindingslogs voor tunnels in deze organisatie",
"sidebarLogsConnection": "Connectie Logs",
"sidebarLogsStreaming": "Streamen",
"sourceAddress": "Bron adres",
"destinationAddress": "Adres bestemming",
"duration": "Duur",
"licenseRequiredToUse": "Een <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> licentie of <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is vereist om deze functie te gebruiken. <bookADemoLink>Boek een demo of POC trial</bookADemoLink>.",
"ossEnterpriseEditionRequired": "De <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is vereist om deze functie te gebruiken. Deze functie is ook beschikbaar in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Boek een demo of POC trial</bookADemoLink>.",
"certResolver": "Certificaat Resolver",
"certResolverDescription": "Selecteer de certificaat resolver die moet worden gebruikt voor deze resource.",
"selectCertResolver": "Certificaat Resolver selecteren",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Toegangs controle",
"editInternalResourceDialogAccessControlDescription": "Beheer welke rollen, gebruikers en machineclients toegang hebben tot deze bron wanneer ze zijn verbonden. Beheerders hebben altijd toegang.",
"editInternalResourceDialogPortRangeValidationError": "Poortbereik moet \"*\" zijn voor alle poorten, of een komma-gescheiden lijst van poorten en bereiken (bijv. \"80,443,8000-9000\"). Poorten moeten tussen 1 en 65535 zijn.",
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon locatie",
"internalResourceAuthDaemonStrategyDescription": "Kies waar de SSH authenticatie daemon wordt uitgevoerd: op de website (Newt) of op een externe host.",
"internalResourceAuthDaemonDescription": "De SSH authenticatie daemon zorgt voor SSH sleutelondertekening en PAM authenticatie voor deze resource. Kies of het wordt uitgevoerd op de website (Nieuw) of op een afzonderlijke externe host. Zie <docsLink>de documentatie</docsLink> voor meer.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Selecteer strategie",
"internalResourceAuthDaemonStrategyLabel": "Locatie",
"internalResourceAuthDaemonSite": "In de site",
"internalResourceAuthDaemonSiteDescription": "Auth daemon draait op de site (Newt).",
"internalResourceAuthDaemonRemote": "Externe host",
"internalResourceAuthDaemonRemoteDescription": "Authenticatiedaemon draait op een host die niet de site is.",
"internalResourceAuthDaemonPort": "Daemon poort (optioneel)",
"orgAuthWhatsThis": "Waar kan ik mijn organisatie-ID vinden?",
"learnMore": "Meer informatie",
"backToHome": "Ga terug naar startpagina",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Toestel goedkeuringen inschakelen",
"approvalsEmptyStateStep2Description": "Bewerk een rol en schakel de optie 'Vereist Apparaat Goedkeuringen' in. Gebruikers met deze rol hebben admin goedkeuring nodig voor nieuwe apparaten.",
"approvalsEmptyStatePreviewDescription": "Voorbeeld: Indien ingeschakeld, zullen in afwachting van apparaatverzoeken hier verschijnen om te beoordelen",
"approvalsEmptyStateButtonText": "Rollen beheren"
"approvalsEmptyStateButtonText": "Rollen beheren",
"domainErrorTitle": "We ondervinden problemen bij het controleren van uw domein",
"idpAdminAutoProvisionPoliciesTabHint": "Configureer rolverrekening en organisatie beleid in het <policiesTabLink>Auto Provision Settings</policiesTabLink> tab.",
"streamingTitle": "Event streaming",
"streamingDescription": "Stream events van uw organisatie naar externe bestemmingen in realtime.",
"streamingUnnamedDestination": "Naamloze bestemming",
"streamingNoUrlConfigured": "Geen URL ingesteld",
"streamingAddDestination": "Bestemming toevoegen",
"streamingHttpWebhookTitle": "HTTP Webhook",
"streamingHttpWebhookDescription": "Stuur gebeurtenissen naar elk HTTP eindpunt met flexibele authenticatie en template.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Stream events naar een S3-compatibele object-opslagemmer. Binnenkort beschikbaar.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Stuur gebeurtenissen rechtstreeks door naar je Datadog account. Binnenkort beschikbaar.",
"streamingTypePickerDescription": "Kies een bestemmingstype om te beginnen.",
"streamingFailedToLoad": "Laden van bestemmingen mislukt",
"streamingUnexpectedError": "Er is een onverwachte fout opgetreden.",
"streamingFailedToUpdate": "Bijwerken bestemming mislukt",
"streamingDeletedSuccess": "Bestemming succesvol verwijderd",
"streamingFailedToDelete": "Verwijderen van bestemming mislukt",
"streamingDeleteTitle": "Verwijder bestemming",
"streamingDeleteButtonText": "Verwijder bestemming",
"streamingDeleteDialogAreYouSure": "Weet u zeker dat u wilt verwijderen",
"streamingDeleteDialogThisDestination": "deze bestemming",
"streamingDeleteDialogPermanentlyRemoved": "? Alle configuratie zal permanent worden verwijderd.",
"httpDestEditTitle": "Bewerk bestemming",
"httpDestAddTitle": "Voeg HTTP bestemming toe",
"httpDestEditDescription": "Werk de configuratie voor deze HTTP-event streaming bestemming bij.",
"httpDestAddDescription": "Configureer een nieuw HTTP-eindpunt om de gebeurtenissen van uw organisatie te ontvangen.",
"httpDestTabSettings": "Instellingen",
"httpDestTabHeaders": "Kopteksten",
"httpDestTabBody": "Lichaam",
"httpDestTabLogs": "Logboeken",
"httpDestNamePlaceholder": "Mijn HTTP-bestemming",
"httpDestUrlLabel": "Bestemming URL",
"httpDestUrlErrorHttpRequired": "URL moet http of https gebruiken",
"httpDestUrlErrorHttpsRequired": "HTTPS is vereist op cloud implementaties",
"httpDestUrlErrorInvalid": "Voer een geldige URL in (bijv. https://example.com/webhook)",
"httpDestAuthTitle": "Authenticatie",
"httpDestAuthDescription": "Kies hoe verzoeken voor uw eindpunt zijn geverifieerd.",
"httpDestAuthNoneTitle": "Geen authenticatie",
"httpDestAuthNoneDescription": "Stuurt verzoeken zonder toestemmingskop.",
"httpDestAuthBearerTitle": "Betere Token",
"httpDestAuthBearerDescription": "Voegt een machtiging toe: Drager <token> header aan elke aanvraag.",
"httpDestAuthBearerPlaceholder": "Uw API-sleutel of -token",
"httpDestAuthBasicTitle": "Basis authenticatie",
"httpDestAuthBasicDescription": "Voegt een Authorizatie toe: Basis <credentials> kop. Geef inloggegevens op als gebruikersnaam:wachtwoord.",
"httpDestAuthBasicPlaceholder": "Gebruikersnaam:wachtwoord",
"httpDestAuthCustomTitle": "Aangepaste koptekst",
"httpDestAuthCustomDescription": "Specificeer een aangepaste HTTP header naam en waarde voor authenticatie (bijv. X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Header naam (bijv. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Header waarde",
"httpDestCustomHeadersTitle": "Aangepaste HTTP Headers",
"httpDestCustomHeadersDescription": "Voeg aangepaste headers toe aan elk uitgaande verzoek. Handig voor statische tokens of een aangepast Content-Type. Standaard Content-Type: application/json wordt verzonden.",
"httpDestNoHeadersConfigured": "Geen aangepaste headers geconfigureerd. Klik op \"Header\" om er een toe te voegen.",
"httpDestHeaderNamePlaceholder": "Naam koptekst",
"httpDestHeaderValuePlaceholder": "Waarde",
"httpDestAddHeader": "Koptekst toevoegen",
"httpDestBodyTemplateTitle": "Aangepaste Body Sjabloon",
"httpDestBodyTemplateDescription": "Bestuur de JSON payload structuur verzonden naar uw eindpunt. Indien uitgeschakeld, wordt een standaard JSON object verzonden voor elke event.",
"httpDestEnableBodyTemplate": "Aangepaste lichaam sjabloon inschakelen",
"httpDestBodyTemplateLabel": "Body sjabloon (JSON)",
"httpDestBodyTemplateHint": "Gebruik sjabloonvariabelen om te verwijzen naar gebeurtenisvelden in uw payload.",
"httpDestPayloadFormatTitle": "Payload formaat",
"httpDestPayloadFormatDescription": "Hoe evenementen worden geserialiseerd in elk verzoeklichaam.",
"httpDestFormatJsonArrayTitle": "JSON matrix",
"httpDestFormatJsonArrayDescription": "Eén verzoek per batch, lichaam is een JSON-array. Compatibel met de meeste algemene webhooks en Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Eén aanvraag per batch, lichaam is nieuwe JSON gescheiden - één object per regel, geen buitenste array. Vereist door Splunk HEC, Elastic / OpenSearch, en Grafana Loki.",
"httpDestFormatSingleTitle": "Eén afspraak per verzoek",
"httpDestFormatSingleDescription": "Stuurt een aparte HTTP POST voor elk individueel event. Gebruik alleen voor eindpunten die geen batches kunnen verwerken.",
"httpDestLogTypesTitle": "Log soorten",
"httpDestLogTypesDescription": "Kies welke log types doorgestuurd worden naar deze bestemming. Alleen ingeschakelde log types worden gestreden.",
"httpDestAccessLogsTitle": "Toegang tot logboek",
"httpDestAccessLogsDescription": "Hulpbrontoegangspogingen, inclusief geauthenticeerde en weigerde aanvragen.",
"httpDestActionLogsTitle": "Actie logs",
"httpDestActionLogsDescription": "Administratieve acties uitgevoerd door gebruikers binnen de organisatie.",
"httpDestConnectionLogsTitle": "Connectie Logs",
"httpDestConnectionLogsDescription": "Verbinding met de Site en tunnel maken verbroken, inclusief verbindingen en verbindingen.",
"httpDestRequestLogsTitle": "Logboeken aanvragen",
"httpDestRequestLogsDescription": "HTTP request logs voor proxied hulpmiddelen, waaronder methode, pad en response code.",
"httpDestSaveChanges": "Wijzigingen opslaan",
"httpDestCreateDestination": "Maak bestemming aan",
"httpDestUpdatedSuccess": "Bestemming succesvol bijgewerkt",
"httpDestCreatedSuccess": "Bestemming succesvol aangemaakt",
"httpDestUpdateFailed": "Bijwerken bestemming mislukt",
"httpDestCreateFailed": "Aanmaken bestemming mislukt"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Utwórz link",
"resourcesNotFound": "Nie znaleziono zasobów",
"resourceSearch": "Szukaj zasobów",
"machineSearch": "Wyszukiwarki",
"machinesSearch": "Szukaj klientów maszyn...",
"machineNotFound": "Nie znaleziono maszyn",
"userDeviceSearch": "Szukaj urządzeń użytkownika",
"userDevicesSearch": "Szukaj urządzeń użytkownika...",
"openMenu": "Otwórz menu",
"resource": "Zasoby",
"title": "Tytuł",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxy zapytań przez HTTPS przy użyciu w pełni kwalifikowanej nazwy domeny.",
"resourceRaw": "Surowy zasób TCP/UDP",
"resourceRawDescription": "Proxy zapytań przez surowe TCP/UDP przy użyciu numeru portu.",
"resourceRawDescriptionCloud": "Żądania proxy nad surowym TCP/UDP przy użyciu numeru portu. Wymaga stron aby połączyć się ze zdalnym węzłem.",
"resourceCreate": "Utwórz zasób",
"resourceCreateDescription": "Wykonaj poniższe kroki, aby utworzyć nowy zasób",
"resourceSeeAll": "Zobacz wszystkie zasoby",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Usuń klucz API",
"apiKeysManage": "Zarządzaj kluczami API",
"apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji",
"provisioningKeysTitle": "Klucz Zaopatrzenia",
"provisioningKeysManage": "Zarządzaj kluczami zaopatrzenia",
"provisioningKeysDescription": "Klucze zaopatrzenia są używane do uwierzytelniania zautomatyzowanego zaopatrzenia twojej organizacji.",
"provisioningManage": "Dostarczanie",
"provisioningDescription": "Zarządzaj kluczami rezerwacji i sprawdzaj oczekujące strony oczekujące na zatwierdzenie.",
"pendingSites": "Witryny oczekujące",
"siteApproveSuccess": "Witryna została pomyślnie zatwierdzona",
"siteApproveError": "Błąd zatwierdzania witryny",
"provisioningKeys": "Klucze Zaopatrzenia",
"searchProvisioningKeys": "Szukaj kluczy zaopatrzenia...",
"provisioningKeysAdd": "Wygeneruj klucz zaopatrzenia",
"provisioningKeysErrorDelete": "Błąd podczas usuwania klucza zaopatrzenia",
"provisioningKeysErrorDeleteMessage": "Błąd podczas usuwania klucza zaopatrzenia",
"provisioningKeysQuestionRemove": "Czy na pewno chcesz usunąć ten klucz rezerwacji z organizacji?",
"provisioningKeysMessageRemove": "Po usunięciu, klucz nie może być już używany do tworzenia witryny.",
"provisioningKeysDeleteConfirm": "Potwierdź usunięcie klucza zaopatrzenia",
"provisioningKeysDelete": "Usuń klucz zaopatrzenia",
"provisioningKeysCreate": "Wygeneruj klucz zaopatrzenia",
"provisioningKeysCreateDescription": "Wygeneruj nowy klucz tworzenia rezerw dla organizacji",
"provisioningKeysSeeAll": "Zobacz wszystkie klucze rezerwacji",
"provisioningKeysSave": "Zapisz klucz zaopatrzenia",
"provisioningKeysSaveDescription": "Możesz to zobaczyć tylko raz. Skopiuj je do bezpiecznego miejsca.",
"provisioningKeysErrorCreate": "Błąd podczas tworzenia klucza zaopatrzenia",
"provisioningKeysList": "Nowy klucz rezerwacji",
"provisioningKeysMaxBatchSize": "Maksymalny rozmiar partii",
"provisioningKeysUnlimitedBatchSize": "Nieograniczony rozmiar partii (bez limitu)",
"provisioningKeysMaxBatchUnlimited": "Nieograniczona",
"provisioningKeysMaxBatchSizeInvalid": "Wprowadź poprawny maksymalny rozmiar partii (11 000,000).",
"provisioningKeysValidUntil": "Ważny do",
"provisioningKeysValidUntilHint": "Pozostaw puste, aby nie wygasnąć.",
"provisioningKeysValidUntilInvalid": "Wprowadź prawidłową datę i godzinę.",
"provisioningKeysNumUsed": "Używane czasy",
"provisioningKeysLastUsed": "Ostatnio używane",
"provisioningKeysNoExpiry": "Brak wygaśnięcia",
"provisioningKeysNeverUsed": "Nigdy",
"provisioningKeysEdit": "Edytuj klucz zaopatrzenia",
"provisioningKeysEditDescription": "Zaktualizuj maksymalny rozmiar partii i czas wygaśnięcia dla tego klucza.",
"provisioningKeysApproveNewSites": "Zatwierdź nowe witryny",
"provisioningKeysApproveNewSitesDescription": "Automatycznie zatwierdzaj witryny, które rejestrują się za pomocą tego klucza.",
"provisioningKeysUpdateError": "Błąd podczas aktualizacji klucza zaopatrzenia",
"provisioningKeysUpdated": "Klucz zaopatrzenia zaktualizowany",
"provisioningKeysUpdatedDescription": "Twoje zmiany zostały zapisane.",
"provisioningKeysBannerTitle": "Klucze Zaopatrzenia witryny",
"provisioningKeysBannerDescription": "Wygeneruj klucz tworzenia rezerw i użyj go z konektorem Newt do automatycznego tworzenia witryn przy pierwszym uruchomieniu — nie ma potrzeby ustawiania oddzielnych poświadczeń dla każdej witryny.",
"provisioningKeysBannerButtonText": "Dowiedz się więcej",
"pendingSitesBannerTitle": "Witryny oczekujące",
"pendingSitesBannerDescription": "Witryny, które łączą się przy użyciu klucza zaopatrzenia, pojawiają się tutaj, aby przejrzeć. Zatwierdź każdą witrynę, zanim stanie się aktywna i uzyska dostęp do twoich zasobów.",
"pendingSitesBannerButtonText": "Dowiedz się więcej",
"apiKeysSettings": "Ustawienia {apiKeyName}",
"userTitle": "Zarządzaj wszystkimi użytkownikami",
"userDescription": "Zobacz i zarządzaj wszystkimi użytkownikami w systemie",
@@ -508,9 +562,12 @@
"userSaved": "Użytkownik zapisany",
"userSavedDescription": "Użytkownik został zaktualizowany.",
"autoProvisioned": "Przesłane automatycznie",
"autoProvisionSettings": "Ustawienia automatycznego dostarczania",
"autoProvisionedDescription": "Pozwól temu użytkownikowi na automatyczne zarządzanie przez dostawcę tożsamości",
"accessControlsDescription": "Zarządzaj tym, do czego użytkownik ma dostęp i co może robić w organizacji",
"accessControlsSubmit": "Zapisz kontrole dostępu",
"singleRolePerUserPlanNotice": "Twój plan obsługuje tylko jedną rolę na użytkownika.",
"singleRolePerUserEditionNotice": "Ta edycja obsługuje tylko jedną rolę na użytkownika.",
"roles": "Role",
"accessUsersRoles": "Zarządzaj użytkownikami i rolami",
"accessUsersRolesDescription": "Zaproś użytkowników i dodaj je do ról do zarządzania dostępem do organizacji",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Nie udało się przełączyć zasobu",
"resourcesErrorUpdateDescription": "Wystąpił błąd podczas aktualizacji zasobu",
"access": "Dostęp",
"accessControl": "Kontrola dostępu",
"shareLink": "Link udostępniania {resource}",
"resourceSelect": "Wybierz zasób",
"shareLinks": "Linki udostępniania",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rola usunięta",
"accessRoleRemovedDescription": "Rola została pomyślnie usunięta.",
"accessRoleRequiredRemove": "Przed usunięciem tej roli, wybierz nową rolę do której zostaną przeniesieni obecni członkowie.",
"network": "Sieć",
"manage": "Zarządzaj",
"sitesNotFound": "Nie znaleziono witryn.",
"pangolinServerAdmin": "Administrator serwera - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Ups! Strona, której szukasz, nie istnieje.",
"overview": "Przegląd",
"home": "Strona główna",
"accessControl": "Kontrola dostępu",
"settings": "Ustawienia",
"usersAll": "Wszyscy użytkownicy",
"license": "Licencja",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Pobierz użytkownika",
"actionGetOrgUser": "Pobierz użytkownika organizacji",
"actionListOrgDomains": "Lista domen organizacji",
"actionGetDomain": "Pobierz domenę",
"actionCreateOrgDomain": "Utwórz domenę",
"actionUpdateOrgDomain": "Aktualizuj domenę",
"actionDeleteOrgDomain": "Usuń domenę",
"actionGetDNSRecords": "Pobierz rekordy DNS",
"actionRestartOrgDomain": "Zrestartuj domenę",
"actionCreateSite": "Utwórz witrynę",
"actionDeleteSite": "Usuń witrynę",
"actionGetSite": "Pobierz witrynę",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Wprowadź token konfiguracji z konsoli serwera.",
"setupTokenRequired": "Wymagany jest token konfiguracji",
"actionUpdateSite": "Aktualizuj witrynę",
"actionResetSiteBandwidth": "Zresetuj przepustowość organizacji",
"actionListSiteRoles": "Lista dozwolonych ról witryny",
"actionCreateResource": "Utwórz zasób",
"actionDeleteResource": "Usuń zasób",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Usuń użytkownika",
"actionListUsers": "Lista użytkowników",
"actionAddUserRole": "Dodaj rolę użytkownika",
"actionSetUserOrgRoles": "Ustaw role użytkownika",
"actionGenerateAccessToken": "Wygeneruj token dostępu",
"actionDeleteAccessToken": "Usuń token dostępu",
"actionListAccessTokens": "Lista tokenów dostępu",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Prywatny",
"sidebarAccessControl": "Kontrola dostępu",
"sidebarLogsAndAnalytics": "Logi i Analityki",
"sidebarTeam": "Drużyna",
"sidebarUsers": "Użytkownicy",
"sidebarAdmin": "Administrator",
"sidebarInvitations": "Zaproszenia",
"sidebarRoles": "Role",
"sidebarShareableLinks": "Linki",
"sidebarApiKeys": "Klucze API",
"sidebarProvisioning": "Dostarczanie",
"sidebarSettings": "Ustawienia",
"sidebarAllUsers": "Wszyscy użytkownicy",
"sidebarIdentityProviders": "Dostawcy tożsamości",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Dziennik & Analityka",
"sidebarBluePrints": "Schematy",
"sidebarOrganization": "Organizacja",
"sidebarManagement": "Zarządzanie",
"sidebarBillingAndLicenses": "Płatność i licencje",
"sidebarLogsAnalytics": "Analityka",
"blueprints": "Schematy",
@@ -1289,7 +1358,6 @@
"parsedContents": "Przetworzona zawartość (tylko do odczytu)",
"enableDockerSocket": "Włącz schemat dokera",
"enableDockerSocketDescription": "Włącz etykietowanie kieszeni dokującej dla etykiet schematów. Ścieżka do gniazda musi być dostarczona do Newt.",
"enableDockerSocketLink": "Dowiedz się więcej",
"viewDockerContainers": "Zobacz kontenery dokujące",
"containersIn": "Pojemniki w {siteName}",
"selectContainerDescription": "Wybierz dowolny kontener do użycia jako nazwa hosta dla tego celu. Kliknij port, aby użyć portu.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Przestrzeń nazw: {namespace}",
"domainPickerShowMore": "Pokaż więcej",
"regionSelectorTitle": "Wybierz region",
"domainPickerRemoteExitNodeWarning": "Podane domeny nie są obsługiwane, gdy witryny łączą się ze zdalnymi węzłami wyjścia. Aby zasoby były dostępne w węzłach zdalnych, użyj domeny niestandardowej.",
"regionSelectorInfo": "Wybór regionu pomaga nam zapewnić lepszą wydajność dla Twojej lokalizacji. Nie musisz być w tym samym regionie co Twój serwer.",
"regionSelectorPlaceholder": "Wybierz region",
"regionSelectorComingSoon": "Wkrótce dostępne",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Powiadomienie o dostępności funkcji",
"billingFeatureLossDescription": "Po obniżeniu wartości funkcje niedostępne w nowym planie zostaną automatycznie wyłączone. Niektóre ustawienia i konfiguracje mogą zostać utracone. Zapoznaj się z matrycą cenową, aby zrozumieć, które funkcje nie będą już dostępne.",
"billingUsageExceedsLimit": "Bieżące użycie ({current}) przekracza limit ({limit})",
"billingPastDueTitle": "Płatność w przeszłości",
"billingPastDueDescription": "Twoja płatność jest zaległa. Zaktualizuj metodę płatności, aby kontynuować korzystanie z funkcji aktualnego planu. Jeśli nie zostanie rozwiązana, Twoja subskrypcja zostanie anulowana i zostaniesz przywrócony do darmowego poziomu.",
"billingUnpaidTitle": "Subskrypcja niezapłacona",
"billingUnpaidDescription": "Twoja subskrypcja jest niezapłacona i została przywrócona do darmowego poziomu. Zaktualizuj swoją metodę płatności, aby przywrócić subskrypcję.",
"billingIncompleteTitle": "Płatność niezakończona",
"billingIncompleteDescription": "Twoja płatność jest niekompletna. Ukończ proces płatności, aby aktywować subskrypcję.",
"billingIncompleteExpiredTitle": "Płatność wygasła",
"billingIncompleteExpiredDescription": "Twoja płatność nigdy nie została zakończona i wygasła. Zostałeś przywrócony do darmowego poziomu. Zapisz się ponownie, aby przywrócić dostęp do płatnych funkcji.",
"billingManageSubscription": "Zarządzaj subskrypcją",
"billingResolvePaymentIssue": "Rozwiąż problem z płatnościami przed aktualizacją lub obniżeniem oceny",
"signUpTerms": {
"IAgreeToThe": "Zgadzam się z",
"termsOfService": "warunkami usługi",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Czas w sekundach",
"requireDeviceApproval": "Wymagaj zatwierdzenia urządzenia",
"requireDeviceApprovalDescription": "Użytkownicy o tej roli potrzebują nowych urządzeń zatwierdzonych przez administratora, zanim będą mogli połączyć się i uzyskać dostęp do zasobów.",
"sshAccess": "Dostęp SSH",
"roleAllowSsh": "Zezwalaj na SSH",
"roleAllowSshAllow": "Zezwól",
"roleAllowSshDisallow": "Nie zezwalaj",
"roleAllowSshDescription": "Zezwalaj użytkownikom z tej roli na łączenie się z zasobami za pomocą SSH. Gdy wyłączone, rola nie może korzystać z dostępu SSH.",
"sshSudoMode": "Dostęp Sudo",
"sshSudoModeNone": "Brak",
"sshSudoModeNoneDescription": "Użytkownik nie może uruchamiać poleceń z sudo.",
"sshSudoModeFull": "Pełne Sudo",
"sshSudoModeFullDescription": "Użytkownik może uruchomić dowolne polecenie z sudo.",
"sshSudoModeCommands": "Polecenia",
"sshSudoModeCommandsDescription": "Użytkownik może uruchamiać tylko określone polecenia z sudo.",
"sshSudo": "Zezwól na sudo",
"sshSudoCommands": "Komendy Sudo",
"sshSudoCommandsDescription": "Lista poleceń oddzielonych przecinkami, które użytkownik może uruchamiać z sudo.",
"sshCreateHomeDir": "Utwórz katalog domowy",
"sshUnixGroups": "Grupy Unix",
"sshUnixGroupsDescription": "Oddzielone przecinkami grupy Unix, aby dodać użytkownika do docelowego hosta.",
"retryAttempts": "Próby Ponowienia",
"expectedResponseCodes": "Oczekiwane Kody Odpowiedzi",
"expectedResponseCodesDescription": "Kod statusu HTTP, który wskazuje zdrowy status. Jeśli pozostanie pusty, uznaje się 200-300 za zdrowy.",
@@ -1851,6 +1948,40 @@
"exitNode": "Węzeł Wyjściowy",
"country": "Kraj",
"rulesMatchCountry": "Obecnie bazuje na adresie IP źródła",
"region": "Region",
"selectRegion": "Wybierz region",
"searchRegions": "Szukaj regionów...",
"noRegionFound": "Nie znaleziono regionu.",
"rulesMatchRegion": "Wybierz regionalną grupę krajów",
"rulesErrorInvalidRegion": "Nieprawidłowy region",
"rulesErrorInvalidRegionDescription": "Proszę wybrać prawidłowy region.",
"regionAfrica": "Afryka",
"regionNorthernAfrica": "Afryka Północna",
"regionEasternAfrica": "Afryka Wschodnia",
"regionMiddleAfrica": "Afryka Środkowa",
"regionSouthernAfrica": "Afryka Południowa",
"regionWesternAfrica": "Afryka Zachodnia",
"regionAmericas": "Ameryka",
"regionCaribbean": "Karaiby",
"regionCentralAmerica": "Ameryka Środkowa",
"regionSouthAmerica": "Ameryka Południowej",
"regionNorthernAmerica": "Ameryka Północna",
"regionAsia": "Akwakultura",
"regionCentralAsia": "Azja Środkowa",
"regionEasternAsia": "Azja Wschodnia",
"regionSouthEasternAsia": "Azja Południowo-Wschodnia",
"regionSouthernAsia": "Azja Południowa",
"regionWesternAsia": "Azja Zachodnia",
"regionEurope": "Europa",
"regionEasternEurope": "Europa Wschodnia",
"regionNorthernEurope": "Europa Północna",
"regionSouthernEurope": "Europa Południowa",
"regionWesternEurope": "Europa Zachodnia",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Australia i Nowa Zelandia",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Zarządzane Samodzielnie-Hostingowane",
"description": "Większa niezawodność i niska konserwacja serwera Pangolin z dodatkowymi dzwonkami i sygnałami",
@@ -1899,6 +2030,25 @@
"invalidValue": "Nieprawidłowa wartość",
"idpTypeLabel": "Typ dostawcy tożsamości",
"roleMappingExpressionPlaceholder": "np. zawiera(grupy, 'admin') && 'Admin' || 'Członek'",
"roleMappingModeFixedRoles": "Stałe role",
"roleMappingModeMappingBuilder": "Konstruktor mapowania",
"roleMappingModeRawExpression": "Surowe wyrażenie",
"roleMappingFixedRolesPlaceholderSelect": "Wybierz jedną lub więcej ról",
"roleMappingFixedRolesPlaceholderFreeform": "Wpisz nazwy ról (dopasowanie na organizację)",
"roleMappingFixedRolesDescriptionSameForAll": "Przypisz tę samą rolę do każdego automatycznie udostępnionego użytkownika.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "W przypadku domyślnych zasad nazwy ról typu które istnieją w każdej organizacji, gdzie użytkownicy są zapisywani. Nazwy muszą się dokładnie zgadzać.",
"roleMappingClaimPath": "Ścieżka przejęcia",
"roleMappingClaimPathPlaceholder": "grupy",
"roleMappingClaimPathDescription": "Ścieżka w payloadzie tokenów, która zawiera wartości źródłowe (np. grupy).",
"roleMappingMatchValue": "Wartość dopasowania",
"roleMappingAssignRoles": "Przypisz role",
"roleMappingAddMappingRule": "Dodaj regułę mapowania",
"roleMappingRawExpressionResultDescription": "Wyrażenie musi ocenić do tablicy ciągów lub ciągów.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Wyrażenie musi oceniać ciąg znaków (pojedyncza nazwa).",
"roleMappingMatchValuePlaceholder": "Wartość dopasowania (na przykład: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Wpisz nazwy ról (aktywizacja na org)",
"roleMappingBuilderFreeformRowHint": "Nazwy ról muszą pasować do roli w każdej organizacji docelowej.",
"roleMappingRemoveRule": "Usuń",
"idpGoogleConfiguration": "Konfiguracja Google",
"idpGoogleConfigurationDescription": "Skonfiguruj dane logowania Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Jak długo zachować dzienniki dostępu",
"logRetentionActionLabel": "Zachowanie dziennika akcji",
"logRetentionActionDescription": "Jak długo zachować dzienniki akcji",
"logRetentionConnectionLabel": "Zachowanie dziennika połączeń",
"logRetentionConnectionDescription": "Jak długo zachować dzienniki połączeń",
"logRetentionDisabled": "Wyłączone",
"logRetention3Days": "3 dni",
"logRetention7Days": "7 dni",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Koniec następnego roku",
"actionLogsDescription": "Zobacz historię działań wykonywanych w tej organizacji",
"accessLogsDescription": "Wyświetl prośby o autoryzację dostępu do zasobów w tej organizacji",
"licenseRequiredToUse": "Do korzystania z tej funkcji wymagana jest licencja <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> . Ta funkcja jest również dostępna w <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> jest wymagany do korzystania z tej funkcji. Ta funkcja jest również dostępna w <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Dzienniki połączeń",
"connectionLogsDescription": "Wyświetl dzienniki połączeń dla tuneli w tej organizacji",
"sidebarLogsConnection": "Dzienniki połączeń",
"sidebarLogsStreaming": "Strumieniowanie",
"sourceAddress": "Adres źródłowy",
"destinationAddress": "Adres docelowy",
"duration": "Czas trwania",
"licenseRequiredToUse": "Do korzystania z tej funkcji wymagana jest licencja <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lub <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> . <bookADemoLink>Zarezerwuj wersję demonstracyjną lub wersję próbną POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> jest wymagany do korzystania z tej funkcji. Ta funkcja jest również dostępna w <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Zarezerwuj demo lub okres próbny POC</bookADemoLink>.",
"certResolver": "Rozwiązywanie certyfikatów",
"certResolverDescription": "Wybierz resolver certyfikatów do użycia dla tego zasobu.",
"selectCertResolver": "Wybierz Resolver certyfikatów",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Kontrola dostępu",
"editInternalResourceDialogAccessControlDescription": "Kontroluj, które role, użytkownicy i klienci maszyn mają dostęp do tego zasobu po połączeniu. Administratorzy zawsze mają dostęp.",
"editInternalResourceDialogPortRangeValidationError": "Zakres portów musi być \"*\" dla wszystkich portów lub listą portów i zakresów oddzielonych przecinkami (np. \"80,443,8000-9000\"). Porty muszą znajdować się w przedziale od 1 do 65535.",
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon Lokalizacja",
"internalResourceAuthDaemonStrategyDescription": "Wybierz, gdzie działa demon uwierzytelniania SSH: na stronie (Newt) lub na zdalnym serwerze.",
"internalResourceAuthDaemonDescription": "Uwierzytelnianie SSH obsługuje podpisywanie klucza SSH i uwierzytelnianie PAM dla tego zasobu. Wybierz, czy działa na stronie (Newt), czy na oddzielnym serwerze zdalnym. Zobacz <docsLink>dokumentację</docsLink> dla więcej.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Wybierz strategię",
"internalResourceAuthDaemonStrategyLabel": "Lokalizacja",
"internalResourceAuthDaemonSite": "Na stronie",
"internalResourceAuthDaemonSiteDescription": "Demon Auth działa na stronie (nowy).",
"internalResourceAuthDaemonRemote": "Zdalny host",
"internalResourceAuthDaemonRemoteDescription": "Demon Auth działa na serwerze, który nie jest stroną.",
"internalResourceAuthDaemonPort": "Port Daemon (opcjonalnie)",
"orgAuthWhatsThis": "Gdzie mogę znaleźć swój identyfikator organizacji?",
"learnMore": "Dowiedz się więcej",
"backToHome": "Wróć do strony głównej",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Włącz zatwierdzanie urządzenia",
"approvalsEmptyStateStep2Description": "Edytuj rolę i włącz opcję \"Wymagaj zatwierdzenia urządzenia\". Użytkownicy z tą rolą będą potrzebowali zatwierdzenia administratora dla nowych urządzeń.",
"approvalsEmptyStatePreviewDescription": "Podgląd: Gdy włączone, oczekujące prośby o sprawdzenie pojawią się tutaj",
"approvalsEmptyStateButtonText": "Zarządzaj rolami"
"approvalsEmptyStateButtonText": "Zarządzaj rolami",
"domainErrorTitle": "Mamy problem z weryfikacją Twojej domeny",
"idpAdminAutoProvisionPoliciesTabHint": "Skonfiguruj mapowanie ról i zasady organizacji na karcie <policiesTabLink>Auto Provivision Settings</policiesTabLink>.",
"streamingTitle": "Strumieniowanie wydarzeń",
"streamingDescription": "Wydarzenia strumieniowe z Twojej organizacji do zewnętrznych miejsc przeznaczenia w czasie rzeczywistym.",
"streamingUnnamedDestination": "Miejsce przeznaczenia bez nazwy",
"streamingNoUrlConfigured": "Brak skonfigurowanego adresu URL",
"streamingAddDestination": "Dodaj cel",
"streamingHttpWebhookTitle": "Webhook HTTP",
"streamingHttpWebhookDescription": "Wyślij zdarzenia do dowolnego punktu końcowego HTTP z elastycznym uwierzytelnianiem i szablonem.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Zdarzenia strumieniowe do magazynu obiektów kompatybilnych z S3. Już wkrótce.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Przekaż wydarzenia bezpośrednio do Twojego konta Datadog. Już wkrótce.",
"streamingTypePickerDescription": "Wybierz typ docelowy, aby rozpocząć.",
"streamingFailedToLoad": "Nie udało się załadować miejsc docelowych",
"streamingUnexpectedError": "Wystąpił nieoczekiwany błąd.",
"streamingFailedToUpdate": "Nie udało się zaktualizować miejsca docelowego",
"streamingDeletedSuccess": "Cel usunięty pomyślnie",
"streamingFailedToDelete": "Nie udało się usunąć miejsca docelowego",
"streamingDeleteTitle": "Usuń cel",
"streamingDeleteButtonText": "Usuń cel",
"streamingDeleteDialogAreYouSure": "Czy na pewno chcesz usunąć",
"streamingDeleteDialogThisDestination": "ten cel",
"streamingDeleteDialogPermanentlyRemoved": "? Wszystkie konfiguracje zostaną trwale usunięte.",
"httpDestEditTitle": "Edytuj cel",
"httpDestAddTitle": "Dodaj cel HTTP",
"httpDestEditDescription": "Aktualizuj konfigurację dla tego celu przesyłania strumieniowego zdarzeń HTTP.",
"httpDestAddDescription": "Skonfiguruj nowy punkt końcowy HTTP, aby otrzymywać wydarzenia organizacji.",
"httpDestTabSettings": "Ustawienia",
"httpDestTabHeaders": "Nagłówki",
"httpDestTabBody": "Ciało",
"httpDestTabLogs": "Logi",
"httpDestNamePlaceholder": "Mój cel HTTP",
"httpDestUrlLabel": "Adres docelowy",
"httpDestUrlErrorHttpRequired": "Adres URL musi używać http lub https",
"httpDestUrlErrorHttpsRequired": "HTTPS jest wymagany dla wdrożenia w chmurze",
"httpDestUrlErrorInvalid": "Wprowadź poprawny adres URL (np. https://example.com/webhook)",
"httpDestAuthTitle": "Uwierzytelnianie",
"httpDestAuthDescription": "Wybierz sposób uwierzytelniania żądań do Twojego punktu końcowego.",
"httpDestAuthNoneTitle": "Brak uwierzytelniania",
"httpDestAuthNoneDescription": "Wysyła żądania bez nagłówka autoryzacji.",
"httpDestAuthBearerTitle": "Token Bearer",
"httpDestAuthBearerDescription": "Dodaje autoryzację: nagłówek Bearer <token> do każdego żądania.",
"httpDestAuthBearerPlaceholder": "Twój klucz API lub token",
"httpDestAuthBasicTitle": "Podstawowa Autoryzacja",
"httpDestAuthBasicDescription": "Dodaje Autoryzacja: Nagłówek Basic <credentials> . Podaj poświadczenia jako nazwę użytkownika: hasło.",
"httpDestAuthBasicPlaceholder": "Nazwa użytkownika:hasło",
"httpDestAuthCustomTitle": "Niestandardowy nagłówek",
"httpDestAuthCustomDescription": "Określ niestandardową nazwę nagłówka HTTP i wartość dla uwierzytelniania (np. X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Nazwa nagłówka (np. klucz X-API)",
"httpDestAuthCustomHeaderValuePlaceholder": "Wartość nagłówka",
"httpDestCustomHeadersTitle": "Niestandardowe nagłówki HTTP",
"httpDestCustomHeadersDescription": "Dodaj własne nagłówki do każdego wychodzącego żądania. Przydatne dla tokenów statycznych lub niestandardowego typu zawartości. Domyślnie Content-Type: aplikacja/json jest wysyłane.",
"httpDestNoHeadersConfigured": "Nie skonfigurowano nagłówków niestandardowych. Kliknij \"Dodaj nagłówek\", aby go dodać.",
"httpDestHeaderNamePlaceholder": "Nazwa nagłówka",
"httpDestHeaderValuePlaceholder": "Wartość",
"httpDestAddHeader": "Dodaj nagłówek",
"httpDestBodyTemplateTitle": "Własny szablon ciała",
"httpDestBodyTemplateDescription": "Kontroluj strukturę JSON wysyłaną do Twojego punktu końcowego. Jeśli wyłączone, dla każdego zdarzenia wysyłany jest domyślny obiekt JSON.",
"httpDestEnableBodyTemplate": "Włącz niestandardowy szablon ciała",
"httpDestBodyTemplateLabel": "Szablon ciała (JSON)",
"httpDestBodyTemplateHint": "Użyj zmiennych szablonu do odniesienia pól zdarzeń w twoim payloadzie.",
"httpDestPayloadFormatTitle": "Format obciążenia",
"httpDestPayloadFormatDescription": "Jak zdarzenia są serializowane w każdym organie żądania.",
"httpDestFormatJsonArrayTitle": "Tablica JSON",
"httpDestFormatJsonArrayDescription": "Jedna prośba na partię, treść jest tablicą JSON. Kompatybilna z najbardziej ogólnymi webhookami i Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Jedno żądanie na partię, ciałem jest plik JSON rozdzielony na newline-delimited — jeden obiekt na wiersz, bez tablicy zewnętrznej. Wymagane przez Splunk HEC, Elastic / OpenSesearch i Grafana Loki.",
"httpDestFormatSingleTitle": "Jedno wydarzenie na żądanie",
"httpDestFormatSingleDescription": "Wysyła oddzielny POST HTTP dla każdego zdarzenia. Użyj tylko dla punktów końcowych, które nie mogą obsługiwać partii.",
"httpDestLogTypesTitle": "Typy logów",
"httpDestLogTypesDescription": "Wybierz, które typy logów są przekazywane do tego miejsca docelowego. Tylko włączone typy logów będą strumieniowane.",
"httpDestAccessLogsTitle": "Logi dostępu",
"httpDestAccessLogsDescription": "Próby dostępu do zasobów, w tym uwierzytelnione i odrzucone żądania.",
"httpDestActionLogsTitle": "Dzienniki działań",
"httpDestActionLogsDescription": "Działania administracyjne wykonywane przez użytkowników w organizacji.",
"httpDestConnectionLogsTitle": "Dzienniki połączeń",
"httpDestConnectionLogsDescription": "Zdarzenia związane z miejscem i tunelem, w tym połączenia i rozłączenia.",
"httpDestRequestLogsTitle": "Dzienniki żądań",
"httpDestRequestLogsDescription": "Logi żądań HTTP dla zasobów proxy, w tym metody, ścieżki i kodu odpowiedzi.",
"httpDestSaveChanges": "Zapisz zmiany",
"httpDestCreateDestination": "Utwórz cel",
"httpDestUpdatedSuccess": "Cel został pomyślnie zaktualizowany",
"httpDestCreatedSuccess": "Cel został utworzony pomyślnie",
"httpDestUpdateFailed": "Nie udało się zaktualizować miejsca docelowego",
"httpDestCreateFailed": "Nie udało się utworzyć miejsca docelowego"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Criar Link",
"resourcesNotFound": "Nenhum recurso encontrado",
"resourceSearch": "Recursos de pesquisa",
"machineSearch": "Procurar máquinas",
"machinesSearch": "Pesquisar clientes de máquina...",
"machineNotFound": "Nenhuma máquina encontrada",
"userDeviceSearch": "Procurar dispositivos do usuário",
"userDevicesSearch": "Pesquisar dispositivos do usuário...",
"openMenu": "Abrir menu",
"resource": "Recurso",
"title": "Título",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Proxies requests sobre HTTPS usando um nome de domínio totalmente qualificado.",
"resourceRaw": "Recurso TCP/UDP bruto",
"resourceRawDescription": "Proxies solicitações sobre TCP/UDP bruto usando um número de porta.",
"resourceRawDescriptionCloud": "Proxy solicita por TCP/UDP bruto usando um número de porta. Requer que sites se conectem a um nó remoto.",
"resourceCreate": "Criar Recurso",
"resourceCreateDescription": "Siga os passos abaixo para criar um novo recurso",
"resourceSeeAll": "Ver todos os recursos",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Excluir Chave API",
"apiKeysManage": "Gerir Chaves API",
"apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração",
"provisioningKeysTitle": "Chave de provisionamento",
"provisioningKeysManage": "Gerenciar chaves de provisionamento",
"provisioningKeysDescription": "Chaves de provisionamento são usadas para autenticar o provisionamento automatizado do site para sua organização.",
"provisioningManage": "Provisionamento",
"provisioningDescription": "Gerenciar chaves de provisionamento e revisar sites pendentes aguardando aprovação.",
"pendingSites": "Sites pendentes",
"siteApproveSuccess": "Site aprovado com sucesso",
"siteApproveError": "Erro ao aprovar site",
"provisioningKeys": "Posicionando chaves",
"searchProvisioningKeys": "Pesquisar chaves de provisionamento...",
"provisioningKeysAdd": "Gerar chave de provisionamento",
"provisioningKeysErrorDelete": "Erro ao excluir chave de provisionamento",
"provisioningKeysErrorDeleteMessage": "Erro ao excluir chave de provisionamento",
"provisioningKeysQuestionRemove": "Tem certeza de que deseja remover esta chave de provisionamento da organização?",
"provisioningKeysMessageRemove": "Uma vez removida, a chave não pode mais ser usada para o provisionamento do site.",
"provisioningKeysDeleteConfirm": "Confirmar chave de exclusão",
"provisioningKeysDelete": "Apagar chave de provisionamento",
"provisioningKeysCreate": "Gerar chave de provisionamento",
"provisioningKeysCreateDescription": "Gerar uma nova chave de provisionamento para a organização",
"provisioningKeysSeeAll": "Ver todas as chaves provisionadas",
"provisioningKeysSave": "Salvar a chave de provisionamento",
"provisioningKeysSaveDescription": "Você só será capaz de ver esta vez. Copiá-lo para um lugar seguro.",
"provisioningKeysErrorCreate": "Erro ao criar chave de provisionamento",
"provisioningKeysList": "Nova chave de aprovisionamento",
"provisioningKeysMaxBatchSize": "Tamanho máximo do lote",
"provisioningKeysUnlimitedBatchSize": "Tamanho ilimitado em lote (sem limite)",
"provisioningKeysMaxBatchUnlimited": "Ilimitado",
"provisioningKeysMaxBatchSizeInvalid": "Informe um tamanho máximo válido em lote (11,000,000).",
"provisioningKeysValidUntil": "Valido ate",
"provisioningKeysValidUntilHint": "Deixe em branco para nenhuma expiração.",
"provisioningKeysValidUntilInvalid": "Informe uma data e hora válidas.",
"provisioningKeysNumUsed": "Use percentual",
"provisioningKeysLastUsed": "Última utilização",
"provisioningKeysNoExpiry": "Sem vencimento",
"provisioningKeysNeverUsed": "nunca",
"provisioningKeysEdit": "Editar chave de provisionamento",
"provisioningKeysEditDescription": "Atualizar o tamanho máximo do lote e tempo de expiração para esta chave.",
"provisioningKeysApproveNewSites": "Aprovar novos sites",
"provisioningKeysApproveNewSitesDescription": "Aprovar automaticamente sites que se registram com esta chave.",
"provisioningKeysUpdateError": "Erro ao atualizar chave de provisionamento",
"provisioningKeysUpdated": "Chave de provisionamento atualizada",
"provisioningKeysUpdatedDescription": "Suas alterações foram salvas.",
"provisioningKeysBannerTitle": "Chaves de provisionamento do site",
"provisioningKeysBannerDescription": "Gerar uma chave de provisionamento e usá-la com o conector de Newt para criar automaticamente sites na primeira inicialização — não é necessário configurar credenciais separadas para cada site.",
"provisioningKeysBannerButtonText": "Saiba mais",
"pendingSitesBannerTitle": "Sites pendentes",
"pendingSitesBannerDescription": "Sites que conectam usando uma chave de provisionamento aparecem aqui para revisão. Aprovar cada site antes de se tornar ativo e ganhar acesso a seus recursos.",
"pendingSitesBannerButtonText": "Saiba mais",
"apiKeysSettings": "Configurações de {apiKeyName}",
"userTitle": "Gerir Todos os Utilizadores",
"userDescription": "Visualizar e gerir todos os utilizadores no sistema",
@@ -508,9 +562,12 @@
"userSaved": "Usuário salvo",
"userSavedDescription": "O utilizador foi atualizado.",
"autoProvisioned": "Auto provisionado",
"autoProvisionSettings": "Configurações de provisão automática",
"autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade",
"accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização",
"accessControlsSubmit": "Guardar Controlos de Acesso",
"singleRolePerUserPlanNotice": "Seu plano suporta apenas uma função por usuário.",
"singleRolePerUserEditionNotice": "Esta edição suporta apenas uma função por usuário.",
"roles": "Funções",
"accessUsersRoles": "Gerir Utilizadores e Funções",
"accessUsersRolesDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à organização",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Falha ao alternar recurso",
"resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso",
"access": "Acesso",
"accessControl": "Controle de Acesso",
"shareLink": "Link de Compartilhamento {resource}",
"resourceSelect": "Selecionar recurso",
"shareLinks": "Links de Compartilhamento",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Função removida",
"accessRoleRemovedDescription": "A função foi removida com sucesso.",
"accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.",
"network": "Rede",
"manage": "Gerir",
"sitesNotFound": "Nenhum site encontrado.",
"pangolinServerAdmin": "Administrador do Servidor - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Ops! A página que você está procurando não existe.",
"overview": "Visão Geral",
"home": "Início",
"accessControl": "Controle de Acesso",
"settings": "Configurações",
"usersAll": "Todos os Utilizadores",
"license": "Licença",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Obter Usuário",
"actionGetOrgUser": "Obter Utilizador da Organização",
"actionListOrgDomains": "Listar Domínios da Organização",
"actionGetDomain": "Obter domínio",
"actionCreateOrgDomain": "Criar domínio",
"actionUpdateOrgDomain": "Atualizar domínio",
"actionDeleteOrgDomain": "Excluir domínio",
"actionGetDNSRecords": "Obter registros de DNS",
"actionRestartOrgDomain": "Reiniciar domínio",
"actionCreateSite": "Criar Site",
"actionDeleteSite": "Eliminar Site",
"actionGetSite": "Obter Site",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Digite o token de configuração do console do servidor.",
"setupTokenRequired": "Token de configuração é necessário",
"actionUpdateSite": "Atualizar Site",
"actionResetSiteBandwidth": "Redefinir banda da organização",
"actionListSiteRoles": "Listar Funções Permitidas do Site",
"actionCreateResource": "Criar Recurso",
"actionDeleteResource": "Eliminar Recurso",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Remover Utilizador",
"actionListUsers": "Listar Utilizadores",
"actionAddUserRole": "Adicionar Função ao Utilizador",
"actionSetUserOrgRoles": "Definir funções do usuário",
"actionGenerateAccessToken": "Gerar Token de Acesso",
"actionDeleteAccessToken": "Eliminar Token de Acesso",
"actionListAccessTokens": "Listar Tokens de Acesso",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Privado",
"sidebarAccessControl": "Controle de Acesso",
"sidebarLogsAndAnalytics": "Registros e Análises",
"sidebarTeam": "Equipe",
"sidebarUsers": "Utilizadores",
"sidebarAdmin": "Administrador",
"sidebarInvitations": "Convites",
"sidebarRoles": "Papéis",
"sidebarShareableLinks": "Links",
"sidebarApiKeys": "Chaves API",
"sidebarProvisioning": "Provisionamento",
"sidebarSettings": "Configurações",
"sidebarAllUsers": "Todos os utilizadores",
"sidebarIdentityProviders": "Provedores de identidade",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Registo & Análise",
"sidebarBluePrints": "Diagramas",
"sidebarOrganization": "Organização",
"sidebarManagement": "Gestão",
"sidebarBillingAndLicenses": "Faturamento e Licenças",
"sidebarLogsAnalytics": "Análises",
"blueprints": "Diagramas",
@@ -1289,7 +1358,6 @@
"parsedContents": "Conteúdo analisado (Somente Leitura)",
"enableDockerSocket": "Habilitar o Diagrama Docker",
"enableDockerSocketDescription": "Ativar a scraping de rótulo Docker para rótulos de diagramas. Caminho de Socket deve ser fornecido para Newt.",
"enableDockerSocketLink": "Saiba mais",
"viewDockerContainers": "Ver contêineres Docker",
"containersIn": "Contêineres em {siteName}",
"selectContainerDescription": "Selecione qualquer contêiner para usar como hostname para este alvo. Clique em uma porta para usar uma porta.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Namespace: {namespace}",
"domainPickerShowMore": "Mostrar Mais",
"regionSelectorTitle": "Selecionar Região",
"domainPickerRemoteExitNodeWarning": "Domínios fornecidos não são suportados quando os sites se conectam a nós de saída remota. Para recursos disponíveis em nós remotos, use um domínio personalizado.",
"regionSelectorInfo": "Selecionar uma região nos ajuda a fornecer melhor desempenho para sua localização. Você não precisa estar na mesma região que seu servidor.",
"regionSelectorPlaceholder": "Escolher uma região",
"regionSelectorComingSoon": "Em breve",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Aviso de disponibilidade de recursos",
"billingFeatureLossDescription": "Ao fazer o downgrading, recursos não disponíveis no novo plano serão desativados automaticamente. Algumas configurações e configurações podem ser perdidas. Por favor, revise a matriz de preços para entender quais características não estarão mais disponíveis.",
"billingUsageExceedsLimit": "Uso atual ({current}) excede o limite ({limit})",
"billingPastDueTitle": "Pagamento passado devido",
"billingPastDueDescription": "Seu pagamento está vencido. Por favor, atualize seu método de pagamento para continuar usando os recursos do seu plano atual. Se não for resolvido, sua assinatura será cancelada e você será revertido para o nível gratuito.",
"billingUnpaidTitle": "Assinatura não paga",
"billingUnpaidDescription": "Sua assinatura não foi paga e você voltou para o nível gratuito. Atualize o seu método de pagamento para restaurar sua assinatura.",
"billingIncompleteTitle": "Pagamento Incompleto",
"billingIncompleteDescription": "Seu pagamento está incompleto. Por favor, complete o processo de pagamento para ativar sua assinatura.",
"billingIncompleteExpiredTitle": "Pagamento expirado",
"billingIncompleteExpiredDescription": "Seu pagamento nunca foi concluído e expirou. Você foi revertido para o nível gratuito. Por favor, inscreva-se novamente para restaurar o acesso a recursos pagos.",
"billingManageSubscription": "Gerencie sua assinatura",
"billingResolvePaymentIssue": "Por favor, resolva seu problema de pagamento antes de atualizar ou rebaixar",
"signUpTerms": {
"IAgreeToThe": "Concordo com",
"termsOfService": "os termos de serviço",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "O tempo está em segundos",
"requireDeviceApproval": "Exigir aprovação do dispositivo",
"requireDeviceApprovalDescription": "Usuários com esta função precisam de novos dispositivos aprovados por um administrador antes que eles possam se conectar e acessar recursos.",
"sshAccess": "Acesso SSH",
"roleAllowSsh": "Permitir SSH",
"roleAllowSshAllow": "Autorizar",
"roleAllowSshDisallow": "Anular",
"roleAllowSshDescription": "Permitir que usuários com esta função se conectem a recursos via SSH. Quando desativado, a função não pode usar o acesso SSH.",
"sshSudoMode": "Acesso Sudo",
"sshSudoModeNone": "Nenhuma",
"sshSudoModeNoneDescription": "O usuário não pode executar comandos com o sudo.",
"sshSudoModeFull": "Sudo Completo",
"sshSudoModeFullDescription": "O usuário pode executar qualquer comando com sudo.",
"sshSudoModeCommands": "Comandos",
"sshSudoModeCommandsDescription": "Usuário só pode executar os comandos especificados com sudo.",
"sshSudo": "Permitir sudo",
"sshSudoCommands": "Comandos Sudo",
"sshSudoCommandsDescription": "Lista separada por vírgulas de comandos que o usuário pode executar com sudo.",
"sshCreateHomeDir": "Criar Diretório Inicial",
"sshUnixGroups": "Grupos Unix",
"sshUnixGroupsDescription": "Grupos Unix separados por vírgulas para adicionar o usuário no host alvo.",
"retryAttempts": "Tentativas de Repetição",
"expectedResponseCodes": "Códigos de Resposta Esperados",
"expectedResponseCodesDescription": "Código de status HTTP que indica estado saudável. Se deixado em branco, 200-300 é considerado saudável.",
@@ -1851,6 +1948,40 @@
"exitNode": "Nodo de Saída",
"country": "País",
"rulesMatchCountry": "Atualmente baseado no IP de origem",
"region": "Região",
"selectRegion": "Selecionar região",
"searchRegions": "Procurar regiões...",
"noRegionFound": "Nenhuma região encontrada.",
"rulesMatchRegion": "Selecione um grupo regional de países",
"rulesErrorInvalidRegion": "Região inválida",
"rulesErrorInvalidRegionDescription": "Por favor, selecione uma região válida.",
"regionAfrica": "África",
"regionNorthernAfrica": "África do Norte",
"regionEasternAfrica": "África Oriental",
"regionMiddleAfrica": "África Média",
"regionSouthernAfrica": "África Austral",
"regionWesternAfrica": "África Ocidental",
"regionAmericas": "Américas",
"regionCaribbean": "Caribe",
"regionCentralAmerica": "América Central",
"regionSouthAmerica": "América do Sul",
"regionNorthernAmerica": "América do Norte",
"regionAsia": "Ásia",
"regionCentralAsia": "Ásia Central",
"regionEasternAsia": "Ásia Oriental",
"regionSouthEasternAsia": "Sudeste da Ásia",
"regionSouthernAsia": "Sudeste da Ásia",
"regionWesternAsia": "Ásia Ocidental",
"regionEurope": "Europa",
"regionEasternEurope": "Europa Oriental",
"regionNorthernEurope": "Europa do Norte",
"regionSouthernEurope": "Europa do Sul",
"regionWesternEurope": "Europa Ocidental",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "Austrália e Nova Zelândia",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "Gerenciado Auto-Hospedado",
"description": "Servidor Pangolin auto-hospedado mais confiável e com baixa manutenção com sinos extras e assobiamentos",
@@ -1899,6 +2030,25 @@
"invalidValue": "Valor Inválido",
"idpTypeLabel": "Tipo de provedor de identidade",
"roleMappingExpressionPlaceholder": "ex.: Contem (grupos, 'administrador') && 'Administrador' 「'Membro'",
"roleMappingModeFixedRoles": "Papéis fixos",
"roleMappingModeMappingBuilder": "Mapeando Construtor",
"roleMappingModeRawExpression": "Expressão Bruta",
"roleMappingFixedRolesPlaceholderSelect": "Selecione um ou mais papéis",
"roleMappingFixedRolesPlaceholderFreeform": "Digite o nome das funções (correspondência exata por organização)",
"roleMappingFixedRolesDescriptionSameForAll": "Atribuir o mesmo conjunto de funções a cada usuário auto-provisionado.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Para políticas padrão, nomes de funções de tipo que existem em cada organização onde os usuários são fornecidos. Nomes devem coincidir exatamente.",
"roleMappingClaimPath": "Caminho da Reivindicação",
"roleMappingClaimPathPlaceholder": "grupos",
"roleMappingClaimPathDescription": "Caminho no payload token que contém valores de origem (por exemplo, grupos).",
"roleMappingMatchValue": "Valor Correspondente",
"roleMappingAssignRoles": "Atribuir Papéis",
"roleMappingAddMappingRule": "Adicionar regra de mapeamento",
"roleMappingRawExpressionResultDescription": "Expressão deve retornar à matriz string ou string.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Expressão deve ser avaliada para uma string (um nome de função única).",
"roleMappingMatchValuePlaceholder": "Valor do jogo (por exemplo: administrador)",
"roleMappingAssignRolesPlaceholderFreeform": "Digite nomes de funções ((exact por org)",
"roleMappingBuilderFreeformRowHint": "Nomes de papéis devem corresponder a um papel em cada organizaçãoalvo.",
"roleMappingRemoveRule": "Remover",
"idpGoogleConfiguration": "Configuração do Google",
"idpGoogleConfigurationDescription": "Configurar as credenciais do Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Por quanto tempo manter os registros de acesso",
"logRetentionActionLabel": "Ação de Retenção no Log",
"logRetentionActionDescription": "Por quanto tempo manter os registros de ação",
"logRetentionConnectionLabel": "Retenção de registro de conexão",
"logRetentionConnectionDescription": "Por quanto tempo manter os registros de conexão",
"logRetentionDisabled": "Desabilitado",
"logRetention3Days": "3 dias",
"logRetention7Days": "7 dias",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Fim do ano seguinte",
"actionLogsDescription": "Visualizar histórico de ações realizadas nesta organização",
"accessLogsDescription": "Ver solicitações de autenticação de recursos nesta organização",
"licenseRequiredToUse": "Uma licença <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> é necessária para usar este recurso. Este recurso também está disponível no <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "O <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> é necessário para usar este recurso. Este recurso também está disponível no <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Logs da conexão",
"connectionLogsDescription": "Ver logs de conexão para túneis nesta organização",
"sidebarLogsConnection": "Logs da conexão",
"sidebarLogsStreaming": "Transmitindo",
"sourceAddress": "Endereço de origem",
"destinationAddress": "Endereço de destino",
"duration": "Duração",
"licenseRequiredToUse": "Uma licença <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> ou <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> é necessária para usar este recurso. <bookADemoLink>Reserve um teste de demonstração ou POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "O <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> é necessário para usar este recurso. Este recurso também está disponível no <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Reserve uma demonstração ou avaliação POC</bookADemoLink>.",
"certResolver": "Resolvedor de Certificado",
"certResolverDescription": "Selecione o resolvedor de certificados para este recurso.",
"selectCertResolver": "Selecionar solucionador de certificado",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Controle de Acesso",
"editInternalResourceDialogAccessControlDescription": "Controle quais funções, usuários e clientes de máquina podem acessar este recurso quando conectados. Os administradores sempre têm acesso.",
"editInternalResourceDialogPortRangeValidationError": "O intervalo de portas deve ser \"*\" para todas as portas, ou uma lista de portas e intervalos separados por vírgulas (por exemplo, \"80,443,8000-9000\"). As portas devem estar entre 1 e 65535.",
"internalResourceAuthDaemonStrategy": "Local do Daemon de autenticação SSH",
"internalResourceAuthDaemonStrategyDescription": "Escolha onde o daemon de autenticação SSH funciona: no site (Newt) ou em um host remoto.",
"internalResourceAuthDaemonDescription": "A autenticação SSH daemon lida com assinatura de chave SSH e autenticação PAM para este recurso. Escolha se ele é executado no site (Newt) ou em um host remoto separado. Veja <docsLink>a documentação</docsLink> para mais informações.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Selecione a estratégia",
"internalResourceAuthDaemonStrategyLabel": "Local:",
"internalResourceAuthDaemonSite": "No Site",
"internalResourceAuthDaemonSiteDescription": "O serviço de autenticação é executado no site (Newt).",
"internalResourceAuthDaemonRemote": "Host Remoto",
"internalResourceAuthDaemonRemoteDescription": "O serviço de autenticação é executado em um host que não é o site.",
"internalResourceAuthDaemonPort": "Porta do Daemon (opcional)",
"orgAuthWhatsThis": "Onde posso encontrar meu ID da organização?",
"learnMore": "Saiba mais",
"backToHome": "Voltar para a página inicial",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Habilitar Aprovações do Dispositivo",
"approvalsEmptyStateStep2Description": "Editar uma função e habilitar a opção 'Exigir aprovação de dispositivos'. Usuários com essa função precisarão de aprovação de administrador para novos dispositivos.",
"approvalsEmptyStatePreviewDescription": "Pré-visualização: Quando ativado, solicitações de dispositivo pendentes aparecerão aqui para revisão",
"approvalsEmptyStateButtonText": "Gerir Funções"
"approvalsEmptyStateButtonText": "Gerir Funções",
"domainErrorTitle": "Estamos tendo problemas ao verificar seu domínio",
"idpAdminAutoProvisionPoliciesTabHint": "Configurar funções de mapeamento e políticas de organização na aba <policiesTabLink>Auto Provision Settings</policiesTabLink>.",
"streamingTitle": "Streaming do Evento",
"streamingDescription": "Transmita eventos de sua organização para destinos externos em tempo real.",
"streamingUnnamedDestination": "Destino sem nome",
"streamingNoUrlConfigured": "Nenhuma URL configurada",
"streamingAddDestination": "Adicionar destino",
"streamingHttpWebhookTitle": "Webhook HTTP",
"streamingHttpWebhookDescription": "Envie os eventos para qualquer endpoint HTTP com autenticação flexível e modelo.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Transmitir eventos para um balde de armazenamento de objetos compatível com S3. Em breve.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Encaminha eventos diretamente para a sua conta no Datadog. Em breve.",
"streamingTypePickerDescription": "Escolha um tipo de destino para começar.",
"streamingFailedToLoad": "Falha ao carregar destinos",
"streamingUnexpectedError": "Ocorreu um erro inesperado.",
"streamingFailedToUpdate": "Falha ao atualizar destino",
"streamingDeletedSuccess": "Destino apagado com sucesso",
"streamingFailedToDelete": "Falha ao excluir destino",
"streamingDeleteTitle": "Excluir destino",
"streamingDeleteButtonText": "Excluir destino",
"streamingDeleteDialogAreYouSure": "Tem certeza de que deseja excluir",
"streamingDeleteDialogThisDestination": "este destino",
"streamingDeleteDialogPermanentlyRemoved": "? Todas as configurações serão permanentemente removidas.",
"httpDestEditTitle": "Editar destino",
"httpDestAddTitle": "Adicionar Destino HTTP",
"httpDestEditDescription": "Atualizar a configuração para este destino de transmissão de eventos HTTP.",
"httpDestAddDescription": "Configure um novo ponto de extremidade HTTP para receber eventos da sua organização.",
"httpDestTabSettings": "Confirgurações",
"httpDestTabHeaders": "Cabeçalhos",
"httpDestTabBody": "Conteúdo",
"httpDestTabLogs": "Registros",
"httpDestNamePlaceholder": "Meu destino HTTP",
"httpDestUrlLabel": "URL de destino",
"httpDestUrlErrorHttpRequired": "A URL deve usar http ou https",
"httpDestUrlErrorHttpsRequired": "HTTPS é necessário em implantações em nuvem",
"httpDestUrlErrorInvalid": "Informe uma URL válida (por exemplo, https://example.com/webhook)",
"httpDestAuthTitle": "Autenticação",
"httpDestAuthDescription": "Escolha como os pedidos para seu endpoint são autenticados.",
"httpDestAuthNoneTitle": "Sem Autenticação",
"httpDestAuthNoneDescription": "Envia pedidos sem um cabeçalho de autorização.",
"httpDestAuthBearerTitle": "Token do portador",
"httpDestAuthBearerDescription": "Adiciona uma autorização: Bearer <token> header a cada requisição.",
"httpDestAuthBearerPlaceholder": "Sua chave de API ou token",
"httpDestAuthBasicTitle": "Autenticação básica",
"httpDestAuthBasicDescription": "Adiciona uma Autorização: cabeçalho <credentials> básico. Forneça credenciais como nome de usuário:senha.",
"httpDestAuthBasicPlaceholder": "Usuário:password",
"httpDestAuthCustomTitle": "Cabeçalho personalizado",
"httpDestAuthCustomDescription": "Especifique um nome e valor de cabeçalho HTTP personalizado para autenticação (por exemplo, X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Nome do cabeçalho (ex: X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Valor do cabeçalho",
"httpDestCustomHeadersTitle": "Cabeçalhos HTTP personalizados",
"httpDestCustomHeadersDescription": "Adicionar cabeçalhos personalizados a todas as solicitações de saída. Útil para tokens estáticos ou um tipo de conteúdo personalizado. Por padrão, Content-Type: application/json é enviado.",
"httpDestNoHeadersConfigured": "Nenhum cabeçalho personalizado configurado. Clique em \"Adicionar Cabeçalho\" para adicionar um.",
"httpDestHeaderNamePlaceholder": "Nome do Cabeçalho",
"httpDestHeaderValuePlaceholder": "Valor",
"httpDestAddHeader": "Adicionar Cabeçalho",
"httpDestBodyTemplateTitle": "Modelo de corpo personalizado",
"httpDestBodyTemplateDescription": "Controla a estrutura de carga JSON enviada ao seu endpoint. Se desativado, um objeto JSON padrão é enviado para cada evento.",
"httpDestEnableBodyTemplate": "Ativar modelo personalizado de corpo",
"httpDestBodyTemplateLabel": "Modelo de corpo (JSON)",
"httpDestBodyTemplateHint": "Use variáveis de template para referenciar campos de evento em seu payload.",
"httpDestPayloadFormatTitle": "Formato de carga",
"httpDestPayloadFormatDescription": "Como os eventos são serializados em cada corpo do pedido.",
"httpDestFormatJsonArrayTitle": "Matriz JSON",
"httpDestFormatJsonArrayDescription": "Um pedido por lote, o corpo é um array JSON. Compatível com a maioria dos webhooks genéricos e Datadog.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Um pedido por lote, o corpo é um JSON delimitado por nova-linha — um objeto por linha, sem array exterior. Requerido pelo Splunk HEC, Elástico / OpenSearch, e Grafana Loki.",
"httpDestFormatSingleTitle": "Um Evento por Requisição",
"httpDestFormatSingleDescription": "Envia um POST HTTP separado para cada evento. Utilize apenas para endpoints que não podem manipular lotes.",
"httpDestLogTypesTitle": "Tipos de log",
"httpDestLogTypesDescription": "Escolha quais tipos de log são encaminhados para este destino. Somente serão racionalizados os tipos de logs habilitados.",
"httpDestAccessLogsTitle": "Logs de Acesso",
"httpDestAccessLogsDescription": "Tentativas de acesso a recursos, incluindo solicitações autenticadas e negadas.",
"httpDestActionLogsTitle": "Logs de Ações",
"httpDestActionLogsDescription": "Ações administrativas realizadas por usuários dentro da organização.",
"httpDestConnectionLogsTitle": "Logs da conexão",
"httpDestConnectionLogsDescription": "Eventos de conexão de site e túnel, incluindo conexões e desconexões.",
"httpDestRequestLogsTitle": "Registro de pedidos",
"httpDestRequestLogsDescription": "Logs de solicitação HTTP para recursos proxy incluindo o método, o caminho e o código de resposta.",
"httpDestSaveChanges": "Salvar as alterações",
"httpDestCreateDestination": "Criar destino",
"httpDestUpdatedSuccess": "Destino atualizado com sucesso",
"httpDestCreatedSuccess": "Destino criado com sucesso",
"httpDestUpdateFailed": "Falha ao atualizar destino",
"httpDestCreateFailed": "Falha ao criar destino"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Создать ссылку",
"resourcesNotFound": "Ресурсы не найдены",
"resourceSearch": "Поиск ресурсов",
"machineSearch": "Поиск машин",
"machinesSearch": "Поиск клиентов машины...",
"machineNotFound": "Машины не найдены",
"userDeviceSearch": "Поиск устройств пользователя",
"userDevicesSearch": "Поиск устройств пользователя...",
"openMenu": "Открыть меню",
"resource": "Ресурс",
"title": "Заголовок",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Проксировать запросы через HTTPS с использованием полного доменного имени.",
"resourceRaw": "Сырой TCP/UDP-ресурс",
"resourceRawDescription": "Проксировать запросы по сырому TCP/UDP с использованием номера порта.",
"resourceRawDescriptionCloud": "Прокси запросы через необработанный TCP/UDP с использованием номера порта. Требуется подключение сайтов к удаленному узлу.",
"resourceCreate": "Создание ресурса",
"resourceCreateDescription": "Следуйте инструкциям ниже для создания нового ресурса",
"resourceSeeAll": "Посмотреть все ресурсы",
@@ -322,6 +328,54 @@
"apiKeysDelete": "Удаление ключа API",
"apiKeysManage": "Управление ключами API",
"apiKeysDescription": "Ключи API используются для аутентификации в интеграционном API",
"provisioningKeysTitle": "Ключ подготовки",
"provisioningKeysManage": "Управление ключами подготовки",
"provisioningKeysDescription": "Ключи подготовки используются для аутентификации автоматического обеспечения сайта для вашей организации.",
"provisioningManage": "Подготовка",
"provisioningDescription": "Управляйте предоставленными ключами и проверять непроверенные сайты, ожидающие утверждения.",
"pendingSites": "Ожидающие сайты",
"siteApproveSuccess": "Сайт успешно утвержден",
"siteApproveError": "Ошибка при утверждении сайта",
"provisioningKeys": "Ключи подготовки",
"searchProvisioningKeys": "Поиск подготовительных ключей...",
"provisioningKeysAdd": "Сгенерировать ключ подготовки",
"provisioningKeysErrorDelete": "Ошибка при удалении подготовительного ключа",
"provisioningKeysErrorDeleteMessage": "Ошибка при удалении подготовительного ключа",
"provisioningKeysQuestionRemove": "Вы уверены, что хотите удалить этот ключ подготовки из организации?",
"provisioningKeysMessageRemove": "После удаления ключ больше не может быть использован для размещения сайта.",
"provisioningKeysDeleteConfirm": "Подтвердите удаление ключа подготовки",
"provisioningKeysDelete": "Удалить ключ подготовки",
"provisioningKeysCreate": "Сгенерировать ключ подготовки",
"provisioningKeysCreateDescription": "Создать новый подготовительный ключ для организации",
"provisioningKeysSeeAll": "Посмотреть все подготовительные ключи",
"provisioningKeysSave": "Сохранить ключ подготовки",
"provisioningKeysSaveDescription": "Вы сможете увидеть это только один раз. Скопируйте его в безопасное место.",
"provisioningKeysErrorCreate": "Ошибка при создании ключа подготовки",
"provisioningKeysList": "Новый подготовительный ключ",
"provisioningKeysMaxBatchSize": "Макс. размер партии",
"provisioningKeysUnlimitedBatchSize": "Неограниченный размер партии (без ограничений)",
"provisioningKeysMaxBatchUnlimited": "Неограниченный",
"provisioningKeysMaxBatchSizeInvalid": "Введите максимальный размер пакета (11,000,000).",
"provisioningKeysValidUntil": "Действителен до",
"provisioningKeysValidUntilHint": "Оставьте пустым для отсутствия срока действия.",
"provisioningKeysValidUntilInvalid": "Введите правильную дату и время.",
"provisioningKeysNumUsed": "Использовано раз",
"provisioningKeysLastUsed": "Последнее использованное",
"provisioningKeysNoExpiry": "Без истечения срока",
"provisioningKeysNeverUsed": "Никогда",
"provisioningKeysEdit": "Редактировать ключ подготовки",
"provisioningKeysEditDescription": "Обновить максимальный размер и срок действия этого ключа.",
"provisioningKeysApproveNewSites": "Одобрить новые сайты",
"provisioningKeysApproveNewSitesDescription": "Автоматически одобрять сайты, регистрирующиеся с этим ключом.",
"provisioningKeysUpdateError": "Ошибка при обновлении ключа подготовки",
"provisioningKeysUpdated": "Ключ подготовки обновлен",
"provisioningKeysUpdatedDescription": "Ваши изменения были сохранены.",
"provisioningKeysBannerTitle": "Ключи подготовки сайта",
"provisioningKeysBannerDescription": "Генерировать подготовительный ключ и использовать его вместе с Новым коннектором для автоматического создания сайтов при первом запуске — нет необходимости настраивать отдельные учетные данные для каждого сайта.",
"provisioningKeysBannerButtonText": "Узнать больше",
"pendingSitesBannerTitle": "Ожидающие сайты",
"pendingSitesBannerDescription": "Сайты, связанные с использованием ключа подготовки, появляются здесь для проверки. Одобрите каждый сайт, прежде чем он станет активным и получит доступ к вашим ресурсам.",
"pendingSitesBannerButtonText": "Узнать больше",
"apiKeysSettings": "Настройки {apiKeyName}",
"userTitle": "Управление всеми пользователями",
"userDescription": "Просмотр и управление всеми пользователями в системе",
@@ -508,9 +562,12 @@
"userSaved": "Пользователь сохранён",
"userSavedDescription": "Пользователь был обновлён.",
"autoProvisioned": "Автоподбор",
"autoProvisionSettings": "Настройки автоматического обеспечения",
"autoProvisionedDescription": "Разрешить автоматическое управление этим пользователем",
"accessControlsDescription": "Управляйте тем, к чему этот пользователь может получить доступ и что делать в организации",
"accessControlsSubmit": "Сохранить контроль доступа",
"singleRolePerUserPlanNotice": "Ваш план поддерживает только одну роль каждого пользователя.",
"singleRolePerUserEditionNotice": "Эта редакция поддерживает только одну роль для каждого пользователя.",
"roles": "Роли",
"accessUsersRoles": "Управление пользователями и ролями",
"accessUsersRolesDescription": "Пригласить пользователей и добавить их в роли для управления доступом к организации",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Не удалось переключить ресурс",
"resourcesErrorUpdateDescription": "Произошла ошибка при обновлении ресурса",
"access": "Доступ",
"accessControl": "Контроль доступа",
"shareLink": "Общая ссылка {resource}",
"resourceSelect": "Выберите ресурс",
"shareLinks": "Общие ссылки",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Роль удалена",
"accessRoleRemovedDescription": "Роль была успешно удалена.",
"accessRoleRequiredRemove": "Перед удалением этой роли выберите новую роль для переноса существующих участников.",
"network": "Сеть",
"manage": "Управление",
"sitesNotFound": "Сайты не найдены.",
"pangolinServerAdmin": "Администратор сервера - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Упс! Страница, которую вы ищете, не существует.",
"overview": "Обзор",
"home": "Главная",
"accessControl": "Контроль доступа",
"settings": "Настройки",
"usersAll": "Все пользователи",
"license": "Лицензия",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Получить пользователя",
"actionGetOrgUser": "Получить пользователя организации",
"actionListOrgDomains": "Список доменов организации",
"actionGetDomain": "Получить домен",
"actionCreateOrgDomain": "Создать домен",
"actionUpdateOrgDomain": "Обновить домен",
"actionDeleteOrgDomain": "Удалить домен",
"actionGetDNSRecords": "Получить записи DNS",
"actionRestartOrgDomain": "Перезапустить домен",
"actionCreateSite": "Создать сайт",
"actionDeleteSite": "Удалить сайт",
"actionGetSite": "Получить сайт",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Введите токен настройки из консоли сервера.",
"setupTokenRequired": "Токен настройки обязателен",
"actionUpdateSite": "Обновить сайт",
"actionResetSiteBandwidth": "Сброс пропускной способности организации",
"actionListSiteRoles": "Список разрешенных ролей сайта",
"actionCreateResource": "Создать ресурс",
"actionDeleteResource": "Удалить ресурс",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Удалить пользователя",
"actionListUsers": "Список пользователей",
"actionAddUserRole": "Добавить роль пользователя",
"actionSetUserOrgRoles": "Установка ролей пользователей",
"actionGenerateAccessToken": "Сгенерировать токен доступа",
"actionDeleteAccessToken": "Удалить токен доступа",
"actionListAccessTokens": "Список токенов доступа",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Приватный",
"sidebarAccessControl": "Контроль доступа",
"sidebarLogsAndAnalytics": "Журналы и аналитика",
"sidebarTeam": "Команда",
"sidebarUsers": "Пользователи",
"sidebarAdmin": "Админ",
"sidebarInvitations": "Приглашения",
"sidebarRoles": "Роли",
"sidebarShareableLinks": "Ссылки",
"sidebarApiKeys": "API ключи",
"sidebarProvisioning": "Подготовка",
"sidebarSettings": "Настройки",
"sidebarAllUsers": "Все пользователи",
"sidebarIdentityProviders": "Поставщики удостоверений",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Журнал и аналитика",
"sidebarBluePrints": "Чертежи",
"sidebarOrganization": "Организация",
"sidebarManagement": "Управление",
"sidebarBillingAndLicenses": "Биллинг и лицензии",
"sidebarLogsAnalytics": "Статистика",
"blueprints": "Чертежи",
@@ -1289,7 +1358,6 @@
"parsedContents": "Переработанное содержимое (только для чтения)",
"enableDockerSocket": "Включить чертёж Docker",
"enableDockerSocketDescription": "Включить scraping ярлыка Docker Socket для ярлыков чертежей. Путь к сокету должен быть предоставлен в Newt.",
"enableDockerSocketLink": "Узнать больше",
"viewDockerContainers": "Просмотр контейнеров Docker",
"containersIn": "Контейнеры в {siteName}",
"selectContainerDescription": "Выберите любой контейнер для использования в качестве имени хоста для этой цели. Нажмите на порт, чтобы использовать порт.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Пространство имен: {namespace}",
"domainPickerShowMore": "Показать еще",
"regionSelectorTitle": "Выберите регион",
"domainPickerRemoteExitNodeWarning": "Предоставленные домены не поддерживаются при подключении сайтов к удаленным узлам. Для доступа к ресурсам на удаленных узлах используйте пользовательский домен.",
"regionSelectorInfo": "Выбор региона помогает нам обеспечить лучшее качество обслуживания для вашего расположения. Вам необязательно находиться в том же регионе, что и ваш сервер.",
"regionSelectorPlaceholder": "Выбор региона",
"regionSelectorComingSoon": "Скоро будет",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Уведомление о доступности функций",
"billingFeatureLossDescription": "При переходе на другой тарифный план функции не будут автоматически отключены. Некоторые настройки и конфигурации могут быть потеряны. Пожалуйста, ознакомьтесь с матрицей ценообразования, чтобы понять, какие функции больше не будут доступны.",
"billingUsageExceedsLimit": "Текущее использование ({current}) превышает предел ({limit})",
"billingPastDueTitle": "Платеж просрочен",
"billingPastDueDescription": "Ваш платеж просрочен. Пожалуйста, обновите способ оплаты, чтобы продолжить использовать текущие функции. Если ваша подписка не будет решена, она будет отменена, и вы вернетесь к бесплатному уровню.",
"billingUnpaidTitle": "Подписка не оплачена",
"billingUnpaidDescription": "Ваша подписка не оплачена, и вы были возвращены к бесплатному уровню. Пожалуйста, обновите способ оплаты, чтобы восстановить вашу подписку.",
"billingIncompleteTitle": "Платеж не завершен",
"billingIncompleteDescription": "Ваш платеж не завершен. Пожалуйста, завершите процесс оплаты, чтобы активировать вашу подписку.",
"billingIncompleteExpiredTitle": "Платеж просрочен",
"billingIncompleteExpiredDescription": "Ваш платеж не был завершен и истек. Вы были возвращены к бесплатному уровню. Пожалуйста, подпишитесь снова, чтобы восстановить доступ к платным функциям.",
"billingManageSubscription": "Управление подпиской",
"billingResolvePaymentIssue": "Пожалуйста, решите проблему оплаты перед обновлением или понижением сорта",
"signUpTerms": {
"IAgreeToThe": "Я согласен с",
"termsOfService": "условия использования",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Время указано в секундах",
"requireDeviceApproval": "Требовать подтверждения устройства",
"requireDeviceApprovalDescription": "Пользователям с этой ролью нужны новые устройства, одобренные администратором, прежде чем они смогут подключаться и получать доступ к ресурсам.",
"sshAccess": "SSH доступ",
"roleAllowSsh": "Разрешить SSH",
"roleAllowSshAllow": "Разрешить",
"roleAllowSshDisallow": "Запретить",
"roleAllowSshDescription": "Разрешить пользователям с этой ролью подключаться к ресурсам через SSH. Если отключено, роль не может использовать доступ SSH.",
"sshSudoMode": "Sudo доступ",
"sshSudoModeNone": "Нет",
"sshSudoModeNoneDescription": "Пользователь не может запускать команды с sudo.",
"sshSudoModeFull": "Полная судо",
"sshSudoModeFullDescription": "Пользователь может запускать любую команду с помощью sudo.",
"sshSudoModeCommands": "Команды",
"sshSudoModeCommandsDescription": "Пользователь может запускать только указанные команды с помощью sudo.",
"sshSudo": "Разрешить sudo",
"sshSudoCommands": "Sudo Команды",
"sshSudoCommandsDescription": "Список команд, разделенных запятыми, которые пользователю разрешено запускать с помощью sudo.",
"sshCreateHomeDir": "Создать домашний каталог",
"sshUnixGroups": "Unix группы",
"sshUnixGroupsDescription": "Группы Unix через запятую, чтобы добавить пользователя на целевой хост.",
"retryAttempts": "Количество попыток повторного запроса",
"expectedResponseCodes": "Ожидаемые коды ответов",
"expectedResponseCodesDescription": "HTTP-код состояния, указывающий на здоровое состояние. Если оставить пустым, 200-300 считается здоровым.",
@@ -1851,6 +1948,40 @@
"exitNode": "Узел выхода",
"country": "Страна",
"rulesMatchCountry": "В настоящее время основано на исходном IP",
"region": "Регион",
"selectRegion": "Выберите регион",
"searchRegions": "Поиск регионов...",
"noRegionFound": "Регион не найден.",
"rulesMatchRegion": "Выберите региональную группу стран",
"rulesErrorInvalidRegion": "Некорректный регион",
"rulesErrorInvalidRegionDescription": "Пожалуйста, выберите корректный регион.",
"regionAfrica": "Африка",
"regionNorthernAfrica": "Северная Африка",
"regionEasternAfrica": "Восточная Африка",
"regionMiddleAfrica": "Центральная Африка",
"regionSouthernAfrica": "Южная Африка",
"regionWesternAfrica": "Западная Африка",
"regionAmericas": "Америка",
"regionCaribbean": "Карибы",
"regionCentralAmerica": "Центральная Америка",
"regionSouthAmerica": "Южная Америка",
"regionNorthernAmerica": "Северная Америка",
"regionAsia": "Азия",
"regionCentralAsia": "Центральная Азия",
"regionEasternAsia": "Восточная Азия",
"regionSouthEasternAsia": "Юго-Восточная Азия",
"regionSouthernAsia": "Южная Азия",
"regionWesternAsia": "Западная Азия",
"regionEurope": "Европа",
"regionEasternEurope": "Восточная Европа",
"regionNorthernEurope": "Северная Европа",
"regionSouthernEurope": "Южная Европа",
"regionWesternEurope": "Западная Европа",
"regionOceania": "Океания",
"regionAustraliaAndNewZealand": "Австралия и Новая Зеландия",
"regionMelanesia": "Меланезия",
"regionMicronesia": "Микронезия",
"regionPolynesia": "Полинезия",
"managedSelfHosted": {
"title": "Управляемый с самовывоза",
"description": "Более надежный и низко обслуживаемый сервер Pangolin с дополнительными колокольнями и свистками",
@@ -1899,6 +2030,25 @@
"invalidValue": "Неверное значение",
"idpTypeLabel": "Тип поставщика удостоверений",
"roleMappingExpressionPlaceholder": "например, contains(groups, 'admin') && 'Admin' || 'Member'",
"roleMappingModeFixedRoles": "Фиксированные роли",
"roleMappingModeMappingBuilder": "Сопоставляющий конструктор",
"roleMappingModeRawExpression": "Сырое выражение",
"roleMappingFixedRolesPlaceholderSelect": "Выберите одну или несколько ролей",
"roleMappingFixedRolesPlaceholderFreeform": "Тип имен ролей (точное совпадение по организации)",
"roleMappingFixedRolesDescriptionSameForAll": "Назначить одну и ту же роль, которая установлена каждому автообеспеченному пользователю.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Для политик по умолчанию, введите имена ролей, которые существуют в каждой организации, где пользователи предоставлены. Имена должны соответствовать точно.",
"roleMappingClaimPath": "Путь к заявлению",
"roleMappingClaimPathPlaceholder": "группы",
"roleMappingClaimPathDescription": "Путь в полезной нагрузке токенов, который содержит исходные значения (например, группы).",
"roleMappingMatchValue": "Значение матча",
"roleMappingAssignRoles": "Назначить роли",
"roleMappingAddMappingRule": "Добавить правило сопоставления",
"roleMappingRawExpressionResultDescription": "Выражение должно быть оценено к строке или строковому массиву.",
"roleMappingRawExpressionResultDescriptionSingleRole": "Выражение должно быть оценено строке (название одной роли).",
"roleMappingMatchValuePlaceholder": "Значение совпадения (например: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Введите имена ролей (точное по организациям)",
"roleMappingBuilderFreeformRowHint": "Имена ролей должны соответствовать роли в каждой целевой организации.",
"roleMappingRemoveRule": "Удалить",
"idpGoogleConfiguration": "Конфигурация Google",
"idpGoogleConfigurationDescription": "Настройка учетных данных Google OAuth2",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Как долго сохранять журналы доступа",
"logRetentionActionLabel": "Сохранение журнала действий",
"logRetentionActionDescription": "Как долго хранить журналы действий",
"logRetentionConnectionLabel": "Сохранение журнала подключений",
"logRetentionConnectionDescription": "Как долго хранить журналы подключений",
"logRetentionDisabled": "Отключено",
"logRetention3Days": "3 дня",
"logRetention7Days": "7 дней",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Конец следующего года",
"actionLogsDescription": "Просмотр истории действий, выполненных в этой организации",
"accessLogsDescription": "Просмотр запросов авторизации доступа к ресурсам этой организации",
"licenseRequiredToUse": "Лицензия на <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> требуется для использования этой функции. Эта функция также доступна в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"ossEnterpriseEditionRequired": "Для использования этой функции требуется <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink>. Эта функция также доступна в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
"connectionLogs": "Журнал подключений",
"connectionLogsDescription": "Просмотр журналов подключения туннелей в этой организации",
"sidebarLogsConnection": "Журнал подключений",
"sidebarLogsStreaming": "Вещание",
"sourceAddress": "Адрес источника",
"destinationAddress": "Адрес назначения",
"duration": "Продолжительность",
"licenseRequiredToUse": "Требуется лицензия на <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> или <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> для использования этой функции. <bookADemoLink>Забронируйте демонстрацию или пробный POC</bookADemoLink>.",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> требуется для использования этой функции. Эта функция также доступна в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Забронируйте демонстрацию или пробный POC</bookADemoLink>.",
"certResolver": "Резольвер сертификата",
"certResolverDescription": "Выберите резолвер сертификата, который будет использоваться для этого ресурса.",
"selectCertResolver": "Выберите резолвер сертификата",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Контроль доступа",
"editInternalResourceDialogAccessControlDescription": "Контролируйте, какие роли, пользователи и машинные клиенты имеют доступ к этому ресурсу при подключении. Администраторы всегда имеют доступ.",
"editInternalResourceDialogPortRangeValidationError": "Диапазон портов должен быть \"*\" для всех портов или списком портов и диапазонов через запятую (например, \"80,443,8000-9000\"). Порты должны находиться в диапазоне от 1 до 65535.",
"internalResourceAuthDaemonStrategy": "Местоположение демона по SSH",
"internalResourceAuthDaemonStrategyDescription": "Выберите, где работает демон аутентификации SSH: на сайте (Newt) или на удаленном узле.",
"internalResourceAuthDaemonDescription": "Демон аутентификации SSH обрабатывает подписание ключей SSH и аутентификацию PAM для этого ресурса. Выберите, запускать ли его на сайте (Newt) или на отдельном удаленном хосте. Подробности смотрите в <docsLink>документации</docsLink>.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Выберите стратегию",
"internalResourceAuthDaemonStrategyLabel": "Местоположение",
"internalResourceAuthDaemonSite": "На сайте",
"internalResourceAuthDaemonSiteDescription": "На сайте работает демон Auth (Newt).",
"internalResourceAuthDaemonRemote": "Удаленный хост",
"internalResourceAuthDaemonRemoteDescription": "Демон Auth запускается на хост, который не является сайтом.",
"internalResourceAuthDaemonPort": "Порт демона (опционально)",
"orgAuthWhatsThis": "Где я могу найти ID моей организации?",
"learnMore": "Узнать больше",
"backToHome": "Вернуться домой",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Включить утверждения устройства",
"approvalsEmptyStateStep2Description": "Редактировать роль и включить опцию 'Требовать утверждения устройств'. Пользователям с этой ролью потребуется подтверждение администратора для новых устройств.",
"approvalsEmptyStatePreviewDescription": "Предпросмотр: Если включено, ожидающие запросы на устройство появятся здесь для проверки",
"approvalsEmptyStateButtonText": "Управление ролями"
"approvalsEmptyStateButtonText": "Управление ролями",
"domainErrorTitle": "У нас возникли проблемы с проверкой вашего домена",
"idpAdminAutoProvisionPoliciesTabHint": "Настройте сопоставление ролей и организационные политики на вкладке <policiesTabLink>Настройки авто-предоставления</policiesTabLink>.",
"streamingTitle": "Поток событий",
"streamingDescription": "Трансляция событий от вашей организации к внешним направлениям в режиме реального времени.",
"streamingUnnamedDestination": "Место назначения без имени",
"streamingNoUrlConfigured": "URL-адрес не настроен",
"streamingAddDestination": "Добавить место назначения",
"streamingHttpWebhookTitle": "HTTP вебхук",
"streamingHttpWebhookDescription": "Отправлять события на любую конечную точку HTTP с гибкой аутентификацией и шаблоном.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Потоковая передача событий к пакету хранения объектов, совместимому с S3.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Перенаправлять события непосредственно на ваш аккаунт в Datadog. Скоро будет доступно.",
"streamingTypePickerDescription": "Выберите тип назначения, чтобы начать.",
"streamingFailedToLoad": "Не удалось загрузить места назначения",
"streamingUnexpectedError": "Произошла непредвиденная ошибка.",
"streamingFailedToUpdate": "Не удалось обновить место назначения",
"streamingDeletedSuccess": "Адрес назначения успешно удален",
"streamingFailedToDelete": "Не удалось удалить место назначения",
"streamingDeleteTitle": "Удалить адрес назначения",
"streamingDeleteButtonText": "Удалить адрес назначения",
"streamingDeleteDialogAreYouSure": "Вы уверены, что хотите удалить",
"streamingDeleteDialogThisDestination": "это место назначения",
"streamingDeleteDialogPermanentlyRemoved": "? Все настройки будут удалены навсегда.",
"httpDestEditTitle": "Изменить адрес назначения",
"httpDestAddTitle": "Добавить HTTP адрес",
"httpDestEditDescription": "Обновление конфигурации для этого HTTP события потокового назначения.",
"httpDestAddDescription": "Настройте новую HTTP-конечную точку для получения событий вашей организации.",
"httpDestTabSettings": "Настройки",
"httpDestTabHeaders": "Заголовки",
"httpDestTabBody": "Тело",
"httpDestTabLogs": "Логи",
"httpDestNamePlaceholder": "Мой HTTP адрес назначения",
"httpDestUrlLabel": "URL назначения",
"httpDestUrlErrorHttpRequired": "URL должен использовать http или https",
"httpDestUrlErrorHttpsRequired": "Требуется HTTPS при развертывании облака",
"httpDestUrlErrorInvalid": "Введите действительный URL (например, https://example.com/webhook)",
"httpDestAuthTitle": "Аутентификация",
"httpDestAuthDescription": "Выберите, как запросы к вашей конечной точке аутентифицированы.",
"httpDestAuthNoneTitle": "Нет аутентификации",
"httpDestAuthNoneDescription": "Отправляет запросы без заголовка авторизации.",
"httpDestAuthBearerTitle": "Жетон носителя",
"httpDestAuthBearerDescription": "Добавляет заголовок Authorization: Bearer <token> к каждому запросу.",
"httpDestAuthBearerPlaceholder": "Ваш ключ API или токен",
"httpDestAuthBasicTitle": "Базовая авторизация",
"httpDestAuthBasicDescription": "Добавляет Authorization: Basic <credentials> header. Предоставьте учетные данные в качестве имени пользователя:password.",
"httpDestAuthBasicPlaceholder": "имя пользователя:пароль",
"httpDestAuthCustomTitle": "Пользовательский заголовок",
"httpDestAuthCustomDescription": "Укажите пользовательское имя заголовка HTTP и значение для аутентификации (например, X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Имя заголовка (например, X-API-ключ)",
"httpDestAuthCustomHeaderValuePlaceholder": "Значение заголовка",
"httpDestCustomHeadersTitle": "Пользовательские HTTP-заголовки",
"httpDestCustomHeadersDescription": "Добавляет пользовательские заголовки к каждому исходящему запросу. Полезно для статических маркеров или пользовательского типа содержимого. По умолчанию отправляется Content-Type: application/json.",
"httpDestNoHeadersConfigured": "Пользовательские заголовки не настроены. Нажмите \"Добавить заголовок\", чтобы добавить их.",
"httpDestHeaderNamePlaceholder": "Название заголовка",
"httpDestHeaderValuePlaceholder": "Значение",
"httpDestAddHeader": "Добавить заголовок",
"httpDestBodyTemplateTitle": "Пользовательский шаблон тела",
"httpDestBodyTemplateDescription": "Контролируйте структуру JSON приложения, отправленную на вашу конечную точку. Если отключено, для каждого события отправляется JSON объект по умолчанию.",
"httpDestEnableBodyTemplate": "Включить настраиваемый шаблон тела",
"httpDestBodyTemplateLabel": "Шаблон тела (JSON)",
"httpDestBodyTemplateHint": "Использовать шаблонные переменные для ссылки поля событий в вашей полезной нагрузке.",
"httpDestPayloadFormatTitle": "Формат нагрузки",
"httpDestPayloadFormatDescription": "Как события сериализуются в каждый орган запроса.",
"httpDestFormatJsonArrayTitle": "JSON массив",
"httpDestFormatJsonArrayDescription": "По одному запросу на каждую партию, тело является JSON-массивом. Совместим с большинством общих вебхуков и Датадог.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "По одному запросу на каждую партию, тело - это JSON, разделённый новой строкой, по одному объекту на строку, без внешнего массива. Требуется в Splunk HEC, Elastic / OpenSearch, и Grafana Loki.",
"httpDestFormatSingleTitle": "Одно событие на запрос",
"httpDestFormatSingleDescription": "Отправляет отдельный HTTP POST для каждого отдельного события. Используйте только для конечных точек, которые не могут обрабатывать пакеты.",
"httpDestLogTypesTitle": "Типы журналов",
"httpDestLogTypesDescription": "Выберите, какие типы журналов пересылаются в этот пункт назначения. Только включенные типы журналов будут транслированы.",
"httpDestAccessLogsTitle": "Журналы доступа",
"httpDestAccessLogsDescription": "Попытки доступа к ресурсам, включая аутентифицированные и отклоненные запросы.",
"httpDestActionLogsTitle": "Журнал действий",
"httpDestActionLogsDescription": "Административные меры, осуществляемые пользователями в рамках организации.",
"httpDestConnectionLogsTitle": "Журнал подключений",
"httpDestConnectionLogsDescription": "События связи с сайтами и туннелями, включая соединения и отключения.",
"httpDestRequestLogsTitle": "Запросить журналы",
"httpDestRequestLogsDescription": "Журналы запросов HTTP для проксируемых ресурсов, включая метод, путь и код ответа.",
"httpDestSaveChanges": "Сохранить изменения",
"httpDestCreateDestination": "Создать адрес назначения",
"httpDestUpdatedSuccess": "Адрес назначения успешно обновлен",
"httpDestCreatedSuccess": "Адрес назначения успешно создан",
"httpDestUpdateFailed": "Не удалось обновить место назначения",
"httpDestCreateFailed": "Не удалось создать место назначения"
}

View File

@@ -148,6 +148,11 @@
"createLink": "Bağlantı Oluştur",
"resourcesNotFound": "Hiçbir kaynak bulunamadı",
"resourceSearch": "Kaynak ara",
"machineSearch": "Makinaları ara",
"machinesSearch": "Makina müşteri...",
"machineNotFound": "Hiçbir makine bulunamadı",
"userDeviceSearch": "Kullanıcı cihazlarını ara",
"userDevicesSearch": "Kullanıcı cihazlarını ara...",
"openMenu": "Menüyü Aç",
"resource": "Kaynak",
"title": "Başlık",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "Tam nitelikli bir etki alanı adı kullanarak HTTPS üzerinden proxy isteklerini yönlendirin.",
"resourceRaw": "Ham TCP/UDP Kaynağı",
"resourceRawDescription": "Port numarası kullanarak ham TCP/UDP üzerinden proxy isteklerini yönlendirin.",
"resourceRawDescriptionCloud": "Proxy isteklerini bir port numarası kullanarak ham TCP/UDP üzerinden yapın. Sitelerin uzak bir düğüme bağlanması gereklidir.",
"resourceCreate": "Kaynak Oluştur",
"resourceCreateDescription": "Yeni bir kaynak oluşturmak için aşağıdaki adımları izleyin",
"resourceSeeAll": "Tüm Kaynakları Gör",
@@ -322,6 +328,54 @@
"apiKeysDelete": "API Anahtarını Sil",
"apiKeysManage": "API Anahtarlarını Yönet",
"apiKeysDescription": "API anahtarları entegrasyon API'sini doğrulamak için kullanılır",
"provisioningKeysTitle": "Tedarik Anahtarı",
"provisioningKeysManage": "Tedarik Anahtarlarını Yönet",
"provisioningKeysDescription": "Tedarik anahtarları, organizasyonunuz için otomatik site sağlama işlemini doğrulamak için kullanılır.",
"provisioningManage": "Tedarik",
"provisioningDescription": "Tedarik anahtarlarını yönetin ve onay bekleyen siteleri gözden geçirin.",
"pendingSites": "Bekleyen Siteler",
"siteApproveSuccess": "Site başarıyla onaylandı",
"siteApproveError": "Site onaylanırken hata oluştu",
"provisioningKeys": "Tedarik Anahtarları",
"searchProvisioningKeys": "Tedarik anahtarlarını ara...",
"provisioningKeysAdd": "Tedarik Anahtarı Üret",
"provisioningKeysErrorDelete": "Tedarik anahtarı silinirken hata oluştu",
"provisioningKeysErrorDeleteMessage": "Tedarik anahtarı silinirken hata oluştu",
"provisioningKeysQuestionRemove": "Bu tedarik anahtarını organizasyondan kaldırmak istediğinizden emin misiniz?",
"provisioningKeysMessageRemove": "Kaldırıldıktan sonra, anahtar site tedariki için artık kullanılamaz.",
"provisioningKeysDeleteConfirm": "Tedarik Anahtarını Silmeyi Onayla",
"provisioningKeysDelete": "Tedarik Anahtarını Sil",
"provisioningKeysCreate": "Tedarik Anahtarı Üret",
"provisioningKeysCreateDescription": "Organizasyon için yeni bir tedarik anahtarı oluşturun",
"provisioningKeysSeeAll": "Tüm tedarik anahtarlarını gör",
"provisioningKeysSave": "Tedarik anahtarını kaydet",
"provisioningKeysSaveDescription": "Bunu yalnızca bir kez görebileceksiniz. Güvenli bir yere kopyalayın.",
"provisioningKeysErrorCreate": "Tedarik anahtarı oluşturulurken hata oluştu",
"provisioningKeysList": "Yeni tedarik anahtarı",
"provisioningKeysMaxBatchSize": "Maksimum toplu iş boyutu",
"provisioningKeysUnlimitedBatchSize": "Sınırsız toplu iş boyutu (sınırlama yok)",
"provisioningKeysMaxBatchUnlimited": "Sınırsız",
"provisioningKeysMaxBatchSizeInvalid": "Geçerli bir maksimum toplu iş boyutu girin (11,000,000).",
"provisioningKeysValidUntil": "Geçerlilik tarihi",
"provisioningKeysValidUntilHint": "Son kullanım tarihi için boş bırakın.",
"provisioningKeysValidUntilInvalid": "Geçerli bir tarih ve saat girin.",
"provisioningKeysNumUsed": "Kullanım Sayısı",
"provisioningKeysLastUsed": "Son kullanım",
"provisioningKeysNoExpiry": "Son kullanma tarihi yok",
"provisioningKeysNeverUsed": "Asla",
"provisioningKeysEdit": "Tedarik Anahtarını Düzenle",
"provisioningKeysEditDescription": "Bu anahtar için maksimum toplu iş boyutunu ve son kullanma zamanını güncelleyin.",
"provisioningKeysApproveNewSites": "Yeni siteleri onayla",
"provisioningKeysApproveNewSitesDescription": "Bu anahtar ile kayıt olan siteleri otomatik olarak onayla.",
"provisioningKeysUpdateError": "Tedarik anahtarı güncellenirken hata oluştu",
"provisioningKeysUpdated": "Tedarik anahtarı güncellendi",
"provisioningKeysUpdatedDescription": "Değişiklikleriniz kaydedildi.",
"provisioningKeysBannerTitle": "Site Tedarik Anahtarları",
"provisioningKeysBannerDescription": "Tedarik anahtarı oluşturun ve ilk başlangıçta siteleri otomatik olarak oluşturmak için Newt konektörüyle kullanın — her site için ayrı kimlik bilgileri ayarlamaya gerek yoktur.",
"provisioningKeysBannerButtonText": "Daha fazla bilgi",
"pendingSitesBannerTitle": "Bekleyen Siteler",
"pendingSitesBannerDescription": "Tedarik anahtarı kullanarak bağlanan siteler burada incelenmek için görünür. Aktif hale gelmeden ve kaynaklarınıza erişim kazanmadan önce her siteyi onaylayın.",
"pendingSitesBannerButtonText": "Daha fazla bilgi",
"apiKeysSettings": "{apiKeyName} Ayarları",
"userTitle": "Tüm Kullanıcıları Yönet",
"userDescription": "Sistemdeki tüm kullanıcıları görün ve yönetin",
@@ -508,9 +562,12 @@
"userSaved": "Kullanıcı kaydedildi",
"userSavedDescription": "Kullanıcı güncellenmiştir.",
"autoProvisioned": "Otomatik Sağlandı",
"autoProvisionSettings": "Otomatik Tedarik Ayarları",
"autoProvisionedDescription": "Bu kullanıcının kimlik sağlayıcısı tarafından otomatik olarak yönetilmesine izin ver",
"accessControlsDescription": "Bu kullanıcının organizasyonda neleri erişebileceğini ve yapabileceğini yönetin",
"accessControlsSubmit": "Erişim Kontrollerini Kaydet",
"singleRolePerUserPlanNotice": "Planınız yalnızca kullanıcı başına bir rol desteler.",
"singleRolePerUserEditionNotice": "Bu sürüm yalnızca kullanıcı başına bir rol destekler.",
"roles": "Roller",
"accessUsersRoles": "Kullanıcılar ve Roller Yönetin",
"accessUsersRolesDescription": "Kullanıcılara davet gönderin ve organizasyona erişimi yönetmek için rollere ekleyin",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "Kaynak değiştirilemedi",
"resourcesErrorUpdateDescription": "Kaynak güncellenirken bir hata oluştu",
"access": "Erişim",
"accessControl": "Erişim Kontrolü",
"shareLink": "{resource} Paylaşım Bağlantısı",
"resourceSelect": "Kaynak seçin",
"shareLinks": "Paylaşım Bağlantıları",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "Rol kaldırıldı",
"accessRoleRemovedDescription": "Rol başarıyla kaldırıldı.",
"accessRoleRequiredRemove": "Bu rolü silmeden önce, mevcut üyeleri aktarmak için yeni bir rol seçin.",
"network": "Ağ",
"manage": "Yönet",
"sitesNotFound": "Site bulunamadı.",
"pangolinServerAdmin": "Sunucu Yöneticisi - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "Oops! Aradığınız sayfa mevcut değil.",
"overview": "Genel Bakış",
"home": "Ana Sayfa",
"accessControl": "Erişim Kontrolü",
"settings": "Ayarlar",
"usersAll": "Tüm Kullanıcılar",
"license": "Lisans",
@@ -1100,6 +1158,12 @@
"actionGetUser": "Kullanıcıyı Getir",
"actionGetOrgUser": "Kuruluş Kullanıcısını Al",
"actionListOrgDomains": "Kuruluş Alan Adlarını Listele",
"actionGetDomain": "Alan Adını Al",
"actionCreateOrgDomain": "Alan Adı Oluştur",
"actionUpdateOrgDomain": "Alan Adını Güncelle",
"actionDeleteOrgDomain": "Alan Adını Sil",
"actionGetDNSRecords": "DNS Kayıtlarını Al",
"actionRestartOrgDomain": "Alanı Yeniden Başlat",
"actionCreateSite": "Site Oluştur",
"actionDeleteSite": "Siteyi Sil",
"actionGetSite": "Siteyi Al",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "Sunucu konsolundan kurulum simgesini girin.",
"setupTokenRequired": "Kurulum simgesi gerekli",
"actionUpdateSite": "Siteyi Güncelle",
"actionResetSiteBandwidth": "Organizasyon Bant Genişliğini Sıfırla",
"actionListSiteRoles": "İzin Verilen Site Rolleri Listele",
"actionCreateResource": "Kaynak Oluştur",
"actionDeleteResource": "Kaynağı Sil",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "Kullanıcıyı Kaldır",
"actionListUsers": "Kullanıcıları Listele",
"actionAddUserRole": "Kullanıcı Rolü Ekle",
"actionSetUserOrgRoles": "Kullanıcı Rolleri Belirle",
"actionGenerateAccessToken": "Erişim Jetonu Oluştur",
"actionDeleteAccessToken": "Erişim Jetonunu Sil",
"actionListAccessTokens": "Erişim Jetonlarını Listele",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "Özel",
"sidebarAccessControl": "Erişim Kontrolü",
"sidebarLogsAndAnalytics": "Kayıtlar & Analitik",
"sidebarTeam": "Ekip",
"sidebarUsers": "Kullanıcılar",
"sidebarAdmin": "Yönetici",
"sidebarInvitations": "Davetiye",
"sidebarRoles": "Roller",
"sidebarShareableLinks": "Bağlantılar",
"sidebarApiKeys": "API Anahtarları",
"sidebarProvisioning": "Tedarik",
"sidebarSettings": "Ayarlar",
"sidebarAllUsers": "Tüm Kullanıcılar",
"sidebarIdentityProviders": "Kimlik Sağlayıcılar",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "Kayıt & Analiz",
"sidebarBluePrints": "Planlar",
"sidebarOrganization": "Organizasyon",
"sidebarManagement": "Yönetim",
"sidebarBillingAndLicenses": "Faturalandırma & Lisanslar",
"sidebarLogsAnalytics": "Analitik",
"blueprints": "Planlar",
@@ -1289,7 +1358,6 @@
"parsedContents": "Verilerin Ayrıştırılmış İçeriği (Salt Okunur)",
"enableDockerSocket": "Docker Soketini Etkinleştir",
"enableDockerSocketDescription": "Plan etiketleri için Docker Socket etiket toplamasını etkinleştirin. Newt'e soket yolu sağlanmalıdır.",
"enableDockerSocketLink": "Daha fazla bilgi",
"viewDockerContainers": "Docker Konteynerlerini Görüntüle",
"containersIn": "{siteName} içindeki konteynerler",
"selectContainerDescription": "Bu hedef için bir ana bilgisayar adı olarak kullanmak üzere herhangi bir konteyner seçin. Bir bağlantı noktası kullanmak için bir bağlantı noktasına tıklayın.",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "Ad Alanı: {namespace}",
"domainPickerShowMore": "Daha Fazla Göster",
"regionSelectorTitle": "Bölge Seç",
"domainPickerRemoteExitNodeWarning": "Belirtilen alan adları, siteler uzak çıkış düğümlerine bağlandığında desteklenmez. Kaynakların uzak düğümlerde kullanılabilir olması için özel bir alan adı kullanın.",
"regionSelectorInfo": "Bir bölge seçmek, konumunuz için daha iyi performans sağlamamıza yardımcı olur. Sunucunuzla aynı bölgede olmanıza gerek yoktur.",
"regionSelectorPlaceholder": "Bölge Seçin",
"regionSelectorComingSoon": "Yakında Geliyor",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "Özellik Kullanılabilirlik Bildirimi",
"billingFeatureLossDescription": "Plan düşürüldüğünde, yeni planda mevcut olmayan özellikler otomatik olarak devre dışı bırakılacaktır. Bazı ayarlar ve yapılar kaybolabilir. Hangi özelliklerin artık mevcut olmayacağını anlamak için fiyat tablosunu inceleyiniz.",
"billingUsageExceedsLimit": "Mevcut kullanım ({current}) limitleri ({limit}) aşıyor",
"billingPastDueTitle": "Ödeme Geçmiş",
"billingPastDueDescription": "Ödemenizın vadesi geçti. Mevcut plan özelliklerinizi kullanmaya devam etmek için lütfen ödeme yöntemini güncelleyin. Sorun çözülmezse aboneliğiniz iptal edilecek ve ücretsiz seviyeye dönüleceksiniz.",
"billingUnpaidTitle": "Ödenmemiş Abonelik",
"billingUnpaidDescription": "Aboneliğiniz ödenmedi ve ücretsiz seviyeye geri döndünüz. Aboneliğinizi geri yüklemek için lütfen ödeme yöntemini güncelleyin.",
"billingIncompleteTitle": "Eksik Ödeme",
"billingIncompleteDescription": "Ödemeniz eksik. Aboneliğinizi etkinleştirmek için lütfen ödeme sürecini tamamlayın.",
"billingIncompleteExpiredTitle": "Ödeme Süresi Doldu",
"billingIncompleteExpiredDescription": "Ödemeniz hiç tamamlanmadı ve süresi doldu. Ücretsiz seviyeye geri döndünüz. Ücretli özelliklere erişimi yeniden sağlamak için lütfen yeniden abone olun.",
"billingManageSubscription": "Aboneliğinizi Yönetin",
"billingResolvePaymentIssue": "Yükseltmeden veya düşürmeden önce ödeme sorunuzu çözün",
"signUpTerms": {
"IAgreeToThe": "Kabul ediyorum",
"termsOfService": "hizmet şartları",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "Zaman saniye cinsindendir",
"requireDeviceApproval": "Cihaz Onaylarını Gerektir",
"requireDeviceApprovalDescription": "Bu role sahip kullanıcıların yeni cihazlarının bağlanabilmesi ve kaynaklara erişebilmesi için bir yönetici tarafından onaylanması gerekiyor.",
"sshAccess": "SSH Erişimi",
"roleAllowSsh": "SSH'a İzin Ver",
"roleAllowSshAllow": "İzin Ver",
"roleAllowSshDisallow": "İzin Verme",
"roleAllowSshDescription": "Bu role sahip kullanıcıların SSH aracılığıyla kaynaklara bağlanmasına izin verin. Devre dışı bırakıldığında, rol SSH erişimini kullanamaz.",
"sshSudoMode": "Sudo Erişimi",
"sshSudoModeNone": "Hiçbiri",
"sshSudoModeNoneDescription": "Kullanıcı, sudo komutunu kullanarak komut çalıştıramaz.",
"sshSudoModeFull": "Tam Sudo",
"sshSudoModeFullDescription": "Kullanıcı, sudo komutuyla her türlü komutu çalıştırabilir.",
"sshSudoModeCommands": "Komutlar",
"sshSudoModeCommandsDescription": "Kullanıcı sadece belirtilen komutları sudo ile çalıştırabilir.",
"sshSudo": "Sudo'ya izin ver",
"sshSudoCommands": "Sudo Komutları",
"sshSudoCommandsDescription": "Kullanıcının sudo ile çalıştırmasına izin verilen komutların virgülle ayrılmış listesi.",
"sshCreateHomeDir": "Ev Dizini Oluştur",
"sshUnixGroups": "Unix Grupları",
"sshUnixGroupsDescription": "Hedef konakta kullanıcıya eklenecek Unix gruplarının virgülle ayrılmış listesi.",
"retryAttempts": "Tekrar Deneme Girişimleri",
"expectedResponseCodes": "Beklenen Yanıt Kodları",
"expectedResponseCodesDescription": "Sağlıklı durumu gösteren HTTP durum kodu. Boş bırakılırsa, 200-300 arası sağlıklı kabul edilir.",
@@ -1851,6 +1948,40 @@
"exitNode": ıkış Düğümü",
"country": "Ülke",
"rulesMatchCountry": "Şu anda kaynak IP'ye dayanarak",
"region": "Bölge",
"selectRegion": "Bölgeyi seçin",
"searchRegions": "Bölgeleri ara...",
"noRegionFound": "Bölge bulunamadı.",
"rulesMatchRegion": "Başka ülkelerin bölgesel gruplandırmasını seçin",
"rulesErrorInvalidRegion": "Geçersiz bölge",
"rulesErrorInvalidRegionDescription": "Lütfen geçerli bir bölge seçin.",
"regionAfrica": "Afrika",
"regionNorthernAfrica": "Kuzey Afrika",
"regionEasternAfrica": "Doğu Afrika",
"regionMiddleAfrica": "Orta Afrika",
"regionSouthernAfrica": "Güney Afrika",
"regionWesternAfrica": "Batı Afrika",
"regionAmericas": "Amerika",
"regionCaribbean": "Karayipler",
"regionCentralAmerica": "Orta Amerika",
"regionSouthAmerica": "Güney Amerika",
"regionNorthernAmerica": "Kuzey Amerika",
"regionAsia": "Asya",
"regionCentralAsia": "Orta Asya",
"regionEasternAsia": "Doğu Asya",
"regionSouthEasternAsia": "Güneydoğu Asya",
"regionSouthernAsia": "Güney Asya",
"regionWesternAsia": "Batı Asya",
"regionEurope": "Avrupa",
"regionEasternEurope": "Doğu Avrupa",
"regionNorthernEurope": "Kuzey Avrupa",
"regionSouthernEurope": "Güney Avrupa",
"regionWesternEurope": "Batı Avrupa",
"regionOceania": "Okyanusya",
"regionAustraliaAndNewZealand": "Avustralya ve Yeni Zelanda",
"regionMelanesia": "Melanezya",
"regionMicronesia": "Mikronezya",
"regionPolynesia": "Polinezya",
"managedSelfHosted": {
"title": "Yönetilen Self-Hosted",
"description": "Daha güvenilir ve düşük bakım gerektiren, ekstra özelliklere sahip kendi kendine barındırabileceğiniz Pangolin sunucusu",
@@ -1899,6 +2030,25 @@
"invalidValue": "Geçersiz değer",
"idpTypeLabel": "Kimlik Sağlayıcı Türü",
"roleMappingExpressionPlaceholder": "örn., contains(gruplar, 'yönetici') && 'Yönetici' || 'Üye'",
"roleMappingModeFixedRoles": "Sabit Roller",
"roleMappingModeMappingBuilder": "Harita Oluşturucu",
"roleMappingModeRawExpression": "Ham İfade",
"roleMappingFixedRolesPlaceholderSelect": "Bir veya daha fazla rol seçin",
"roleMappingFixedRolesPlaceholderFreeform": "Rol isimlerini yazın (organizasyon başına tam eşleşme)",
"roleMappingFixedRolesDescriptionSameForAll": "Her otomatik tedarik edilmiş kullanıcıya aynı rol setini atayın.",
"roleMappingFixedRolesDescriptionDefaultPolicy": "Varsayılan politikalar için, kullanıcıların sağlandığı her organizasyonda mevcut olan rol isimlerini yazın. İsimler tam olarak eşleşmelidir.",
"roleMappingClaimPath": "Hak Talep Yolu",
"roleMappingClaimPathPlaceholder": "gruplar",
"roleMappingClaimPathDescription": "Kaynak değerleri içeren belirteç yükündeki yol (örneğin, gruplar).",
"roleMappingMatchValue": "Eşleme Değeri",
"roleMappingAssignRoles": "Rolleri Ata",
"roleMappingAddMappingRule": "Eşleme Kuralı Ekle",
"roleMappingRawExpressionResultDescription": "İfade bir string veya string dizisine değerlendirilmelidir.",
"roleMappingRawExpressionResultDescriptionSingleRole": "İfade bir string (tek rol ismi) olarak değerlendirilmelidir.",
"roleMappingMatchValuePlaceholder": "Eşleme değeri (örneğin: admin)",
"roleMappingAssignRolesPlaceholderFreeform": "Rol isimlerini yazın (organizasyon başına tam eşleşme)",
"roleMappingBuilderFreeformRowHint": "Rol isimleri her hedef organizasyondaki bir rol ile eşleşmelidir.",
"roleMappingRemoveRule": "Kaldır",
"idpGoogleConfiguration": "Google Yapılandırması",
"idpGoogleConfigurationDescription": "Google OAuth2 kimlik bilgilerinizi yapılandırın",
"idpGoogleClientIdDescription": "Google OAuth2 İstemci Kimliğiniz",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "Erişim günlüklerini ne kadar süre tutacağını belirle",
"logRetentionActionLabel": "Eylem Günlüğü Saklama",
"logRetentionActionDescription": "Eylem günlüklerini ne kadar süre tutacağını belirle",
"logRetentionConnectionLabel": "Bağlantı kayıtlarını ne kadar süre saklayacağınız",
"logRetentionConnectionDescription": "Bağlantı kayıtlarını ne kadar süre saklayacağınız",
"logRetentionDisabled": "Devre Dışı",
"logRetention3Days": "3 gün",
"logRetention7Days": "7 gün",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "Bir sonraki yılın sonu",
"actionLogsDescription": "Bu organizasyondaki eylemler geçmişini görüntüleyin",
"accessLogsDescription": "Bu organizasyondaki kaynaklar için erişim kimlik doğrulama isteklerini görüntüleyin",
"licenseRequiredToUse": "Bu özelliği kullanmak için bir <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisansı gereklidir. Bu özellik ayrıca <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>'da da mevcuttur.",
"ossEnterpriseEditionRequired": "Bu özelliği kullanmak için <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> gereklidir. Bu özellik ayrıca <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>'da da mevcuttur.",
"connectionLogs": "Bağlantı Kayıtları",
"connectionLogsDescription": "Bu organizasyondaki tüneller için bağlantı geçmişine bakın",
"sidebarLogsConnection": "Bağlantı Kayıtları",
"sidebarLogsStreaming": "Akış",
"sourceAddress": "Kaynak Adresi",
"destinationAddress": "Hedef Adresi",
"duration": "Süre",
"licenseRequiredToUse": "Bu özelliği kullanmak için bir <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisansı veya <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> gereklidir. <bookADemoLink>Tanıtım veya POC denemesi ayarlayın</bookADemoLink>.",
"ossEnterpriseEditionRequired": "Bu özelliği kullanmak için <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> gereklidir. Bu özellik ayrıca <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>da da mevcuttur. <bookADemoLink>Tanıtım veya POC denemesi ayarlayın</bookADemoLink>.",
"certResolver": "Sertifika Çözücü",
"certResolverDescription": "Bu kaynak için kullanılacak sertifika çözücüsünü seçin.",
"selectCertResolver": "Sertifika Çözücü Seçin",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "Erişim Kontrolü",
"editInternalResourceDialogAccessControlDescription": "Bağlandığında bu kaynağa erişimi olan roller, kullanıcılar ve makine müşterilerini kontrol edin. Yöneticiler her zaman erişime sahiptir.",
"editInternalResourceDialogPortRangeValidationError": "Port aralığı, tüm portlar için \"*\" veya virgülle ayrılmış bir port ve aralık listesi olmalıdır (ör. \"80,443,8000-9000\"). Portlar 1 ile 65535 arasında olmalıdır.",
"internalResourceAuthDaemonStrategy": "SSH Kimlik Doğrulama Daemon Yeri",
"internalResourceAuthDaemonStrategyDescription": "SSH kimlik doğrulama sunucusunun nerede çalışacağını seçin: sitede (Newt) veya uzak bir ana bilgisayarda.",
"internalResourceAuthDaemonDescription": "SSH kimlik doğrulama sunucusu, bu kaynak için SSH anahtar imzalama ve PAM kimlik doğrulamasını yapar. Sitede (Newt) veya ayrı bir uzak ana bilgisayarda çalışıp çalışmayacağını seçin. Daha fazla bilgi için <docsLink> belgeleri</docsLink> görün.",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "Strateji Seçin",
"internalResourceAuthDaemonStrategyLabel": "Konum",
"internalResourceAuthDaemonSite": "Sitede",
"internalResourceAuthDaemonSiteDescription": "Kimlik doğrulama sunucusu sitede (Newt) çalışır.",
"internalResourceAuthDaemonRemote": "Uzak Ana Bilgisayar",
"internalResourceAuthDaemonRemoteDescription": "Kimlik doğrulama sunucusu, site olmayan bir ana bilgisayarda çalışır.",
"internalResourceAuthDaemonPort": "Daemon Portu (isteğe bağlı)",
"orgAuthWhatsThis": "Kuruluş kimliğimi nerede bulabilirim?",
"learnMore": "Daha fazla bilgi",
"backToHome": "Ana sayfaya geri dön",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "Cihaz Onaylarını Etkinleştir",
"approvalsEmptyStateStep2Description": "Bir rolü düzenleyin ve 'Cihaz Onaylarını Gerektir' seçeneğini etkinleştirin. Bu role sahip kullanıcıların yeni cihazlar için yönetici onayına ihtiyacı olacaktır.",
"approvalsEmptyStatePreviewDescription": "Önizleme: Etkinleştirildiğinde, bekleyen cihaz talepleri incelenmek üzere burada görünecektir.",
"approvalsEmptyStateButtonText": "Rolleri Yönet"
"approvalsEmptyStateButtonText": "Rolleri Yönet",
"domainErrorTitle": "Alan adınızı doğrulamada sorun yaşıyoruz",
"idpAdminAutoProvisionPoliciesTabHint": "Rol eşleme ve organizasyon politikalarını <policiesTabLink>Otomatik Tedarik Ayarları</policiesTabLink> sekmesinde yapılandırın.",
"streamingTitle": "Olay Akışı",
"streamingDescription": "Olayları organizasyonunuzdan dış hedeflere gerçek zamanlı olarak iletin.",
"streamingUnnamedDestination": "Adsız hedef",
"streamingNoUrlConfigured": "URL yapılandırılmadı",
"streamingAddDestination": "Hedef Ekle",
"streamingHttpWebhookTitle": "HTTP Webhook",
"streamingHttpWebhookDescription": "Esnek kimlik doğrulama ve şablon oluşturmayla her HTTP uç noktasına olaylar gönderin.",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "Olayları S3 uyumlu bir nesne depolama kovasına iletin. Yakında gelicek.",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "Olayları doğrudan Datadog hesabınıza iletin. Yakında gelicek.",
"streamingTypePickerDescription": "Başlamak için bir hedef türü seçin.",
"streamingFailedToLoad": "Hedefler yüklenemedi",
"streamingUnexpectedError": "Beklenmeyen bir hata oluştu.",
"streamingFailedToUpdate": "Hedef güncellenemedi",
"streamingDeletedSuccess": "Hedef başarıyla silindi",
"streamingFailedToDelete": "Hedef silinemedi",
"streamingDeleteTitle": "Hedefi Sil",
"streamingDeleteButtonText": "Hedefi Sil",
"streamingDeleteDialogAreYouSure": "Silmek istediğinizden emin misiniz",
"streamingDeleteDialogThisDestination": "bu hedefi",
"streamingDeleteDialogPermanentlyRemoved": "? Tüm yapılandırma kalıcı olarak kaldırılacak.",
"httpDestEditTitle": "Hedefi Düzenle",
"httpDestAddTitle": "HTTP Hedefi Ekle",
"httpDestEditDescription": "Bu HTTP olay akışı hedefine yapılandırmayı güncelleyin.",
"httpDestAddDescription": "Organizasyonunuzun olaylarını almak için yeni bir HTTP uç noktası yapılandırın.",
"httpDestTabSettings": "Ayarlar",
"httpDestTabHeaders": "Başlıklar",
"httpDestTabBody": "Gövde",
"httpDestTabLogs": "Kayıtlar",
"httpDestNamePlaceholder": "Benim HTTP hedefim",
"httpDestUrlLabel": "Hedef URL",
"httpDestUrlErrorHttpRequired": "URL http veya https kullanmalıdır",
"httpDestUrlErrorHttpsRequired": "Bulut dağıtımlarında HTTPS gereklidir",
"httpDestUrlErrorInvalid": "Geçerli bir URL girin (örn. https://example.com/webhook)",
"httpDestAuthTitle": "Kimlik Doğrulama",
"httpDestAuthDescription": "Uç noktanıza yapılan isteklerin nasıl kimlik doğrulandığını seçin.",
"httpDestAuthNoneTitle": "Kimlik Doğrulama Yok",
"httpDestAuthNoneDescription": "Yetkilendirme başlığı olmadan istekler gönderir.",
"httpDestAuthBearerTitle": "Taşıyıcı Jetonu",
"httpDestAuthBearerDescription": "Her isteğe bir Yetkilendirme: Taşıyıcı <token> başlığı ekler.",
"httpDestAuthBearerPlaceholder": "API anahtarınız veya jetonunuz",
"httpDestAuthBasicTitle": "Temel Kimlik Doğrulama",
"httpDestAuthBasicDescription": "Authorization: Temel <belirtecikler> başlığı ekler. Yetkilendirmeleri kullanıcı adı:şifre olarak sağlayın.",
"httpDestAuthBasicPlaceholder": "kullanıcı adı:şifre",
"httpDestAuthCustomTitle": "Özel Başlık",
"httpDestAuthCustomDescription": "Kimlik doğrulama için özel bir HTTP başlık adı ve değer belirtin (örn. X-API-Key).",
"httpDestAuthCustomHeaderNamePlaceholder": "Başlık adı (örn. X-API-Key)",
"httpDestAuthCustomHeaderValuePlaceholder": "Başlık değeri",
"httpDestCustomHeadersTitle": "Özel HTTP Başlıkları",
"httpDestCustomHeadersDescription": "Her giden isteğe özel başlıklar ekleyin. Statik jetonlar veya özel bir İçerik Türü için kullanışlıdır. Varsayılan olarak İçerik Türü: application/json gönderilir.",
"httpDestNoHeadersConfigured": "Özel başlık yapılandırılmamış. Bir tane eklemek için \"Başlık Ekle\"ye tıklayın.",
"httpDestHeaderNamePlaceholder": "Başlık adı",
"httpDestHeaderValuePlaceholder": "Değer",
"httpDestAddHeader": "Başlık Ekle",
"httpDestBodyTemplateTitle": "Özel Gövde Şablonu",
"httpDestBodyTemplateDescription": "Uç noktanıza gönderilen JSON yük yapısını kontrol edin. Devre dışı bırakılırsa, her olay için varsayılan bir JSON nesnesi gönderilir.",
"httpDestEnableBodyTemplate": "Özel gövde şablonunu etkinleştir",
"httpDestBodyTemplateLabel": "Gövde Şablonu (JSON)",
"httpDestBodyTemplateHint": "Yükünüzdeki olay alanlarına atıfta bulunmak için şablon değişkenlerini kullanın.",
"httpDestPayloadFormatTitle": "Yük Formatı",
"httpDestPayloadFormatDescription": "Her bir istek gövdesine olayların nasıl serileştirildiği.",
"httpDestFormatJsonArrayTitle": "JSON Dizisi",
"httpDestFormatJsonArrayDescription": "Her bir toplu işte bir istek, gövde bir JSON dizisidir. Çoğu genel webhook ve Datadog ile uyumludur.",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "Her bir toplu işte bir istek, gövde satırlarla ayrılmış JSON'dur - her satıra bir nesne, dış dizi yoktur. Splunk HEC, Elastic / OpenSearch ve Grafana Loki tarafından gereklidir.",
"httpDestFormatSingleTitle": "Her İstek Başına Bir Olay",
"httpDestFormatSingleDescription": "Her olay için ayrı bir HTTP POST gönderir. Toplu işlere yetkemeyen uç noktalar için kullanın.",
"httpDestLogTypesTitle": "Kayıt Türleri",
"httpDestLogTypesDescription": "Bu hedefe hangi kayıt türlerinin iletileceğini seçin. Yalnızca etkin kayıt türleri yayınlanacaktır.",
"httpDestAccessLogsTitle": "Erişim Kayıtları",
"httpDestAccessLogsDescription": "Kimlik doğrulanmış ve reddedilen talepler dahil kaynak erişim denemeleri.",
"httpDestActionLogsTitle": "Eylem Kayıtları",
"httpDestActionLogsDescription": "Kullanıcılar tarafından organizasyon içerisinde yapılan yönetici eylemleri.",
"httpDestConnectionLogsTitle": "Bağlantı Kayıtları",
"httpDestConnectionLogsDescription": "Site ve tünel bağlantı olayları, bağlantılar ve bağlantı kesilmeleri dahil.",
"httpDestRequestLogsTitle": "İstek Kayıtları",
"httpDestRequestLogsDescription": "Yönlendirilmiş kaynaklar için HTTP istek kayıtları, yöntem, yol ve yanıt kodu dahil.",
"httpDestSaveChanges": "Değişiklikleri Kaydet",
"httpDestCreateDestination": "Hedef Oluştur",
"httpDestUpdatedSuccess": "Hedef başarıyla güncellendi",
"httpDestCreatedSuccess": "Hedef başarıyla oluşturuldu",
"httpDestUpdateFailed": "Hedef güncellenemedi",
"httpDestCreateFailed": "Hedef oluşturulamadı"
}

View File

@@ -148,6 +148,11 @@
"createLink": "创建链接",
"resourcesNotFound": "找不到资源",
"resourceSearch": "搜索资源",
"machineSearch": "搜索机",
"machinesSearch": "搜索机器客户端...",
"machineNotFound": "未找到任何机",
"userDeviceSearch": "搜索用户设备",
"userDevicesSearch": "搜索用户设备...",
"openMenu": "打开菜单",
"resource": "资源",
"title": "标题",
@@ -175,6 +180,7 @@
"resourceHTTPDescription": "通过使用完全限定的域名的HTTPS代理请求。",
"resourceRaw": "TCP/UDP 资源",
"resourceRawDescription": "通过使用端口号的原始TCP/UDP代理请求。",
"resourceRawDescriptionCloud": "正在使用端口号使用 TCP/UDP 代理请求。需要站点连接到远程节点。",
"resourceCreate": "创建资源",
"resourceCreateDescription": "按照下面的步骤创建新资源",
"resourceSeeAll": "查看所有资源",
@@ -322,6 +328,54 @@
"apiKeysDelete": "删除 API 密钥",
"apiKeysManage": "管理 API 密钥",
"apiKeysDescription": "API 密钥用于认证集成 API",
"provisioningKeysTitle": "置备密钥",
"provisioningKeysManage": "管理置备键",
"provisioningKeysDescription": "置备密钥用于验证您组织的自动站点配置。",
"provisioningManage": "置备中",
"provisioningDescription": "管理预配键和审查等待批准的站点。",
"pendingSites": "待定站点",
"siteApproveSuccess": "站点批准成功",
"siteApproveError": "批准站点出错",
"provisioningKeys": "置备键",
"searchProvisioningKeys": "搜索配备密钥...",
"provisioningKeysAdd": "生成置备键",
"provisioningKeysErrorDelete": "删除预配键时出错",
"provisioningKeysErrorDeleteMessage": "删除预配键时出错",
"provisioningKeysQuestionRemove": "您确定要从组织中删除此预配键吗?",
"provisioningKeysMessageRemove": "一旦移除,密钥不能再用于站点预配。",
"provisioningKeysDeleteConfirm": "确认删除置备键",
"provisioningKeysDelete": "删除置备键",
"provisioningKeysCreate": "生成置备键",
"provisioningKeysCreateDescription": "为组织生成一个新的预置密钥",
"provisioningKeysSeeAll": "查看所有预配键",
"provisioningKeysSave": "保存预配键",
"provisioningKeysSaveDescription": "您只能看到一次。复制它到一个安全的地方。",
"provisioningKeysErrorCreate": "创建预配键时出错",
"provisioningKeysList": "新建预配键",
"provisioningKeysMaxBatchSize": "最大批量大小",
"provisioningKeysUnlimitedBatchSize": "无限批量大小(无限制)",
"provisioningKeysMaxBatchUnlimited": "无限制",
"provisioningKeysMaxBatchSizeInvalid": "输入一个有效的最大批处理大小(1-1,000,000)。",
"provisioningKeysValidUntil": "有效期至",
"provisioningKeysValidUntilHint": "留空为无过期。",
"provisioningKeysValidUntilInvalid": "输入一个有效的日期和时间。",
"provisioningKeysNumUsed": "使用的时间",
"provisioningKeysLastUsed": "上次使用",
"provisioningKeysNoExpiry": "没有过期",
"provisioningKeysNeverUsed": "永不过期",
"provisioningKeysEdit": "编辑置备键",
"provisioningKeysEditDescription": "更新此密钥的最大批量大小和过期时间。",
"provisioningKeysApproveNewSites": "批准新站点",
"provisioningKeysApproveNewSitesDescription": "自动批准使用此密钥注册的站点。",
"provisioningKeysUpdateError": "更新预配键时出错",
"provisioningKeysUpdated": "置备密钥已更新",
"provisioningKeysUpdatedDescription": "您的更改已保存。",
"provisioningKeysBannerTitle": "站点置备密钥",
"provisioningKeysBannerDescription": "生成一个预配键并使用它来在首次启动时自动创建站点——无需为每个站点设置单独的凭证。",
"provisioningKeysBannerButtonText": "了解更多",
"pendingSitesBannerTitle": "待定站点",
"pendingSitesBannerDescription": "使用预配键连接的站点会出现在这里供审核。在站点开始运行之前批准并获取对您资源的访问权限。",
"pendingSitesBannerButtonText": "了解更多",
"apiKeysSettings": "{apiKeyName} 设置",
"userTitle": "管理所有用户",
"userDescription": "查看和管理系统中的所有用户",
@@ -508,9 +562,12 @@
"userSaved": "用户已保存",
"userSavedDescription": "用户已更新。",
"autoProvisioned": "自动设置",
"autoProvisionSettings": "自动提供设置",
"autoProvisionedDescription": "允许此用户由身份提供商自动管理",
"accessControlsDescription": "管理此用户在组织中可以访问和做什么",
"accessControlsSubmit": "保存访问控制",
"singleRolePerUserPlanNotice": "您的计划仅支持每个用户一个角色。",
"singleRolePerUserEditionNotice": "此版本仅支持每个用户一个角色。",
"roles": "角色",
"accessUsersRoles": "管理用户和角色",
"accessUsersRolesDescription": "邀请用户加入角色来管理访问组织",
@@ -650,6 +707,7 @@
"resourcesErrorUpdate": "切换资源失败",
"resourcesErrorUpdateDescription": "更新资源时出错",
"access": "访问权限",
"accessControl": "访问控制",
"shareLink": "{resource} 的分享链接",
"resourceSelect": "选择资源",
"shareLinks": "分享链接",
@@ -790,6 +848,7 @@
"accessRoleRemoved": "角色已删除",
"accessRoleRemovedDescription": "角色已成功删除。",
"accessRoleRequiredRemove": "删除此角色之前,请选择一个新角色来转移现有成员。",
"network": "网络",
"manage": "管理",
"sitesNotFound": "未找到站点。",
"pangolinServerAdmin": "服务器管理员 - Pangolin",
@@ -1037,7 +1096,6 @@
"pageNotFoundDescription": "哎呀!您正在查找的页面不存在。",
"overview": "概览",
"home": "首页",
"accessControl": "访问控制",
"settings": "设置",
"usersAll": "所有用户",
"license": "许可协议",
@@ -1100,6 +1158,12 @@
"actionGetUser": "获取用户",
"actionGetOrgUser": "获取组织用户",
"actionListOrgDomains": "列出组织域",
"actionGetDomain": "获取域",
"actionCreateOrgDomain": "创建域",
"actionUpdateOrgDomain": "更新域",
"actionDeleteOrgDomain": "删除域",
"actionGetDNSRecords": "获取 DNS 记录",
"actionRestartOrgDomain": "重新启动域",
"actionCreateSite": "创建站点",
"actionDeleteSite": "删除站点",
"actionGetSite": "获取站点",
@@ -1111,6 +1175,7 @@
"setupTokenDescription": "从服务器控制台输入设置令牌。",
"setupTokenRequired": "需要设置令牌",
"actionUpdateSite": "更新站点",
"actionResetSiteBandwidth": "重置组织带宽",
"actionListSiteRoles": "允许站点角色列表",
"actionCreateResource": "创建资源",
"actionDeleteResource": "删除资源",
@@ -1140,6 +1205,7 @@
"actionRemoveUser": "删除用户",
"actionListUsers": "列出用户",
"actionAddUserRole": "添加用户角色",
"actionSetUserOrgRoles": "设置用户角色",
"actionGenerateAccessToken": "生成访问令牌",
"actionDeleteAccessToken": "删除访问令牌",
"actionListAccessTokens": "访问令牌",
@@ -1249,12 +1315,14 @@
"sidebarClientResources": "非公开的",
"sidebarAccessControl": "访问控制",
"sidebarLogsAndAnalytics": "日志与分析",
"sidebarTeam": "团队",
"sidebarUsers": "用户",
"sidebarAdmin": "管理员",
"sidebarInvitations": "邀请",
"sidebarRoles": "角色",
"sidebarShareableLinks": "链接",
"sidebarApiKeys": "API密钥",
"sidebarProvisioning": "置备中",
"sidebarSettings": "设置",
"sidebarAllUsers": "所有用户",
"sidebarIdentityProviders": "身份提供商",
@@ -1267,6 +1335,7 @@
"sidebarLogAndAnalytics": "日志与分析",
"sidebarBluePrints": "蓝图",
"sidebarOrganization": "组织",
"sidebarManagement": "管理",
"sidebarBillingAndLicenses": "帐单和许可证",
"sidebarLogsAnalytics": "分析",
"blueprints": "蓝图",
@@ -1289,7 +1358,6 @@
"parsedContents": "解析内容 (只读)",
"enableDockerSocket": "启用 Docker 蓝图",
"enableDockerSocketDescription": "启用 Docker Socket 标签擦除蓝图标签。套接字路径必须提供给新的。",
"enableDockerSocketLink": "了解更多",
"viewDockerContainers": "查看停靠容器",
"containersIn": "{siteName} 中的容器",
"selectContainerDescription": "选择任何容器作为目标的主机名。点击端口使用端口。",
@@ -1417,6 +1485,7 @@
"domainPickerNamespace": "命名空间:{namespace}",
"domainPickerShowMore": "显示更多",
"regionSelectorTitle": "选择区域",
"domainPickerRemoteExitNodeWarning": "当站点连接到远程退出节点时不支持所提供的域。为了资源可在远程节点上使用,请使用自定义域名。",
"regionSelectorInfo": "选择区域以帮助提升您所在地的性能。您不必与服务器在相同的区域。",
"regionSelectorPlaceholder": "选择一个区域",
"regionSelectorComingSoon": "即将推出",
@@ -1570,6 +1639,16 @@
"billingFeatureLossWarning": "功能可用通知",
"billingFeatureLossDescription": "如果降级,新计划中不可用的功能将被自动禁用。一些设置和配置可能会丢失。 请查看定价矩阵以了解哪些功能将不再可用。",
"billingUsageExceedsLimit": "当前使用量 ({current}) 超出限制 ({limit})",
"billingPastDueTitle": "过去到期的付款",
"billingPastDueDescription": "您的付款已过期。请更新您的付款方法以继续使用您当前的计划功能。 如果不解决,您的订阅将被取消,您将被恢复到免费等级。",
"billingUnpaidTitle": "订阅未付款",
"billingUnpaidDescription": "您的订阅未付,您已恢复到免费等级。请更新您的付款方法以恢复您的订阅。",
"billingIncompleteTitle": "付款不完成",
"billingIncompleteDescription": "您的付款不完整。请完成付款过程以激活您的订阅。",
"billingIncompleteExpiredTitle": "付款已过期",
"billingIncompleteExpiredDescription": "您的付款尚未完成且已过期。您已恢复到免费级别。请再次订阅以恢复对已支付功能的访问。",
"billingManageSubscription": "管理您的订阅",
"billingResolvePaymentIssue": "请在升级或降级之前解决您的付款问题",
"signUpTerms": {
"IAgreeToThe": "我同意",
"termsOfService": "服务条款",
@@ -1643,6 +1722,24 @@
"timeIsInSeconds": "时间以秒为单位",
"requireDeviceApproval": "需要设备批准",
"requireDeviceApprovalDescription": "具有此角色的用户需要管理员批准的新设备才能连接和访问资源。",
"sshAccess": "SSH 访问",
"roleAllowSsh": "允许 SSH",
"roleAllowSshAllow": "允许",
"roleAllowSshDisallow": "不允许",
"roleAllowSshDescription": "允许具有此角色的用户通过 SSH 连接到资源。禁用时,角色不能使用 SSH 访问。",
"sshSudoMode": "Sudo 访问",
"sshSudoModeNone": "无",
"sshSudoModeNoneDescription": "用户不能用sudo运行命令。",
"sshSudoModeFull": "全苏多",
"sshSudoModeFullDescription": "用户可以用 sudo 运行任何命令。",
"sshSudoModeCommands": "命令",
"sshSudoModeCommandsDescription": "用户只能用 sudo 运行指定的命令。",
"sshSudo": "允许Sudo",
"sshSudoCommands": "Sudo 命令",
"sshSudoCommandsDescription": "逗号分隔的用户允许使用 sudo 运行的命令列表。",
"sshCreateHomeDir": "创建主目录",
"sshUnixGroups": "Unix 组",
"sshUnixGroupsDescription": "用逗号分隔了Unix组将用户添加到目标主机上。",
"retryAttempts": "重试次数",
"expectedResponseCodes": "期望响应代码",
"expectedResponseCodesDescription": "HTTP 状态码表示健康状态。如留空200-300 被视为健康。",
@@ -1851,6 +1948,40 @@
"exitNode": "出口节点",
"country": "国家",
"rulesMatchCountry": "当前基于源 IP",
"region": "地区",
"selectRegion": "选择区域",
"searchRegions": "搜索区域...",
"noRegionFound": "未找到区域。",
"rulesMatchRegion": "选择一个区域国家组",
"rulesErrorInvalidRegion": "无效区域",
"rulesErrorInvalidRegionDescription": "请选择一个有效的区域。",
"regionAfrica": "非洲",
"regionNorthernAfrica": "B. 北非地区",
"regionEasternAfrica": "东部非洲",
"regionMiddleAfrica": "中东",
"regionSouthernAfrica": "D. 南 非",
"regionWesternAfrica": "D. 西部非洲",
"regionAmericas": "Americas",
"regionCaribbean": "加勒比",
"regionCentralAmerica": "中美洲:",
"regionSouthAmerica": "南 非",
"regionNorthernAmerica": "北美洲:",
"regionAsia": "亚洲",
"regionCentralAsia": "B. 亚 洲",
"regionEasternAsia": "东亚",
"regionSouthEasternAsia": "D. 东南亚区域",
"regionSouthernAsia": "D. 亚 洲",
"regionWesternAsia": "西亚",
"regionEurope": "欧洲",
"regionEasternEurope": "D. 欧 洲",
"regionNorthernEurope": "北欧洲",
"regionSouthernEurope": "南欧洲",
"regionWesternEurope": "西欧洲",
"regionOceania": "Oceania",
"regionAustraliaAndNewZealand": "澳大利亚和新西兰",
"regionMelanesia": "Melanesia",
"regionMicronesia": "Micronesia",
"regionPolynesia": "Polynesia",
"managedSelfHosted": {
"title": "托管自托管",
"description": "更可靠和低维护自我托管的 Pangolin 服务器,带有额外的铃声和告密器",
@@ -1899,6 +2030,25 @@
"invalidValue": "无效的值",
"idpTypeLabel": "身份提供者类型",
"roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'",
"roleMappingModeFixedRoles": "固定角色",
"roleMappingModeMappingBuilder": "映射构建器",
"roleMappingModeRawExpression": "原始表达式",
"roleMappingFixedRolesPlaceholderSelect": "选择一个或多个角色",
"roleMappingFixedRolesPlaceholderFreeform": "输入角色名称 (每个组织确切匹配)",
"roleMappingFixedRolesDescriptionSameForAll": "将相同的角色分配给每个自动配备的用户。",
"roleMappingFixedRolesDescriptionDefaultPolicy": "对于缺省策略,每个提供用户的组织中存在的角色名称类型。名称必须完全匹配。",
"roleMappingClaimPath": "认领路径",
"roleMappingClaimPathPlaceholder": "组",
"roleMappingClaimPathDescription": "包含源值的 token 有效负载路径 (例如组)。",
"roleMappingMatchValue": "匹配值",
"roleMappingAssignRoles": "分配角色",
"roleMappingAddMappingRule": "添加映射规则",
"roleMappingRawExpressionResultDescription": "表达式必须值为字符串或字符串。",
"roleMappingRawExpressionResultDescriptionSingleRole": "表达式必须计算到字符串(单个角色名称)。",
"roleMappingMatchValuePlaceholder": "匹配值(例如: 管理员)",
"roleMappingAssignRolesPlaceholderFreeform": "输入角色名称 (每个组织确切)",
"roleMappingBuilderFreeformRowHint": "角色名称必须匹配每个目标组织的角色。",
"roleMappingRemoveRule": "删除",
"idpGoogleConfiguration": "Google 配置",
"idpGoogleConfigurationDescription": "配置 Google OAuth2 凭据",
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
@@ -2295,6 +2445,8 @@
"logRetentionAccessDescription": "保留访问日志的时间",
"logRetentionActionLabel": "动作日志保留",
"logRetentionActionDescription": "保留操作日志的时间",
"logRetentionConnectionLabel": "连接日志保留",
"logRetentionConnectionDescription": "保留连接日志的时间",
"logRetentionDisabled": "已禁用",
"logRetention3Days": "3 天",
"logRetention7Days": "7 天",
@@ -2305,8 +2457,15 @@
"logRetentionEndOfFollowingYear": "下一年结束",
"actionLogsDescription": "查看此机构执行的操作历史",
"accessLogsDescription": "查看此机构资源的访问认证请求",
"licenseRequiredToUse": "需要 <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> 许可才能使用此功能。此功能也可在 <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> 中使用。",
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> 需要使用此功能。此功能也可在 <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> 中使用。",
"connectionLogs": "连接日志",
"connectionLogsDescription": "查看此机构隧道的连接日志",
"sidebarLogsConnection": "连接日志",
"sidebarLogsStreaming": "流流",
"sourceAddress": "源地址",
"destinationAddress": "目的地址",
"duration": "期限",
"licenseRequiredToUse": "使用此功能需要<enterpriseLicenseLink>企业版</enterpriseLicenseLink>许可证或<pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>。<bookADemoLink>预约演示或POC试用</bookADemoLink>。",
"ossEnterpriseEditionRequired": "需要 <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> 才能使用此功能。 此功能也可在 <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>上获取。 <bookADemoLink>预订演示或POC 试用</bookADemoLink>。",
"certResolver": "证书解决器",
"certResolverDescription": "选择用于此资源的证书解析器。",
"selectCertResolver": "选择证书解析",
@@ -2503,6 +2662,17 @@
"editInternalResourceDialogAccessControl": "访问控制",
"editInternalResourceDialogAccessControlDescription": "控制当连接到此资源时,哪些角色、用户和机器客户端可以访问。管理员始终具有访问权。",
"editInternalResourceDialogPortRangeValidationError": "端口范围必须为\"*\"表示所有端口,或一个用逗号分隔的端口和范围列表(例如:\"80,443,8000-9000\"。端口必须在1到65535之间。",
"internalResourceAuthDaemonStrategy": "SSH 认证守护进程位置",
"internalResourceAuthDaemonStrategyDescription": "选择 SSH 身份验证守护进程在哪里运行:站点(新建) 或远程主机。",
"internalResourceAuthDaemonDescription": "SSH 身份验证守护程序处理此资源的 SSH 密钥签名和PAM 身份验证。 选择它是在站点(新建)还是在单独的远程主机上运行。请参阅 <docsLink>文档</docsLink>。",
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
"internalResourceAuthDaemonStrategyPlaceholder": "选择策略",
"internalResourceAuthDaemonStrategyLabel": "地点",
"internalResourceAuthDaemonSite": "在站点",
"internalResourceAuthDaemonSiteDescription": "认证守护进程在站点上运行(新建)。",
"internalResourceAuthDaemonRemote": "远程主机",
"internalResourceAuthDaemonRemoteDescription": "认证守护进程运行在不是站点的主机上。",
"internalResourceAuthDaemonPort": "守护进程端口(可选)",
"orgAuthWhatsThis": "我的组织ID在哪里可以找到",
"learnMore": "了解更多",
"backToHome": "返回首页",
@@ -2632,5 +2802,91 @@
"approvalsEmptyStateStep2Title": "启用设备批准",
"approvalsEmptyStateStep2Description": "编辑角色并启用“需要设备审批”选项。具有此角色的用户需要管理员批准新设备。",
"approvalsEmptyStatePreviewDescription": "预览:如果启用,待处理设备请求将出现在这里供审核",
"approvalsEmptyStateButtonText": "管理角色"
"approvalsEmptyStateButtonText": "管理角色",
"domainErrorTitle": "我们在验证您的域名时遇到了问题",
"idpAdminAutoProvisionPoliciesTabHint": "在 <policiesTabLink>自动供应设置</policiesTabLink> 选项卡上配置角色映射和组织策略。",
"streamingTitle": "事件流",
"streamingDescription": "实时将事件从您的组织流到外部目的地。",
"streamingUnnamedDestination": "未命名目标",
"streamingNoUrlConfigured": "未配置URL",
"streamingAddDestination": "添加目标",
"streamingHttpWebhookTitle": "HTTP Webhook",
"streamingHttpWebhookDescription": "将事件发送到任意HTTP端点并灵活验证和模板。",
"streamingS3Title": "Amazon S3",
"streamingS3Description": "将事件串流到 S3 兼容的对象存储桶。即将推出。",
"streamingDatadogTitle": "Datadog",
"streamingDatadogDescription": "直接转发事件到您的Datadog 帐户。即将推出。",
"streamingTypePickerDescription": "选择要开始的目标类型。",
"streamingFailedToLoad": "加载目的地失败",
"streamingUnexpectedError": "发生意外错误.",
"streamingFailedToUpdate": "更新目标失败",
"streamingDeletedSuccess": "目标删除成功",
"streamingFailedToDelete": "删除目标失败",
"streamingDeleteTitle": "删除目标",
"streamingDeleteButtonText": "删除目标",
"streamingDeleteDialogAreYouSure": "您确定要删除吗?",
"streamingDeleteDialogThisDestination": "这个目标",
"streamingDeleteDialogPermanentlyRemoved": "? 所有配置将被永久删除。",
"httpDestEditTitle": "编辑目标",
"httpDestAddTitle": "添加 HTTP 目标",
"httpDestEditDescription": "更新此 HTTP 事件流媒体目的地的配置。",
"httpDestAddDescription": "配置新的 HTTP 端点来接收您的组织事件。",
"httpDestTabSettings": "设置",
"httpDestTabHeaders": "信头",
"httpDestTabBody": "正文内容",
"httpDestTabLogs": "日志",
"httpDestNamePlaceholder": "我的 HTTP 目标",
"httpDestUrlLabel": "目标网址",
"httpDestUrlErrorHttpRequired": "URL 必须使用 http 或 https",
"httpDestUrlErrorHttpsRequired": "云端部署需要HTTPS",
"httpDestUrlErrorInvalid": "输入一个有效的 URL (例如https://example.com/webhook)",
"httpDestAuthTitle": "认证",
"httpDestAuthDescription": "选择如何验证您的端点的请求。",
"httpDestAuthNoneTitle": "无身份验证",
"httpDestAuthNoneDescription": "在没有授权头的情况下发送请求。",
"httpDestAuthBearerTitle": "持有者令牌",
"httpDestAuthBearerDescription": "添加授权:每个请求的标题为 <token>。",
"httpDestAuthBearerPlaceholder": "您的 API 密钥或令牌",
"httpDestAuthBasicTitle": "基本认证",
"httpDestAuthBasicDescription": "添加授权:基本 <credentials> 头。提供用户名:密码的凭据。",
"httpDestAuthBasicPlaceholder": "用户名:密码",
"httpDestAuthCustomTitle": "自定义标题",
"httpDestAuthCustomDescription": "指定自定义 HTTP 头名称和身份验证值 (例如X-API 键)。",
"httpDestAuthCustomHeaderNamePlaceholder": "标题名称(例如X-API-键)",
"httpDestAuthCustomHeaderValuePlaceholder": "页眉值",
"httpDestCustomHeadersTitle": "自定义 HTTP 头",
"httpDestCustomHeadersDescription": "向每个输出请求添加自定义标题。用于静态令牌或自定义内容类型。默认情况下,内容类型:应用程序/json已发送。",
"httpDestNoHeadersConfigured": "未配置自定义头。单击\"添加头\"以添加一个。",
"httpDestHeaderNamePlaceholder": "标题名称",
"httpDestHeaderValuePlaceholder": "值",
"httpDestAddHeader": "添加标题",
"httpDestBodyTemplateTitle": "自定义实体模板",
"httpDestBodyTemplateDescription": "控制发送到您的端点的 JSON 有效载荷结构。如果禁用,将为每个事件发送一个 JSON 默认对象。",
"httpDestEnableBodyTemplate": "启用自定义实体模板",
"httpDestBodyTemplateLabel": "身体模板 (JSON)",
"httpDestBodyTemplateHint": "将模板变量用于您有效载荷中的参考事件字段。",
"httpDestPayloadFormatTitle": "有效载荷格式",
"httpDestPayloadFormatDescription": "事件如何序列化为每个请求实体。",
"httpDestFormatJsonArrayTitle": "JSON 数组",
"httpDestFormatJsonArrayDescription": "每批一个请求,实体是一个 JSON 数组。与大多数通用的 Web 钩子和数据兼容。",
"httpDestFormatNdjsonTitle": "NDJSON",
"httpDestFormatNdjsonDescription": "每批有一个请求,物体是换行符限制的 JSON ——每行一个对象,不是外部数组。 Sluk HEC、Elastic / OpenSearch和Grafana Loki所需。",
"httpDestFormatSingleTitle": "每个请求一个事件",
"httpDestFormatSingleDescription": "为每个事件单独发送一个 HTTP POST。仅用于无法处理批量的端点。",
"httpDestLogTypesTitle": "日志类型",
"httpDestLogTypesDescription": "选择转发到此目的地的日志类型。只有启用的日志类型才会被连续使用。",
"httpDestAccessLogsTitle": "访问日志",
"httpDestAccessLogsDescription": "资源访问尝试,包括已验证和拒绝的请求。",
"httpDestActionLogsTitle": "操作日志",
"httpDestActionLogsDescription": "组织内部用户采取的行政行动。",
"httpDestConnectionLogsTitle": "连接日志",
"httpDestConnectionLogsDescription": "站点和隧道连接事件,包括连接和断开连接。",
"httpDestRequestLogsTitle": "请求日志",
"httpDestRequestLogsDescription": "HTTP 请求代理资源日志,包括方法、路径和响应代码。",
"httpDestSaveChanges": "保存更改",
"httpDestCreateDestination": "创建目标",
"httpDestUpdatedSuccess": "目标已成功更新",
"httpDestCreatedSuccess": "目标创建成功",
"httpDestUpdateFailed": "更新目标失败",
"httpDestCreateFailed": "创建目标失败"
}

View File

@@ -1091,6 +1091,7 @@
"actionRemoveUser": "刪除用戶",
"actionListUsers": "列出用戶",
"actionAddUserRole": "添加用戶角色",
"actionSetUserOrgRoles": "Set User Roles",
"actionGenerateAccessToken": "生成訪問令牌",
"actionDeleteAccessToken": "刪除訪問令牌",
"actionListAccessTokens": "訪問令牌",

5890
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -32,9 +32,9 @@
"format": "prettier --write ."
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "8.4.0",
"@aws-sdk/client-s3": "3.989.0",
"@faker-js/faker": "10.3.0",
"@asteasolutions/zod-to-openapi": "8.5.0",
"@aws-sdk/client-s3": "3.1021.0",
"@faker-js/faker": "10.4.0",
"@headlessui/react": "2.2.9",
"@hookform/resolvers": "5.2.2",
"@monaco-editor/react": "4.7.0",
@@ -59,16 +59,16 @@
"@radix-ui/react-tabs": "1.1.13",
"@radix-ui/react-toast": "1.2.15",
"@radix-ui/react-tooltip": "1.2.8",
"@react-email/components": "1.0.7",
"@react-email/components": "1.0.8",
"@react-email/render": "2.0.4",
"@react-email/tailwind": "2.0.4",
"@simplewebauthn/browser": "13.2.2",
"@simplewebauthn/server": "13.2.2",
"@react-email/tailwind": "2.0.5",
"@simplewebauthn/browser": "13.3.0",
"@simplewebauthn/server": "13.3.0",
"@tailwindcss/forms": "0.5.11",
"@tanstack/react-query": "5.90.21",
"@tanstack/react-query": "5.96.0",
"@tanstack/react-table": "8.21.3",
"arctic": "3.7.0",
"axios": "1.13.5",
"axios": "1.14.0",
"better-sqlite3": "11.9.1",
"canvas-confetti": "1.9.4",
"class-variance-authority": "0.7.1",
@@ -80,61 +80,61 @@
"d3": "7.9.0",
"drizzle-orm": "0.45.1",
"express": "5.2.1",
"express-rate-limit": "8.2.1",
"glob": "13.0.3",
"express-rate-limit": "8.3.0",
"glob": "13.0.6",
"helmet": "8.1.0",
"http-errors": "2.0.1",
"input-otp": "1.4.2",
"ioredis": "5.9.3",
"ioredis": "5.10.0",
"jmespath": "0.16.0",
"js-yaml": "4.1.1",
"jsonwebtoken": "9.0.3",
"lucide-react": "0.563.0",
"lucide-react": "0.577.0",
"maxmind": "5.0.5",
"moment": "2.30.1",
"next": "15.5.12",
"next-intl": "4.8.2",
"next": "15.5.14",
"next-intl": "4.8.3",
"next-themes": "0.4.6",
"nextjs-toploader": "3.9.17",
"node-cache": "5.1.2",
"nodemailer": "8.0.1",
"nodemailer": "8.0.4",
"oslo": "1.2.1",
"pg": "8.18.0",
"posthog-node": "5.24.15",
"pg": "8.20.0",
"posthog-node": "5.28.0",
"qrcode.react": "4.2.0",
"react": "19.2.4",
"react-day-picker": "9.13.2",
"react-day-picker": "9.14.0",
"react-dom": "19.2.4",
"react-easy-sort": "1.8.0",
"react-hook-form": "7.71.1",
"react-icons": "5.5.0",
"react-hook-form": "7.72.0",
"react-icons": "5.6.0",
"recharts": "2.15.4",
"reodotdev": "1.0.0",
"resend": "6.9.2",
"reodotdev": "1.1.0",
"resend": "6.10.0",
"semver": "7.7.4",
"sshpk": "^1.18.0",
"stripe": "20.3.1",
"sshpk": "1.18.0",
"stripe": "20.4.1",
"swagger-ui-express": "5.0.1",
"tailwind-merge": "3.4.0",
"tailwind-merge": "3.5.0",
"topojson-client": "3.1.0",
"tw-animate-css": "1.4.0",
"use-debounce": "^10.1.0",
"use-debounce": "10.1.0",
"uuid": "13.0.0",
"vaul": "1.1.2",
"visionscarto-world-atlas": "1.0.0",
"winston": "3.19.0",
"winston-daily-rotate-file": "5.0.0",
"ws": "8.19.0",
"yaml": "2.8.2",
"ws": "8.20.0",
"yaml": "2.8.3",
"yargs": "18.0.0",
"zod": "4.3.6",
"zod-validation-error": "5.0.0"
},
"devDependencies": {
"@dotenvx/dotenvx": "1.52.0",
"@dotenvx/dotenvx": "1.54.1",
"@esbuild-plugins/tsconfig-paths": "0.1.2",
"@react-email/preview-server": "5.2.8",
"@tailwindcss/postcss": "4.1.18",
"@react-email/preview-server": "5.2.10",
"@tailwindcss/postcss": "4.2.2",
"@tanstack/react-query-devtools": "5.91.3",
"@types/better-sqlite3": "7.6.13",
"@types/cookie-parser": "1.4.10",
@@ -146,31 +146,35 @@
"@types/jmespath": "0.15.2",
"@types/js-yaml": "4.0.9",
"@types/jsonwebtoken": "9.0.10",
"@types/node": "25.2.3",
"@types/nodemailer": "7.0.9",
"@types/node": "25.3.5",
"@types/nodemailer": "7.0.11",
"@types/nprogress": "0.2.3",
"@types/pg": "8.16.0",
"@types/pg": "8.18.0",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@types/semver": "7.7.1",
"@types/sshpk": "^1.17.4",
"@types/sshpk": "1.17.4",
"@types/swagger-ui-express": "4.1.8",
"@types/topojson-client": "3.1.5",
"@types/ws": "8.18.1",
"@types/yargs": "17.0.35",
"babel-plugin-react-compiler": "1.0.0",
"drizzle-kit": "0.31.9",
"esbuild": "0.27.3",
"drizzle-kit": "0.31.10",
"esbuild": "0.27.4",
"esbuild-node-externals": "1.20.1",
"eslint": "9.39.2",
"eslint-config-next": "16.1.6",
"postcss": "8.5.6",
"eslint": "10.0.3",
"eslint-config-next": "16.1.7",
"postcss": "8.5.8",
"prettier": "3.8.1",
"react-email": "5.2.8",
"tailwindcss": "4.1.18",
"react-email": "5.2.10",
"tailwindcss": "4.2.2",
"tsc-alias": "1.8.16",
"tsx": "4.21.0",
"typescript": "5.9.3",
"typescript-eslint": "8.55.0"
"typescript-eslint": "8.56.1"
},
"overrides": {
"esbuild": "0.27.4",
"dompurify": "3.3.2"
}
}

BIN
public/third-party/dd.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
public/third-party/s3.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,9 +1,10 @@
import { Request } from "express";
import { db } from "@server/db";
import { userActions, roleActions, userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import { userActions, roleActions } from "@server/db";
import { and, eq, inArray } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
import { getUserOrgRoleIds } from "@server/lib/userOrgRoles";
export enum ActionsEnum {
createOrgUser = "createOrgUser",
@@ -19,6 +20,7 @@ export enum ActionsEnum {
getSite = "getSite",
listSites = "listSites",
updateSite = "updateSite",
resetSiteBandwidth = "resetSiteBandwidth",
reGenerateSecret = "reGenerateSecret",
createResource = "createResource",
deleteResource = "deleteResource",
@@ -52,6 +54,8 @@ export enum ActionsEnum {
listRoleResources = "listRoleResources",
// listRoleActions = "listRoleActions",
addUserRole = "addUserRole",
removeUserRole = "removeUserRole",
setUserOrgRoles = "setUserOrgRoles",
// addUserSite = "addUserSite",
// addUserAction = "addUserAction",
// removeUserAction = "removeUserAction",
@@ -108,6 +112,10 @@ export enum ActionsEnum {
listApiKeyActions = "listApiKeyActions",
listApiKeys = "listApiKeys",
getApiKey = "getApiKey",
createSiteProvisioningKey = "createSiteProvisioningKey",
listSiteProvisioningKeys = "listSiteProvisioningKeys",
updateSiteProvisioningKey = "updateSiteProvisioningKey",
deleteSiteProvisioningKey = "deleteSiteProvisioningKey",
getCertificate = "getCertificate",
restartCertificate = "restartCertificate",
billing = "billing",
@@ -132,7 +140,11 @@ export enum ActionsEnum {
exportLogs = "exportLogs",
listApprovals = "listApprovals",
updateApprovals = "updateApprovals",
signSshKey = "signSshKey"
signSshKey = "signSshKey",
createEventStreamingDestination = "createEventStreamingDestination",
updateEventStreamingDestination = "updateEventStreamingDestination",
deleteEventStreamingDestination = "deleteEventStreamingDestination",
listEventStreamingDestinations = "listEventStreamingDestinations"
}
export async function checkUserActionPermission(
@@ -153,29 +165,16 @@ export async function checkUserActionPermission(
}
try {
let userOrgRoleId = req.userOrgRoleId;
let userOrgRoleIds = req.userOrgRoleIds;
// If userOrgRoleId is not available on the request, fetch it
if (userOrgRoleId === undefined) {
const userOrgRole = await db
.select()
.from(userOrgs)
.where(
and(
eq(userOrgs.userId, userId),
eq(userOrgs.orgId, req.userOrgId!)
)
)
.limit(1);
if (userOrgRole.length === 0) {
if (userOrgRoleIds === undefined) {
userOrgRoleIds = await getUserOrgRoleIds(userId, req.userOrgId!);
if (userOrgRoleIds.length === 0) {
throw createHttpError(
HttpCode.FORBIDDEN,
"User does not have access to this organization"
);
}
userOrgRoleId = userOrgRole[0].roleId;
}
// Check if the user has direct permission for the action in the current org
@@ -186,7 +185,7 @@ export async function checkUserActionPermission(
and(
eq(userActions.userId, userId),
eq(userActions.actionId, actionId),
eq(userActions.orgId, req.userOrgId!) // TODO: we cant pass the org id if we are not checking the org
eq(userActions.orgId, req.userOrgId!)
)
)
.limit(1);
@@ -195,14 +194,14 @@ export async function checkUserActionPermission(
return true;
}
// If no direct permission, check role-based permission
// If no direct permission, check role-based permission (any of user's roles)
const roleActionPermission = await db
.select()
.from(roleActions)
.where(
and(
eq(roleActions.actionId, actionId),
eq(roleActions.roleId, userOrgRoleId!),
inArray(roleActions.roleId, userOrgRoleIds),
eq(roleActions.orgId, req.userOrgId!)
)
)

View File

@@ -1,26 +1,29 @@
import { db } from "@server/db";
import { and, eq } from "drizzle-orm";
import { and, eq, inArray } from "drizzle-orm";
import { roleResources, userResources } from "@server/db";
export async function canUserAccessResource({
userId,
resourceId,
roleId
roleIds
}: {
userId: string;
resourceId: number;
roleId: number;
roleIds: number[];
}): Promise<boolean> {
const roleResourceAccess = await db
.select()
.from(roleResources)
.where(
and(
eq(roleResources.resourceId, resourceId),
eq(roleResources.roleId, roleId)
)
)
.limit(1);
const roleResourceAccess =
roleIds.length > 0
? await db
.select()
.from(roleResources)
.where(
and(
eq(roleResources.resourceId, resourceId),
inArray(roleResources.roleId, roleIds)
)
)
.limit(1)
: [];
if (roleResourceAccess.length > 0) {
return true;

View File

@@ -1,26 +1,29 @@
import { db } from "@server/db";
import { and, eq } from "drizzle-orm";
import { and, eq, inArray } from "drizzle-orm";
import { roleSiteResources, userSiteResources } from "@server/db";
export async function canUserAccessSiteResource({
userId,
resourceId,
roleId
roleIds
}: {
userId: string;
resourceId: number;
roleId: number;
roleIds: number[];
}): Promise<boolean> {
const roleResourceAccess = await db
.select()
.from(roleSiteResources)
.where(
and(
eq(roleSiteResources.siteResourceId, resourceId),
eq(roleSiteResources.roleId, roleId)
)
)
.limit(1);
const roleResourceAccess =
roleIds.length > 0
? await db
.select()
.from(roleSiteResources)
.where(
and(
eq(roleSiteResources.siteResourceId, resourceId),
inArray(roleSiteResources.roleId, roleIds)
)
)
.limit(1)
: [];
if (roleResourceAccess.length > 0) {
return true;

View File

@@ -3,7 +3,14 @@ import {
encodeHexLowerCase
} from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { resourceSessions, Session, sessions, User, users } from "@server/db";
import {
resourceSessions,
safeRead,
Session,
sessions,
User,
users
} from "@server/db";
import { db } from "@server/db";
import { eq, inArray } from "drizzle-orm";
import config from "@server/lib/config";
@@ -54,11 +61,15 @@ export async function validateSessionToken(
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token))
);
const result = await db
.select({ user: users, session: sessions })
.from(sessions)
.innerJoin(users, eq(sessions.userId, users.userId))
.where(eq(sessions.sessionId, sessionId));
const result = await safeRead((db) =>
db
.select({ user: users, session: sessions })
.from(sessions)
.innerJoin(users, eq(sessions.userId, users.userId))
.where(eq(sessions.sessionId, sessionId))
);
if (result.length < 1) {
return { session: null, user: null };
}

View File

@@ -1,7 +1,7 @@
import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { resourceSessions, ResourceSession } from "@server/db";
import { db } from "@server/db";
import { db, safeRead } from "@server/db";
import { eq, and } from "drizzle-orm";
import config from "@server/lib/config";
@@ -66,15 +66,17 @@ export async function validateResourceSessionToken(
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token))
);
const result = await db
.select()
.from(resourceSessions)
.where(
and(
eq(resourceSessions.sessionId, sessionId),
eq(resourceSessions.resourceId, resourceId)
const result = await safeRead((db) =>
db
.select()
.from(resourceSessions)
.where(
and(
eq(resourceSessions.sessionId, sessionId),
eq(resourceSessions.resourceId, resourceId)
)
)
);
);
if (result.length < 1) {
return { resourceSession: null };
@@ -85,7 +87,7 @@ export async function validateResourceSessionToken(
if (Date.now() >= resourceSession.expiresAt) {
await db
.delete(resourceSessions)
.where(eq(resourceSessions.sessionId, resourceSessions.sessionId));
.where(eq(resourceSessions.sessionId, sessionId));
return { resourceSession: null };
} else if (
Date.now() >=
@@ -179,7 +181,7 @@ export function serializeResourceSessionCookie(
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${domain}`;
} else {
if (expiresAt === undefined) {
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=$domain}`;
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=${domain}`;
}
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${domain}`;
}

View File

@@ -1,6 +1,14 @@
import { flushBandwidthToDb } from "@server/routers/newt/handleReceiveBandwidthMessage";
import { flushConnectionLogToDb } from "#dynamic/routers/newt";
import { flushSiteBandwidthToDb } from "@server/routers/gerbil/receiveBandwidth";
import { stopPingAccumulator } from "@server/routers/newt/pingAccumulator";
import { cleanup as wsCleanup } from "#dynamic/routers/ws";
async function cleanup() {
await stopPingAccumulator();
await flushBandwidthToDb();
await flushConnectionLogToDb();
await flushSiteBandwidthToDb();
await wsCleanup();
process.exit(0);

View File

@@ -1,7 +1,7 @@
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { readConfigFile } from "@server/lib/readConfigFile";
import { withReplicas } from "drizzle-orm/pg-core";
import { createPool } from "./poolConfig";
function createDb() {
const config = readConfigFile();
@@ -39,12 +39,17 @@ function createDb() {
// Create connection pools instead of individual connections
const poolConfig = config.postgres.pool;
const primaryPool = new Pool({
const maxConnections = poolConfig?.max_connections || 20;
const idleTimeoutMs = poolConfig?.idle_timeout_ms || 30000;
const connectionTimeoutMs = poolConfig?.connection_timeout_ms || 5000;
const primaryPool = createPool(
connectionString,
max: poolConfig?.max_connections || 20,
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000,
connectionTimeoutMillis: poolConfig?.connection_timeout_ms || 5000
});
maxConnections,
idleTimeoutMs,
connectionTimeoutMs,
"primary"
);
const replicas = [];
@@ -55,14 +60,15 @@ function createDb() {
})
);
} else {
const maxReplicaConnections = poolConfig?.max_replica_connections || 20;
for (const conn of replicaConnections) {
const replicaPool = new Pool({
connectionString: conn.connection_string,
max: poolConfig?.max_replica_connections || 20,
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000,
connectionTimeoutMillis:
poolConfig?.connection_timeout_ms || 5000
});
const replicaPool = createPool(
conn.connection_string,
maxReplicaConnections,
idleTimeoutMs,
connectionTimeoutMs,
"replica"
);
replicas.push(
DrizzlePostgres(replicaPool, {
logger: process.env.QUERY_LOGGING == "true"
@@ -85,3 +91,4 @@ export const primaryDb = db.$primary;
export type Transaction = Parameters<
Parameters<(typeof db)["transaction"]>[0]
>[0];
export const DB_TYPE: "pg" | "sqlite" = "pg";

View File

@@ -1,4 +1,6 @@
export * from "./driver";
export * from "./logsDriver";
export * from "./safeRead";
export * from "./schema/schema";
export * from "./schema/privateSchema";
export * from "./migrate";

View File

@@ -0,0 +1,94 @@
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
import { readConfigFile } from "@server/lib/readConfigFile";
import { withReplicas } from "drizzle-orm/pg-core";
import { build } from "@server/build";
import { db as mainDb, primaryDb as mainPrimaryDb } from "./driver";
import { createPool } from "./poolConfig";
function createLogsDb() {
// Only use separate logs database in SaaS builds
if (build !== "saas") {
return mainDb;
}
const config = readConfigFile();
// Merge configs, prioritizing private config
const logsConfig = config.postgres_logs;
// Check environment variable first
let connectionString = process.env.POSTGRES_LOGS_CONNECTION_STRING;
let replicaConnections: Array<{ connection_string: string }> = [];
if (!connectionString && logsConfig) {
connectionString = logsConfig.connection_string;
replicaConnections = logsConfig.replicas || [];
}
// If POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS is set, use it
if (process.env.POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS) {
replicaConnections =
process.env.POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS.split(",").map(
(conn) => ({
connection_string: conn.trim()
})
);
}
// If no logs database is configured, fall back to main database
if (!connectionString) {
return mainDb;
}
// Create separate connection pool for logs database
const poolConfig = logsConfig?.pool || config.postgres?.pool;
const maxConnections = poolConfig?.max_connections || 20;
const idleTimeoutMs = poolConfig?.idle_timeout_ms || 30000;
const connectionTimeoutMs = poolConfig?.connection_timeout_ms || 5000;
const primaryPool = createPool(
connectionString,
maxConnections,
idleTimeoutMs,
connectionTimeoutMs,
"logs-primary"
);
const replicas = [];
if (!replicaConnections.length) {
replicas.push(
DrizzlePostgres(primaryPool, {
logger: process.env.QUERY_LOGGING == "true"
})
);
} else {
const maxReplicaConnections =
poolConfig?.max_replica_connections || 20;
for (const conn of replicaConnections) {
const replicaPool = createPool(
conn.connection_string,
maxReplicaConnections,
idleTimeoutMs,
connectionTimeoutMs,
"logs-replica"
);
replicas.push(
DrizzlePostgres(replicaPool, {
logger: process.env.QUERY_LOGGING == "true"
})
);
}
}
return withReplicas(
DrizzlePostgres(primaryPool, {
logger: process.env.QUERY_LOGGING == "true"
}),
replicas as any
);
}
export const logsDb = createLogsDb();
export default logsDb;
export const primaryLogsDb = logsDb.$primary;

View File

@@ -0,0 +1,63 @@
import { Pool, PoolConfig } from "pg";
import logger from "@server/logger";
export function createPoolConfig(
connectionString: string,
maxConnections: number,
idleTimeoutMs: number,
connectionTimeoutMs: number
): PoolConfig {
return {
connectionString,
max: maxConnections,
idleTimeoutMillis: idleTimeoutMs,
connectionTimeoutMillis: connectionTimeoutMs,
// TCP keepalive to prevent silent connection drops by NAT gateways,
// load balancers, and other intermediate network devices (e.g. AWS
// NAT Gateway drops idle TCP connections after ~350s)
keepAlive: true,
keepAliveInitialDelayMillis: 10000, // send first keepalive after 10s of idle
// Allow connections to be released and recreated more aggressively
// to avoid stale connections building up
allowExitOnIdle: false
};
}
export function attachPoolErrorHandlers(pool: Pool, label: string): void {
pool.on("error", (err) => {
// This catches errors on idle clients in the pool. Without this
// handler an unexpected disconnect would crash the process.
logger.error(
`Unexpected error on idle ${label} database client: ${err.message}`
);
});
pool.on("connect", (client) => {
// Set a statement timeout on every new connection so a single slow
// query can't block the pool forever
client.query("SET statement_timeout = '30s'").catch((err: Error) => {
logger.warn(
`Failed to set statement_timeout on ${label} client: ${err.message}`
);
});
});
}
export function createPool(
connectionString: string,
maxConnections: number,
idleTimeoutMs: number,
connectionTimeoutMs: number,
label: string
): Pool {
const pool = new Pool(
createPoolConfig(
connectionString,
maxConnections,
idleTimeoutMs,
connectionTimeoutMs
)
);
attachPoolErrorHandlers(pool, label);
return pool;
}

24
server/db/pg/safeRead.ts Normal file
View File

@@ -0,0 +1,24 @@
import { db, primaryDb } from "./driver";
/**
* Runs a read query with replica fallback for Postgres.
* Executes the query against the replica first (when replicas exist).
* If the query throws or returns no data (null, undefined, or empty array),
* runs the same query against the primary.
*/
export async function safeRead<T>(
query: (d: typeof db | typeof primaryDb) => Promise<T>
): Promise<T> {
try {
const result = await query(db);
if (result === undefined || result === null) {
return query(primaryDb);
}
if (Array.isArray(result) && result.length === 0) {
return query(primaryDb);
}
return result;
} catch {
return query(primaryDb);
}
}

View File

@@ -7,7 +7,9 @@ import {
bigint,
real,
text,
index
index,
primaryKey,
uniqueIndex
} from "drizzle-orm/pg-core";
import { InferSelectModel } from "drizzle-orm";
import {
@@ -17,7 +19,9 @@ import {
users,
exitNodes,
sessions,
clients
clients,
siteResources,
sites
} from "./schema";
export const certificates = pgTable("certificates", {
@@ -89,7 +93,9 @@ export const subscriptions = pgTable("subscriptions", {
export const subscriptionItems = pgTable("subscriptionItems", {
subscriptionItemId: serial("subscriptionItemId").primaryKey(),
stripeSubscriptionItemId: varchar("stripeSubscriptionItemId", { length: 255 }),
stripeSubscriptionItemId: varchar("stripeSubscriptionItemId", {
length: 255
}),
subscriptionId: varchar("subscriptionId", { length: 255 })
.notNull()
.references(() => subscriptions.subscriptionId, {
@@ -286,6 +292,7 @@ export const accessAuditLog = pgTable(
actor: varchar("actor", { length: 255 }),
actorId: varchar("actorId", { length: 255 }),
resourceId: integer("resourceId"),
siteResourceId: integer("siteResourceId"),
ip: varchar("ip", { length: 45 }),
type: varchar("type", { length: 100 }).notNull(),
action: boolean("action").notNull(),
@@ -302,6 +309,45 @@ export const accessAuditLog = pgTable(
]
);
export const connectionAuditLog = pgTable(
"connectionAuditLog",
{
id: serial("id").primaryKey(),
sessionId: text("sessionId").notNull(),
siteResourceId: integer("siteResourceId").references(
() => siteResources.siteResourceId,
{ onDelete: "cascade" }
),
orgId: text("orgId").references(() => orgs.orgId, {
onDelete: "cascade"
}),
siteId: integer("siteId").references(() => sites.siteId, {
onDelete: "cascade"
}),
clientId: integer("clientId").references(() => clients.clientId, {
onDelete: "cascade"
}),
userId: text("userId").references(() => users.userId, {
onDelete: "cascade"
}),
sourceAddr: text("sourceAddr").notNull(),
destAddr: text("destAddr").notNull(),
protocol: text("protocol").notNull(),
startedAt: integer("startedAt").notNull(),
endedAt: integer("endedAt"),
bytesTx: integer("bytesTx"),
bytesRx: integer("bytesRx")
},
(table) => [
index("idx_accessAuditLog_startedAt").on(table.startedAt),
index("idx_accessAuditLog_org_startedAt").on(
table.orgId,
table.startedAt
),
index("idx_accessAuditLog_siteResourceId").on(table.siteResourceId)
]
);
export const approvals = pgTable("approvals", {
approvalId: serial("approvalId").primaryKey(),
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
@@ -328,6 +374,90 @@ export const approvals = pgTable("approvals", {
.notNull()
});
export const bannedEmails = pgTable("bannedEmails", {
email: varchar("email", { length: 255 }).primaryKey()
});
export const bannedIps = pgTable("bannedIps", {
ip: varchar("ip", { length: 255 }).primaryKey()
});
export const siteProvisioningKeys = pgTable("siteProvisioningKeys", {
siteProvisioningKeyId: varchar("siteProvisioningKeyId", {
length: 255
}).primaryKey(),
name: varchar("name", { length: 255 }).notNull(),
siteProvisioningKeyHash: text("siteProvisioningKeyHash").notNull(),
lastChars: varchar("lastChars", { length: 4 }).notNull(),
createdAt: varchar("dateCreated", { length: 255 }).notNull(),
lastUsed: varchar("lastUsed", { length: 255 }),
maxBatchSize: integer("maxBatchSize"), // null = no limit
numUsed: integer("numUsed").notNull().default(0),
validUntil: varchar("validUntil", { length: 255 }),
approveNewSites: boolean("approveNewSites").notNull().default(true)
});
export const siteProvisioningKeyOrg = pgTable(
"siteProvisioningKeyOrg",
{
siteProvisioningKeyId: varchar("siteProvisioningKeyId", {
length: 255
})
.notNull()
.references(() => siteProvisioningKeys.siteProvisioningKeyId, {
onDelete: "cascade"
}),
orgId: varchar("orgId", { length: 255 })
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" })
},
(table) => [
primaryKey({
columns: [table.siteProvisioningKeyId, table.orgId]
})
]
);
export const eventStreamingDestinations = pgTable(
"eventStreamingDestinations",
{
destinationId: serial("destinationId").primaryKey(),
orgId: varchar("orgId", { length: 255 })
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
sendConnectionLogs: boolean("sendConnectionLogs").notNull().default(false),
sendRequestLogs: boolean("sendRequestLogs").notNull().default(false),
sendActionLogs: boolean("sendActionLogs").notNull().default(false),
sendAccessLogs: boolean("sendAccessLogs").notNull().default(false),
type: varchar("type", { length: 50 }).notNull(), // e.g. "http", "kafka", etc.
config: text("config").notNull(), // JSON string with the configuration for the destination
enabled: boolean("enabled").notNull().default(true),
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
}
);
export const eventStreamingCursors = pgTable(
"eventStreamingCursors",
{
cursorId: serial("cursorId").primaryKey(),
destinationId: integer("destinationId")
.notNull()
.references(() => eventStreamingDestinations.destinationId, {
onDelete: "cascade"
}),
logType: varchar("logType", { length: 50 }).notNull(), // "request" | "action" | "access" | "connection"
lastSentId: bigint("lastSentId", { mode: "number" }).notNull().default(0),
lastSentAt: bigint("lastSentAt", { mode: "number" }) // epoch milliseconds, null if never sent
},
(table) => [
uniqueIndex("idx_eventStreamingCursors_dest_type").on(
table.destinationId,
table.logType
)
]
);
export type Approval = InferSelectModel<typeof approvals>;
export type Limit = InferSelectModel<typeof limits>;
export type Account = InferSelectModel<typeof account>;
@@ -349,3 +479,19 @@ export type LoginPage = InferSelectModel<typeof loginPage>;
export type LoginPageBranding = InferSelectModel<typeof loginPageBranding>;
export type ActionAuditLog = InferSelectModel<typeof actionAuditLog>;
export type AccessAuditLog = InferSelectModel<typeof accessAuditLog>;
export type ConnectionAuditLog = InferSelectModel<typeof connectionAuditLog>;
export type SessionTransferToken = InferSelectModel<
typeof sessionTransferToken
>;
export type BannedEmail = InferSelectModel<typeof bannedEmails>;
export type BannedIp = InferSelectModel<typeof bannedIps>;
export type SiteProvisioningKey = InferSelectModel<typeof siteProvisioningKeys>;
export type SiteProvisioningKeyOrg = InferSelectModel<
typeof siteProvisioningKeyOrg
>;
export type EventStreamingDestination = InferSelectModel<
typeof eventStreamingDestinations
>;
export type EventStreamingCursor = InferSelectModel<
typeof eventStreamingCursors
>;

View File

@@ -6,9 +6,11 @@ import {
index,
integer,
pgTable,
primaryKey,
real,
serial,
text,
unique,
varchar
} from "drizzle-orm/pg-core";
@@ -22,7 +24,8 @@ export const domains = pgTable("domains", {
tries: integer("tries").notNull().default(0),
certResolver: varchar("certResolver"),
customCertResolver: varchar("customCertResolver"),
preferWildcardCert: boolean("preferWildcardCert")
preferWildcardCert: boolean("preferWildcardCert"),
errorMessage: text("errorMessage")
});
export const dnsRecords = pgTable("dnsRecords", {
@@ -54,6 +57,9 @@ export const orgs = pgTable("orgs", {
settingsLogRetentionDaysAction: integer("settingsLogRetentionDaysAction") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
.notNull()
.default(0),
settingsLogRetentionDaysConnection: integer("settingsLogRetentionDaysConnection") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
.notNull()
.default(0),
sshCaPrivateKey: text("sshCaPrivateKey"), // Encrypted SSH CA private key (PEM format)
sshCaPublicKey: text("sshCaPublicKey"), // SSH CA public key (OpenSSH format)
isBillingOrg: boolean("isBillingOrg"),
@@ -88,12 +94,14 @@ export const sites = pgTable("sites", {
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
type: varchar("type").notNull(), // "newt" or "wireguard"
online: boolean("online").notNull().default(false),
lastPing: integer("lastPing"),
address: varchar("address"),
endpoint: varchar("endpoint"),
publicKey: varchar("publicKey"),
lastHolePunch: bigint("lastHolePunch", { mode: "number" }),
listenPort: integer("listenPort"),
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true)
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true),
status: varchar("status").$type<"pending" | "approved">().default("approved")
});
export const resources = pgTable("resources", {
@@ -232,7 +240,11 @@ export const siteResources = pgTable("siteResources", {
aliasAddress: varchar("aliasAddress"),
tcpPortRangeString: varchar("tcpPortRangeString").notNull().default("*"),
udpPortRangeString: varchar("udpPortRangeString").notNull().default("*"),
disableIcmp: boolean("disableIcmp").notNull().default(false)
disableIcmp: boolean("disableIcmp").notNull().default(false),
authDaemonPort: integer("authDaemonPort").default(22123),
authDaemonMode: varchar("authDaemonMode", { length: 32 })
.$type<"site" | "remote">()
.default("site")
});
export const clientSiteResources = pgTable("clientSiteResources", {
@@ -279,8 +291,10 @@ export const users = pgTable("user", {
dateCreated: varchar("dateCreated").notNull(),
termsAcceptedTimestamp: varchar("termsAcceptedTimestamp"),
termsVersion: varchar("termsVersion"),
marketingEmailConsent: boolean("marketingEmailConsent").default(false),
serverAdmin: boolean("serverAdmin").notNull().default(false),
lastPasswordChange: bigint("lastPasswordChange", { mode: "number" })
lastPasswordChange: bigint("lastPasswordChange", { mode: "number" }),
locale: varchar("locale")
});
export const newts = pgTable("newt", {
@@ -328,9 +342,6 @@ export const userOrgs = pgTable("userOrgs", {
onDelete: "cascade"
})
.notNull(),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId),
isOwner: boolean("isOwner").notNull().default(false),
autoProvisioned: boolean("autoProvisioned").default(false),
pamUsername: varchar("pamUsername") // cleaned username for ssh and such
@@ -372,9 +383,29 @@ export const roles = pgTable("roles", {
isAdmin: boolean("isAdmin"),
name: varchar("name").notNull(),
description: varchar("description"),
requireDeviceApproval: boolean("requireDeviceApproval").default(false)
requireDeviceApproval: boolean("requireDeviceApproval").default(false),
sshSudoMode: varchar("sshSudoMode", { length: 32 }).default("none"), // "none" | "full" | "commands"
sshSudoCommands: text("sshSudoCommands").default("[]"),
sshCreateHomeDir: boolean("sshCreateHomeDir").default(true),
sshUnixGroups: text("sshUnixGroups").default("[]")
});
export const userOrgRoles = pgTable(
"userOrgRoles",
{
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
},
(t) => [unique().on(t.userId, t.orgId, t.roleId)]
);
export const roleActions = pgTable("roleActions", {
roleId: integer("roleId")
.notNull()
@@ -442,12 +473,22 @@ export const userInvites = pgTable("userInvites", {
.references(() => orgs.orgId, { onDelete: "cascade" }),
email: varchar("email").notNull(),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
tokenHash: varchar("token").notNull(),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
tokenHash: varchar("token").notNull()
});
export const userInviteRoles = pgTable(
"userInviteRoles",
{
inviteId: varchar("inviteId")
.notNull()
.references(() => userInvites.inviteId, { onDelete: "cascade" }),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
},
(t) => [primaryKey({ columns: [t.inviteId, t.roleId] })]
);
export const resourcePincode = pgTable("resourcePincode", {
pincodeId: serial("pincodeId").primaryKey(),
resourceId: integer("resourceId")
@@ -711,6 +752,7 @@ export const clientSitesAssociationsCache = pgTable(
.notNull(),
siteId: integer("siteId").notNull(),
isRelayed: boolean("isRelayed").notNull().default(false),
isJitMode: boolean("isJitMode").notNull().default(false),
endpoint: varchar("endpoint"),
publicKey: varchar("publicKey") // this will act as the session's public key for hole punching so we can track when it changes
}
@@ -1022,7 +1064,9 @@ export type UserSite = InferSelectModel<typeof userSites>;
export type RoleResource = InferSelectModel<typeof roleResources>;
export type UserResource = InferSelectModel<typeof userResources>;
export type UserInvite = InferSelectModel<typeof userInvites>;
export type UserInviteRole = InferSelectModel<typeof userInviteRoles>;
export type UserOrg = InferSelectModel<typeof userOrgs>;
export type UserOrgRole = InferSelectModel<typeof userOrgRoles>;
export type ResourceSession = InferSelectModel<typeof resourceSessions>;
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
@@ -1059,4 +1103,6 @@ export type SecurityKey = InferSelectModel<typeof securityKeys>;
export type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;
export type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
export type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
export type RoundTripMessageTracker = InferSelectModel<typeof roundTripMessageTracker>;
export type RoundTripMessageTracker = InferSelectModel<
typeof roundTripMessageTracker
>;

View File

@@ -1,4 +1,12 @@
import { db, loginPage, LoginPage, loginPageOrg, Org, orgs, roles } from "@server/db";
import {
db,
loginPage,
LoginPage,
loginPageOrg,
Org,
orgs,
roles
} from "@server/db";
import {
Resource,
ResourcePassword,
@@ -12,13 +20,12 @@ import {
resources,
roleResources,
sessions,
userOrgs,
userResources,
users,
ResourceHeaderAuthExtendedCompatibility,
resourceHeaderAuthExtendedCompatibility
} from "@server/db";
import { and, eq } from "drizzle-orm";
import { and, eq, inArray } from "drizzle-orm";
export type ResourceWithAuth = {
resource: Resource | null;
@@ -104,24 +111,15 @@ export async function getUserSessionWithUser(
}
/**
* Get user organization role
* Get role name by role ID (for display).
*/
export async function getUserOrgRole(userId: string, orgId: string) {
const userOrgRole = await db
.select({
userId: userOrgs.userId,
orgId: userOrgs.orgId,
roleId: userOrgs.roleId,
isOwner: userOrgs.isOwner,
autoProvisioned: userOrgs.autoProvisioned,
roleName: roles.name
})
.from(userOrgs)
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
.leftJoin(roles, eq(userOrgs.roleId, roles.roleId))
export async function getRoleName(roleId: number): Promise<string | null> {
const [row] = await db
.select({ name: roles.name })
.from(roles)
.where(eq(roles.roleId, roleId))
.limit(1);
return userOrgRole.length > 0 ? userOrgRole[0] : null;
return row?.name ?? null;
}
/**
@@ -129,7 +127,7 @@ export async function getUserOrgRole(userId: string, orgId: string) {
*/
export async function getRoleResourceAccess(
resourceId: number,
roleId: number
roleIds: number[]
) {
const roleResourceAccess = await db
.select()
@@ -137,12 +135,11 @@ export async function getRoleResourceAccess(
.where(
and(
eq(roleResources.resourceId, resourceId),
eq(roleResources.roleId, roleId)
inArray(roleResources.roleId, roleIds)
)
)
.limit(1);
);
return roleResourceAccess.length > 0 ? roleResourceAccess[0] : null;
return roleResourceAccess.length > 0 ? roleResourceAccess : null;
}
/**

196
server/db/regions.ts Normal file
View File

@@ -0,0 +1,196 @@
// Regions of the World
// as of 2025-10-25
//
// Adapted according to the United Nations Geoscheme
// see https://www.unicode.org/cldr/charts/48/supplemental/territory_containment_un_m_49.html
// see https://unstats.un.org/unsd/methodology/m49
export const REGIONS = [
{
name: "regionAfrica",
id: "002",
includes: [
{
name: "regionNorthernAfrica",
id: "015",
countries: ["DZ", "EG", "LY", "MA", "SD", "TN", "EH"]
},
{
name: "regionEasternAfrica",
id: "014",
countries: ["IO", "BI", "KM", "DJ", "ER", "ET", "TF", "KE", "MG", "MW", "MU", "YT", "MZ", "RE", "RW", "SC", "SO", "SS", "UG", "ZM", "ZW"]
},
{
name: "regionMiddleAfrica",
id: "017",
countries: ["AO", "CM", "CF", "TD", "CG", "CD", "GQ", "GA", "ST"]
},
{
name: "regionSouthernAfrica",
id: "018",
countries: ["BW", "SZ", "LS", "NA", "ZA"]
},
{
name: "regionWesternAfrica",
id: "011",
countries: ["BJ", "BF", "CV", "CI", "GM", "GH", "GN", "GW", "LR", "ML", "MR", "NE", "NG", "SH", "SN", "SL", "TG"]
}
]
},
{
name: "regionAmericas",
id: "019",
includes: [
{
name: "regionCaribbean",
id: "029",
countries: ["AI", "AG", "AW", "BS", "BB", "BQ", "VG", "KY", "CU", "CW", "DM", "DO", "GD", "GP", "HT", "JM", "MQ", "MS", "PR", "BL", "KN", "LC", "MF", "VC", "SX", "TT", "TC", "VI"]
},
{
name: "regionCentralAmerica",
id: "013",
countries: ["BZ", "CR", "SV", "GT", "HN", "MX", "NI", "PA"]
},
{
name: "regionSouthAmerica",
id: "005",
countries: ["AR", "BO", "BV", "BR", "CL", "CO", "EC", "FK", "GF", "GY", "PY", "PE", "GS", "SR", "UY", "VE"]
},
{
name: "regionNorthernAmerica",
id: "021",
countries: ["BM", "CA", "GL", "PM", "US"]
}
]
},
{
name: "regionAsia",
id: "142",
includes: [
{
name: "regionCentralAsia",
id: "143",
countries: ["KZ", "KG", "TJ", "TM", "UZ"]
},
{
name: "regionEasternAsia",
id: "030",
countries: ["CN", "HK", "MO", "KP", "JP", "MN", "KR"]
},
{
name: "regionSouthEasternAsia",
id: "035",
countries: ["BN", "KH", "ID", "LA", "MY", "MM", "PH", "SG", "TH", "TL", "VN"]
},
{
name: "regionSouthernAsia",
id: "034",
countries: ["AF", "BD", "BT", "IN", "IR", "MV", "NP", "PK", "LK"]
},
{
name: "regionWesternAsia",
id: "145",
countries: ["AM", "AZ", "BH", "CY", "GE", "IQ", "IL", "JO", "KW", "LB", "OM", "QA", "SA", "PS", "SY", "TR", "AE", "YE"]
}
]
},
{
name: "regionEurope",
id: "150",
includes: [
{
name: "regionEasternEurope",
id: "151",
countries: ["BY", "BG", "CZ", "HU", "PL", "MD", "RO", "RU", "SK", "UA"]
},
{
name: "regionNorthernEurope",
id: "154",
countries: ["AX", "DK", "EE", "FO", "FI", "GG", "IS", "IE", "IM", "JE", "LV", "LT", "NO", "SJ", "SE", "GB"]
},
{
name: "regionSouthernEurope",
id: "039",
countries: ["AL", "AD", "BA", "HR", "GI", "GR", "VA", "IT", "MT", "ME", "MK", "PT", "SM", "RS", "SI", "ES"]
},
{
name: "regionWesternEurope",
id: "155",
countries: ["AT", "BE", "FR", "DE", "LI", "LU", "MC", "NL", "CH"]
}
]
},
{
name: "regionOceania",
id: "009",
includes: [
{
name: "regionAustraliaAndNewZealand",
id: "053",
countries: ["AU", "CX", "CC", "HM", "NZ", "NF"]
},
{
name: "regionMelanesia",
id: "054",
countries: ["FJ", "NC", "PG", "SB", "VU"]
},
{
name: "regionMicronesia",
id: "057",
countries: ["GU", "KI", "MH", "FM", "NR", "MP", "PW", "UM"]
},
{
name: "regionPolynesia",
id: "061",
countries: ["AS", "CK", "PF", "NU", "PN", "WS", "TK", "TO", "TV", "WF"]
}
]
}
];
type Subregion = {
name: string;
id: string;
countries: string[];
};
type Region = {
name: string;
id: string;
includes: Subregion[];
};
export function getRegionNameById(regionId: string): string | undefined {
// Check top-level regions
const region = REGIONS.find((r) => r.id === regionId);
if (region) {
return region.name;
}
// Check subregions
for (const region of REGIONS) {
for (const subregion of region.includes) {
if (subregion.id === regionId) {
return subregion.name;
}
}
}
return undefined;
}
export function isValidRegionId(regionId: string): boolean {
// Check top-level regions
if (REGIONS.find((r) => r.id === regionId)) {
return true;
}
// Check subregions
for (const region of REGIONS) {
if (region.includes.find((s) => s.id === regionId)) {
return true;
}
}
return false;
}

View File

@@ -23,7 +23,8 @@ export default db;
export const primaryDb = db;
export type Transaction = Parameters<
Parameters<(typeof db)["transaction"]>[0]
>[0];
>[0];
export const DB_TYPE: "pg" | "sqlite" = "sqlite";
function checkFileExists(filePath: string): boolean {
try {

View File

@@ -1,4 +1,6 @@
export * from "./driver";
export * from "./logsDriver";
export * from "./safeRead";
export * from "./schema/schema";
export * from "./schema/privateSchema";
export * from "./migrate";

View File

@@ -0,0 +1,7 @@
import { db as mainDb } from "./driver";
// SQLite doesn't support separate databases for logs in the same way as Postgres
// Always use the main database connection for SQLite
export const logsDb = mainDb;
export default logsDb;
export const primaryLogsDb = logsDb;

View File

@@ -0,0 +1,11 @@
import { db } from "./driver";
/**
* Runs a read query. For SQLite there is no replica/primary distinction,
* so the query is executed once against the database.
*/
export async function safeRead<T>(
query: (d: typeof db) => Promise<T>
): Promise<T> {
return query(db);
}

View File

@@ -2,11 +2,22 @@ import { InferSelectModel } from "drizzle-orm";
import {
index,
integer,
primaryKey,
real,
sqliteTable,
text
text,
uniqueIndex
} from "drizzle-orm/sqlite-core";
import { clients, domains, exitNodes, orgs, sessions, users } from "./schema";
import {
clients,
domains,
exitNodes,
orgs,
sessions,
siteResources,
sites,
users
} from "./schema";
export const certificates = sqliteTable("certificates", {
certId: integer("certId").primaryKey({ autoIncrement: true }),
@@ -278,6 +289,7 @@ export const accessAuditLog = sqliteTable(
actor: text("actor"),
actorId: text("actorId"),
resourceId: integer("resourceId"),
siteResourceId: integer("siteResourceId"),
ip: text("ip"),
location: text("location"),
type: text("type").notNull(),
@@ -294,6 +306,45 @@ export const accessAuditLog = sqliteTable(
]
);
export const connectionAuditLog = sqliteTable(
"connectionAuditLog",
{
id: integer("id").primaryKey({ autoIncrement: true }),
sessionId: text("sessionId").notNull(),
siteResourceId: integer("siteResourceId").references(
() => siteResources.siteResourceId,
{ onDelete: "cascade" }
),
orgId: text("orgId").references(() => orgs.orgId, {
onDelete: "cascade"
}),
siteId: integer("siteId").references(() => sites.siteId, {
onDelete: "cascade"
}),
clientId: integer("clientId").references(() => clients.clientId, {
onDelete: "cascade"
}),
userId: text("userId").references(() => users.userId, {
onDelete: "cascade"
}),
sourceAddr: text("sourceAddr").notNull(),
destAddr: text("destAddr").notNull(),
protocol: text("protocol").notNull(),
startedAt: integer("startedAt").notNull(),
endedAt: integer("endedAt"),
bytesTx: integer("bytesTx"),
bytesRx: integer("bytesRx")
},
(table) => [
index("idx_accessAuditLog_startedAt").on(table.startedAt),
index("idx_accessAuditLog_org_startedAt").on(
table.orgId,
table.startedAt
),
index("idx_accessAuditLog_siteResourceId").on(table.siteResourceId)
]
);
export const approvals = sqliteTable("approvals", {
approvalId: integer("approvalId").primaryKey({ autoIncrement: true }),
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
@@ -318,6 +369,92 @@ export const approvals = sqliteTable("approvals", {
.notNull()
});
export const bannedEmails = sqliteTable("bannedEmails", {
email: text("email").primaryKey()
});
export const bannedIps = sqliteTable("bannedIps", {
ip: text("ip").primaryKey()
});
export const siteProvisioningKeys = sqliteTable("siteProvisioningKeys", {
siteProvisioningKeyId: text("siteProvisioningKeyId").primaryKey(),
name: text("name").notNull(),
siteProvisioningKeyHash: text("siteProvisioningKeyHash").notNull(),
lastChars: text("lastChars").notNull(),
createdAt: text("dateCreated").notNull(),
lastUsed: text("lastUsed"),
maxBatchSize: integer("maxBatchSize"), // null = no limit
numUsed: integer("numUsed").notNull().default(0),
validUntil: text("validUntil"),
approveNewSites: integer("approveNewSites", { mode: "boolean" })
.notNull()
.default(true)
});
export const siteProvisioningKeyOrg = sqliteTable(
"siteProvisioningKeyOrg",
{
siteProvisioningKeyId: text("siteProvisioningKeyId")
.notNull()
.references(() => siteProvisioningKeys.siteProvisioningKeyId, {
onDelete: "cascade"
}),
orgId: text("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" })
},
(table) => [
primaryKey({
columns: [table.siteProvisioningKeyId, table.orgId]
})
]
);
export const eventStreamingDestinations = sqliteTable(
"eventStreamingDestinations",
{
destinationId: integer("destinationId").primaryKey({
autoIncrement: true
}),
orgId: text("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
sendConnectionLogs: integer("sendConnectionLogs", { mode: "boolean" }).notNull().default(false),
sendRequestLogs: integer("sendRequestLogs", { mode: "boolean" }).notNull().default(false),
sendActionLogs: integer("sendActionLogs", { mode: "boolean" }).notNull().default(false),
sendAccessLogs: integer("sendAccessLogs", { mode: "boolean" }).notNull().default(false),
type: text("type").notNull(), // e.g. "http", "kafka", etc.
config: text("config").notNull(), // JSON string with the configuration for the destination
enabled: integer("enabled", { mode: "boolean" })
.notNull()
.default(true),
createdAt: integer("createdAt").notNull(),
updatedAt: integer("updatedAt").notNull()
}
);
export const eventStreamingCursors = sqliteTable(
"eventStreamingCursors",
{
cursorId: integer("cursorId").primaryKey({ autoIncrement: true }),
destinationId: integer("destinationId")
.notNull()
.references(() => eventStreamingDestinations.destinationId, {
onDelete: "cascade"
}),
logType: text("logType").notNull(), // "request" | "action" | "access" | "connection"
lastSentId: integer("lastSentId").notNull().default(0),
lastSentAt: integer("lastSentAt") // epoch milliseconds, null if never sent
},
(table) => [
uniqueIndex("idx_eventStreamingCursors_dest_type").on(
table.destinationId,
table.logType
)
]
);
export type Approval = InferSelectModel<typeof approvals>;
export type Limit = InferSelectModel<typeof limits>;
export type Account = InferSelectModel<typeof account>;
@@ -339,3 +476,13 @@ export type LoginPage = InferSelectModel<typeof loginPage>;
export type LoginPageBranding = InferSelectModel<typeof loginPageBranding>;
export type ActionAuditLog = InferSelectModel<typeof actionAuditLog>;
export type AccessAuditLog = InferSelectModel<typeof accessAuditLog>;
export type ConnectionAuditLog = InferSelectModel<typeof connectionAuditLog>;
export type BannedEmail = InferSelectModel<typeof bannedEmails>;
export type BannedIp = InferSelectModel<typeof bannedIps>;
export type SiteProvisioningKey = InferSelectModel<typeof siteProvisioningKeys>;
export type EventStreamingDestination = InferSelectModel<
typeof eventStreamingDestinations
>;
export type EventStreamingCursor = InferSelectModel<
typeof eventStreamingCursors
>;

View File

@@ -1,6 +1,13 @@
import { randomUUID } from "crypto";
import { InferSelectModel } from "drizzle-orm";
import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
import {
index,
integer,
primaryKey,
sqliteTable,
text,
unique
} from "drizzle-orm/sqlite-core";
export const domains = sqliteTable("domains", {
domainId: text("domainId").primaryKey(),
@@ -13,7 +20,8 @@ export const domains = sqliteTable("domains", {
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
tries: integer("tries").notNull().default(0),
certResolver: text("certResolver"),
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" })
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" }),
errorMessage: text("errorMessage")
});
export const dnsRecords = sqliteTable("dnsRecords", {
@@ -46,6 +54,9 @@ export const orgs = sqliteTable("orgs", {
settingsLogRetentionDaysAction: integer("settingsLogRetentionDaysAction") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
.notNull()
.default(0),
settingsLogRetentionDaysConnection: integer("settingsLogRetentionDaysConnection") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
.notNull()
.default(0),
sshCaPrivateKey: text("sshCaPrivateKey"), // Encrypted SSH CA private key (PEM format)
sshCaPublicKey: text("sshCaPublicKey"), // SSH CA public key (OpenSSH format)
isBillingOrg: integer("isBillingOrg", { mode: "boolean" }),
@@ -89,6 +100,7 @@ export const sites = sqliteTable("sites", {
lastBandwidthUpdate: text("lastBandwidthUpdate"),
type: text("type").notNull(), // "newt" or "wireguard"
online: integer("online", { mode: "boolean" }).notNull().default(false),
lastPing: integer("lastPing"),
// exit node stuff that is how to connect to the site when it has a wg server
address: text("address"), // this is the address of the wireguard interface in newt
@@ -98,7 +110,8 @@ export const sites = sqliteTable("sites", {
listenPort: integer("listenPort"),
dockerSocketEnabled: integer("dockerSocketEnabled", { mode: "boolean" })
.notNull()
.default(true)
.default(true),
status: text("status").$type<"pending" | "approved">().default("approved")
});
export const resources = sqliteTable("resources", {
@@ -257,7 +270,11 @@ export const siteResources = sqliteTable("siteResources", {
udpPortRangeString: text("udpPortRangeString").notNull().default("*"),
disableIcmp: integer("disableIcmp", { mode: "boolean" })
.notNull()
.default(false)
.default(false),
authDaemonPort: integer("authDaemonPort").default(22123),
authDaemonMode: text("authDaemonMode")
.$type<"site" | "remote">()
.default("site")
});
export const clientSiteResources = sqliteTable("clientSiteResources", {
@@ -310,10 +327,14 @@ export const users = sqliteTable("user", {
dateCreated: text("dateCreated").notNull(),
termsAcceptedTimestamp: text("termsAcceptedTimestamp"),
termsVersion: text("termsVersion"),
marketingEmailConsent: integer("marketingEmailConsent", {
mode: "boolean"
}).default(false),
serverAdmin: integer("serverAdmin", { mode: "boolean" })
.notNull()
.default(false),
lastPasswordChange: integer("lastPasswordChange")
lastPasswordChange: integer("lastPasswordChange"),
locale: text("locale")
});
export const securityKeys = sqliteTable("webauthnCredentials", {
@@ -402,6 +423,9 @@ export const clientSitesAssociationsCache = sqliteTable(
isRelayed: integer("isRelayed", { mode: "boolean" })
.notNull()
.default(false),
isJitMode: integer("isJitMode", { mode: "boolean" })
.notNull()
.default(false),
endpoint: text("endpoint"),
publicKey: text("publicKey") // this will act as the session's public key for hole punching so we can track when it changes
}
@@ -631,9 +655,6 @@ export const userOrgs = sqliteTable("userOrgs", {
onDelete: "cascade"
})
.notNull(),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId),
isOwner: integer("isOwner", { mode: "boolean" }).notNull().default(false),
autoProvisioned: integer("autoProvisioned", {
mode: "boolean"
@@ -679,9 +700,31 @@ export const roles = sqliteTable("roles", {
description: text("description"),
requireDeviceApproval: integer("requireDeviceApproval", {
mode: "boolean"
}).default(false)
}).default(false),
sshSudoMode: text("sshSudoMode").default("none"), // "none" | "full" | "commands"
sshSudoCommands: text("sshSudoCommands").default("[]"),
sshCreateHomeDir: integer("sshCreateHomeDir", { mode: "boolean" }).default(
true
),
sshUnixGroups: text("sshUnixGroups").default("[]")
});
export const userOrgRoles = sqliteTable(
"userOrgRoles",
{
userId: text("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
orgId: text("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
},
(t) => [unique().on(t.userId, t.orgId, t.roleId)]
);
export const roleActions = sqliteTable("roleActions", {
roleId: integer("roleId")
.notNull()
@@ -767,12 +810,22 @@ export const userInvites = sqliteTable("userInvites", {
.references(() => orgs.orgId, { onDelete: "cascade" }),
email: text("email").notNull(),
expiresAt: integer("expiresAt").notNull(),
tokenHash: text("token").notNull(),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
tokenHash: text("token").notNull()
});
export const userInviteRoles = sqliteTable(
"userInviteRoles",
{
inviteId: text("inviteId")
.notNull()
.references(() => userInvites.inviteId, { onDelete: "cascade" }),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
},
(t) => [primaryKey({ columns: [t.inviteId, t.roleId] })]
);
export const resourcePincode = sqliteTable("resourcePincode", {
pincodeId: integer("pincodeId").primaryKey({
autoIncrement: true
@@ -1115,7 +1168,9 @@ export type UserSite = InferSelectModel<typeof userSites>;
export type RoleResource = InferSelectModel<typeof roleResources>;
export type UserResource = InferSelectModel<typeof userResources>;
export type UserInvite = InferSelectModel<typeof userInvites>;
export type UserInviteRole = InferSelectModel<typeof userInviteRoles>;
export type UserOrg = InferSelectModel<typeof userOrgs>;
export type UserOrgRole = InferSelectModel<typeof userOrgRoles>;
export type ResourceSession = InferSelectModel<typeof resourceSessions>;
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;

View File

@@ -74,7 +74,7 @@ declare global {
session: Session;
userOrg?: UserOrg;
apiKeyOrg?: ApiKeyOrg;
userOrgRoleId?: number;
userOrgRoleIds?: number[];
userOrgId?: string;
userOrgIds?: string[];
remoteExitNode?: RemoteExitNode;

View File

@@ -17,6 +17,7 @@ import fs from "fs";
import path from "path";
import { APP_PATH } from "./lib/consts";
import yaml from "js-yaml";
import { z } from "zod";
const dev = process.env.ENVIRONMENT !== "prod";
const externalPort = config.getRawConfig().server.integration_port;
@@ -38,12 +39,24 @@ export function createIntegrationApiServer() {
apiServer.use(cookieParser());
apiServer.use(express.json());
const openApiDocumentation = getOpenApiDocumentation();
apiServer.use(
"/v1/docs",
swaggerUi.serve,
swaggerUi.setup(getOpenApiDocumentation())
swaggerUi.setup(openApiDocumentation)
);
// Unauthenticated OpenAPI spec endpoints
apiServer.get("/v1/openapi.json", (_req, res) => {
res.json(openApiDocumentation);
});
apiServer.get("/v1/openapi.yaml", (_req, res) => {
const yamlOutput = yaml.dump(openApiDocumentation);
res.type("application/yaml").send(yamlOutput);
});
// API routes
const prefix = `/v1`;
apiServer.use(logIncomingMiddleware);
@@ -75,16 +88,6 @@ function getOpenApiDocumentation() {
}
);
for (const def of registry.definitions) {
if (def.type === "route") {
def.route.security = [
{
[bearerAuth.name]: []
}
];
}
}
registry.registerPath({
method: "get",
path: "/",
@@ -94,6 +97,74 @@ function getOpenApiDocumentation() {
responses: {}
});
registry.registerPath({
method: "get",
path: "/openapi.json",
description: "Get OpenAPI specification as JSON",
tags: [],
request: {},
responses: {
"200": {
description: "OpenAPI specification as JSON",
content: {
"application/json": {
schema: {
type: "object"
}
}
}
}
}
});
registry.registerPath({
method: "get",
path: "/openapi.yaml",
description: "Get OpenAPI specification as YAML",
tags: [],
request: {},
responses: {
"200": {
description: "OpenAPI specification as YAML",
content: {
"application/yaml": {
schema: {
type: "string"
}
}
}
}
}
});
for (const def of registry.definitions) {
if (def.type === "route") {
def.route.security = [
{
[bearerAuth.name]: []
}
];
// Ensure every route has a generic JSON response schema so Swagger UI can render responses
const existingResponses = def.route.responses;
const hasExistingResponses =
existingResponses && Object.keys(existingResponses).length > 0;
if (!hasExistingResponses) {
def.route.responses = {
"*": {
description: "",
content: {
"application/json": {
schema: z.object({})
}
}
}
};
}
}
}
const generator = new OpenApiGeneratorV3(registry.definitions);
const generated = generator.generateDocument({

View File

@@ -16,6 +16,11 @@ const internalPort = config.getRawConfig().server.internal_port;
export function createInternalServer() {
const internalServer = express();
const trustProxy = config.getRawConfig().server.trust_proxy;
if (trustProxy) {
internalServer.set("trust proxy", trustProxy);
}
internalServer.use(helmet());
internalServer.use(cors());
internalServer.use(stripDuplicateSesions);

View File

@@ -8,6 +8,7 @@ export enum TierFeature {
LogExport = "logExport",
AccessLogs = "accessLogs", // set the retention period to none on downgrade
ActionLogs = "actionLogs", // set the retention period to none on downgrade
ConnectionLogs = "connectionLogs",
RotateCredentials = "rotateCredentials",
MaintencePage = "maintencePage", // handle downgrade
DevicePosture = "devicePosture",
@@ -15,7 +16,10 @@ export enum TierFeature {
SessionDurationPolicies = "sessionDurationPolicies", // handle downgrade by setting to default duration
PasswordExpirationPolicies = "passwordExpirationPolicies", // handle downgrade by setting to default duration
AutoProvisioning = "autoProvisioning", // handle downgrade by disabling auto provisioning
SshPam = "sshPam"
SshPam = "sshPam",
FullRbac = "fullRbac",
SiteProvisioningKeys = "siteProvisioningKeys", // handle downgrade by revoking keys if needed
SIEM = "siem" // handle downgrade by disabling SIEM integrations
}
export const tierMatrix: Record<TierFeature, Tier[]> = {
@@ -26,6 +30,7 @@ export const tierMatrix: Record<TierFeature, Tier[]> = {
[TierFeature.LogExport]: ["tier3", "enterprise"],
[TierFeature.AccessLogs]: ["tier2", "tier3", "enterprise"],
[TierFeature.ActionLogs]: ["tier2", "tier3", "enterprise"],
[TierFeature.ConnectionLogs]: ["tier2", "tier3", "enterprise"],
[TierFeature.RotateCredentials]: ["tier1", "tier2", "tier3", "enterprise"],
[TierFeature.MaintencePage]: ["tier1", "tier2", "tier3", "enterprise"],
[TierFeature.DevicePosture]: ["tier2", "tier3", "enterprise"],
@@ -48,5 +53,8 @@ export const tierMatrix: Record<TierFeature, Tier[]> = {
"enterprise"
],
[TierFeature.AutoProvisioning]: ["tier1", "tier3", "enterprise"],
[TierFeature.SshPam]: ["enterprise"]
[TierFeature.SshPam]: ["tier1", "tier3", "enterprise"],
[TierFeature.FullRbac]: ["tier1", "tier2", "tier3", "enterprise"],
[TierFeature.SiteProvisioningKeys]: ["tier3", "enterprise"],
[TierFeature.SIEM]: ["enterprise"]
};

View File

@@ -12,7 +12,7 @@ import {
import { FeatureId, getFeatureMeterId } from "./features";
import logger from "@server/logger";
import { build } from "@server/build";
import cache from "@server/lib/cache";
import cache from "#dynamic/lib/cache";
export function noop() {
if (build !== "saas") {
@@ -230,7 +230,7 @@ export class UsageService {
const orgIdToUse = await this.getBillingOrg(orgId);
const cacheKey = `customer_${orgIdToUse}_${featureId}`;
const cached = cache.get<string>(cacheKey);
const cached = await cache.get<string>(cacheKey);
if (cached) {
return cached;
@@ -253,7 +253,7 @@ export class UsageService {
const customerId = customer.customerId;
// Cache the result
cache.set(cacheKey, customerId, 300); // 5 minute TTL
await cache.set(cacheKey, customerId, 300); // 5 minute TTL
return customerId;
} catch (error) {

View File

@@ -107,7 +107,7 @@ export async function applyBlueprint({
[target],
matchingHealthcheck ? [matchingHealthcheck] : [],
result.proxyResource.protocol,
result.proxyResource.proxyPort
site.newt.version
);
}
}

View File

@@ -11,7 +11,7 @@ import {
userSiteResources
} from "@server/db";
import { sites } from "@server/db";
import { eq, and, ne, inArray } from "drizzle-orm";
import { eq, and, ne, inArray, or } from "drizzle-orm";
import { Config } from "./types";
import logger from "@server/logger";
import { getNextAvailableAliasAddress } from "../ip";
@@ -142,7 +142,10 @@ export async function updateClientResources(
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
inArray(users.username, resourceData.users),
or(
inArray(users.username, resourceData.users),
inArray(users.email, resourceData.users)
),
eq(userOrgs.orgId, orgId)
)
);
@@ -276,7 +279,10 @@ export async function updateClientResources(
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(
and(
inArray(users.username, resourceData.users),
or(
inArray(users.username, resourceData.users),
inArray(users.email, resourceData.users)
),
eq(userOrgs.orgId, orgId)
)
);

View File

@@ -31,6 +31,7 @@ import { pickPort } from "@server/routers/target/helpers";
import { resourcePassword } from "@server/db";
import { hashPassword } from "@server/auth/password";
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
import { isValidRegionId } from "@server/db/regions";
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
import { tierMatrix } from "../billing/tierMatrix";
@@ -212,7 +213,10 @@ export async function updateProxyResources(
} else {
// Update existing resource
const isLicensed = await isLicensedOrSubscribed(orgId, tierMatrix.maintencePage);
const isLicensed = await isLicensedOrSubscribed(
orgId,
tierMatrix.maintencePage
);
if (!isLicensed) {
resourceData.maintenance = undefined;
}
@@ -590,7 +594,10 @@ export async function updateProxyResources(
existingRule.action !== getRuleAction(rule.action) ||
existingRule.match !== rule.match.toUpperCase() ||
existingRule.value !==
getRuleValue(rule.match.toUpperCase(), rule.value) ||
getRuleValue(
rule.match.toUpperCase(),
rule.value
) ||
existingRule.priority !== intendedPriority
) {
validateRule(rule);
@@ -648,7 +655,10 @@ export async function updateProxyResources(
);
}
const isLicensed = await isLicensedOrSubscribed(orgId, tierMatrix.maintencePage);
const isLicensed = await isLicensedOrSubscribed(
orgId,
tierMatrix.maintencePage
);
if (!isLicensed) {
resourceData.maintenance = undefined;
}
@@ -854,6 +864,10 @@ function validateRule(rule: any) {
if (!isValidUrlGlobPattern(rule.value)) {
throw new Error(`Invalid URL glob pattern: ${rule.value}`);
}
} else if (rule.match === "region") {
if (!isValidRegionId(rule.value)) {
throw new Error(`Invalid region ID provided: ${rule.value}`);
}
}
}
@@ -935,7 +949,12 @@ async function syncUserResources(
.select()
.from(users)
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
.where(and(eq(users.username, username), eq(userOrgs.orgId, orgId)))
.where(
and(
or(eq(users.username, username), eq(users.email, username)),
eq(userOrgs.orgId, orgId)
)
)
.limit(1);
if (!user) {

View File

@@ -1,6 +1,7 @@
import { z } from "zod";
import { portRangeStringSchema } from "@server/lib/ip";
import { MaintenanceSchema } from "#dynamic/lib/blueprints/MaintenanceSchema";
import { isValidRegionId } from "@server/db/regions";
export const SiteSchema = z.object({
name: z.string().min(1).max(100),
@@ -69,7 +70,7 @@ export const AuthSchema = z.object({
.refine((roles) => !roles.includes("Admin"), {
error: "Admin role cannot be included in sso-roles"
}),
"sso-users": z.array(z.email()).optional().default([]),
"sso-users": z.array(z.string()).optional().default([]),
"whitelist-users": z.array(z.email()).optional().default([]),
"auto-login-idp": z.int().positive().optional()
});
@@ -77,7 +78,7 @@ export const AuthSchema = z.object({
export const RuleSchema = z
.object({
action: z.enum(["allow", "deny", "pass"]),
match: z.enum(["cidr", "path", "ip", "country", "asn"]),
match: z.enum(["cidr", "path", "ip", "country", "asn", "region"]),
value: z.string(),
priority: z.int().optional()
})
@@ -137,6 +138,19 @@ export const RuleSchema = z
message:
"Value must be 'AS<number>' format or 'ALL' when match is 'asn'"
}
)
.refine(
(rule) => {
if (rule.match === "region") {
return isValidRegionId(rule.value);
}
return true;
},
{
path: ["value"],
message:
"Value must be a valid UN M.49 region or subregion ID when match is 'region'"
}
);
export const HeaderSchema = z.object({
@@ -335,7 +349,7 @@ export const ClientResourceSchema = z
.refine((roles) => !roles.includes("Admin"), {
error: "Admin role cannot be included in roles"
}),
users: z.array(z.email()).optional().default([]),
users: z.array(z.string()).optional().default([]),
machines: z.array(z.string()).optional().default([])
})
.refine(

View File

@@ -1,9 +1,9 @@
import NodeCache from "node-cache";
import logger from "@server/logger";
// Create cache with maxKeys limit to prevent memory leaks
// Create local cache with maxKeys limit to prevent memory leaks
// With ~10k requests/day and 5min TTL, 10k keys should be more than sufficient
export const cache = new NodeCache({
export const localCache = new NodeCache({
stdTTL: 3600,
checkperiod: 120,
maxKeys: 10000
@@ -11,10 +11,151 @@ export const cache = new NodeCache({
// Log cache statistics periodically for monitoring
setInterval(() => {
const stats = cache.getStats();
const stats = localCache.getStats();
logger.debug(
`Cache stats - Keys: ${stats.keys}, Hits: ${stats.hits}, Misses: ${stats.misses}, Hit rate: ${stats.hits > 0 ? ((stats.hits / (stats.hits + stats.misses)) * 100).toFixed(2) : 0}%`
`Local cache stats - Keys: ${stats.keys}, Hits: ${stats.hits}, Misses: ${stats.misses}, Hit rate: ${stats.hits > 0 ? ((stats.hits / (stats.hits + stats.misses)) * 100).toFixed(2) : 0}%`
);
}, 300000); // Every 5 minutes
/**
* Adaptive cache that uses Redis when available in multi-node environments,
* otherwise falls back to local memory cache for single-node deployments.
*/
class AdaptiveCache {
/**
* Set a value in the cache
* @param key - Cache key
* @param value - Value to cache (will be JSON stringified for Redis)
* @param ttl - Time to live in seconds (0 = no expiration)
* @returns boolean indicating success
*/
async set(key: string, value: any, ttl?: number): Promise<boolean> {
const effectiveTtl = ttl === 0 ? undefined : ttl;
// Use local cache as fallback or primary
const success = localCache.set(key, value, effectiveTtl || 0);
if (success) {
logger.debug(`Set key in local cache: ${key}`);
}
return success;
}
/**
* Get a value from the cache
* @param key - Cache key
* @returns The cached value or undefined if not found
*/
async get<T = any>(key: string): Promise<T | undefined> {
// Use local cache as fallback or primary
const value = localCache.get<T>(key);
if (value !== undefined) {
logger.debug(`Cache hit in local cache: ${key}`);
} else {
logger.debug(`Cache miss in local cache: ${key}`);
}
return value;
}
/**
* Delete a value from the cache
* @param key - Cache key or array of keys
* @returns Number of deleted entries
*/
async del(key: string | string[]): Promise<number> {
const keys = Array.isArray(key) ? key : [key];
let deletedCount = 0;
// Use local cache as fallback or primary
for (const k of keys) {
const success = localCache.del(k);
if (success > 0) {
deletedCount++;
logger.debug(`Deleted key from local cache: ${k}`);
}
}
return deletedCount;
}
/**
* Check if a key exists in the cache
* @param key - Cache key
* @returns boolean indicating if key exists
*/
async has(key: string): Promise<boolean> {
// Use local cache as fallback or primary
return localCache.has(key);
}
/**
* Get multiple values from the cache
* @param keys - Array of cache keys
* @returns Array of values (undefined for missing keys)
*/
async mget<T = any>(keys: string[]): Promise<(T | undefined)[]> {
// Use local cache as fallback or primary
return keys.map((key) => localCache.get<T>(key));
}
/**
* Flush all keys from the cache
*/
async flushAll(): Promise<void> {
localCache.flushAll();
logger.debug("Flushed local cache");
}
/**
* Get cache statistics
* Note: Only returns local cache stats, Redis stats are not included
*/
getStats() {
return localCache.getStats();
}
/**
* Get the current cache backend being used
* @returns "redis" if Redis is available and healthy, "local" otherwise
*/
getCurrentBackend(): "redis" | "local" {
return "local";
}
/**
* Take a key from the cache and delete it
* @param key - Cache key
* @returns The value or undefined if not found
*/
async take<T = any>(key: string): Promise<T | undefined> {
const value = await this.get<T>(key);
if (value !== undefined) {
await this.del(key);
}
return value;
}
/**
* Get TTL (time to live) for a key
* @param key - Cache key
* @returns TTL in seconds, 0 if no expiration, -1 if key doesn't exist
*/
getTtl(key: string): number {
const ttl = localCache.getTtl(key);
if (ttl === undefined) {
return -1;
}
return Math.max(0, Math.floor((ttl - Date.now()) / 1000));
}
/**
* Get all keys from the cache
* Note: Only returns local cache keys, Redis keys are not included
*/
keys(): string[] {
return localCache.keys();
}
}
// Export singleton instance
export const cache = new AdaptiveCache();
export default cache;

View File

@@ -10,6 +10,7 @@ import {
roles,
Transaction,
userClients,
userOrgRoles,
userOrgs
} from "@server/db";
import { getUniqueClientName } from "@server/db/names";
@@ -39,20 +40,36 @@ export async function calculateUserClientsForOrgs(
return;
}
// Get all user orgs
const allUserOrgs = await transaction
// Get all user orgs with all roles (for org list and role-based logic)
const userOrgRoleRows = await transaction
.select()
.from(userOrgs)
.innerJoin(roles, eq(roles.roleId, userOrgs.roleId))
.innerJoin(
userOrgRoles,
and(
eq(userOrgs.userId, userOrgRoles.userId),
eq(userOrgs.orgId, userOrgRoles.orgId)
)
)
.innerJoin(roles, eq(userOrgRoles.roleId, roles.roleId))
.where(eq(userOrgs.userId, userId));
const userOrgIds = allUserOrgs.map(({ userOrgs: uo }) => uo.orgId);
const userOrgIds = [...new Set(userOrgRoleRows.map((r) => r.userOrgs.orgId))];
const orgIdToRoleRows = new Map<
string,
(typeof userOrgRoleRows)[0][]
>();
for (const r of userOrgRoleRows) {
const list = orgIdToRoleRows.get(r.userOrgs.orgId) ?? [];
list.push(r);
orgIdToRoleRows.set(r.userOrgs.orgId, list);
}
// For each OLM, ensure there's a client in each org the user is in
for (const olm of userOlms) {
for (const userRoleOrg of allUserOrgs) {
const { userOrgs: userOrg, roles: role } = userRoleOrg;
const orgId = userOrg.orgId;
for (const orgId of orgIdToRoleRows.keys()) {
const roleRowsForOrg = orgIdToRoleRows.get(orgId)!;
const userOrg = roleRowsForOrg[0].userOrgs;
const [org] = await transaction
.select()
@@ -196,7 +213,7 @@ export async function calculateUserClientsForOrgs(
const requireApproval =
build !== "oss" &&
isOrgLicensed &&
role.requireDeviceApproval;
roleRowsForOrg.some((r) => r.roles.requireDeviceApproval);
const newClientData: InferInsertModel<typeof clients> = {
userId,

View File

@@ -2,10 +2,15 @@ import { db, orgs } from "@server/db";
import { cleanUpOldLogs as cleanUpOldAccessLogs } from "#dynamic/lib/logAccessAudit";
import { cleanUpOldLogs as cleanUpOldActionLogs } from "#dynamic/middlewares/logActionAudit";
import { cleanUpOldLogs as cleanUpOldRequestLogs } from "@server/routers/badger/logRequestAudit";
import { cleanUpOldLogs as cleanUpOldConnectionLogs } from "#dynamic/routers/newt";
import { gt, or } from "drizzle-orm";
import { cleanUpOldFingerprintSnapshots } from "@server/routers/olm/fingerprintingUtils";
import { build } from "@server/build";
export function initLogCleanupInterval() {
if (build == "saas") { // skip log cleanup for saas builds
return null;
}
return setInterval(
async () => {
const orgsToClean = await db
@@ -16,14 +21,17 @@ export function initLogCleanupInterval() {
settingsLogRetentionDaysAccess:
orgs.settingsLogRetentionDaysAccess,
settingsLogRetentionDaysRequest:
orgs.settingsLogRetentionDaysRequest
orgs.settingsLogRetentionDaysRequest,
settingsLogRetentionDaysConnection:
orgs.settingsLogRetentionDaysConnection
})
.from(orgs)
.where(
or(
gt(orgs.settingsLogRetentionDaysAction, 0),
gt(orgs.settingsLogRetentionDaysAccess, 0),
gt(orgs.settingsLogRetentionDaysRequest, 0)
gt(orgs.settingsLogRetentionDaysRequest, 0),
gt(orgs.settingsLogRetentionDaysConnection, 0)
)
);
@@ -33,7 +41,8 @@ export function initLogCleanupInterval() {
orgId,
settingsLogRetentionDaysAction,
settingsLogRetentionDaysAccess,
settingsLogRetentionDaysRequest
settingsLogRetentionDaysRequest,
settingsLogRetentionDaysConnection
} = org;
if (settingsLogRetentionDaysAction > 0) {
@@ -56,6 +65,13 @@ export function initLogCleanupInterval() {
settingsLogRetentionDaysRequest
);
}
if (settingsLogRetentionDaysConnection > 0) {
await cleanUpOldConnectionLogs(
orgId,
settingsLogRetentionDaysConnection
);
}
}
await cleanUpOldFingerprintSnapshots(365);

View File

@@ -0,0 +1,20 @@
import semver from "semver";
export function canCompress(
clientVersion: string | null | undefined,
type: "newt" | "olm"
): boolean {
try {
if (!clientVersion) return false;
// check if it is a valid semver
if (!semver.valid(clientVersion)) return false;
if (type === "newt") {
return semver.gte(clientVersion, "1.10.3");
} else if (type === "olm") {
return semver.gte(clientVersion, "1.4.3");
}
return false;
} catch {
return false;
}
}

View File

@@ -2,7 +2,7 @@ import path from "path";
import { fileURLToPath } from "url";
// This is a placeholder value replaced by the build process
export const APP_VERSION = "1.15.4";
export const APP_VERSION = "1.17.0";
export const __FILENAME = fileURLToPath(import.meta.url);
export const __DIRNAME = path.dirname(__FILENAME);

View File

@@ -85,9 +85,7 @@ export async function deleteOrgById(
deletedNewtIds.push(deletedNewt.newtId);
await trx
.delete(newtSessions)
.where(
eq(newtSessions.newtId, deletedNewt.newtId)
);
.where(eq(newtSessions.newtId, deletedNewt.newtId));
}
}
}
@@ -121,33 +119,38 @@ export async function deleteOrgById(
eq(clientSitesAssociationsCache.clientId, client.clientId)
);
}
await trx.delete(resources).where(eq(resources.orgId, orgId));
const allOrgDomains = await trx
.select()
.from(orgDomains)
.innerJoin(domains, eq(domains.domainId, orgDomains.domainId))
.innerJoin(domains, eq(orgDomains.domainId, domains.domainId))
.where(
and(
eq(orgDomains.orgId, orgId),
eq(domains.configManaged, false)
)
);
logger.info(`Found ${allOrgDomains.length} domains to delete`);
const domainIdsToDelete: string[] = [];
for (const orgDomain of allOrgDomains) {
const domainId = orgDomain.domains.domainId;
const orgCount = await trx
.select({ count: sql<number>`count(*)` })
const [orgCount] = await trx
.select({ count: count() })
.from(orgDomains)
.where(eq(orgDomains.domainId, domainId));
if (orgCount[0].count === 1) {
logger.info(`Found ${orgCount.count} orgs using domain ${domainId}`);
if (orgCount.count === 1) {
domainIdsToDelete.push(domainId);
}
}
logger.info(`Found ${domainIdsToDelete.length} domains to delete`);
if (domainIdsToDelete.length > 0) {
await trx
.delete(domains)
.where(inArray(domains.domainId, domainIdsToDelete));
}
await trx.delete(resources).where(eq(resources.orgId, orgId));
await usageService.add(orgId, FeatureId.ORGINIZATIONS, -1, trx); // here we are decreasing the org count BEFORE deleting the org because we need to still be able to get the org to get the billing org inside of here
@@ -231,15 +234,13 @@ export function sendTerminationMessages(result: DeleteOrgByIdResult): void {
);
}
for (const olmId of result.olmsToTerminate) {
sendTerminateClient(
0,
OlmErrorCodes.TERMINATED_REKEYED,
olmId
).catch((error) => {
logger.error(
"Failed to send termination message to olm:",
error
);
});
sendTerminateClient(0, OlmErrorCodes.TERMINATED_REKEYED, olmId).catch(
(error) => {
logger.error(
"Failed to send termination message to olm:",
error
);
}
);
}
}

View File

@@ -571,6 +571,133 @@ export function generateSubnetProxyTargets(
return targets;
}
export type SubnetProxyTargetV2 = {
sourcePrefixes: string[]; // must be cidrs
destPrefix: string; // must be a cidr
disableIcmp?: boolean;
rewriteTo?: string; // must be a cidr
portRange?: {
min: number;
max: number;
protocol: "tcp" | "udp";
}[];
resourceId?: number;
};
export function generateSubnetProxyTargetV2(
siteResource: SiteResource,
clients: {
clientId: number;
pubKey: string | null;
subnet: string | null;
}[]
): SubnetProxyTargetV2 | undefined {
if (clients.length === 0) {
logger.debug(
`No clients have access to site resource ${siteResource.siteResourceId}, skipping target generation.`
);
return;
}
let target: SubnetProxyTargetV2 | null = null;
const portRange = [
...parsePortRangeString(siteResource.tcpPortRangeString, "tcp"),
...parsePortRangeString(siteResource.udpPortRangeString, "udp")
];
const disableIcmp = siteResource.disableIcmp ?? false;
if (siteResource.mode == "host") {
let destination = siteResource.destination;
// check if this is a valid ip
const ipSchema = z.union([z.ipv4(), z.ipv6()]);
if (ipSchema.safeParse(destination).success) {
destination = `${destination}/32`;
target = {
sourcePrefixes: [],
destPrefix: destination,
portRange,
disableIcmp,
resourceId: siteResource.siteResourceId,
};
}
if (siteResource.alias && siteResource.aliasAddress) {
// also push a match for the alias address
target = {
sourcePrefixes: [],
destPrefix: `${siteResource.aliasAddress}/32`,
rewriteTo: destination,
portRange,
disableIcmp,
resourceId: siteResource.siteResourceId,
};
}
} else if (siteResource.mode == "cidr") {
target = {
sourcePrefixes: [],
destPrefix: siteResource.destination,
portRange,
disableIcmp,
resourceId: siteResource.siteResourceId,
};
}
if (!target) {
return;
}
for (const clientSite of clients) {
if (!clientSite.subnet) {
logger.debug(
`Client ${clientSite.clientId} has no subnet, skipping for site resource ${siteResource.siteResourceId}.`
);
continue;
}
const clientPrefix = `${clientSite.subnet.split("/")[0]}/32`;
// add client prefix to source prefixes
target.sourcePrefixes.push(clientPrefix);
}
// print a nice representation of the targets
// logger.debug(
// `Generated subnet proxy targets for: ${JSON.stringify(targets, null, 2)}`
// );
return target;
}
/**
* Converts a SubnetProxyTargetV2 to an array of SubnetProxyTarget (v1)
* by expanding each source prefix into its own target entry.
* @param targetV2 - The v2 target to convert
* @returns Array of v1 SubnetProxyTarget objects
*/
export function convertSubnetProxyTargetsV2ToV1(
targetsV2: SubnetProxyTargetV2[]
): SubnetProxyTarget[] {
return targetsV2.flatMap((targetV2) =>
targetV2.sourcePrefixes.map((sourcePrefix) => ({
sourcePrefix,
destPrefix: targetV2.destPrefix,
...(targetV2.disableIcmp !== undefined && {
disableIcmp: targetV2.disableIcmp
}),
...(targetV2.rewriteTo !== undefined && {
rewriteTo: targetV2.rewriteTo
}),
...(targetV2.portRange !== undefined && {
portRange: targetV2.portRange
})
}))
);
}
// Custom schema for validating port range strings
// Format: "80,443,8000-9000" or "*" for all ports, or empty string
export const portRangeStringSchema = z

View File

@@ -79,6 +79,7 @@ export const configSchema = z
.default(3001)
.transform(stoi)
.pipe(portSchema),
badger_override: z.string().optional(),
next_port: portSchema
.optional()
.default(3002)
@@ -189,6 +190,46 @@ export const configSchema = z
.prefault({})
})
.optional(),
postgres_logs: z
.object({
connection_string: z
.string()
.optional()
.transform(getEnvOrYaml("POSTGRES_LOGS_CONNECTION_STRING")),
replicas: z
.array(
z.object({
connection_string: z.string()
})
)
.optional(),
pool: z
.object({
max_connections: z
.number()
.positive()
.optional()
.default(20),
max_replica_connections: z
.number()
.positive()
.optional()
.default(10),
idle_timeout_ms: z
.number()
.positive()
.optional()
.default(30000),
connection_timeout_ms: z
.number()
.positive()
.optional()
.default(5000)
})
.optional()
.prefault({})
})
.optional(),
traefik: z
.object({
http_entrypoint: z.string().optional().default("web"),
@@ -262,8 +303,8 @@ export const configSchema = z
.optional()
.default({
block_size: 24,
subnet_group: "100.90.128.0/24",
utility_subnet_group: "100.96.128.0/24"
subnet_group: "100.90.128.0/20",
utility_subnet_group: "100.96.128.0/20"
}),
rate_limits: z
.object({

View File

@@ -14,6 +14,7 @@ import {
siteResources,
sites,
Transaction,
userOrgRoles,
userOrgs,
userSiteResources
} from "@server/db";
@@ -32,7 +33,7 @@ import logger from "@server/logger";
import {
generateAliasConfig,
generateRemoteSubnets,
generateSubnetProxyTargets,
generateSubnetProxyTargetV2,
parseEndpoint,
formatEndpoint
} from "@server/lib/ip";
@@ -77,10 +78,10 @@ export async function getClientSiteResourceAccess(
// get all of the users in these roles
const userIdsFromRoles = await trx
.select({
userId: userOrgs.userId
userId: userOrgRoles.userId
})
.from(userOrgs)
.where(inArray(userOrgs.roleId, roleIds))
.from(userOrgRoles)
.where(inArray(userOrgRoles.roleId, roleIds))
.then((rows) => rows.map((row) => row.userId));
const newAllUserIds = Array.from(
@@ -477,6 +478,7 @@ async function handleMessagesForSiteClients(
}
if (isAdd) {
// TODO: if we are in jit mode here should we really be sending this?
await initPeerAddHandshake(
// this will kick off the add peer process for the client
client.clientId,
@@ -571,7 +573,7 @@ export async function updateClientSiteDestinations(
destinations: [
{
destinationIP: site.sites.subnet.split("/")[0],
destinationPort: site.sites.listenPort || 0
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
}
]
};
@@ -579,7 +581,7 @@ export async function updateClientSiteDestinations(
// add to the existing destinations
destinations.destinations.push({
destinationIP: site.sites.subnet.split("/")[0],
destinationPort: site.sites.listenPort || 0
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
});
}
@@ -659,17 +661,18 @@ async function handleSubnetProxyTargetUpdates(
);
if (addedClients.length > 0) {
const targetsToAdd = generateSubnetProxyTargets(
const targetToAdd = generateSubnetProxyTargetV2(
siteResource,
addedClients
);
if (targetsToAdd.length > 0) {
logger.info(
`Adding ${targetsToAdd.length} subnet proxy targets for siteResource ${siteResource.siteResourceId}`
);
if (targetToAdd) {
proxyJobs.push(
addSubnetProxyTargets(newt.newtId, targetsToAdd)
addSubnetProxyTargets(
newt.newtId,
[targetToAdd],
newt.version
)
);
}
@@ -695,17 +698,18 @@ async function handleSubnetProxyTargetUpdates(
);
if (removedClients.length > 0) {
const targetsToRemove = generateSubnetProxyTargets(
const targetToRemove = generateSubnetProxyTargetV2(
siteResource,
removedClients
);
if (targetsToRemove.length > 0) {
logger.info(
`Removing ${targetsToRemove.length} subnet proxy targets for siteResource ${siteResource.siteResourceId}`
);
if (targetToRemove) {
proxyJobs.push(
removeSubnetProxyTargets(newt.newtId, targetsToRemove)
removeSubnetProxyTargets(
newt.newtId,
[targetToRemove],
newt.version
)
);
}
@@ -811,12 +815,12 @@ export async function rebuildClientAssociationsFromClient(
// Role-based access
const roleIds = await trx
.select({ roleId: userOrgs.roleId })
.from(userOrgs)
.select({ roleId: userOrgRoles.roleId })
.from(userOrgRoles)
.where(
and(
eq(userOrgs.userId, client.userId),
eq(userOrgs.orgId, client.orgId)
eq(userOrgRoles.userId, client.userId),
eq(userOrgRoles.orgId, client.orgId)
)
) // this needs to be locked onto this org or else cross-org access could happen
.then((rows) => rows.map((row) => row.roleId));
@@ -1080,6 +1084,7 @@ async function handleMessagesForClientSites(
continue;
}
// TODO: if we are in jit mode here should we really be sending this?
await initPeerAddHandshake(
// this will kick off the add peer process for the client
client.clientId,
@@ -1146,7 +1151,7 @@ async function handleMessagesForClientResources(
// Add subnet proxy targets for each site
for (const [siteId, resources] of addedBySite.entries()) {
const [newt] = await trx
.select({ newtId: newts.newtId })
.select({ newtId: newts.newtId, version: newts.version })
.from(newts)
.where(eq(newts.siteId, siteId))
.limit(1);
@@ -1159,7 +1164,7 @@ async function handleMessagesForClientResources(
}
for (const resource of resources) {
const targets = generateSubnetProxyTargets(resource, [
const target = generateSubnetProxyTargetV2(resource, [
{
clientId: client.clientId,
pubKey: client.pubKey,
@@ -1167,8 +1172,14 @@ async function handleMessagesForClientResources(
}
]);
if (targets.length > 0) {
proxyJobs.push(addSubnetProxyTargets(newt.newtId, targets));
if (target) {
proxyJobs.push(
addSubnetProxyTargets(
newt.newtId,
[target],
newt.version
)
);
}
try {
@@ -1217,7 +1228,7 @@ async function handleMessagesForClientResources(
// Remove subnet proxy targets for each site
for (const [siteId, resources] of removedBySite.entries()) {
const [newt] = await trx
.select({ newtId: newts.newtId })
.select({ newtId: newts.newtId, version: newts.version })
.from(newts)
.where(eq(newts.siteId, siteId))
.limit(1);
@@ -1230,7 +1241,7 @@ async function handleMessagesForClientResources(
}
for (const resource of resources) {
const targets = generateSubnetProxyTargets(resource, [
const target = generateSubnetProxyTargetV2(resource, [
{
clientId: client.clientId,
pubKey: client.pubKey,
@@ -1238,9 +1249,13 @@ async function handleMessagesForClientResources(
}
]);
if (targets.length > 0) {
if (target) {
proxyJobs.push(
removeSubnetProxyTargets(newt.newtId, targets)
removeSubnetProxyTargets(
newt.newtId,
[target],
newt.version
)
);
}

View File

@@ -1,16 +0,0 @@
export enum AudienceIds {
SignUps = "",
Subscribed = "",
Churned = "",
Newsletter = ""
}
let resend;
export default resend;
export async function moveEmailToAudience(
email: string,
audienceId: AudienceIds
) {
return;
}

40
server/lib/sanitize.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* Sanitize a string field before inserting into a database TEXT column.
*
* Two passes are applied:
*
* 1. Lone UTF-16 surrogates JavaScript strings can hold unpaired surrogates
* (e.g. \uD800 without a following \uDC00-\uDFFF codepoint). These are
* valid in JS but cannot be encoded as UTF-8, triggering
* `report_invalid_encoding` in SQLite / Postgres. They are replaced with
* the Unicode replacement character U+FFFD so the data is preserved as a
* visible signal that something was malformed.
*
* 2. Null bytes and C0 control characters SQLite stores TEXT as
* null-terminated C strings, so \x00 in a value causes
* `report_invalid_encoding`. Bots and scanners routinely inject null bytes
* into URLs (e.g. `/path\u0000.jpg`). All C0 control characters in the
* range \x00-\x1F are stripped except for the three that are legitimate in
* text payloads: HT (\x09), LF (\x0A), and CR (\x0D). DEL (\x7F) is also
* stripped.
*/
export function sanitizeString(value: string): string;
export function sanitizeString(
value: string | null | undefined
): string | undefined;
export function sanitizeString(
value: string | null | undefined
): string | undefined {
if (value == null) return undefined;
return (
value
// Replace lone high surrogates (not followed by a low surrogate)
// and lone low surrogates (not preceded by a high surrogate).
.replace(
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
"\uFFFD"
)
// Strip null bytes, C0 control chars (except HT/LF/CR), and DEL.
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "")
);
}

View File

@@ -1,16 +1,3 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import * as crypto from "crypto";
/**
@@ -61,7 +48,10 @@ function encodeUInt64(value: bigint): Buffer {
* Decode a string from SSH wire format at the given offset
* Returns the string buffer and the new offset
*/
function decodeString(data: Buffer, offset: number): { value: Buffer; newOffset: number } {
function decodeString(
data: Buffer,
offset: number
): { value: Buffer; newOffset: number } {
const len = data.readUInt32BE(offset);
const value = data.subarray(offset + 4, offset + 4 + len);
return { value, newOffset: offset + 4 + len };
@@ -91,7 +81,9 @@ function parseOpenSSHPublicKey(pubKeyLine: string): {
// Verify the key type in the blob matches
const { value: blobKeyType } = decodeString(keyData, 0);
if (blobKeyType.toString("utf8") !== keyType) {
throw new Error(`Key type mismatch: ${blobKeyType.toString("utf8")} vs ${keyType}`);
throw new Error(
`Key type mismatch: ${blobKeyType.toString("utf8")} vs ${keyType}`
);
}
return { keyType, keyData, comment };
@@ -238,7 +230,7 @@ export interface SignedCertificate {
* @param comment - Optional comment for the CA public key
* @returns CA key pair and configuration info
*/
export function generateCA(comment: string = "ssh-ca"): CAKeyPair {
export function generateCA(comment: string = "pangolin-ssh-ca"): CAKeyPair {
// Generate Ed25519 key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519", {
publicKeyEncoding: { type: "spki", format: "pem" },
@@ -269,7 +261,7 @@ export function generateCA(comment: string = "ssh-ca"): CAKeyPair {
/**
* Get and decrypt the SSH CA keys for an organization.
*
*
* @param orgId - Organization ID
* @param decryptionKey - Key to decrypt the CA private key (typically server.secret from config)
* @returns CA key pair or null if not found
@@ -307,7 +299,10 @@ export async function getOrgCAKeys(
key: privateKeyPem,
format: "pem"
});
const publicKeyPem = pubKeyObj.export({ type: "spki", format: "pem" }) as string;
const publicKeyPem = pubKeyObj.export({
type: "spki",
format: "pem"
}) as string;
return {
privateKeyPem,
@@ -365,8 +360,8 @@ export function signPublicKey(
const serial = options.serial ?? BigInt(Date.now());
const certType = options.certType ?? 1; // 1 = user cert
const now = BigInt(Math.floor(Date.now() / 1000));
const validAfter = options.validAfter ?? (now - 60n); // 1 minute ago
const validBefore = options.validBefore ?? (now + 86400n * 365n); // 1 year from now
const validAfter = options.validAfter ?? now - 60n; // 1 minute ago
const validBefore = options.validBefore ?? now + 86400n * 365n; // 1 year from now
// Default extensions for user certificates
const defaultExtensions = [
@@ -422,10 +417,7 @@ export function signPublicKey(
]);
// Build complete certificate
const certificate = Buffer.concat([
certBody,
encodeString(signatureBlob)
]);
const certificate = Buffer.concat([certBody, encodeString(signatureBlob)]);
// Format as OpenSSH certificate line
const certLine = `${certTypeString} ${certificate.toString("base64")} ${options.keyId}`;

22
server/lib/tokenCache.ts Normal file
View File

@@ -0,0 +1,22 @@
/**
* Returns a cached plaintext token from Redis if one exists and decrypts
* cleanly, otherwise calls `createSession` to mint a fresh token, stores the
* encrypted value in Redis with the given TTL, and returns it.
*
* Failures at the Redis layer are non-fatal the function always falls
* through to session creation so the caller is never blocked by a Redis outage.
*
* @param cacheKey Unique Redis key, e.g. `"newt:token_cache:abc123"`
* @param secret Server secret used for AES encryption/decryption
* @param ttlSeconds Cache TTL in seconds (should match session expiry)
* @param createSession Factory that mints a new session and returns its raw token
*/
export async function getOrCreateCachedToken(
cacheKey: string,
secret: string,
ttlSeconds: number,
createSession: () => Promise<string>
): Promise<string> {
const token = await createSession();
return token;
}

View File

@@ -218,10 +218,11 @@ export class TraefikConfigManager {
return true;
}
// Fetch if it's been more than 24 hours (for renewals)
const dayInMs = 24 * 60 * 60 * 1000;
const timeSinceLastFetch =
Date.now() - this.lastCertificateFetch.getTime();
// Fetch if it's been more than 24 hours (daily routine check)
if (timeSinceLastFetch > dayInMs) {
logger.info("Fetching certificates due to 24-hour renewal check");
return true;
@@ -265,7 +266,7 @@ export class TraefikConfigManager {
return true;
}
// Check if any local certificates are missing or appear to be outdated
// Check if any local certificates are missing (needs immediate fetch)
for (const domain of domainsNeedingCerts) {
const localState = this.lastLocalCertificateState.get(domain);
if (!localState || !localState.exists) {
@@ -274,17 +275,46 @@ export class TraefikConfigManager {
);
return true;
}
}
// Check if certificate is expiring soon (within 30 days)
if (localState.expiresAt) {
const nowInSeconds = Math.floor(Date.now() / 1000);
const secondsUntilExpiry = localState.expiresAt - nowInSeconds;
const daysUntilExpiry = secondsUntilExpiry / (60 * 60 * 24);
if (daysUntilExpiry < 30) {
logger.info(
`Fetching certificates due to upcoming expiry for ${domain} (${Math.round(daysUntilExpiry)} days remaining)`
);
return true;
// For expiry checks, throttle to every 6 hours to avoid querying the
// API/DB on every monitor loop. The certificate-service renews certs
// 45 days before expiry, so checking every 6 hours is plenty frequent
// to pick up renewed certs promptly.
const renewalCheckIntervalMs = 6 * 60 * 60 * 1000; // 6 hours
if (timeSinceLastFetch > renewalCheckIntervalMs) {
// Check non-wildcard certs for expiry (within 45 days to match
// the server-side renewal window in certificate-service)
for (const domain of domainsNeedingCerts) {
const localState = this.lastLocalCertificateState.get(domain);
if (localState?.expiresAt) {
const nowInSeconds = Math.floor(Date.now() / 1000);
const secondsUntilExpiry =
localState.expiresAt - nowInSeconds;
const daysUntilExpiry = secondsUntilExpiry / (60 * 60 * 24);
if (daysUntilExpiry < 45) {
logger.info(
`Fetching certificates due to upcoming expiry for ${domain} (${Math.round(daysUntilExpiry)} days remaining)`
);
return true;
}
}
}
// Also check wildcard certificates for expiry. These are not
// included in domainsNeedingCerts since their subdomains are
// filtered out, so we must check them separately.
for (const [certDomain, state] of this.lastLocalCertificateState) {
if (state.exists && state.wildcard && state.expiresAt) {
const nowInSeconds = Math.floor(Date.now() / 1000);
const secondsUntilExpiry = state.expiresAt - nowInSeconds;
const daysUntilExpiry = secondsUntilExpiry / (60 * 60 * 24);
if (daysUntilExpiry < 45) {
logger.info(
`Fetching certificates due to upcoming expiry for wildcard cert ${certDomain} (${Math.round(daysUntilExpiry)} days remaining)`
);
return true;
}
}
}
}
@@ -361,6 +391,26 @@ export class TraefikConfigManager {
}
}
// Also include wildcard cert base domains that are
// expiring or expired so they get re-fetched even though
// their subdomains were filtered out above.
for (const [certDomain, state] of this
.lastLocalCertificateState) {
if (state.exists && state.wildcard && state.expiresAt) {
const nowInSeconds = Math.floor(Date.now() / 1000);
const secondsUntilExpiry =
state.expiresAt - nowInSeconds;
const daysUntilExpiry =
secondsUntilExpiry / (60 * 60 * 24);
if (daysUntilExpiry < 45) {
domainsToFetch.add(certDomain);
logger.info(
`Including expiring wildcard cert domain ${certDomain} in fetch (${Math.round(daysUntilExpiry)} days remaining)`
);
}
}
}
if (domainsToFetch.size > 0) {
// Get valid certificates for domains not covered by wildcards
validCertificates =
@@ -507,11 +557,18 @@ export class TraefikConfigManager {
config.getRawConfig().server
.session_cookie_name,
// deprecated
accessTokenQueryParam:
config.getRawConfig().server
.resource_access_token_param,
accessTokenIdHeader:
config.getRawConfig().server
.resource_access_token_headers.id,
accessTokenHeader:
config.getRawConfig().server
.resource_access_token_headers.token,
resourceSessionRequestParam:
config.getRawConfig().server
.resource_session_request_param

View File

@@ -14,7 +14,7 @@ import logger from "@server/logger";
import config from "@server/lib/config";
import { resources, sites, Target, targets } from "@server/db";
import createPathRewriteMiddleware from "./middleware";
import { sanitize, validatePathRewriteConfig } from "./utils";
import { sanitize, encodePath, validatePathRewriteConfig } from "./utils";
const redirectHttpsMiddlewareName = "redirect-to-https";
const badgerMiddlewareName = "badger";
@@ -44,7 +44,7 @@ export async function getTraefikConfig(
filterOutNamespaceDomains = false, // UNUSED BUT USED IN PRIVATE
generateLoginPageRouters = false, // UNUSED BUT USED IN PRIVATE
allowRawResources = true,
allowMaintenancePage = true, // UNUSED BUT USED IN PRIVATE
allowMaintenancePage = true // UNUSED BUT USED IN PRIVATE
): Promise<any> {
// Get resources with their targets and sites in a single optimized query
// Start from sites on this exit node, then join to targets and resources
@@ -127,7 +127,7 @@ export async function getTraefikConfig(
resourcesWithTargetsAndSites.forEach((row) => {
const resourceId = row.resourceId;
const resourceName = sanitize(row.resourceName) || "";
const targetPath = sanitize(row.path) || ""; // Handle null/undefined paths
const targetPath = encodePath(row.path); // Use encodePath to avoid collisions (e.g. "/a/b" vs "/a-b")
const pathMatchType = row.pathMatchType || "";
const rewritePath = row.rewritePath || "";
const rewritePathType = row.rewritePathType || "";
@@ -145,7 +145,7 @@ export async function getTraefikConfig(
const mapKey = [resourceId, pathKey].filter(Boolean).join("-");
const key = sanitize(mapKey);
if (!resourcesMap.has(key)) {
if (!resourcesMap.has(mapKey)) {
const validation = validatePathRewriteConfig(
row.path,
row.pathMatchType,
@@ -160,9 +160,10 @@ export async function getTraefikConfig(
return;
}
resourcesMap.set(key, {
resourcesMap.set(mapKey, {
resourceId: row.resourceId,
name: resourceName,
key: key,
fullDomain: row.fullDomain,
ssl: row.ssl,
http: row.http,
@@ -190,7 +191,7 @@ export async function getTraefikConfig(
});
}
resourcesMap.get(key).targets.push({
resourcesMap.get(mapKey).targets.push({
resourceId: row.resourceId,
targetId: row.targetId,
ip: row.ip,
@@ -227,8 +228,9 @@ export async function getTraefikConfig(
};
// get the key and the resource
for (const [key, resource] of resourcesMap.entries()) {
for (const [, resource] of resourcesMap.entries()) {
const targets = resource.targets as TargetWithSite[];
const key = resource.key;
const routerName = `${key}-${resource.name}-router`;
const serviceName = `${key}-${resource.name}-service`;
@@ -477,7 +479,10 @@ export async function getTraefikConfig(
// TODO: HOW TO HANDLE ^^^^^^ BETTER
const anySitesOnline = targets.some(
(target) => target.site.online
(target) =>
target.site.online ||
target.site.type === "local" ||
target.site.type === "wireguard"
);
return (
@@ -490,7 +495,7 @@ export async function getTraefikConfig(
if (target.health == "unhealthy") {
return false;
}
// If any sites are online, exclude offline sites
if (anySitesOnline && !target.site.online) {
return false;
@@ -605,7 +610,10 @@ export async function getTraefikConfig(
servers: (() => {
// Check if any sites are online
const anySitesOnline = targets.some(
(target) => target.site.online
(target) =>
target.site.online ||
target.site.type === "local" ||
target.site.type === "wireguard"
);
return targets
@@ -613,7 +621,7 @@ export async function getTraefikConfig(
if (!target.enabled) {
return false;
}
// If any sites are online, exclude offline sites
if (anySitesOnline && !target.site.online) {
return false;

View File

@@ -0,0 +1,323 @@
import { assertEquals } from "../../../test/assert";
// ── Pure function copies (inlined to avoid pulling in server dependencies) ──
function sanitize(input: string | null | undefined): string | undefined {
if (!input) return undefined;
if (input.length > 50) {
input = input.substring(0, 50);
}
return input
.replace(/[^a-zA-Z0-9-]/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}
function encodePath(path: string | null | undefined): string {
if (!path) return "";
return path.replace(/[^a-zA-Z0-9]/g, (ch) => {
return ch.charCodeAt(0).toString(16);
});
}
// ── Helpers ──────────────────────────────────────────────────────────
/**
* Exact replica of the OLD key computation from upstream main.
* Uses sanitize() for paths — this is what had the collision bug.
*/
function oldKeyComputation(
resourceId: number,
path: string | null,
pathMatchType: string | null,
rewritePath: string | null,
rewritePathType: string | null
): string {
const targetPath = sanitize(path) || "";
const pmt = pathMatchType || "";
const rp = rewritePath || "";
const rpt = rewritePathType || "";
const pathKey = [targetPath, pmt, rp, rpt].filter(Boolean).join("-");
const mapKey = [resourceId, pathKey].filter(Boolean).join("-");
return sanitize(mapKey) || "";
}
/**
* Replica of the NEW key computation from our fix.
* Uses encodePath() for paths — collision-free.
*/
function newKeyComputation(
resourceId: number,
path: string | null,
pathMatchType: string | null,
rewritePath: string | null,
rewritePathType: string | null
): string {
const targetPath = encodePath(path);
const pmt = pathMatchType || "";
const rp = rewritePath || "";
const rpt = rewritePathType || "";
const pathKey = [targetPath, pmt, rp, rpt].filter(Boolean).join("-");
const mapKey = [resourceId, pathKey].filter(Boolean).join("-");
return sanitize(mapKey) || "";
}
// ── Tests ────────────────────────────────────────────────────────────
function runTests() {
console.log("Running path encoding tests...\n");
let passed = 0;
// ── encodePath unit tests ────────────────────────────────────────
// Test 1: null/undefined/empty
{
assertEquals(encodePath(null), "", "null should return empty");
assertEquals(
encodePath(undefined),
"",
"undefined should return empty"
);
assertEquals(encodePath(""), "", "empty string should return empty");
console.log(" PASS: encodePath handles null/undefined/empty");
passed++;
}
// Test 2: root path
{
assertEquals(encodePath("/"), "2f", "/ should encode to 2f");
console.log(" PASS: encodePath encodes root path");
passed++;
}
// Test 3: alphanumeric passthrough
{
assertEquals(encodePath("/api"), "2fapi", "/api encodes slash only");
assertEquals(encodePath("/v1"), "2fv1", "/v1 encodes slash only");
assertEquals(encodePath("abc"), "abc", "plain alpha passes through");
console.log(" PASS: encodePath preserves alphanumeric chars");
passed++;
}
// Test 4: all special chars produce unique hex
{
const paths = ["/a/b", "/a-b", "/a.b", "/a_b", "/a b"];
const results = paths.map((p) => encodePath(p));
const unique = new Set(results);
assertEquals(
unique.size,
paths.length,
"all special-char paths must produce unique encodings"
);
console.log(
" PASS: encodePath produces unique output for different special chars"
);
passed++;
}
// Test 5: output is always alphanumeric (safe for Traefik names)
{
const paths = [
"/",
"/api",
"/a/b",
"/a-b",
"/a.b",
"/complex/path/here"
];
for (const p of paths) {
const e = encodePath(p);
assertEquals(
/^[a-zA-Z0-9]+$/.test(e),
true,
`encodePath("${p}") = "${e}" must be alphanumeric`
);
}
console.log(" PASS: encodePath output is always alphanumeric");
passed++;
}
// Test 6: deterministic
{
assertEquals(
encodePath("/api"),
encodePath("/api"),
"same input same output"
);
assertEquals(
encodePath("/a/b/c"),
encodePath("/a/b/c"),
"same input same output"
);
console.log(" PASS: encodePath is deterministic");
passed++;
}
// Test 7: many distinct paths never collide
{
const paths = [
"/",
"/api",
"/api/v1",
"/api/v2",
"/a/b",
"/a-b",
"/a.b",
"/a_b",
"/health",
"/health/check",
"/admin",
"/admin/users",
"/api/v1/users",
"/api/v1/posts",
"/app",
"/app/dashboard"
];
const encoded = new Set(paths.map((p) => encodePath(p)));
assertEquals(
encoded.size,
paths.length,
`expected ${paths.length} unique encodings, got ${encoded.size}`
);
console.log(" PASS: 16 realistic paths all produce unique encodings");
passed++;
}
// ── Collision fix: the actual bug we're fixing ───────────────────
// Test 8: /a/b and /a-b now have different keys (THE BUG FIX)
{
const keyAB = newKeyComputation(1, "/a/b", "prefix", null, null);
const keyDash = newKeyComputation(1, "/a-b", "prefix", null, null);
assertEquals(
keyAB !== keyDash,
true,
"/a/b and /a-b MUST have different keys"
);
console.log(" PASS: collision fix — /a/b vs /a-b have different keys");
passed++;
}
// Test 9: demonstrate the old bug — old code maps /a/b and /a-b to same key
{
const oldKeyAB = oldKeyComputation(1, "/a/b", "prefix", null, null);
const oldKeyDash = oldKeyComputation(1, "/a-b", "prefix", null, null);
assertEquals(
oldKeyAB,
oldKeyDash,
"old code MUST have this collision (confirms the bug exists)"
);
console.log(" PASS: confirmed old code bug — /a/b and /a-b collided");
passed++;
}
// Test 10: /api/v1 and /api-v1 — old code collision, new code fixes it
{
const oldKey1 = oldKeyComputation(1, "/api/v1", "prefix", null, null);
const oldKey2 = oldKeyComputation(1, "/api-v1", "prefix", null, null);
assertEquals(
oldKey1,
oldKey2,
"old code collision for /api/v1 vs /api-v1"
);
const newKey1 = newKeyComputation(1, "/api/v1", "prefix", null, null);
const newKey2 = newKeyComputation(1, "/api-v1", "prefix", null, null);
assertEquals(
newKey1 !== newKey2,
true,
"new code must separate /api/v1 and /api-v1"
);
console.log(" PASS: collision fix — /api/v1 vs /api-v1");
passed++;
}
// Test 11: /app.v2 and /app/v2 and /app-v2 — three-way collision fixed
{
const a = newKeyComputation(1, "/app.v2", "prefix", null, null);
const b = newKeyComputation(1, "/app/v2", "prefix", null, null);
const c = newKeyComputation(1, "/app-v2", "prefix", null, null);
const keys = new Set([a, b, c]);
assertEquals(
keys.size,
3,
"three paths must produce three unique keys"
);
console.log(
" PASS: collision fix — three-way /app.v2, /app/v2, /app-v2"
);
passed++;
}
// ── Edge cases ───────────────────────────────────────────────────
// Test 12: same path in different resources — always separate
{
const key1 = newKeyComputation(1, "/api", "prefix", null, null);
const key2 = newKeyComputation(2, "/api", "prefix", null, null);
assertEquals(
key1 !== key2,
true,
"different resources with same path must have different keys"
);
console.log(" PASS: edge case — same path, different resources");
passed++;
}
// Test 13: same resource, different pathMatchType — separate keys
{
const exact = newKeyComputation(1, "/api", "exact", null, null);
const prefix = newKeyComputation(1, "/api", "prefix", null, null);
assertEquals(
exact !== prefix,
true,
"exact vs prefix must have different keys"
);
console.log(" PASS: edge case — same path, different match types");
passed++;
}
// Test 14: same resource and path, different rewrite config — separate keys
{
const noRewrite = newKeyComputation(1, "/api", "prefix", null, null);
const withRewrite = newKeyComputation(
1,
"/api",
"prefix",
"/backend",
"prefix"
);
assertEquals(
noRewrite !== withRewrite,
true,
"with vs without rewrite must have different keys"
);
console.log(" PASS: edge case — same path, different rewrite config");
passed++;
}
// Test 15: paths with special URL characters
{
const paths = ["/api?foo", "/api#bar", "/api%20baz", "/api+qux"];
const keys = new Set(
paths.map((p) => newKeyComputation(1, p, "prefix", null, null))
);
assertEquals(
keys.size,
paths.length,
"special URL chars must produce unique keys"
);
console.log(" PASS: edge case — special URL characters in paths");
passed++;
}
console.log(`\nAll ${passed} tests passed!`);
}
try {
runTests();
} catch (error) {
console.error("Test failed:", error);
process.exit(1);
}

View File

@@ -13,6 +13,26 @@ export function sanitize(input: string | null | undefined): string | undefined {
.replace(/^-|-$/g, "");
}
/**
* Encode a URL path into a collision-free alphanumeric string suitable for use
* in Traefik map keys.
*
* Unlike sanitize(), this preserves uniqueness by encoding each non-alphanumeric
* character as its hex code. Different paths always produce different outputs.
*
* encodePath("/api") => "2fapi"
* encodePath("/a/b") => "2fa2fb"
* encodePath("/a-b") => "2fa2db" (different from /a/b)
* encodePath("/") => "2f"
* encodePath(null) => ""
*/
export function encodePath(path: string | null | undefined): string {
if (!path) return "";
return path.replace(/[^a-zA-Z0-9]/g, (ch) => {
return ch.charCodeAt(0).toString(16);
});
}
export function validatePathRewriteConfig(
path: string | null,
pathMatchType: string | null,

View File

@@ -6,7 +6,7 @@ import {
siteResources,
sites,
Transaction,
UserOrg,
userOrgRoles,
userOrgs,
userResources,
userSiteResources,
@@ -19,9 +19,22 @@ import { FeatureId } from "@server/lib/billing";
export async function assignUserToOrg(
org: Org,
values: typeof userOrgs.$inferInsert,
roleIds: number[],
trx: Transaction | typeof db = db
) {
const uniqueRoleIds = [...new Set(roleIds)];
if (uniqueRoleIds.length === 0) {
throw new Error("assignUserToOrg requires at least one roleId");
}
const [userOrg] = await trx.insert(userOrgs).values(values).returning();
await trx.insert(userOrgRoles).values(
uniqueRoleIds.map((roleId) => ({
userId: userOrg.userId,
orgId: userOrg.orgId,
roleId
}))
);
// calculate if the user is in any other of the orgs before we count it as an add to the billing org
if (org.billingOrgId) {
@@ -58,6 +71,14 @@ export async function removeUserFromOrg(
userId: string,
trx: Transaction | typeof db = db
) {
await trx
.delete(userOrgRoles)
.where(
and(
eq(userOrgRoles.userId, userId),
eq(userOrgRoles.orgId, org.orgId)
)
);
await trx
.delete(userOrgs)
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, org.orgId)));

View File

@@ -0,0 +1,36 @@
import { db, roles, userOrgRoles } from "@server/db";
import { and, eq } from "drizzle-orm";
/**
* Get all role IDs a user has in an organization.
* Returns empty array if the user has no roles in the org (callers must treat as no access).
*/
export async function getUserOrgRoleIds(
userId: string,
orgId: string
): Promise<number[]> {
const rows = await db
.select({ roleId: userOrgRoles.roleId })
.from(userOrgRoles)
.where(
and(
eq(userOrgRoles.userId, userId),
eq(userOrgRoles.orgId, orgId)
)
);
return rows.map((r) => r.roleId);
}
export async function getUserOrgRoles(
userId: string,
orgId: string
): Promise<{ roleId: number; roleName: string }[]> {
const rows = await db
.select({ roleId: userOrgRoles.roleId, roleName: roles.name })
.from(userOrgRoles)
.innerJoin(roles, eq(userOrgRoles.roleId, roles.roleId))
.where(
and(eq(userOrgRoles.userId, userId), eq(userOrgRoles.orgId, orgId))
);
return rows;
}

View File

@@ -21,8 +21,7 @@ export async function getUserOrgs(
try {
const userOrganizations = await db
.select({
orgId: userOrgs.orgId,
roleId: userOrgs.roleId
orgId: userOrgs.orgId
})
.from(userOrgs)
.where(eq(userOrgs.userId, userId));

View File

@@ -17,6 +17,7 @@ export * from "./verifyAccessTokenAccess";
export * from "./requestTimeout";
export * from "./verifyClientAccess";
export * from "./verifyUserHasAction";
export * from "./verifyUserCanSetUserOrgRoles";
export * from "./verifyUserIsServerAdmin";
export * from "./verifyIsLoggedInUser";
export * from "./verifyIsLoggedInUser";
@@ -24,6 +25,7 @@ export * from "./verifyClientAccess";
export * from "./integration";
export * from "./verifyUserHasAction";
export * from "./verifyApiKeyAccess";
export * from "./verifySiteProvisioningKeyAccess";
export * from "./verifyDomainAccess";
export * from "./verifyUserIsOrgOwner";
export * from "./verifySiteResourceAccess";

View File

@@ -1,6 +1,7 @@
export * from "./verifyApiKey";
export * from "./verifyApiKeyOrgAccess";
export * from "./verifyApiKeyHasAction";
export * from "./verifyApiKeyCanSetUserOrgRoles";
export * from "./verifyApiKeySiteAccess";
export * from "./verifyApiKeyResourceAccess";
export * from "./verifyApiKeyTargetAccess";
@@ -14,3 +15,4 @@ export * from "./verifyApiKeyApiKeyAccess";
export * from "./verifyApiKeyClientAccess";
export * from "./verifyApiKeySiteResourceAccess";
export * from "./verifyApiKeyIdpAccess";
export * from "./verifyApiKeyDomainAccess";

View File

@@ -0,0 +1,74 @@
import { Request, Response, NextFunction } from "express";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
import logger from "@server/logger";
import { ActionsEnum } from "@server/auth/actions";
import { db } from "@server/db";
import { apiKeyActions } from "@server/db";
import { and, eq } from "drizzle-orm";
async function apiKeyHasAction(apiKeyId: string, actionId: ActionsEnum) {
const [row] = await db
.select()
.from(apiKeyActions)
.where(
and(
eq(apiKeyActions.apiKeyId, apiKeyId),
eq(apiKeyActions.actionId, actionId)
)
);
return !!row;
}
/**
* Allows setUserOrgRoles on the key, or both addUserRole and removeUserRole.
*/
export function verifyApiKeyCanSetUserOrgRoles() {
return async function (
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
if (!req.apiKey) {
return next(
createHttpError(
HttpCode.UNAUTHORIZED,
"API Key not authenticated"
)
);
}
const keyId = req.apiKey.apiKeyId;
if (await apiKeyHasAction(keyId, ActionsEnum.setUserOrgRoles)) {
return next();
}
const hasAdd = await apiKeyHasAction(keyId, ActionsEnum.addUserRole);
const hasRemove = await apiKeyHasAction(
keyId,
ActionsEnum.removeUserRole
);
if (hasAdd && hasRemove) {
return next();
}
return next(
createHttpError(
HttpCode.FORBIDDEN,
"Key does not have permission perform this action"
)
);
} catch (error) {
logger.error("Error verifying API key set user org roles:", error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Error verifying key action access"
)
);
}
};
}

Some files were not shown because too many files have changed in this diff Show More