Compare commits

...

388 Commits

Author SHA1 Message Date
Owen Schwartz
cf596d980f Merge pull request #2971 from fosrl/dev
1.18.2
2026-05-02 20:59:51 -07:00
Owen Schwartz
70f619b726 Merge pull request #2970 from fosrl/crowdin_dev
New Crowdin updates
2026-05-02 20:59:16 -07:00
Owen Schwartz
7743e3890b New translations en-us.json (Spanish)
[ci skip]
2026-05-02 20:57:57 -07:00
Owen Schwartz
d8df250555 New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-05-02 20:57:55 -07:00
Owen Schwartz
45c9f217c6 New translations en-us.json (Chinese Simplified)
[ci skip]
2026-05-02 20:57:54 -07:00
Owen Schwartz
8371692cc5 New translations en-us.json (Turkish)
[ci skip]
2026-05-02 20:57:52 -07:00
Owen Schwartz
5377dc7a1c New translations en-us.json (Russian)
[ci skip]
2026-05-02 20:57:51 -07:00
Owen Schwartz
02649468e0 New translations en-us.json (Portuguese)
[ci skip]
2026-05-02 20:57:49 -07:00
Owen Schwartz
c5ef00fb0e New translations en-us.json (Polish)
[ci skip]
2026-05-02 20:57:48 -07:00
Owen Schwartz
6f4325e9a0 New translations en-us.json (Dutch)
[ci skip]
2026-05-02 20:57:45 -07:00
Owen Schwartz
a2a031dfe7 New translations en-us.json (Korean)
[ci skip]
2026-05-02 20:57:44 -07:00
Owen Schwartz
e34a4c82eb New translations en-us.json (Italian)
[ci skip]
2026-05-02 20:57:42 -07:00
Owen Schwartz
52fd7df727 New translations en-us.json (German)
[ci skip]
2026-05-02 20:57:41 -07:00
Owen Schwartz
d5f08437d7 New translations en-us.json (Czech)
[ci skip]
2026-05-02 20:57:39 -07:00
Owen Schwartz
9ee07ba343 New translations en-us.json (Bulgarian)
[ci skip]
2026-05-02 20:57:38 -07:00
Owen Schwartz
4baaa5fc14 New translations en-us.json (French)
[ci skip]
2026-05-02 20:57:36 -07:00
Owen
61de100630 Fix imports 2026-05-02 20:46:52 -07:00
miloschwartz
3694f43ae8 dont early return on multi org 2026-05-02 20:38:14 -07:00
Owen
279211142d Bump version 2026-05-02 13:48:25 -07:00
Owen
b8822b4d25 Fix CE not processing alert status
Fixes #2968
2026-05-02 13:38:05 -07:00
Owen
e1afbc226c Allow configuring the webhook body 2026-05-02 13:26:54 -07:00
miloschwartz
96c450fd08 update private resources screenshot 2026-05-02 13:19:00 -07:00
Owen Schwartz
587e4d104b New translations en-us.json (Spanish)
[ci skip]
2026-05-02 13:16:28 -07:00
Owen Schwartz
368c5c374f New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-05-02 13:16:26 -07:00
Owen Schwartz
7675b6409c New translations en-us.json (Chinese Simplified)
[ci skip]
2026-05-02 13:16:24 -07:00
Owen Schwartz
d31da1a41e New translations en-us.json (Turkish)
[ci skip]
2026-05-02 13:16:23 -07:00
Owen Schwartz
49e259e259 New translations en-us.json (Russian)
[ci skip]
2026-05-02 13:16:21 -07:00
Owen Schwartz
f4684c1858 New translations en-us.json (Portuguese)
[ci skip]
2026-05-02 13:16:19 -07:00
Owen Schwartz
6e223bb363 New translations en-us.json (Polish)
[ci skip]
2026-05-02 13:16:18 -07:00
Owen Schwartz
22e7038b2c New translations en-us.json (Dutch)
[ci skip]
2026-05-02 13:16:16 -07:00
Owen Schwartz
76ba4c1fdf New translations en-us.json (Korean)
[ci skip]
2026-05-02 13:16:14 -07:00
Owen Schwartz
7f25d94a83 New translations en-us.json (Italian)
[ci skip]
2026-05-02 13:16:13 -07:00
Owen Schwartz
769ba27e3a New translations en-us.json (German)
[ci skip]
2026-05-02 13:16:11 -07:00
Owen Schwartz
a188552ba0 New translations en-us.json (Czech)
[ci skip]
2026-05-02 13:16:09 -07:00
Owen Schwartz
208132082e New translations en-us.json (Bulgarian)
[ci skip]
2026-05-02 13:16:08 -07:00
Owen Schwartz
fcd5789221 New translations en-us.json (French)
[ci skip]
2026-05-02 13:16:06 -07:00
miloschwartz
c6a8b09cff log in page improvements 2026-05-02 12:46:39 -07:00
miloschwartz
380ff381fc fix credenza scroll extra spacing above footer 2026-05-02 12:19:00 -07:00
Owen Schwartz
5eb3951f00 New translations en-us.json (Spanish)
[ci skip]
2026-05-02 12:13:08 -07:00
Owen Schwartz
c30e94da98 New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-05-02 12:13:06 -07:00
Owen Schwartz
6ca24d51a1 New translations en-us.json (Chinese Simplified)
[ci skip]
2026-05-02 12:13:05 -07:00
Owen Schwartz
13f512aed6 New translations en-us.json (Turkish)
[ci skip]
2026-05-02 12:13:03 -07:00
Owen Schwartz
2bdbc9d688 New translations en-us.json (Russian)
[ci skip]
2026-05-02 12:13:02 -07:00
Owen Schwartz
8e2f30d8de New translations en-us.json (Portuguese)
[ci skip]
2026-05-02 12:13:00 -07:00
Owen Schwartz
a84e1cc9e0 New translations en-us.json (Polish)
[ci skip]
2026-05-02 12:12:58 -07:00
Owen Schwartz
6b28f0c81e New translations en-us.json (Dutch)
[ci skip]
2026-05-02 12:12:56 -07:00
Owen Schwartz
d28d3ba6ea New translations en-us.json (Korean)
[ci skip]
2026-05-02 12:12:55 -07:00
Owen Schwartz
6efaf9f40d New translations en-us.json (Italian)
[ci skip]
2026-05-02 12:12:53 -07:00
Owen Schwartz
5379b32959 New translations en-us.json (German)
[ci skip]
2026-05-02 12:12:51 -07:00
Owen Schwartz
9bb936a40d New translations en-us.json (Czech)
[ci skip]
2026-05-02 12:12:50 -07:00
Owen Schwartz
960fe760f1 New translations en-us.json (Bulgarian)
[ci skip]
2026-05-02 12:12:48 -07:00
Owen Schwartz
2f2105a085 New translations en-us.json (French)
[ci skip]
2026-05-02 12:12:46 -07:00
miloschwartz
de92a28435 update mac models 2026-05-02 12:09:55 -07:00
Owen
d8c3484ed5 Have to import from private 2026-05-02 12:00:51 -07:00
Owen
726e000154 Show remote nodes update in table 2026-05-02 11:55:01 -07:00
Owen Schwartz
9df46f7014 Merge pull request #2966 from fosrl/dev
Try to pull domains from host regex
2026-05-01 20:54:09 -07:00
Owen
908f0d54e2 Try to pull domains from host regex 2026-05-01 20:53:39 -07:00
Milo Schwartz
f0010ea12a Merge pull request #2965 from fosrl/dev
add new screenshots
2026-05-01 17:35:29 -07:00
miloschwartz
cab8be1a9a add new screenshots 2026-05-01 17:34:05 -07:00
Owen Schwartz
0a9dab7cca Merge pull request #2964 from fosrl/dev
Update translations
2026-05-01 17:02:41 -07:00
Owen Schwartz
889ab1f8a8 Merge pull request #2963 from fosrl/crowdin_dev
New Crowdin updates
2026-05-01 17:02:10 -07:00
Owen Schwartz
a9019cfb23 New translations en-us.json (Spanish)
[ci skip]
2026-05-01 17:00:49 -07:00
Owen Schwartz
441d4bce6e New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-05-01 17:00:47 -07:00
Owen Schwartz
dd1e681a9c New translations en-us.json (Chinese Simplified)
[ci skip]
2026-05-01 17:00:45 -07:00
Owen Schwartz
a882619eaf New translations en-us.json (Turkish)
[ci skip]
2026-05-01 17:00:43 -07:00
Owen Schwartz
f43baaaf1f New translations en-us.json (Russian)
[ci skip]
2026-05-01 17:00:41 -07:00
Owen Schwartz
c3dc0bd015 New translations en-us.json (Portuguese)
[ci skip]
2026-05-01 17:00:39 -07:00
Owen Schwartz
1fd2a0fae2 New translations en-us.json (Polish)
[ci skip]
2026-05-01 17:00:37 -07:00
Owen Schwartz
8ba5b43569 New translations en-us.json (Dutch)
[ci skip]
2026-05-01 17:00:35 -07:00
Owen Schwartz
6deefcd003 New translations en-us.json (Korean)
[ci skip]
2026-05-01 17:00:33 -07:00
Owen Schwartz
4d6cea5fcd New translations en-us.json (Italian)
[ci skip]
2026-05-01 17:00:31 -07:00
Owen Schwartz
f175ac774f New translations en-us.json (German)
[ci skip]
2026-05-01 17:00:29 -07:00
Owen Schwartz
0fe2b24f6b New translations en-us.json (Czech)
[ci skip]
2026-05-01 17:00:27 -07:00
Owen Schwartz
6ad06e6faf New translations en-us.json (Bulgarian)
[ci skip]
2026-05-01 17:00:25 -07:00
Owen Schwartz
d47faeced1 New translations en-us.json (French)
[ci skip]
2026-05-01 17:00:23 -07:00
Owen Schwartz
498f586eeb New translations en-us.json (Spanish)
[ci skip]
2026-05-01 16:57:38 -07:00
Owen Schwartz
e94fc6bc65 New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-05-01 16:57:37 -07:00
Owen Schwartz
0a1fe1b725 New translations en-us.json (Chinese Simplified)
[ci skip]
2026-05-01 16:57:35 -07:00
Owen Schwartz
eb40b04b43 New translations en-us.json (Turkish)
[ci skip]
2026-05-01 16:57:33 -07:00
Owen Schwartz
6685afdcf9 New translations en-us.json (Russian)
[ci skip]
2026-05-01 16:57:32 -07:00
Owen Schwartz
49232e32bf New translations en-us.json (Portuguese)
[ci skip]
2026-05-01 16:57:30 -07:00
Owen Schwartz
aec0aed211 New translations en-us.json (Polish)
[ci skip]
2026-05-01 16:57:28 -07:00
Owen Schwartz
d43b3176f5 New translations en-us.json (Dutch)
[ci skip]
2026-05-01 16:57:26 -07:00
Owen Schwartz
190074ea0c New translations en-us.json (Korean)
[ci skip]
2026-05-01 16:57:24 -07:00
Owen Schwartz
c5a7719239 New translations en-us.json (Italian)
[ci skip]
2026-05-01 16:57:22 -07:00
Owen Schwartz
5eac131d2e New translations en-us.json (German)
[ci skip]
2026-05-01 16:57:21 -07:00
Owen Schwartz
0bc3276ee2 New translations en-us.json (Czech)
[ci skip]
2026-05-01 16:57:18 -07:00
Owen Schwartz
5073507b90 New translations en-us.json (Bulgarian)
[ci skip]
2026-05-01 16:57:16 -07:00
Owen Schwartz
805e6f856a New translations en-us.json (French)
[ci skip]
2026-05-01 16:57:14 -07:00
Owen Schwartz
412a9b5294 Merge pull request #2962 from fosrl/dev
1.18.1-s.6
2026-05-01 16:54:37 -07:00
Owen
fbf95c5363 Start creating ns one level down 2026-05-01 16:51:42 -07:00
Owen
b907850344 Add missing heading 2026-05-01 16:51:42 -07:00
miloschwartz
22116373e3 increase target site selector width 2026-05-01 16:33:40 -07:00
miloschwartz
9757c3d8b6 show newt version on site 2026-05-01 16:26:45 -07:00
miloschwartz
f8b85d4b4e fix sidebar product updates spacing 2026-05-01 16:14:06 -07:00
Owen
4651f19c53 Support acme_json_path as a directory of acme file
Fixes #2961
2026-05-01 16:06:37 -07:00
Owen
4524bdc094 Add http cert syncing for use with the controller 2026-05-01 15:42:38 -07:00
Owen Schwartz
741850880e Merge pull request #2959 from fosrl/dev
1.18.1-s.4
2026-05-01 15:05:59 -07:00
Owen
53e096f7cb Allow deleting account with trial 2026-05-01 15:01:48 -07:00
Owen
3dfd7e8a43 Update limits 2026-05-01 11:47:14 -07:00
Owen
db6e60d0a3 Adjust language 2026-05-01 10:48:09 -07:00
Owen
54d2d689c1 Run messaging for delete in the background as well 2026-04-30 14:38:03 -07:00
Owen Schwartz
bb5853827b Merge pull request #2948 from fosrl/dev
1.18.1-s.3
2026-04-30 14:11:16 -07:00
Owen
68f5512732 Handle messaging in the background; dont time out 2026-04-30 14:00:32 -07:00
Owen
416e124c02 Rotate the secret on the new things using it 2026-04-30 11:53:55 -07:00
Owen
d3e4d8cda8 Fix pr blueprints not picking up site 2026-04-30 11:39:37 -07:00
Owen
81972dbb73 Add name to migration
Fixes #2943
2026-04-30 10:56:12 -07:00
Owen Schwartz
b715786a1e Merge pull request #2939 from fosrl/dev
1.18.1-s.2
2026-04-29 21:33:03 -07:00
Owen
ae24eb2d2c Disable the alerts and hc when downgrading 2026-04-29 21:31:02 -07:00
Owen
20fc59dcda Delete trial when upgrading 2026-04-29 21:25:58 -07:00
Owen
93b09de425 Adjust cloud api endpoints 2026-04-29 21:04:11 -07:00
Owen
bacc130453 Clean up sign and verify 2026-04-29 17:14:22 -07:00
Owen Schwartz
79541ec7b8 Merge pull request #2936 from fosrl/dev
1.18.1 patch over
2026-04-29 16:43:06 -07:00
Owen
81197f8a86 Update the database if the wildcard changes 2026-04-29 16:42:10 -07:00
miloschwartz
dcfc7822f4 hide cert in public resources col on oss 2026-04-29 16:03:59 -07:00
Owen Schwartz
269bd9aa0f Merge pull request #2934 from fosrl/dev
1.18.1-s.1
2026-04-29 15:18:28 -07:00
Owen
0a0817b860 Restrict alerting 2026-04-29 15:15:53 -07:00
Owen Schwartz
b7a903ab32 Merge pull request #2933 from fosrl/dev
1.18.1
2026-04-29 15:00:29 -07:00
Owen Schwartz
ab60438aa7 Merge pull request #2917 from fosrl/crowdin_dev
New Crowdin updates
2026-04-29 14:55:53 -07:00
Owen Schwartz
b9f3f90de6 New translations en-us.json (Spanish)
[ci skip]
2026-04-29 14:54:32 -07:00
Owen Schwartz
b53cc397be New translations en-us.json (Norwegian Bokmal)
[ci skip]
2026-04-29 14:54:30 -07:00
Owen Schwartz
994fb456c2 New translations en-us.json (Chinese Simplified)
[ci skip]
2026-04-29 14:54:29 -07:00
Owen Schwartz
b36927c7a0 New translations en-us.json (Turkish)
[ci skip]
2026-04-29 14:54:27 -07:00
Owen Schwartz
1c57473b6d New translations en-us.json (Russian)
[ci skip]
2026-04-29 14:54:25 -07:00
Owen Schwartz
c02c3eaa4a New translations en-us.json (Portuguese)
[ci skip]
2026-04-29 14:54:23 -07:00
Owen Schwartz
3c265ee577 New translations en-us.json (Polish)
[ci skip]
2026-04-29 14:54:22 -07:00
Owen Schwartz
98dfd05f06 New translations en-us.json (Dutch)
[ci skip]
2026-04-29 14:54:20 -07:00
Owen Schwartz
faa2e97530 New translations en-us.json (Korean)
[ci skip]
2026-04-29 14:54:18 -07:00
Owen Schwartz
175f10a51d New translations en-us.json (Italian)
[ci skip]
2026-04-29 14:54:16 -07:00
Owen Schwartz
6284930fce New translations en-us.json (German)
[ci skip]
2026-04-29 14:54:15 -07:00
Owen Schwartz
6c93aca444 New translations en-us.json (Czech)
[ci skip]
2026-04-29 14:54:13 -07:00
Owen Schwartz
d83318cbfc New translations en-us.json (Bulgarian)
[ci skip]
2026-04-29 14:54:11 -07:00
Owen Schwartz
143f362a48 New translations en-us.json (French)
[ci skip]
2026-04-29 14:54:09 -07:00
miloschwartz
698cd868a8 show cert status in public reosurces table 2026-04-29 14:47:34 -07:00
Owen
a55842ffff Scrape certs from ALL resolvers 2026-04-29 14:29:15 -07:00
Owen
2ffe254879 Dont include site resources on the cloud 2026-04-29 14:08:42 -07:00
miloschwartz
e173f59d89 visual improvements 2026-04-29 13:44:35 -07:00
miloschwartz
d3870f4920 cert status in priv resources table first pass 2026-04-29 13:05:26 -07:00
miloschwartz
227501d8f8 fix rounded buttons in target input 2026-04-29 12:39:08 -07:00
miloschwartz
a16f805709 fix style for unknown status 2026-04-29 12:36:47 -07:00
miloschwartz
a029b107ae dont show site online status for local sites 2026-04-29 12:35:08 -07:00
miloschwartz
f03389a9a0 fix cert styling 2026-04-29 12:18:52 -07:00
Owen
78fff6bfde Filter to only allow newt sites 2026-04-29 12:18:28 -07:00
Owen
bc585c24fc Calculate actual resource status
Fixes #2930
2026-04-29 12:07:32 -07:00
miloschwartz
0f6c66dc67 use localfont and updated mona sans closes #2924 2026-04-29 11:58:06 -07:00
Owen
6be150bafe Handle possible not null for tcp, udp, and icmp
Fixes #2929
2026-04-29 11:42:18 -07:00
Owen
1eac7741a5 Show the certs elsewhere when required 2026-04-29 11:34:10 -07:00
Owen
b8ca0499af Dont show the cert box oss and dont check license 2026-04-29 11:28:30 -07:00
Owen
b39a2bcfb1 Quiet logs 2026-04-29 11:25:43 -07:00
Owen
d45b727dca Dont show cert status because not saved yet 2026-04-29 11:06:14 -07:00
Owen
5c31d35e28 Handle sans in the acme.json 2026-04-29 10:59:49 -07:00
Owen
8c645315f3 Handle when siteIds is not provided 2026-04-29 10:59:36 -07:00
Milo Schwartz
ab6377e086 Merge pull request #2923 from fosrl/miloschwartz-patch-2
Update README.md
2026-04-28 23:03:31 -07:00
Milo Schwartz
8685cf4208 Update README.md 2026-04-29 02:03:18 -04:00
Owen Schwartz
26fe1259da Merge pull request #2922 from fosrl/dev
1.18.0-s.2
2026-04-28 22:28:35 -07:00
Owen
3bcbeb24f3 Query the right column 2026-04-28 22:27:35 -07:00
Owen
1d0a92c83e Its in the transaction so we wait 2026-04-28 22:22:06 -07:00
Owen
a44100c2bd Handle deleting client and orphaning resources 2026-04-28 22:19:22 -07:00
miloschwartz
2203ebf723 show user idp in devices 2026-04-28 21:27:11 -07:00
Owen Schwartz
70958185bd Merge pull request #2921 from fosrl/dev
1.18.0-s.1
2026-04-28 21:03:36 -07:00
Owen
7e374baee9 Update if the ssl toggle changes 2026-04-28 20:45:20 -07:00
Owen
4cf6ca1d55 Force tcp and udp ports when http mode 2026-04-28 20:27:27 -07:00
Owen Schwartz
2957d6592d Merge pull request #2919 from fosrl/dev
Add missing imports
2026-04-28 16:52:53 -07:00
Owen
de2a22aad8 Add missing imports 2026-04-28 16:52:35 -07:00
Owen Schwartz
b96db4f133 Merge pull request #2918 from fosrl/dev
Use logsDb for the status history
2026-04-28 16:38:43 -07:00
Owen
2a29062659 Use logsDb for the status history 2026-04-28 16:38:13 -07:00
Owen Schwartz
8ed9adbfae New translations en-us.json (German)
[ci skip]
2026-04-28 16:21:16 -07:00
Owen Schwartz
98406f63af Merge pull request #2916 from fosrl/dev
1.18.0
2026-04-28 15:53:31 -07:00
Owen
85415176ab Clean imports 2026-04-28 15:41:00 -07:00
Owen
b81ae3d998 Seed satatus data for resources, sites, and hc 2026-04-28 15:15:09 -07:00
Owen
208289f498 Select all networks to prevent delete issues 2026-04-28 12:02:21 -07:00
Owen
8783c47a3c Dont allow clicking the wildcard resource link 2026-04-28 11:21:32 -07:00
Owen
592ca64253 Fix delete 2026-04-28 09:57:46 -07:00
Owen Schwartz
1de6e58eef Merge pull request #2912 from fosrl/crowdin_dev
New Crowdin updates
2026-04-27 21:23:34 -07:00
Owen Schwartz
92822a20e8 New translations en-us.json (Spanish) 2026-04-27 20:25:22 -07:00
Owen Schwartz
4a3035d597 New translations en-us.json (Norwegian Bokmal) 2026-04-27 20:25:20 -07:00
Owen Schwartz
bd866a5fd2 New translations en-us.json (Chinese Simplified) 2026-04-27 20:25:18 -07:00
Owen Schwartz
1c6cd57c31 New translations en-us.json (Turkish) 2026-04-27 20:25:16 -07:00
Owen Schwartz
a0619868be New translations en-us.json (Russian) 2026-04-27 20:25:15 -07:00
Owen Schwartz
6c2dd4331a New translations en-us.json (Portuguese) 2026-04-27 20:25:13 -07:00
Owen Schwartz
5e6171263b New translations en-us.json (Polish) 2026-04-27 20:25:11 -07:00
Owen Schwartz
d33c704f76 New translations en-us.json (Dutch) 2026-04-27 20:25:09 -07:00
Owen Schwartz
3cb1cd9f2f New translations en-us.json (Korean) 2026-04-27 20:25:07 -07:00
Owen Schwartz
926fe5e474 New translations en-us.json (Italian) 2026-04-27 20:25:06 -07:00
Owen Schwartz
243da6379b New translations en-us.json (German) 2026-04-27 20:25:04 -07:00
Owen Schwartz
68ea7d1d98 New translations en-us.json (Czech) 2026-04-27 20:25:02 -07:00
Owen Schwartz
c0a4541455 New translations en-us.json (Bulgarian) 2026-04-27 20:25:00 -07:00
Owen Schwartz
e4bf2da2e5 New translations en-us.json (French) 2026-04-27 20:24:58 -07:00
Owen
85334f082c Only support alerts and newt sync on saas 2026-04-27 18:20:30 -07:00
Owen
c771722127 Dont include rewrite to 2026-04-27 17:52:41 -07:00
Owen
f89b0a17ac Set the default to unknown 2026-04-27 17:15:44 -07:00
Owen
81a6fb8d00 Dont import from postgres 2026-04-27 17:04:04 -07:00
Owen
dbee049ac8 Fix oss build issues 2026-04-27 16:30:54 -07:00
Owen
c03519b7f5 Send updates when the full domain changes 2026-04-27 16:19:37 -07:00
Owen
7affaf63d0 Update get cert to now allow restarting 2026-04-27 16:19:37 -07:00
Owen
08e9cb862d Fix deleting resource 2026-04-27 16:19:36 -07:00
miloschwartz
cbb2388a46 add multi site help link 2026-04-27 15:38:19 -07:00
Owen
24f437e260 Cap degraded in the mail 2026-04-27 15:24:16 -07:00
Owen
3439a3690f Fix site offline not respecting hc enabled 2026-04-27 15:24:16 -07:00
Owen
b88469f901 Hide the icmp and snmp for now 2026-04-27 15:24:16 -07:00
miloschwartz
e573125934 update wildcard resources link 2026-04-27 15:14:29 -07:00
Owen
c5072bed80 Fix healthcheck not showing data 2026-04-27 14:33:28 -07:00
Owen
28dd06c41f Add caching to the hc and fix resource stuff 2026-04-27 14:29:57 -07:00
Owen
61aaa5a832 Wrap in transactions 2026-04-27 13:46:15 -07:00
Owen
512ba2150b Fix sanitizing the domain causing problems 2026-04-27 13:46:15 -07:00
Owen
d1f7a9c6df Add health to the resource 2026-04-27 13:46:15 -07:00
miloschwartz
1cdb261f7e add loading indicator to resources 2026-04-27 12:31:31 -07:00
Owen
17631599a2 Remove delay 2026-04-26 21:25:53 -07:00
Owen
7563b37cd0 Add missing health column 2026-04-26 21:25:14 -07:00
Owen
7318c86cca Fix display and query issues 2026-04-26 20:33:58 -07:00
Owen
467cd70b72 Handle delete correctly 2026-04-26 20:26:03 -07:00
Owen
8ca72a39da Handle deleting targets 2026-04-26 17:55:26 -07:00
Owen
4ff811c5bd Use http by default 2026-04-26 17:38:24 -07:00
Owen
ca2370e31d Add logging when manually changing the hc status 2026-04-26 17:29:20 -07:00
miloschwartz
06af53c4d6 increase refresh rate 2026-04-26 16:57:10 -07:00
miloschwartz
6befdfe01e improve cert restart button style 2026-04-26 16:50:52 -07:00
Owen
5695137280 Dont create alerts with 300 second cooldowns 2026-04-26 16:43:28 -07:00
miloschwartz
e2e0936f43 improve cert status style 2026-04-26 11:27:53 -07:00
miloschwartz
32d8bde96d adjust wildcard placeholder 2026-04-26 11:15:23 -07:00
miloschwartz
f24f867684 add hyphens to random blueprint name 2026-04-26 11:11:31 -07:00
miloschwartz
491636851f Merge branch 'dev' of https://github.com/fosrl/pangolin into dev 2026-04-26 10:23:36 -07:00
Owen
bf1870608b Exclude wildcard resources 2026-04-25 15:51:39 -07:00
miloschwartz
6f6c24b6df use semibold 2026-04-25 15:42:19 -07:00
Owen
7c7d1f641e Support unknown and degraded status 2026-04-25 15:34:04 -07:00
Owen
82212af643 Add resource degraded 2026-04-25 15:34:04 -07:00
miloschwartz
8e16ff07a9 move switch toggle above tabs on health check dialog 2026-04-25 15:23:22 -07:00
miloschwartz
56816c7584 change column order on sites table 2026-04-25 15:17:39 -07:00
miloschwartz
477712b73c show site resources 2026-04-25 15:08:08 -07:00
Owen
ecacb26445 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-04-24 17:32:28 -07:00
Owen
cca7cea2f1 Handeling the different health status 2026-04-24 17:30:54 -07:00
miloschwartz
07154d2a16 add links to view resources on site 2026-04-24 17:07:11 -07:00
miloschwartz
b509c8aeec dont distribute info section cols 2026-04-24 16:57:53 -07:00
miloschwartz
a2c76cbb24 set standard filter popover width 2026-04-24 16:44:57 -07:00
miloschwartz
960ada4d66 add site column and filter to public resources 2026-04-24 16:24:26 -07:00
Owen
34296e5f40 Fix health check status 2026-04-24 16:19:35 -07:00
miloschwartz
33f1662c91 support site filter in private resources table 2026-04-24 16:12:15 -07:00
Owen
29f26021df Add the right pending record 2026-04-24 16:07:44 -07:00
Owen
15f02cf79a Quiet up messages 2026-04-24 16:06:19 -07:00
Owen
2a5d836747 Fix gear icon 2026-04-24 16:06:04 -07:00
Owen
593a7fdd69 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-04-24 15:13:25 -07:00
miloschwartz
99f9b68efe fix full sudo mode calculation 2026-04-24 14:53:11 -07:00
miloschwartz
9655f119a5 fix text 2026-04-24 13:47:54 -07:00
Owen
48ddc700a0 Catch the domains the right way 2026-04-24 13:40:31 -07:00
Owen
0473d5f639 Get the cert correctly 2026-04-24 12:18:50 -07:00
Owen
537f9ae66b Always update the domain even if wildcard changes 2026-04-24 12:14:06 -07:00
Owen
d08f276794 Use the provided host in the cookie 2026-04-24 11:55:09 -07:00
Owen
6a96f743aa Update exchange session to support wildcards 2026-04-23 21:38:12 -07:00
Owen
b4f0b4e285 Handle matching wildcards 2026-04-23 21:25:13 -07:00
Owen
07c7501669 New columns 2026-04-23 20:30:34 -07:00
Owen
009bac64bf Adding guiderails 2026-04-23 18:02:32 -07:00
Owen
5e293e8364 Handle getting resources 2026-04-23 17:14:05 -07:00
Owen
1ba7fca798 Update traefik config 2026-04-23 15:08:55 -07:00
Owen
e7a9a19816 Basic crud working? 2026-04-23 15:01:43 -07:00
Owen
fa117198a0 Pass one at getting it into the db 2026-04-23 14:05:08 -07:00
Owen
f03d0cd47f Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-04-23 13:37:44 -07:00
Owen Schwartz
925a59c080 Merge pull request #2873 from sidd190/fix/crowdsec-traefik-logrotate
(fix): Added a logrotate function to the crowdsec.go installer file
2026-04-23 12:18:00 -07:00
Owen
a7c7319407 Deprecated sites should be optional 2026-04-23 12:09:22 -07:00
Owen
230f77118a Also check when getting the cert 2026-04-22 21:11:52 -07:00
Owen
bcb5b7b4a7 Show status in messages 2026-04-22 20:44:35 -07:00
Owen
90a2ed2f10 Create pending cert 2026-04-22 20:39:04 -07:00
Owen
fc69364feb Show cert status 2026-04-22 20:36:00 -07:00
Owen
245755a140 Use transactions 2026-04-22 18:13:15 -07:00
Owen
dcbd22b4ad Handle all of the alerting from the functions 2026-04-22 18:13:15 -07:00
miloschwartz
8481b0a073 dont filter admin role in role selector for alerts 2026-04-22 17:52:31 -07:00
miloschwartz
f651ca84fa remove empty table state lines 2026-04-22 17:43:29 -07:00
miloschwartz
6b83d3c3f1 add meta titles to alert pages 2026-04-22 17:27:30 -07:00
Owen
d463a578c2 Handle *. wildcard domains in the db 2026-04-22 17:06:22 -07:00
Owen
9d0a8ecb09 Update placeholder and handle wildcard certs 2026-04-22 16:48:51 -07:00
Owen
af5394d464 Add more information about caches 2026-04-22 16:48:51 -07:00
miloschwartz
c956e0d401 add meta titles to auth pages 2026-04-22 16:09:16 -07:00
miloschwartz
2a281ec002 update telemetry 2026-04-22 15:06:37 -07:00
miloschwartz
4c000c1d49 add site online indicator to selector 2026-04-22 14:33:28 -07:00
miloschwartz
ea4ff75552 cosmetic adjustments 2026-04-22 14:25:06 -07:00
Owen
c78b866087 Add translations 2026-04-22 14:04:21 -07:00
miloschwartz
48b6e98bbc visual improvements 2026-04-22 12:25:01 -07:00
Owen
3d5260b13e Fix strings and local sites 2026-04-22 12:23:59 -07:00
miloschwartz
d0b0d95b9a fix squished alert card when disabled 2026-04-22 12:16:39 -07:00
miloschwartz
c2c8b7a631 disable overflow on header row for tables 2026-04-22 12:08:57 -07:00
Owen
9bc11b8717 Merge branch 'main' into dev 2026-04-22 11:38:14 -07:00
miloschwartz
1d53211fe0 fix logo size 2026-04-21 23:16:06 -07:00
Owen Schwartz
81922f54d5 Merge pull request #2889 from fosrl/dev
Fix type imports
2026-04-21 22:18:14 -07:00
Owen
9474792e14 Fix type imports 2026-04-21 22:17:49 -07:00
Owen
0c6acfe282 Fix types 2026-04-21 22:11:06 -07:00
Owen Schwartz
0ae20c0b25 Merge pull request #2888 from fosrl/dev
Fix imports
2026-04-21 22:05:41 -07:00
Owen
bcd3bee148 Properly resolve import issues 2026-04-21 22:05:01 -07:00
Owen
e2814517d6 Fix stub wrong function name 2026-04-21 21:54:46 -07:00
Owen Schwartz
c24db3df0e Merge pull request #2887 from fosrl/dev
Fix cert vars issue in stub
2026-04-21 21:49:31 -07:00
Owen
7ecfc9cbd3 Fix cert vars issue in stub 2026-04-21 21:48:54 -07:00
Owen Schwartz
0b18194397 Merge pull request #2885 from fosrl/dev
1.18.0-rc.0
2026-04-21 21:41:23 -07:00
Owen Schwartz
18dfc21197 Merge pull request #2884 from fosrl/crowdin_dev
New Crowdin updates
2026-04-21 21:36:27 -07:00
Owen Schwartz
e178ed12ab New translations en-us.json (Spanish) 2026-04-21 21:35:26 -07:00
Owen Schwartz
7a0b7dc17b New translations en-us.json (Norwegian Bokmal) 2026-04-21 21:35:24 -07:00
Owen Schwartz
c40dd7bb43 New translations en-us.json (Chinese Simplified) 2026-04-21 21:35:22 -07:00
Owen Schwartz
059ea57b88 New translations en-us.json (Turkish) 2026-04-21 21:35:20 -07:00
Owen Schwartz
1ce11d0f5f New translations en-us.json (Russian) 2026-04-21 21:35:18 -07:00
Owen Schwartz
cba1a67b8f New translations en-us.json (Portuguese) 2026-04-21 21:35:16 -07:00
Owen Schwartz
a218f5dc82 New translations en-us.json (Polish) 2026-04-21 21:35:15 -07:00
Owen Schwartz
a83126a67e New translations en-us.json (Dutch) 2026-04-21 21:35:13 -07:00
Owen Schwartz
0620fed9c1 New translations en-us.json (Korean) 2026-04-21 21:35:11 -07:00
Owen Schwartz
87e09dd407 New translations en-us.json (Italian) 2026-04-21 21:35:10 -07:00
Owen Schwartz
77b38c757a New translations en-us.json (German) 2026-04-21 21:35:08 -07:00
Owen Schwartz
5e29572f49 New translations en-us.json (Czech) 2026-04-21 21:35:06 -07:00
Owen Schwartz
520cc0d0bf New translations en-us.json (Bulgarian) 2026-04-21 21:35:04 -07:00
Owen Schwartz
ebb4630472 New translations en-us.json (French) 2026-04-21 21:35:02 -07:00
Owen Schwartz
c7b8e9c5b9 Merge pull request #2792 from fosrl/dependabot/github_actions/actions/setup-go-6.4.0
Bump actions/setup-go from 6.3.0 to 6.4.0
2026-04-21 21:34:26 -07:00
Owen Schwartz
0a65e200b6 Merge pull request #2793 from fosrl/dependabot/github_actions/docker/login-action-4.1.0
Bump docker/login-action from 4.0.0 to 4.1.0
2026-04-21 21:34:19 -07:00
Owen Schwartz
70b87c04aa Merge pull request #2810 from fosrl/dependabot/npm_and_yarn/drizzle-orm-0.45.2
Bump drizzle-orm from 0.45.1 to 0.45.2
2026-04-21 21:33:43 -07:00
Owen Schwartz
1a8e9072b4 Merge pull request #2814 from fosrl/dependabot/npm_and_yarn/nodemailer-8.0.5
Bump nodemailer from 8.0.4 to 8.0.5
2026-04-21 21:33:17 -07:00
Owen Schwartz
55261c43f8 Merge pull request #2820 from fosrl/dependabot/go_modules/install/minor-updates-f42dbfae3f
Bump golang.org/x/term from 0.41.0 to 0.42.0 in /install in the minor-updates group across 1 directory
2026-04-21 21:33:04 -07:00
Owen Schwartz
e02545ada7 Merge pull request #2822 from fosrl/dependabot/npm_and_yarn/lodash-4.18.1
Bump lodash from 4.17.23 to 4.18.1
2026-04-21 21:32:52 -07:00
Owen Schwartz
4edeb26e32 Merge pull request #2838 from fosrl/dependabot/npm_and_yarn/axios-1.15.0
Bump axios from 1.13.5 to 1.15.0
2026-04-21 21:32:05 -07:00
Owen Schwartz
6f007da609 Merge pull request #2844 from fosrl/dependabot/github_actions/actions/upload-artifact-7.0.1
Bump actions/upload-artifact from 7.0.0 to 7.0.1
2026-04-21 21:31:28 -07:00
Owen Schwartz
c3e59b73b9 Merge pull request #2855 from fosrl/dependabot/npm_and_yarn/next-15.5.15
Bump next from 15.5.14 to 15.5.15
2026-04-21 21:31:14 -07:00
miloschwartz
cc44b46d91 fix url parse error 2026-04-21 21:30:38 -07:00
Owen Schwartz
dfe4888123 Merge pull request #2863 from fosrl/dependabot/npm_and_yarn/follow-redirects-1.16.0
Bump follow-redirects from 1.15.11 to 1.16.0
2026-04-21 21:29:59 -07:00
Owen
45fb24d0c8 Remove hardcoding 2026-04-21 21:20:47 -07:00
Owen
3f1c5d305b Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-04-21 21:20:24 -07:00
Owen
c9caa44c06 Making the alerts work 2026-04-21 21:13:31 -07:00
miloschwartz
19e0452d84 remove arrow icon on server admin 2026-04-21 20:54:57 -07:00
miloschwartz
7f5c164e16 change logging 2026-04-21 20:51:59 -07:00
miloschwartz
4df3613df7 add table empty state 2026-04-21 20:40:56 -07:00
miloschwartz
4f9f235398 add way to reject a pending site 2026-04-21 20:29:05 -07:00
miloschwartz
a7c212ffa4 badge fixes 2026-04-21 20:20:33 -07:00
miloschwartz
320543f7f8 change titles 2026-04-21 19:37:38 -07:00
miloschwartz
88eb1649e4 add server filters to health check table 2026-04-21 18:35:38 -07:00
miloschwartz
6f07156075 adjust email template for alerts 2026-04-21 18:19:38 -07:00
Owen
b3aafa5fe6 Handle toggles 2026-04-21 18:05:17 -07:00
miloschwartz
f71355fe7a remove domain picker modal 2026-04-21 17:48:45 -07:00
Owen
6ea3f69fea Fix cache 2026-04-21 17:40:39 -07:00
miloschwartz
95fc30f21d Merge branch 'dev' of https://github.com/fosrl/pangolin into dev 2026-04-21 17:39:24 -07:00
miloschwartz
a2d8386b4a fix uptime graph styles 2026-04-21 17:39:16 -07:00
Owen
73a59bc1de Fix missing arg 2026-04-21 17:23:36 -07:00
miloschwartz
0434b1a656 fix site and resource filters on alert 2026-04-21 17:22:50 -07:00
Owen
13afa90d28 Fix the linking out and deleting for target ones 2026-04-21 17:02:21 -07:00
Owen
90eb6d66c0 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2026-04-21 16:55:42 -07:00
miloschwartz
22a6dabeb2 fix alerting layout 2026-04-21 16:54:28 -07:00
Owen
84346fc23e Add missing header 2026-04-21 16:52:45 -07:00
Owen
09744cf2f0 Make paid feature 2026-04-21 16:52:45 -07:00
Owen
38f1387db1 Update package lock 2026-04-21 16:52:44 -07:00
miloschwartz
db2942447a make alerts and health checks table server side 2026-04-21 16:49:54 -07:00
Owen
b22ac17178 Remove self call 2026-04-21 16:45:55 -07:00
miloschwartz
709f2c187d remove loading state on the alert rule 2026-04-21 16:27:27 -07:00
miloschwartz
ccfa165632 show all sites|resources|health-checks in alert table 2026-04-21 16:23:08 -07:00
miloschwartz
a68ba9e04d Merge branch 'dev' of https://github.com/fosrl/pangolin into dev 2026-04-21 16:23:03 -07:00
Owen
23293da845 Fix the insert 2026-04-21 16:19:48 -07:00
miloschwartz
b5dd20e499 fix cant save form 2026-04-21 16:19:32 -07:00
Owen
38243ad887 Create the new networks for each site resource 2026-04-21 16:13:39 -07:00
Owen
5b18612426 Only works on saas 2026-04-21 16:13:39 -07:00
miloschwartz
8dbe0a4bfe add server side filter and sort to alerts 2026-04-21 15:57:23 -07:00
Owen
7d9a0cd0cc Add 1.18 migrations 2026-04-21 15:46:27 -07:00
miloschwartz
e1efae7426 add help banners to alerts 2026-04-21 15:35:55 -07:00
miloschwartz
f9a4e25dc9 more cosmetic changes to alert rules 2026-04-21 15:30:49 -07:00
Owen
85029ff518 Merge branch 'trial' into dev 2026-04-21 15:05:23 -07:00
Owen
adf15bdc87 Merge branch 'alerting-rules' into dev 2026-04-21 15:05:12 -07:00
miloschwartz
a20111043f Merge branch 'alerting-rules' of https://github.com/fosrl/pangolin into alerting-rules 2026-04-21 15:03:10 -07:00
miloschwartz
177ce20dda remove graph 2026-04-21 15:02:56 -07:00
Owen
1a0bde2ee9 Merge branch 'alerting-rules' into trial 2026-04-21 14:57:25 -07:00
Owen
ff1ca7eafb Just use the targetHealthCheckId as the id 2026-04-21 14:56:25 -07:00
Owen
dc299a740b Add the site to the ui and allow picking 2026-04-21 14:34:28 -07:00
Owen
7b3c10c7b0 Handle crud to newt with new hcs 2026-04-21 14:21:58 -07:00
Owen
b1293e6f56 Add siteId to api 2026-04-21 14:12:05 -07:00
Owen
4ce4e63a0a Accept nice id when creating 2026-04-21 09:57:22 -07:00
Milo Schwartz
30c4010c8b Merge pull request #2696 from Fredkiss3/feat/paginate-user-roles-table
feat: paginate users & roles table
2026-04-20 22:06:01 -07:00
miloschwartz
85f7c1e87b support server side table for admin users table 2026-04-20 22:05:29 -07:00
miloschwartz
6f06f98cc1 add filter by idp and role in users table 2026-04-20 21:51:53 -07:00
miloschwartz
e3aabc6b2d Merge branch 'dev' into feat/paginate-user-roles-table 2026-04-20 21:27:51 -07:00
Siddharth Bansal
473bce856d Pass installdir as a parameter 2026-04-20 21:36:42 +05:30
dependabot[bot]
86ec8eedac 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.41.0 to 0.42.0
- [Commits](https://github.com/golang/term/compare/v0.41.0...v0.42.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 01:36:49 +00:00
Siddharth Bansal
2c8b7b5ca5 (fix): Added a logrotate function to the crowdsec.go installer file 2026-04-19 12:33:59 +05:30
Owen
55989c2019 Add trial system 2026-04-18 13:40:50 -07:00
dependabot[bot]
06e7c1d6cb Bump follow-redirects from 1.15.11 to 1.16.0
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 19:35:30 +00:00
dependabot[bot]
1c95d46eaa Bump next from 15.5.14 to 15.5.15
Bumps [next](https://github.com/vercel/next.js) from 15.5.14 to 15.5.15.
- [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.14...v15.5.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-14 00:05:43 +00:00
dependabot[bot]
7a483ab1e2 Bump actions/upload-artifact from 7.0.0 to 7.0.1
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](bbbca2ddaa...043fb46d1a)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 01:35:57 +00:00
dependabot[bot]
63a38de059 Bump axios from 1.13.5 to 1.15.0
Bumps [axios](https://github.com/axios/axios) from 1.13.5 to 1.15.0.
- [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/compare/v1.13.5...v1.15.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.15.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-12 10:39:44 +00:00
dependabot[bot]
4430042419 Bump lodash from 4.17.23 to 4.18.1
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-10 03:58:55 +00:00
dependabot[bot]
efc1f67017 Bump nodemailer from 8.0.4 to 8.0.5
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.4 to 8.0.5.
- [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.4...v8.0.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-09 00:15:55 +00:00
dependabot[bot]
3dc819eb31 Bump drizzle-orm from 0.45.1 to 0.45.2
Bumps [drizzle-orm](https://github.com/drizzle-team/drizzle-orm) from 0.45.1 to 0.45.2.
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/compare/0.45.1...0.45.2)

---
updated-dependencies:
- dependency-name: drizzle-orm
  dependency-version: 0.45.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-08 06:57:05 +00:00
dependabot[bot]
e8d1b779cc Bump docker/login-action from 4.0.0 to 4.1.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](b45d80f862...4907a6ddec)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 01:35:40 +00:00
dependabot[bot]
d9000b55e3 Bump actions/setup-go from 6.3.0 to 6.4.0
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](4b73464bb3...4a3601121d)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 01:35:36 +00:00
Fred KISSIE
543542713b ♻️ refactor 2026-03-31 22:44:18 +02:00
Fred KISSIE
a4d8789c20 ♻️ move from react.forwardref to normal ref prop 2026-03-31 21:13:23 +02:00
Fred KISSIE
152b452bee Merge branch 'dev' into feat/paginate-user-roles-table 2026-03-31 18:55:21 +02:00
Fred KISSIE
efb2e78d9d 🐛 fix imports 2026-03-23 21:34:58 +01:00
Fred KISSIE
294532ecbb roles table 2026-03-23 21:34:44 +01:00
Fred KISSIE
062bec23b6 🌐 update translation for single user edit 2026-03-23 21:11:12 +01:00
Fred KISSIE
0461b5a764 finish users table 2026-03-23 21:09:49 +01:00
Fred KISSIE
6d0e10a4aa 🚧 user table pagination 2026-03-23 20:02:53 +01:00
Fred KISSIE
b648aa605c 🔧 un comment volumes in docker compose 2026-03-23 18:50:09 +01:00
297 changed files with 18707 additions and 6100 deletions

View File

@@ -0,0 +1,5 @@
---
alwaysApply: true
---
Always localize strings and use the `t` function to convert keys to strings. Add the keys to the en-us.json file. Never edit the other language files, as en-us.json is the single source of truth.

View File

@@ -0,0 +1,7 @@
---
description:
alwaysApply: true
---
Proxy resources = public resources
Private resources = client resources = site resources

View File

@@ -77,7 +77,7 @@ jobs:
fi fi
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with: with:
registry: docker.io registry: docker.io
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
@@ -149,7 +149,7 @@ jobs:
fi fi
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with: with:
registry: docker.io registry: docker.io
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
@@ -204,7 +204,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with: with:
registry: docker.io registry: docker.io
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
@@ -264,7 +264,7 @@ jobs:
shell: bash shell: bash
- name: Install Go - name: Install Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with: with:
go-version: 1.25 go-version: 1.25
@@ -299,7 +299,7 @@ jobs:
shell: bash shell: bash
- name: Upload artifacts from /install/bin - name: Upload artifacts from /install/bin
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: install-bin name: install-bin
path: install/bin/ path: install/bin/
@@ -407,35 +407,25 @@ jobs:
shell: bash shell: bash
- name: Login to GitHub Container Registry (for cosign) - name: Login to GitHub Container Registry (for cosign)
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Install cosign - name: Install cosign
# cosign is used to sign and verify container images (key and keyless) # cosign is used to sign container images using keyless (OIDC) signing
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
- name: Dual-sign and verify (GHCR & Docker Hub) - name: Sign (GHCR, keyless)
# Sign each image by digest using keyless (OIDC) and key-based signing, # Sign each GHCR image by digest using keyless (OIDC) signing via Sigstore/Rekor.
# then verify both the public key signature and the keyless OIDC signature. # Signatures are stored in the registry alongside the image.
env: env:
TAG: ${{ env.TAG }} TAG: ${{ env.TAG }}
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
COSIGN_PUBLIC_KEY: ${{ secrets.COSIGN_PUBLIC_KEY }}
COSIGN_YES: "true" COSIGN_YES: "true"
run: | run: |
set -euo pipefail set -euo pipefail
issuer="https://token.actions.githubusercontent.com"
id_regex="^https://github.com/${{ github.repository }}/.+" # accept this repo (all workflows/refs)
# Track failures
FAILED_TAGS=()
SUCCESSFUL_TAGS=()
# Determine if this is an RC release # Determine if this is an RC release
IS_RC="false" IS_RC="false"
if [[ "$TAG" == *"-rc."* ]]; then if [[ "$TAG" == *"-rc."* ]]; then
@@ -463,95 +453,47 @@ jobs:
) )
fi fi
# Sign each image variant for both registries FAILED_TAGS=()
for BASE_IMAGE in "${GHCR_IMAGE}" "${DOCKERHUB_IMAGE}"; do SUCCESSFUL_TAGS=()
for IMAGE_TAG in "${IMAGE_TAGS[@]}"; do
echo "Processing ${BASE_IMAGE}:${IMAGE_TAG}"
TAG_FAILED=false
# Wrap the entire tag processing in error handling for IMAGE_TAG in "${IMAGE_TAGS[@]}"; do
( echo "Processing ${GHCR_IMAGE}:${IMAGE_TAG}"
set -e TAG_FAILED=false
DIGEST="$(skopeo inspect --retry-times 3 docker://${BASE_IMAGE}:${IMAGE_TAG} | jq -r '.Digest')"
REF="${BASE_IMAGE}@${DIGEST}"
echo "Resolved digest: ${REF}"
echo "==> cosign sign (keyless) --recursive ${REF}" (
cosign sign --recursive "${REF}" set -e
DIGEST="$(skopeo inspect --retry-times 3 docker://${GHCR_IMAGE}:${IMAGE_TAG} | jq -r '.Digest')"
REF="${GHCR_IMAGE}@${DIGEST}"
echo "Resolved digest: ${REF}"
echo "==> cosign sign (key) --recursive ${REF}" echo "==> cosign sign (keyless) --recursive ${REF}"
cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}" cosign sign --recursive "${REF}"
) || TAG_FAILED=true
# Retry wrapper for verification to handle registry propagation delays if [ "$TAG_FAILED" = "true" ]; then
retry_verify() { echo "⚠️ WARNING: Failed to sign ${GHCR_IMAGE}:${IMAGE_TAG}"
local cmd="$1" FAILED_TAGS+=("${GHCR_IMAGE}:${IMAGE_TAG}")
local attempts=6 else
local delay=5 echo "✓ Successfully signed ${GHCR_IMAGE}:${IMAGE_TAG}"
local i=1 SUCCESSFUL_TAGS+=("${GHCR_IMAGE}:${IMAGE_TAG}")
until eval "$cmd"; do fi
if [ $i -ge $attempts ]; then
echo "Verification failed after $attempts attempts"
return 1
fi
echo "Verification not yet available. Retry $i/$attempts after ${delay}s..."
sleep $delay
i=$((i+1))
delay=$((delay*2))
# Cap the delay to avoid very long waits
if [ $delay -gt 60 ]; then delay=60; fi
done
return 0
}
echo "==> cosign verify (public key) ${REF}"
if retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${REF}' -o text"; then
VERIFIED_INDEX=true
else
VERIFIED_INDEX=false
fi
echo "==> cosign verify (keyless policy) ${REF}"
if retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${REF}' -o text"; then
VERIFIED_INDEX_KEYLESS=true
else
VERIFIED_INDEX_KEYLESS=false
fi
# Check if verification succeeded
if [ "${VERIFIED_INDEX}" != "true" ] && [ "${VERIFIED_INDEX_KEYLESS}" != "true" ]; then
echo "⚠️ WARNING: Verification not available for ${BASE_IMAGE}:${IMAGE_TAG}"
echo "This may be due to registry propagation delays. Continuing anyway."
fi
) || TAG_FAILED=true
if [ "$TAG_FAILED" = "true" ]; then
echo "⚠️ WARNING: Failed to sign/verify ${BASE_IMAGE}:${IMAGE_TAG}"
FAILED_TAGS+=("${BASE_IMAGE}:${IMAGE_TAG}")
else
echo "✓ Successfully signed and verified ${BASE_IMAGE}:${IMAGE_TAG}"
SUCCESSFUL_TAGS+=("${BASE_IMAGE}:${IMAGE_TAG}")
fi
done
done done
# Report summary
echo "" echo ""
echo "==========================================" echo "=========================================="
echo "Sign and Verify Summary" echo "Sign Summary"
echo "==========================================" echo "=========================================="
echo "Successful: ${#SUCCESSFUL_TAGS[@]}" echo "Successful: ${#SUCCESSFUL_TAGS[@]}"
echo "Failed: ${#FAILED_TAGS[@]}" echo "Failed: ${#FAILED_TAGS[@]}"
echo ""
if [ ${#FAILED_TAGS[@]} -gt 0 ]; then if [ ${#FAILED_TAGS[@]} -gt 0 ]; then
echo "Failed tags:" echo "Failed tags:"
for tag in "${FAILED_TAGS[@]}"; do for tag in "${FAILED_TAGS[@]}"; do
echo " - $tag" echo " - $tag"
done done
echo "" echo "⚠️ WARNING: Some tags failed to sign, but continuing anyway"
echo "⚠️ WARNING: Some tags failed to sign/verify, but continuing anyway"
else else
echo "✓ All images signed and verified successfully!" echo "✓ All images signed successfully!"
fi fi
shell: bash shell: bash

View File

@@ -41,7 +41,7 @@
</strong> </strong>
</p> </p>
Pangolin is an open-source, identity-based remote access platform built on WireGuard that enables secure, seamless connectivity to private and public resources. Pangolin combines reverse proxy and VPN capabilities into one platform, providing browser-based access to web applications and client-based access to any private resources with NAT traversal, all with granular access controls. Pangolin is an open-source, identity-based remote access platform built on WireGuard® that enables secure, seamless connectivity to private and public resources. Pangolin combines reverse proxy and VPN capabilities into one platform, providing browser-based access to web applications and client-based access to any private resources with NAT traversal, all with granular access controls.
## Installation ## Installation

View File

@@ -1,5 +1,5 @@
import { CommandModule } from "yargs"; import { CommandModule } from "yargs";
import { db, idpOidcConfig, licenseKey } from "@server/db"; import { db, idpOidcConfig, licenseKey, certificates, eventStreamingDestinations, alertWebhookActions } from "@server/db";
import { encrypt, decrypt } from "@server/lib/crypto"; import { encrypt, decrypt } from "@server/lib/crypto";
import { configFilePath1, configFilePath2 } from "@server/lib/consts"; import { configFilePath1, configFilePath2 } from "@server/lib/consts";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
@@ -129,9 +129,15 @@ export const rotateServerSecret: CommandModule<
console.log("\nReading encrypted data from database..."); console.log("\nReading encrypted data from database...");
const idpConfigs = await db.select().from(idpOidcConfig); const idpConfigs = await db.select().from(idpOidcConfig);
const licenseKeys = await db.select().from(licenseKey); const licenseKeys = await db.select().from(licenseKey);
const certs = await db.select().from(certificates);
const streamingDestinations = await db.select().from(eventStreamingDestinations);
const webhookActions = await db.select().from(alertWebhookActions);
console.log(`Found ${idpConfigs.length} OIDC IdP configuration(s)`); console.log(`Found ${idpConfigs.length} OIDC IdP configuration(s)`);
console.log(`Found ${licenseKeys.length} license key(s)`); console.log(`Found ${licenseKeys.length} license key(s)`);
console.log(`Found ${certs.length} certificate(s)`);
console.log(`Found ${streamingDestinations.length} event streaming destination(s)`);
console.log(`Found ${webhookActions.length} alert webhook action(s)`);
// Prepare all decrypted and re-encrypted values // Prepare all decrypted and re-encrypted values
console.log("\nDecrypting and re-encrypting values..."); console.log("\nDecrypting and re-encrypting values...");
@@ -149,8 +155,27 @@ export const rotateServerSecret: CommandModule<
encryptedInstanceId: string; encryptedInstanceId: string;
}; };
type CertUpdate = {
certId: number;
encryptedCertFile: string | null;
encryptedKeyFile: string | null;
};
type StreamingDestinationUpdate = {
destinationId: number;
encryptedConfig: string;
};
type WebhookActionUpdate = {
webhookActionId: number;
encryptedConfig: string;
};
const idpUpdates: IdpUpdate[] = []; const idpUpdates: IdpUpdate[] = [];
const licenseKeyUpdates: LicenseKeyUpdate[] = []; const licenseKeyUpdates: LicenseKeyUpdate[] = [];
const certUpdates: CertUpdate[] = [];
const streamingDestinationUpdates: StreamingDestinationUpdate[] = [];
const webhookActionUpdates: WebhookActionUpdate[] = [];
// Process idpOidcConfig entries // Process idpOidcConfig entries
for (const idpConfig of idpConfigs) { for (const idpConfig of idpConfigs) {
@@ -217,6 +242,70 @@ export const rotateServerSecret: CommandModule<
} }
} }
// Process certificate entries
for (const cert of certs) {
try {
const encryptedCertFile = cert.certFile
? encrypt(decrypt(cert.certFile, oldSecret), newSecret)
: null;
const encryptedKeyFile = cert.keyFile
? encrypt(decrypt(cert.keyFile, oldSecret), newSecret)
: null;
certUpdates.push({
certId: cert.certId,
encryptedCertFile,
encryptedKeyFile
});
} catch (error) {
console.error(
`Error processing certificate ${cert.certId} (${cert.domain}):`,
error
);
throw error;
}
}
// Process eventStreamingDestinations entries
for (const dest of streamingDestinations) {
try {
const decryptedConfig = decrypt(dest.config, oldSecret);
const encryptedConfig = encrypt(decryptedConfig, newSecret);
streamingDestinationUpdates.push({
destinationId: dest.destinationId,
encryptedConfig
});
} catch (error) {
console.error(
`Error processing event streaming destination ${dest.destinationId}:`,
error
);
throw error;
}
}
// Process alertWebhookActions entries
for (const webhook of webhookActions) {
try {
if (webhook.config == null) continue;
const decryptedConfig = decrypt(webhook.config, oldSecret);
const encryptedConfig = encrypt(decryptedConfig, newSecret);
webhookActionUpdates.push({
webhookActionId: webhook.webhookActionId,
encryptedConfig
});
} catch (error) {
console.error(
`Error processing alert webhook action ${webhook.webhookActionId}:`,
error
);
throw error;
}
}
// Perform all database updates in a single transaction // Perform all database updates in a single transaction
console.log("\nUpdating database in transaction..."); console.log("\nUpdating database in transaction...");
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
@@ -250,10 +339,50 @@ export const rotateServerSecret: CommandModule<
instanceId: update.encryptedInstanceId instanceId: update.encryptedInstanceId
}); });
} }
// Update certificate entries
for (const update of certUpdates) {
await trx
.update(certificates)
.set({
certFile: update.encryptedCertFile,
keyFile: update.encryptedKeyFile
})
.where(eq(certificates.certId, update.certId));
}
// Update event streaming destination entries
for (const update of streamingDestinationUpdates) {
await trx
.update(eventStreamingDestinations)
.set({ config: update.encryptedConfig })
.where(
eq(
eventStreamingDestinations.destinationId,
update.destinationId
)
);
}
// Update alert webhook action entries
for (const update of webhookActionUpdates) {
await trx
.update(alertWebhookActions)
.set({ config: update.encryptedConfig })
.where(
eq(
alertWebhookActions.webhookActionId,
update.webhookActionId
)
);
}
}); });
console.log(`Rotated ${idpUpdates.length} OIDC IdP configuration(s)`); console.log(`Rotated ${idpUpdates.length} OIDC IdP configuration(s)`);
console.log(`Rotated ${licenseKeyUpdates.length} license key(s)`); console.log(`Rotated ${licenseKeyUpdates.length} license key(s)`);
console.log(`Rotated ${certUpdates.length} certificate(s)`);
console.log(`Rotated ${streamingDestinationUpdates.length} event streaming destination(s)`);
console.log(`Rotated ${webhookActionUpdates.length} alert webhook action(s)`);
// Update config file with new secret // Update config file with new secret
console.log("\nUpdating config file..."); console.log("\nUpdating config file...");
@@ -270,6 +399,9 @@ export const rotateServerSecret: CommandModule<
console.log(`\nSummary:`); console.log(`\nSummary:`);
console.log(` - OIDC IdP configurations: ${idpUpdates.length}`); console.log(` - OIDC IdP configurations: ${idpUpdates.length}`);
console.log(` - License keys: ${licenseKeyUpdates.length}`); console.log(` - License keys: ${licenseKeyUpdates.length}`);
console.log(` - Certificates: ${certUpdates.length}`);
console.log(` - Event streaming destinations: ${streamingDestinationUpdates.length}`);
console.log(` - Alert webhook actions: ${webhookActionUpdates.length}`);
console.log( console.log(
`\n IMPORTANT: Restart the server for the new secret to take effect.` `\n IMPORTANT: Restart the server for the new secret to take effect.`
); );

View File

@@ -7,8 +7,8 @@ services:
POSTGRES_DB: postgres # Default database name POSTGRES_DB: postgres # Default database name
POSTGRES_USER: postgres # Default user POSTGRES_USER: postgres # Default user
POSTGRES_PASSWORD: password # Default password (change for production!) POSTGRES_PASSWORD: password # Default password (change for production!)
# volumes: volumes:
# - ./config/postgres:/var/lib/postgresql/data - ./config/postgres:/var/lib/postgresql/data
ports: ports:
- "5432:5432" # Map host port 5432 to container port 5432 - "5432:5432" # Map host port 5432 to container port 5432
restart: no restart: no

View File

@@ -6,12 +6,13 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings" "strings"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
func installCrowdsec(config Config) error { func installCrowdsec(config Config, installDir string) error {
if err := stopContainers(config.InstallationContainerType); err != nil { if err := stopContainers(config.InstallationContainerType); err != nil {
return fmt.Errorf("failed to stop containers: %v", err) return fmt.Errorf("failed to stop containers: %v", err)
@@ -40,6 +41,8 @@ func installCrowdsec(config Config) error {
os.Exit(1) os.Exit(1)
} }
setupTraefikLogRotate(installDir)
if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil { if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil {
fmt.Printf("Error copying docker service: %v\n", err) fmt.Printf("Error copying docker service: %v\n", err)
os.Exit(1) os.Exit(1)
@@ -208,3 +211,69 @@ func CheckAndAddCrowdsecDependency(composePath string) error {
fmt.Println("Added dependency of crowdsec to traefik") fmt.Println("Added dependency of crowdsec to traefik")
return nil return nil
} }
// setupTraefikLogRotate writes a logrotate config for the Traefik access log
// that CrowdSec depends on. This is only needed when CrowdSec is installed
// because the default Pangolin install does not enable Traefik access logs.
//
// copytruncate is used so Traefik does not need to be restarted or sent a
// signal after rotation — it keeps writing to the same file descriptor while
// the rotated copy is made and the original is truncated in place.
func setupTraefikLogRotate(installDir string) {
const logrotateDir = "/etc/logrotate.d"
const logrotateFile = "/etc/logrotate.d/pangolin-traefik"
logPath := filepath.Join(installDir, "config/traefik/logs/access.log")
if os.Geteuid() != 0 {
fmt.Println("\n[logrotate] Skipping automatic logrotate setup: not running as root.")
fmt.Println("[logrotate] To prevent unbounded growth of the Traefik access log used by CrowdSec,")
fmt.Println("[logrotate] create the file /etc/logrotate.d/pangolin-traefik manually with:")
printLogrotateConfig(logPath)
return
}
config := fmt.Sprintf(`# Logrotate config for Traefik access logs used by CrowdSec.
# Generated by the Pangolin installer. Safe to edit.
%s {
daily
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
}
`, logPath)
if err := os.MkdirAll(logrotateDir, 0755); err != nil {
fmt.Printf("[logrotate] Warning: could not create %s: %v\n", logrotateDir, err)
return
}
if err := os.WriteFile(logrotateFile, []byte(config), 0644); err != nil {
fmt.Printf("[logrotate] Warning: could not write %s: %v\n", logrotateFile, err)
fmt.Println("[logrotate] Set it up manually:")
printLogrotateConfig(logPath)
return
}
fmt.Printf("[logrotate] Wrote logrotate config to %s\n", logrotateFile)
fmt.Println("[logrotate] Traefik access logs will be rotated daily, keeping 7 compressed copies.")
}
// printLogrotateConfig prints a logrotate config block to stdout so users can
// set it up manually when the installer cannot write to /etc.
func printLogrotateConfig(logPath string) {
fmt.Printf(`
%s {
daily
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
}
`, logPath)
}

View File

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

View File

@@ -69,10 +69,10 @@ golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

View File

@@ -259,7 +259,7 @@ func main() {
} }
config.DoCrowdsecInstall = true config.DoCrowdsecInstall = true
err := installCrowdsec(config) err := installCrowdsec(config, installDir)
if err != nil { if err != nil {
fmt.Printf("Error installing CrowdSec: %v\n", err) fmt.Printf("Error installing CrowdSec: %v\n", err)
return return

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Свържете се с отдел продажби, за да активирате тази функция.",
"contactSalesBookDemo": "Резервирайте демонстрация",
"contactSalesOr": "или",
"contactSalesContactUs": "свържете се с нас",
"setupCreate": "Създайте организацията, сайта и ресурсите", "setupCreate": "Създайте организацията, сайта и ресурсите",
"headerAuthCompatibilityInfo": "Активирайте това, за да принудите отговор '401 Неупълномощено', когато липсва токен за автентификация. Това е необходимо за браузъри или специфични HTTP библиотеки, които не изпращат идентификационни данни без сървърно предизвикателство.", "headerAuthCompatibilityInfo": "Активирайте това, за да принудите отговор '401 Неупълномощено', когато липсва токен за автентификация. Това е необходимо за браузъри или специфични HTTP библиотеки, които не изпращат идентификационни данни без сървърно предизвикателство.",
"headerAuthCompatibility": "Разширена съвместимост.", "headerAuthCompatibility": "Разширена съвместимост.",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Засечен е невалиден или изтекъл лиценз. Проверете лицензионните условия, за да се възползвате от всички функционалности.", "componentsInvalidKey": "Засечен е невалиден или изтекъл лиценз. Проверете лицензионните условия, за да се възползвате от всички функционалности.",
"dismiss": "Отхвърляне", "dismiss": "Отхвърляне",
"subscriptionViolationMessage": "Превишихте ограничението на текущия си план. Коригирайте проблема, като премахнете сайтове, потребители или други ресурси, за да оставате в рамките на плана си.", "subscriptionViolationMessage": "Превишихте ограничението на текущия си план. Коригирайте проблема, като премахнете сайтове, потребители или други ресурси, за да оставате в рамките на плана си.",
"trialBannerMessage": "Пробният Ви период изтича след {countdown}. Актуализирайте за запазване на достъпа.",
"trialBannerExpired": "Пробният Ви период е изтекъл. Актуализирайте сега, за да възстановите достъпа.",
"trialActive": "Активен пробен период",
"trialExpired": "Пробният период е изтекъл",
"trialHasEnded": "Пробният Ви период е приключил.",
"trialDaysRemaining": "{count, plural, one {# ден остава} other {# дни остават}}",
"trialDaysLeftShort": "{days}д остават до края на пробния период",
"trialGoToBilling": "Отидете на страницата за фактуриране",
"subscriptionViolationViewBilling": "Преглед на фактурирането", "subscriptionViolationViewBilling": "Преглед на фактурирането",
"componentsLicenseViolation": "Нарушение на лиценза: Сървърът използва {usedSites} сайта, което надвишава лицензионния лимит от {maxSites} сайта. Проверете лицензионните условия, за да се възползвате от всички функционалности.", "componentsLicenseViolation": "Нарушение на лиценза: Сървърът използва {usedSites} сайта, което надвишава лицензионния лимит от {maxSites} сайта. Проверете лицензионните условия, за да се възползвате от всички функционалности.",
"componentsSupporterMessage": "Благодарим ви, че подкрепяте Pangolin като {tier}!", "componentsSupporterMessage": "Благодарим ви, че подкрепяте Pangolin като {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Копирах конфигурацията", "siteConfirmCopy": "Копирах конфигурацията",
"searchSitesProgress": "Търсене на сайтове...", "searchSitesProgress": "Търсене на сайтове...",
"siteAdd": "Добавете сайт", "siteAdd": "Добавете сайт",
"sitesTableViewPublicResources": "Вижте публични ресурси",
"sitesTableViewPrivateResources": "Вижте частни ресурси",
"siteInstallNewt": "Инсталирайте Newt", "siteInstallNewt": "Инсталирайте Newt",
"siteInstallNewtDescription": "Пуснете Newt на вашата система", "siteInstallNewtDescription": "Пуснете Newt на вашата система",
"WgConfiguration": "WireGuard конфигурация", "WgConfiguration": "WireGuard конфигурация",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Сайтът е актуализиран.", "siteUpdatedDescription": "Сайтът е актуализиран.",
"siteGeneralDescription": "Конфигурирайте общи настройки за този сайт", "siteGeneralDescription": "Конфигурирайте общи настройки за този сайт",
"siteSettingDescription": "Конфигурирайте настройките на сайта", "siteSettingDescription": "Конфигурирайте настройките на сайта",
"siteResourcesTab": "Ресурси",
"siteResourcesNoneOnSite": "Този сайт все още няма публични или частни ресурси.",
"siteResourcesSectionPublic": "Публични ресурси",
"siteResourcesSectionPrivate": "Частни ресурси",
"siteResourcesSectionPublicDescription": "Ресурси, които са изложени външно чрез домейни или портове.",
"siteResourcesSectionPrivateDescription": "Ресурси, които са достъпни в частната ви мрежа през сайта.",
"siteResourcesViewAllPublic": "Виж всички ресурси",
"siteResourcesViewAllPrivate": "Виж всички ресурси",
"siteResourcesDialogDescription": "Преглед на публични и частни ресурси, свързани с този сайт.",
"siteResourcesShowMore": "Покажи повече",
"siteResourcesPermissionDenied": "Нямате разрешение да изброите тези ресурси.",
"siteResourcesEmptyPublic": "Няма публични ресурси, насочени към този сайт все още.",
"siteResourcesEmptyPrivate": "Няма частни ресурси, свързани с този сайт още.",
"siteResourcesHowToAccess": "Как да получите достъп",
"siteResourcesTargetsOnSite": "Цели на този сайт",
"siteSetting": "Настройки на {siteName}", "siteSetting": "Настройки на {siteName}",
"siteNewtTunnel": "Нов Сайт (Препоръчително)", "siteNewtTunnel": "Нов Сайт (Препоръчително)",
"siteNewtTunnelDescription": "Най-лесният начин да създадете точка за достъп до всяка мрежа. Няма нужда от допълнителни настройки.", "siteNewtTunnelDescription": "Най-лесният начин да създадете точка за достъп до всяка мрежа. Няма нужда от допълнителни настройки.",
@@ -267,8 +296,11 @@
"orgMissing": "Липсва идентификатор на организация", "orgMissing": "Липсва идентификатор на организация",
"orgMissingMessage": "Невъзможност за регенериране на покана без идентификатор на организация.", "orgMissingMessage": "Невъзможност за регенериране на покана без идентификатор на организация.",
"accessUsersManage": "Управление на потребители", "accessUsersManage": "Управление на потребители",
"accessUserManage": "Управление на потребител",
"accessUsersDescription": "Канете и управлявайте потребители с достъп до тази организация", "accessUsersDescription": "Канете и управлявайте потребители с достъп до тази организация",
"accessUsersSearch": "Търсене на потребители...", "accessUsersSearch": "Търсене на потребители...",
"accessUsersRoleFilterCount": "{count, plural, one {# роля} other {# роли}}",
"accessUsersRoleFilterClear": "Изчистване на филтрите за роли",
"accessUserCreate": "Създайте потребител", "accessUserCreate": "Създайте потребител",
"accessUserRemove": "Премахнете потребител", "accessUserRemove": "Премахнете потребител",
"username": "Потребителско име", "username": "Потребителско име",
@@ -731,6 +763,7 @@
"newtEndpoint": "Крайна точка", "newtEndpoint": "Крайна точка",
"newtId": "Идентификационен номер", "newtId": "Идентификационен номер",
"newtSecretKey": "Секретен ключ", "newtSecretKey": "Секретен ключ",
"newtVersion": "Версия",
"architecture": "Архитектура", "architecture": "Архитектура",
"sites": "Сайтове", "sites": "Сайтове",
"siteWgAnyClients": "Използвайте клиент на WireGuard, за да се свържете. Ще трябва да използвате вътрешните ресурси чрез IP адреса на връстника.", "siteWgAnyClients": "Използвайте клиент на WireGuard, за да се свържете. Ще трябва да използвате вътрешните ресурси чрез IP адреса на връстника.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Преглед на дневници", "actionViewLogs": "Преглед на дневници",
"noneSelected": "Нищо не е избрано", "noneSelected": "Нищо не е избрано",
"orgNotFound2": "Няма намерени организации.", "orgNotFound2": "Няма намерени организации.",
"search": "Търси…",
"searchPlaceholder": "Търсене...", "searchPlaceholder": "Търсене...",
"emptySearchOptions": "Няма намерени опции", "emptySearchOptions": "Няма намерени опции",
"create": "Създаване", "create": "Създаване",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Управление.", "sidebarGeneral": "Управление.",
"sidebarLogAndAnalytics": "Лог & Анализи", "sidebarLogAndAnalytics": "Лог & Анализи",
"sidebarBluePrints": "Чертежи", "sidebarBluePrints": "Чертежи",
"sidebarAlerting": "Извеждане на предупреждения",
"sidebarHealthChecks": "Проверки на състоянието",
"sidebarOrganization": "Организация", "sidebarOrganization": "Организация",
"sidebarManagement": "Управление", "sidebarManagement": "Управление",
"sidebarBillingAndLicenses": "Фактуриране & Лицензи", "sidebarBillingAndLicenses": "Фактуриране & Лицензи",
"sidebarLogsAnalytics": "Анализи", "sidebarLogsAnalytics": "Анализи",
"alertingTitle": "Извеждане на предупреждения",
"alertingDescription": "Определете източници, тригери и действия за уведомления",
"alertingRules": "Правила за предупреждение",
"alertingSearchRules": "Търсене на правила…",
"alertingAddRule": "Създаване на правило",
"alertingColumnSource": "Източник",
"alertingColumnTrigger": "Тригер",
"alertingColumnActions": "Действия",
"alertingColumnEnabled": "Активирано",
"alertingDeleteQuestion": "Моля, потвърдете, че искате да изтриете това правило за предупреждение.",
"alertingDeleteRule": "Изтриване на правило за предупреждение",
"alertingRuleDeleted": "Правилото за предупреждение е изтрито",
"alertingRuleSaved": "Правилото за предупреждение е запазено",
"alertingRuleSavedCreatedDescription": "Вашето ново правило за предупреждение беше създадено. Все още можете да го редактирате на тази страница.",
"alertingRuleSavedUpdatedDescription": "Промените, направени по това правило за предупреждение, бяха запазени.",
"alertingEditRule": "Редактиране на правило за предупреждение",
"alertingCreateRule": "Създаване на правило за предупреждение",
"alertingRuleCredenzaDescription": "Изберете какво да наблюдавате, кога да се активира и как да уведомите",
"alertingRuleNamePlaceholder": "Сайтът на производство е недостъпен",
"alertingRuleEnabled": "Правилото е активирано",
"alertingSectionSource": "Източник",
"alertingSourceType": "Тип на източника",
"alertingSourceSite": "Сайт",
"alertingSourceHealthCheck": "Проверка на състоянието",
"alertingPickSites": "Сайтове",
"alertingPickHealthChecks": "Проверки на състоянието",
"alertingPickResources": "Ресурси",
"alertingAllSites": "Всички сайтове",
"alertingAllSitesDescription": "Предупреждението се активира за всеки сайт",
"alertingSpecificSites": "Специфични сайтове",
"alertingSpecificSitesDescription": "Изберете специфични сайтове за наблюдение",
"alertingAllHealthChecks": "Всички проверки на състоянието",
"alertingAllHealthChecksDescription": "Предупреждението се активира за всяка проверка на състоянието",
"alertingSpecificHealthChecks": "Специфични проверки на състоянието",
"alertingSpecificHealthChecksDescription": "Изберете специфични проверки на състоянието за наблюдение",
"alertingAllResources": "Всички ресурси",
"alertingAllResourcesDescription": "Предупреждението се активира за всеки ресурс",
"alertingSpecificResources": "Специфични ресурси",
"alertingSpecificResourcesDescription": "Изберете специфични ресурси за наблюдение",
"alertingSelectResources": "Изберете ресурси…",
"alertingResourcesSelected": "Избрани {count} ресурса",
"alertingResourcesEmpty": "Няма ресурси с целите в първите 10 резултата.",
"alertingSectionTrigger": "Тригер",
"alertingTrigger": "Кога да се активира",
"alertingTriggerSiteOnline": "Сайтът е онлайн",
"alertingTriggerSiteOffline": "Сайтът е офлайн",
"alertingTriggerSiteToggle": "Състоянието на сайта се променя",
"alertingTriggerHcHealthy": "Проверка на състоянието е здрава",
"alertingTriggerHcUnhealthy": "Проверка на състоянието не е здрава",
"alertingTriggerHcToggle": "Състоянието на проверката се променя",
"alertingTriggerResourceHealthy": "Ресурсът е здрав",
"alertingTriggerResourceUnhealthy": "Ресурсът не е здрав",
"alertingTriggerResourceDegraded": "Деградирал ресурс",
"alertingSearchHealthChecks": "Търсене на проверки на състоянието…",
"alertingHealthChecksEmpty": "Няма налични проверки на състоянието.",
"alertingTriggerResourceToggle": "Състоянието на ресурса се променя",
"alertingSourceResource": "Ресурс",
"alertingSectionActions": "Действия",
"alertingAddAction": "Добавяне на действие",
"alertingActionNotify": "Имейл",
"alertingActionNotifyDescription": "Изпращане на имейл известия на потребители или роли",
"alertingActionWebhook": "Уеб кука",
"alertingActionWebhookDescription": "Изпращане на HTTP заявка към персонализирана крайна точка",
"alertingExternalIntegration": "Външна интеграция",
"alertingExternalPagerDutyDescription": "Изпратете предупреждения към PagerDuty за управление на инциденти",
"alertingExternalOpsgenieDescription": "Пренасочете предупрежденията към Opsgenie за управление на дежурните отчети",
"alertingExternalServiceNowDescription": "Създавайте инциденти в ServiceNow от събития на предупреждения",
"alertingExternalIncidentIoDescription": "Активирайте работни потоци в Incident.io от събития на предупреждения",
"alertingActionType": "Тип на действието",
"alertingNotifyUsers": "Потребители",
"alertingNotifyRoles": "Роли",
"alertingNotifyEmails": "Имейл адреси",
"alertingEmailPlaceholder": "Добавете имейл и натиснете Enter",
"alertingWebhookMethod": "HTTP метод",
"alertingWebhookSecret": "Секрет за подписване (по избор)",
"alertingWebhookSecretPlaceholder": "HMAC секрет",
"alertingWebhookHeaders": "Заглавия",
"alertingAddHeader": "Добавете заглавие",
"alertingSelectSites": "Изберете сайтове…",
"alertingSitesSelected": "Избрани {count} сайта",
"alertingSelectHealthChecks": "Изберете проверки на състоянието…",
"alertingHealthChecksSelected": "Избрани {count} проверки на състоянието",
"alertingNoHealthChecks": "Няма цели с активирани проверки на състоянието",
"alertingHealthCheckStub": "Изборът на източник за проверки на състоянието все още не е свързан - все още можете да конфигурирате тригери и действия.",
"alertingSelectUsers": "Изберете потребители…",
"alertingUsersSelected": "Избрани {count} потребителя",
"alertingSelectRoles": "Изберете роли…",
"alertingRolesSelected": "Избрани {count} роли",
"alertingSummarySites": "Сайтове ({count})",
"alertingSummaryAllSites": "Всички сайтове",
"alertingSummaryHealthChecks": "Проверки на състоянието ({count})",
"alertingSummaryAllHealthChecks": "Всички проверки на състоянието",
"alertingSummaryResources": "Ресурси ({count})",
"alertingSummaryAllResources": "Всички ресурси",
"alertingErrorNameRequired": "Въведете име",
"alertingErrorActionsMin": "Добавете поне едно действие",
"alertingErrorPickSites": "Изберете поне един сайт",
"alertingErrorPickHealthChecks": "Изберете поне една проверка на състоянието",
"alertingErrorPickResources": "Изберете поне един ресурс",
"alertingErrorTriggerSite": "Изберете тригер за сайт",
"alertingErrorTriggerHealth": "Изберете тригер за проверка на състоянието",
"alertingErrorTriggerResource": "Изберете тригер за ресурс",
"alertingErrorNotifyRecipients": "Изберете потребители, роли или поне един имейл",
"alertingConfigureSource": "Конфигуриране на източник",
"alertingConfigureTrigger": "Конфигуриране на тригер",
"alertingConfigureActions": "Конфигуриране на действия",
"alertingBackToRules": "Назад към правилата",
"alertingRuleCooldown": "Време за изчакване (секунди)",
"alertingRuleCooldownDescription": "Минимално време между повторни предупреждения за същото правило. Задайте на 0, за да се задейства всеки път.",
"alertingDraftBadge": "Чернова - запазете, за да съхраните правилото",
"alertingSidebarHint": "Кликнете върху стъпка на платното, за да я редактирате тук.",
"alertingGraphCanvasTitle": "Последователност на правилото",
"alertingGraphCanvasDescription": "Визуален преглед на източник, тригер и действия. Изберете елемент за редакция в панела.",
"alertingNodeNotConfigured": "Още не е конфигурирано",
"alertingNodeActionsCount": "{count, plural, one {# действие} other {# действия}}",
"alertingNodeRoleSource": "Източник",
"alertingNodeRoleTrigger": "Тригер",
"alertingNodeRoleAction": "Действие",
"alertingTabRules": "Правила за предупреждение",
"alertingTabHealthChecks": "Проверки на състоянието",
"alertingRulesBannerTitle": "Получавайте известия",
"alertingRulesBannerDescription": "Всяко правило свързва това, което да се наблюдава (сайт, проверка на състоянието или ресурс), кога да се активира (например офлайн или нездраве) и как да уведомите екипа чрез имейл, уеб куки или интеграции. Използвайте този списък, за да създавате, активирате и управлявате тези правила.",
"alertingHealthChecksBannerTitle": "Наблюдавайте здравето и ресурсите",
"alertingHealthChecksBannerDescription": "Проверките на състоянието са HTTP или TCP монитори, които определяте веднъж. След това можете да ги използвате като източници в правила за предупреждения, така че да бъдете уведомени, когато целта стане здраве или нездраве. Проверките на състоянието на ресурсите също се появяват тук.",
"standaloneHcTableTitle": "Проверки на състоянието",
"standaloneHcSearchPlaceholder": "Търсене на проверки на състоянието…",
"standaloneHcAddButton": "Създаване на проверка на състоянието",
"standaloneHcCreateTitle": "Създаване на проверка на състоянието",
"standaloneHcEditTitle": "Редактиране на проверка на състоянието",
"standaloneHcDescription": "Конфигурирайте HTTP или TCP проверка на състоянието за използване в правилата за предупреждения.",
"standaloneHcNameLabel": "Име",
"standaloneHcNamePlaceholder": "Моят HTTP монитор",
"standaloneHcDeleteTitle": "Изтриване на проверка на състоянието",
"standaloneHcDeleteQuestion": "Моля, потвърдете, че искате да изтриете тази проверка на състоянието.",
"standaloneHcDeleted": "Проверката на състоянието е изтрита",
"standaloneHcSaved": "Проверката на състоянието е запазена",
"standaloneHcColumnHealth": "Здраве",
"standaloneHcColumnMode": "Режим",
"standaloneHcColumnTarget": "Цел",
"standaloneHcHealthStateHealthy": "Здраве",
"standaloneHcHealthStateUnhealthy": "Нездраве",
"standaloneHcHealthStateUnknown": "Неизвестно",
"standaloneHcFilterAnySite": "Всички сайтове",
"standaloneHcFilterAnyResource": "Всички ресурси",
"standaloneHcFilterMode": "Режим",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Пинг",
"standaloneHcFilterHealth": "Здраве",
"standaloneHcFilterEnabled": "Активирано",
"standaloneHcFilterEnabledOn": "Активирано",
"standaloneHcFilterEnabledOff": "Деактивирано",
"standaloneHcFilterSiteIdFallback": "Сайт {id}",
"standaloneHcFilterResourceIdFallback": "Ресурс {id}",
"blueprints": "Чертежи", "blueprints": "Чертежи",
"blueprintsDescription": "Прилагайте декларативни конфигурации и преглеждайте предишни изпълнения", "blueprintsDescription": "Прилагайте декларативни конфигурации и преглеждайте предишни изпълнения",
"blueprintAdd": "Добави Чертеж", "blueprintAdd": "Добави Чертеж",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Създайте администраторски акаунт на сървъра. Може да съществува само един администраторски акаунт. Винаги можете да промените тези данни по-късно.", "initialSetupDescription": "Създайте администраторски акаунт на сървъра. Може да съществува само един администраторски акаунт. Винаги можете да промените тези данни по-късно.",
"createAdminAccount": "Създаване на админ акаунт", "createAdminAccount": "Създаване на админ акаунт",
"setupErrorCreateAdmin": "Възникна грешка при създаване на админ акаунт.", "setupErrorCreateAdmin": "Възникна грешка при създаване на админ акаунт.",
"certificateStatus": "Статус на сертификата", "certificateStatus": "Сертификат",
"certificateStatusAutoRefreshHint": "Състоянието се опреснява автоматично.",
"loading": "Зареждане", "loading": "Зареждане",
"loadingAnalytics": "Зареждане на анализи", "loadingAnalytics": "Зареждане на анализи",
"restart": "Рестарт", "restart": "Рестарт",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Преглед на бележките за изданието", "pangolinUpdateAvailableReleaseNotes": "Преглед на бележките за изданието",
"newtUpdateAvailable": "Ново обновление", "newtUpdateAvailable": "Ново обновление",
"newtUpdateAvailableInfo": "Нова версия на Newt е налична. Моля, обновете до последната версия за най-добро изживяване.", "newtUpdateAvailableInfo": "Нова версия на Newt е налична. Моля, обновете до последната версия за най-добро изживяване.",
"pangolinNodeUpdateAvailableInfo": "Налична е нова версия на Pangolin Node. Моля, актуализирайте до последната версия за най-добро изживяване.",
"domainPickerEnterDomain": "Домейн", "domainPickerEnterDomain": "Домейн",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Въведете пълния домейн на ресурса, за да видите наличните опции.", "domainPickerDescription": "Въведете пълния домейн на ресурса, за да видите наличните опции.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Конфигуриране на проверка на здравето", "configureHealthCheck": "Конфигуриране на проверка на здравето",
"configureHealthCheckDescription": "Настройте мониторинг на здравето за {target}", "configureHealthCheckDescription": "Настройте мониторинг на здравето за {target}",
"enableHealthChecks": "Разрешаване на проверки на здравето", "enableHealthChecks": "Разрешаване на проверки на здравето",
"healthCheckDisabledStateDescription": "Когато е деактивиран, сайтът не изпълнява проверки и състоянието се счита за неизвестно.",
"enableHealthChecksDescription": "Мониторинг на здравето на тази цел. Можете да наблюдавате различен краен пункт от целта, ако е необходимо.", "enableHealthChecksDescription": "Мониторинг на здравето на тази цел. Можете да наблюдавате различен краен пункт от целта, ако е необходимо.",
"healthScheme": "Метод", "healthScheme": "Метод",
"healthSelectScheme": "Избор на метод", "healthSelectScheme": "Избор на метод",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Интервалът за проверка трябва да е поне 5 секунди", "healthCheckIntervalMin": "Интервалът за проверка трябва да е поне 5 секунди",
"healthCheckTimeoutMin": "Времето за изчакване трябва да е поне 1 секунда", "healthCheckTimeoutMin": "Времето за изчакване трябва да е поне 1 секунда",
"healthCheckRetryMin": "Опитите за повторение трябва да са поне 1", "healthCheckRetryMin": "Опитите за повторение трябва да са поне 1",
"healthCheckMode": "Режим на проверка",
"healthCheckStrategy": "Стратегия",
"healthCheckModeDescription": "Режимът TCP проверява само свързаността. Режимът HTTP валидира HTTP отговора.",
"healthyThreshold": "Праг за здраве",
"healthyThresholdDescription": "Поредица от успехи, необходими за отбелязване като здраве.",
"unhealthyThreshold": "Праг за нездраве",
"unhealthyThresholdDescription": "Поредица от провали, необходими за отбелязване като нездраве.",
"healthCheckHealthyThresholdMin": "Прагът за здраве трябва да бъде поне 1",
"healthCheckUnhealthyThresholdMin": "Прагът за нездраве трябва да бъде поне 1",
"httpMethod": "HTTP Метод", "httpMethod": "HTTP Метод",
"selectHttpMethod": "Изберете HTTP метод", "selectHttpMethod": "Изберете HTTP метод",
"domainPickerSubdomainLabel": "Поддомен", "domainPickerSubdomainLabel": "Поддомен",
"domainPickerWildcard": "Уайлдкард",
"domainPickerWildcardPaidOnly": "Уайлдкард подсайтовете са платена функция. Моля, надстройте за достъп до тази функция.",
"domainPickerBaseDomainLabel": "Основен домейн", "domainPickerBaseDomainLabel": "Основен домейн",
"domainPickerSearchDomains": "Търсене на домейни...", "domainPickerSearchDomains": "Търсене на домейни...",
"domainPickerNoDomainsFound": "Не са намерени домейни", "domainPickerNoDomainsFound": "Не са намерени домейни",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Този адрес е част от подсистемата на организацията. Използва се за разрешаване на псевдонимни записи чрез вътрешно DNS разрешаване.", "resourcesTableAliasAddressInfo": "Този адрес е част от подсистемата на организацията. Използва се за разрешаване на псевдонимни записи чрез вътрешно DNS разрешаване.",
"resourcesTableClients": "Клиенти", "resourcesTableClients": "Клиенти",
"resourcesTableAndOnlyAccessibleInternally": са достъпни само вътрешно при свързване с клиент.", "resourcesTableAndOnlyAccessibleInternally": са достъпни само вътрешно при свързване с клиент.",
"resourcesTableNoTargets": "Без цели",
"resourcesTableHealthy": "Здрав", "resourcesTableHealthy": "Здрав",
"resourcesTableDegraded": "Влошен", "resourcesTableDegraded": "Влошен",
"resourcesTableOffline": "Извън линия", "resourcesTableUnhealthy": "Нездравословно",
"resourcesTableUnknown": "Неизвестно", "resourcesTableUnknown": "Неизвестно",
"resourcesTableNotMonitored": "Не е наблюдавано", "resourcesTableNotMonitored": "Не е наблюдавано",
"resourcesTableNoTargets": "Няма цели",
"editInternalResourceDialogEditClientResource": "Редактиране на частен ресурс", "editInternalResourceDialogEditClientResource": "Редактиране на частен ресурс",
"editInternalResourceDialogUpdateResourceProperties": "Актуализирайте конфигурацията на ресурса и контрола на достъпа за {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Актуализирайте конфигурацията на ресурса и контрола на достъпа за {resourceName}",
"editInternalResourceDialogResourceProperties": "Свойствата на ресурса", "editInternalResourceDialogResourceProperties": "Свойствата на ресурса",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Порт", "editInternalResourceDialogModePort": "Порт",
"editInternalResourceDialogModeHost": "Хост", "editInternalResourceDialogModeHost": "Хост",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Метод",
"editInternalResourceDialogEnableSsl": "Активирайте SSL",
"editInternalResourceDialogEnableSslDescription": "Активирайте SSL/TLS криптиране за сигурни HTTPS връзки към целта.",
"editInternalResourceDialogDestination": "Дестинация", "editInternalResourceDialogDestination": "Дестинация",
"editInternalResourceDialogDestinationHostDescription": "IP адресът или името на хоста на ресурса в мрежата на сайта.", "editInternalResourceDialogDestinationHostDescription": "IP адресът или името на хоста на ресурса в мрежата на сайта.",
"editInternalResourceDialogDestinationIPDescription": "IP адресът или името на хоста на ресурса в мрежата на сайта.", "editInternalResourceDialogDestinationIPDescription": "IP адресът или името на хоста на ресурса в мрежата на сайта.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Име", "createInternalResourceDialogName": "Име",
"createInternalResourceDialogSite": "Сайт", "createInternalResourceDialogSite": "Сайт",
"selectSite": "Изберете сайт...", "selectSite": "Изберете сайт...",
"multiSitesSelectorSitesCount": "{count, plural, one {# сайт} other {# сайтове}}",
"noSitesFound": "Не са намерени сайтове.", "noSitesFound": "Не са намерени сайтове.",
"createInternalResourceDialogProtocol": "Протокол", "createInternalResourceDialogProtocol": "Протокол",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Порт", "createInternalResourceDialogModePort": "Порт",
"createInternalResourceDialogModeHost": "Хост", "createInternalResourceDialogModeHost": "Хост",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Метод",
"createInternalResourceDialogScheme": "Метод",
"createInternalResourceDialogEnableSsl": "Активирайте SSL",
"createInternalResourceDialogEnableSslDescription": "Активирайте SSL/TLS криптиране за сигурни HTTPS връзки към целта.",
"createInternalResourceDialogDestination": "Дестинация", "createInternalResourceDialogDestination": "Дестинация",
"createInternalResourceDialogDestinationHostDescription": "IP адресът или името на хоста на ресурса в мрежата на сайта.", "createInternalResourceDialogDestinationHostDescription": "IP адресът или името на хоста на ресурса в мрежата на сайта.",
"createInternalResourceDialogDestinationCidrDescription": "CIDR диапазонът на ресурса в мрежата на сайта.", "createInternalResourceDialogDestinationCidrDescription": "CIDR диапазонът на ресурса в мрежата на сайта.",
"createInternalResourceDialogAlias": "Псевдоним", "createInternalResourceDialogAlias": "Псевдоним",
"createInternalResourceDialogAliasDescription": "По избор вътрешен DNS псевдоним за този ресурс.", "createInternalResourceDialogAliasDescription": "По избор вътрешен DNS псевдоним за този ресурс.",
"internalResourceDownstreamSchemeRequired": "Методът е задължителен за HTTP ресурси",
"internalResourceHttpPortRequired": "Портът към целта е задължителен за HTTP ресурси",
"siteConfiguration": "Конфигурация", "siteConfiguration": "Конфигурация",
"siteAcceptClientConnections": "Приемане на клиентски връзки", "siteAcceptClientConnections": "Приемане на клиентски връзки",
"siteAcceptClientConnectionsDescription": "Позволете на потребителските устройства и клиенти да получават достъп до ресурси на този сайт. Това може да бъде променено по-късно.", "siteAcceptClientConnectionsDescription": "Позволете на потребителските устройства и клиенти да получават достъп до ресурси на този сайт. Това може да бъде променено по-късно.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Проверено", "domainPickerVerified": "Проверено",
"domainPickerUnverified": "Непроверено", "domainPickerUnverified": "Непроверено",
"domainPickerManual": "Ръчно", "domainPickerManual": "Ръчно",
"domainPickerInvalidSubdomainStructure": "Този поддомен съдържа невалидни знаци или структура. Ще бъде автоматично пречистен при запазване.", "domainPickerInvalidSubdomainStructure": "Невалидните символи ще бъдат почистени при записване.",
"domainPickerError": "Грешка", "domainPickerError": "Грешка",
"domainPickerErrorLoadDomains": "Неуспешно зареждане на домейни на организацията", "domainPickerErrorLoadDomains": "Неуспешно зареждане на домейни на организацията",
"domainPickerErrorCheckAvailability": "Неуспешна проверка на наличността на домейни", "domainPickerErrorCheckAvailability": "Неуспешна проверка на наличността на домейни",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Изберете своя доставчик на идентичност, за да продължите", "orgAuthChooseIdpDescription": "Изберете своя доставчик на идентичност, за да продължите",
"orgAuthNoIdpConfigured": "Тази организация няма конфигурирани доставчици на идентичност. Можете да влезете с вашата Pangolin идентичност.", "orgAuthNoIdpConfigured": "Тази организация няма конфигурирани доставчици на идентичност. Можете да влезете с вашата Pangolin идентичност.",
"orgAuthSignInWithPangolin": "Впишете се с Pangolin", "orgAuthSignInWithPangolin": "Впишете се с Pangolin",
"orgAuthSignInToOrg": "Влезте в организация", "orgAuthSignInToOrg": "Идентификационен доставчик на организация (SSO)",
"orgAuthSelectOrgTitle": "Вход в организация.", "orgAuthSelectOrgTitle": "Вход в организация.",
"orgAuthSelectOrgDescription": "Въведете идентификатора на вашата организация, за да продължите.", "orgAuthSelectOrgDescription": "Въведете идентификатора на вашата организация, за да продължите.",
"orgAuthOrgIdPlaceholder": "вашата-организация", "orgAuthOrgIdPlaceholder": "вашата-организация",
@@ -2429,6 +2648,7 @@
"validPassword": "Валидна парола", "validPassword": "Валидна парола",
"validEmail": "Валиден имейл", "validEmail": "Валиден имейл",
"validSSO": "Валидно SSO", "validSSO": "Валидно SSO",
"connectedClient": "Свързан клиент",
"resourceBlocked": "Блокирани ресурси", "resourceBlocked": "Блокирани ресурси",
"droppedByRule": "Прекратено от правило", "droppedByRule": "Прекратено от правило",
"noSessions": "Няма сесии", "noSessions": "Няма сесии",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Добавяне на клиенти.", "editInternalResourceDialogAddClients": "Добавяне на клиенти.",
"editInternalResourceDialogDestinationLabel": "Дестинация.", "editInternalResourceDialogDestinationLabel": "Дестинация.",
"editInternalResourceDialogDestinationDescription": "Посочете адреса дестинация за вътрешния ресурс. Това може да бъде име на хост, IP адрес или CIDR обхват в зависимост от избрания режим. По избор настройте вътрешен DNS алиас за по-лесно идентифициране.", "editInternalResourceDialogDestinationDescription": "Посочете адреса дестинация за вътрешния ресурс. Това може да бъде име на хост, IP адрес или CIDR обхват в зависимост от избрания режим. По избор настройте вътрешен DNS алиас за по-лесно идентифициране.",
"internalResourceFormMultiSiteRoutingHelp": "Избирайки няколко сайта, се осигурява сигурен път и пренасочване при висока достъпност.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Научете повече",
"editInternalResourceDialogPortRestrictionsDescription": "Ограничете достъпа до конкретни TCP/UDP портове или позволете/блокирайте всички портове.", "editInternalResourceDialogPortRestrictionsDescription": "Ограничете достъпа до конкретни TCP/UDP портове или позволете/блокирайте всички портове.",
"createInternalResourceDialogHttpConfiguration": "Конфигурация HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Изберете домейна, който клиентите ще използват, за да достигнат този ресурс чрез HTTP или HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Конфигурация HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Изберете домейна, който клиентите ще използват, за да достигнат този ресурс чрез HTTP или HTTPS.",
"editInternalResourceDialogTcp": "TCP.", "editInternalResourceDialogTcp": "TCP.",
"editInternalResourceDialogUdp": "UDP.", "editInternalResourceDialogUdp": "UDP.",
"editInternalResourceDialogIcmp": "ICMP.", "editInternalResourceDialogIcmp": "ICMP.",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Ще се върнем скоро! Нашият сайт понастоящем е в процес на планирана поддръжка.", "maintenancePageMessagePlaceholder": "Ще се върнем скоро! Нашият сайт понастоящем е в процес на планирана поддръжка.",
"maintenancePageMessageDescription": "Подробно съобщение, обясняващо поддръжката.", "maintenancePageMessageDescription": "Подробно съобщение, обясняващо поддръжката.",
"maintenancePageTimeTitle": "Очаквано време за завършване (по избор).", "maintenancePageTimeTitle": "Очаквано време за завършване (по избор).",
"privateMaintenanceScreenTitle": "Екран за поддръжка",
"privateMaintenanceScreenMessage": "Този домейн се използва при частен ресурс. Моля, свържете се с клиента на Pangolin, за да получите достъп до този ресурс.",
"privateMaintenanceScreenSteps": "След свързване, ако все още виждате това съобщение, кешът на DNS на вашия браузър все още може да сочи към стария адрес. За да коригирате това: напълно затворете и отворете отново този раздел, или браузъра си, след това се върнете на тази страница.",
"maintenanceTime": "например, 2 часа, 1 ноември в 17:00.", "maintenanceTime": "например, 2 часа, 1 ноември в 17:00.",
"maintenanceEstimatedTimeDescription": "Кога очаквате поддръжката да бъде завършена?", "maintenanceEstimatedTimeDescription": "Кога очаквате поддръжката да бъде завършена?",
"editDomain": "Редактиране на домейна.", "editDomain": "Редактиране на домейна.",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Добавяне на HTTP дестинация", "httpDestAddTitle": "Добавяне на HTTP дестинация",
"httpDestEditDescription": "Актуализирайте конфигурацията за този HTTP събитий.", "httpDestEditDescription": "Актуализирайте конфигурацията за този HTTP събитий.",
"httpDestAddDescription": "Конфигурирайте нов HTTP крайна точка, за да получавате събития на вашата организация.", "httpDestAddDescription": "Конфигурирайте нов HTTP крайна точка, за да получавате събития на вашата организация.",
"S3DestEditTitle": "Редактиране на дестинацията",
"S3DestAddTitle": "Добавете S3 дестинация",
"S3DestEditDescription": "Актуализирайте конфигурацията за тази S3 дестинация за предаване на събития.",
"S3DestAddDescription": "Конфигурирайте нов крайна точка на S3, за да получавате събития на вашата организация.",
"datadogDestEditTitle": "Редактиране на дестинация",
"datadogDestAddTitle": "Добавяне на Datadog дестинация",
"datadogDestEditDescription": "Актуализирайте конфигурацията за тази Datadog дестинация за предаване на събития.",
"datadogDestAddDescription": "Конфигурирайте нова крайна точка на Datadog, за да получавате събития на вашата организация.",
"httpDestTabSettings": "Настройки", "httpDestTabSettings": "Настройки",
"httpDestTabHeaders": "Заглавки", "httpDestTabHeaders": "Заглавки",
"httpDestTabBody": "Тяло", "httpDestTabBody": "Тяло",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Дестинацията беше създадена успешно", "httpDestCreatedSuccess": "Дестинацията беше създадена успешно",
"httpDestUpdateFailed": "Неуспешно актуализиране на дестинацията", "httpDestUpdateFailed": "Неуспешно актуализиране на дестинацията",
"httpDestCreateFailed": "Неуспешно създаване на дестинацията", "httpDestCreateFailed": "Неуспешно създаване на дестинацията",
"followRedirects": "Следвайте пренасочвания",
"followRedirectsDescription": "Автоматично следвайте HTTP пренасочвания за заявки.",
"alertingErrorWebhookUrl": "Моля, въведете валид URL адрес за уеб куката.",
"healthCheckStrategyHttp": "Проверява свързаността и проверява статуса на HTTP отговора.",
"healthCheckStrategyTcp": "Проверява само TCP свързаност, без да изследва отговора.",
"healthCheckStrategySnmp": "Прави SNMP get заявка, за да провери здравето на мрежовите устройства и инфраструктура.",
"healthCheckStrategyIcmp": "Използва ICMP echo заявки (пинг), за да провери дали ресурсът е достъпен и отговаря.",
"healthCheckTabStrategy": "Стратегия",
"healthCheckTabConnection": "Връзка",
"healthCheckTabAdvanced": "Разширени",
"healthCheckStrategyNotAvailable": "Тази стратегия не е достъпна. Моля, свържете се с отдел продажби, за да активирате тази функция.",
"uptime30d": "Работно време (30д)",
"idpAddActionCreateNew": "Създайте нов доставчик на самоличност", "idpAddActionCreateNew": "Създайте нов доставчик на самоличност",
"idpAddActionImportFromOrg": "Импортиране от друга организация", "idpAddActionImportFromOrg": "Импортиране от друга организация",
"idpImportDialogTitle": "Импортиране на доставчик на самоличност", "idpImportDialogTitle": "Импортиране на доставчик на самоличност",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Това не може да бъде отменено за тази организация.", "idpUnassociateWarning": "Това не може да бъде отменено за тази организация.",
"idpUnassociatedDescription": "Доставчика на самоличност е успешно отвързан от тази организация", "idpUnassociatedDescription": "Доставчика на самоличност е успешно отвързан от тази организация",
"idpUnassociateMenu": "Отвързване", "idpUnassociateMenu": "Отвързване",
"idpDeleteAllOrgsMenu": "Изтриване" "idpDeleteAllOrgsMenu": "Изтриване",
"publicIpEndpoint": "Крайна точка",
"lastTriggeredAt": "Последен тригер",
"reject": "Отхвърляне",
"uptimeDaysAgo": "{count} days ago",
"uptimeToday": "Днес",
"uptimeNoDataAvailable": "Няма налични данни",
"uptimeSuffix": "време без прекъсване",
"uptimeDowntimeSuffix": "време на прекъсване",
"uptimeTooltipUptimeLabel": "Време без прекъсване",
"uptimeTooltipDowntimeLabel": "Време на прекъсване",
"uptimeOngoing": "текущо",
"uptimeNoMonitoringData": "Няма данни за наблюдение",
"uptimeNoData": "Няма данни",
"uptimeMiniBarDown": "Прекъсване",
"uptimeSectionTitle": "Време без прекъсване",
"uptimeSectionDescription": "Наличност през последните {days} дни",
"uptimeAddAlert": "Добавяне на известие",
"uptimeViewAlerts": "Преглед на известията",
"uptimeCreateEmailAlert": "Създаване на електронна известие",
"uptimeAlertDescriptionSite": "Получавайте известия по електронна поща, когато този сайт се изключи или отново стане онлайн.",
"uptimeAlertDescriptionResource": "Получавайте известия по електронна поща, когато този ресурс се изключи или отново стане онлайн.",
"uptimeAlertNamePlaceholder": "Име на известието",
"uptimeAdditionalEmails": "Допълнителни имейли",
"uptimeCreateAlert": "Създаване на известие",
"uptimeAlertNoRecipients": "Няма получатели",
"uptimeAlertNoRecipientsDescription": "Моля, добавете поне един потребител, рол, или имейл за известяване.",
"uptimeAlertCreated": "Известието е създадено",
"uptimeAlertCreatedDescription": "Ще бъдете известени, когато това промени статуса си.",
"uptimeAlertCreateFailed": "Неуспешно създаване на известие",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Ключ",
"webhookHeaderValuePlaceholder": "Стойност",
"alertLabel": "Известие",
"domainPickerWildcardSubdomainNotAllowed": "Уайлдкард подсайтове не са позволени.",
"domainPickerWildcardCertWarning": "Ресурсите с уайлдкард може да изискват допълнителна конфигурация за правилна работа.",
"domainPickerWildcardCertWarningLink": "Научете повече",
"health": "Здраве",
"domainPendingErrorTitle": "Проблем при проверка"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Obraťte se na prodejce, aby tuto funkci povolil.",
"contactSalesBookDemo": "Zarezervovat demo",
"contactSalesOr": "nebo",
"contactSalesContactUs": "kontaktujte nás",
"setupCreate": "Vytvořte organizaci, stránku a zdroje", "setupCreate": "Vytvořte organizaci, stránku a zdroje",
"headerAuthCompatibilityInfo": "Povolte toto, aby vyvolalo odpověď 401 Neoprávněné, když chybí autentizační token. Toto je potřeba pro prohlížeče nebo specifické HTTP knihovny, které neposílají přihlašovací údaje bez výzvy serveru.", "headerAuthCompatibilityInfo": "Povolte toto, aby vyvolalo odpověď 401 Neoprávněné, když chybí autentizační token. Toto je potřeba pro prohlížeče nebo specifické HTTP knihovny, které neposílají přihlašovací údaje bez výzvy serveru.",
"headerAuthCompatibility": "Rozšířená kompatibilita", "headerAuthCompatibility": "Rozšířená kompatibilita",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Byly nalezeny neplatné nebo propadlé licenční klíče. Pokud chcete nadále používat všechny funkce, postupujte podle licenčních podmínek.", "componentsInvalidKey": "Byly nalezeny neplatné nebo propadlé licenční klíče. Pokud chcete nadále používat všechny funkce, postupujte podle licenčních podmínek.",
"dismiss": "Zavřít", "dismiss": "Zavřít",
"subscriptionViolationMessage": "Jste za hranicemi vašeho aktuálního plánu. Opravte problém odstraněním webů, uživatelů nebo jiných zdrojů, abyste zůstali ve vašem tarifu.", "subscriptionViolationMessage": "Jste za hranicemi vašeho aktuálního plánu. Opravte problém odstraněním webů, uživatelů nebo jiných zdrojů, abyste zůstali ve vašem tarifu.",
"trialBannerMessage": "Vaše zkušební verze vyprší za {countdown}. Pro udržení přístupu upgraduje.",
"trialBannerExpired": "Vaše zkušební verze vypršela. Upgradujte nyní pro obnovu přístupu.",
"trialActive": "Zkušební verze je aktivní",
"trialExpired": "Zkušební verze vypršela",
"trialHasEnded": "Vaše zkušební verze skončila.",
"trialDaysRemaining": "{count, plural, one {# den zbývá} few {# dny zbývají} many {# dní zbývá} other {# dny zbývají}}",
"trialDaysLeftShort": "Zbývá {days} d ve zkušební verzi",
"trialGoToBilling": "Přejděte na fakturační stránku",
"subscriptionViolationViewBilling": "Zobrazit fakturaci", "subscriptionViolationViewBilling": "Zobrazit fakturaci",
"componentsLicenseViolation": "Porušení licenčních podmínek: Tento server používá {usedSites} stránek, což překračuje limit {maxSites} licencovaných stránek. Pokud chcete nadále používat všechny funkce, postupujte podle licenčních podmínek.", "componentsLicenseViolation": "Porušení licenčních podmínek: Tento server používá {usedSites} stránek, což překračuje limit {maxSites} licencovaných stránek. Pokud chcete nadále používat všechny funkce, postupujte podle licenčních podmínek.",
"componentsSupporterMessage": "Děkujeme, že podporujete Pangolin jako {tier}!", "componentsSupporterMessage": "Děkujeme, že podporujete Pangolin jako {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Konfiguraci jsem zkopíroval", "siteConfirmCopy": "Konfiguraci jsem zkopíroval",
"searchSitesProgress": "Hledat lokality...", "searchSitesProgress": "Hledat lokality...",
"siteAdd": "Přidat lokalitu", "siteAdd": "Přidat lokalitu",
"sitesTableViewPublicResources": "Zobrazit veřejné zdroje",
"sitesTableViewPrivateResources": "Zobrazit soukromé zdroje",
"siteInstallNewt": "Nainstalovat Newt", "siteInstallNewt": "Nainstalovat Newt",
"siteInstallNewtDescription": "Spustit Newt na vašem systému", "siteInstallNewtDescription": "Spustit Newt na vašem systému",
"WgConfiguration": "Konfigurace WireGuard", "WgConfiguration": "Konfigurace WireGuard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Lokalita byla upravena.", "siteUpdatedDescription": "Lokalita byla upravena.",
"siteGeneralDescription": "Upravte obecná nastavení pro tuto lokalitu", "siteGeneralDescription": "Upravte obecná nastavení pro tuto lokalitu",
"siteSettingDescription": "Konfigurace nastavení na webu", "siteSettingDescription": "Konfigurace nastavení na webu",
"siteResourcesTab": "Zdroje",
"siteResourcesNoneOnSite": "Tento web zatím nemá veřejné ani soukromé zdroje.",
"siteResourcesSectionPublic": "Veřejné zdroje",
"siteResourcesSectionPrivate": "Soukromé zdroje",
"siteResourcesSectionPublicDescription": "Zdroje zpřístupněné externě prostřednictvím domén nebo portů.",
"siteResourcesSectionPrivateDescription": "Zdroje dostupné ve vaší soukromé síti prostřednictvím webu.",
"siteResourcesViewAllPublic": "Zobrazit všechny zdroje",
"siteResourcesViewAllPrivate": "Zobrazit všechny zdroje",
"siteResourcesDialogDescription": "Přehled veřejných a soukromých zdrojů spojených s tímto webem.",
"siteResourcesShowMore": "Ukázat více",
"siteResourcesPermissionDenied": "Nemáte oprávnění k vypsání těchto zdrojů.",
"siteResourcesEmptyPublic": "Žádné veřejné zdroje ještě necílí na tento web.",
"siteResourcesEmptyPrivate": "Žádné soukromé zdroje ještě nejsou spojené s tímto webem.",
"siteResourcesHowToAccess": "Jak získat přístup",
"siteResourcesTargetsOnSite": "Cíle na tomto webu",
"siteSetting": "Nastavení {siteName}", "siteSetting": "Nastavení {siteName}",
"siteNewtTunnel": "Novinka (doporučeno)", "siteNewtTunnel": "Novinka (doporučeno)",
"siteNewtTunnelDescription": "Nejjednodušší způsob, jak vytvořit vstupní bod do jakékoli sítě. Žádné další nastavení.", "siteNewtTunnelDescription": "Nejjednodušší způsob, jak vytvořit vstupní bod do jakékoli sítě. Žádné další nastavení.",
@@ -267,8 +296,11 @@
"orgMissing": "Chybí ID organizace", "orgMissing": "Chybí ID organizace",
"orgMissingMessage": "Nelze obnovit pozvánku bez ID organizace.", "orgMissingMessage": "Nelze obnovit pozvánku bez ID organizace.",
"accessUsersManage": "Spravovat uživatele", "accessUsersManage": "Spravovat uživatele",
"accessUserManage": "Spravovat uživatele",
"accessUsersDescription": "Pozvat a spravovat uživatele s přístupem k této organizaci", "accessUsersDescription": "Pozvat a spravovat uživatele s přístupem k této organizaci",
"accessUsersSearch": "Hledat uživatele...", "accessUsersSearch": "Hledat uživatele...",
"accessUsersRoleFilterCount": "{count, plural, one {# role} few {# role} many {# rolí} other {# roli}}",
"accessUsersRoleFilterClear": "Vymazat filtry rolí",
"accessUserCreate": "Vytvořit uživatele", "accessUserCreate": "Vytvořit uživatele",
"accessUserRemove": "Odstranit uživatele", "accessUserRemove": "Odstranit uživatele",
"username": "Uživatelské jméno", "username": "Uživatelské jméno",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Tajný klíč", "newtSecretKey": "Tajný klíč",
"newtVersion": "Verze",
"architecture": "Architektura", "architecture": "Architektura",
"sites": "Stránky", "sites": "Stránky",
"siteWgAnyClients": "K připojení použijte jakéhokoli klienta WireGuard. Budete muset řešit interní zdroje pomocí klientské IP adresy.", "siteWgAnyClients": "K připojení použijte jakéhokoli klienta WireGuard. Budete muset řešit interní zdroje pomocí klientské IP adresy.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Zobrazit logy", "actionViewLogs": "Zobrazit logy",
"noneSelected": "Není vybráno", "noneSelected": "Není vybráno",
"orgNotFound2": "Nebyly nalezeny žádné organizace.", "orgNotFound2": "Nebyly nalezeny žádné organizace.",
"search": "Vyhledávání…",
"searchPlaceholder": "Hledat...", "searchPlaceholder": "Hledat...",
"emptySearchOptions": "Nebyly nalezeny žádné možnosti", "emptySearchOptions": "Nebyly nalezeny žádné možnosti",
"create": "Vytvořit", "create": "Vytvořit",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Spravovat", "sidebarGeneral": "Spravovat",
"sidebarLogAndAnalytics": "Log & Analytics", "sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Plány", "sidebarBluePrints": "Plány",
"sidebarAlerting": "Upozornění",
"sidebarHealthChecks": "Kontroly stavu",
"sidebarOrganization": "Organizace", "sidebarOrganization": "Organizace",
"sidebarManagement": "Správa", "sidebarManagement": "Správa",
"sidebarBillingAndLicenses": "Fakturace a licence", "sidebarBillingAndLicenses": "Fakturace a licence",
"sidebarLogsAnalytics": "Analytici", "sidebarLogsAnalytics": "Analytici",
"alertingTitle": "Upozornění",
"alertingDescription": "Definujte zdroje, spouštěče a akce pro oznámení",
"alertingRules": "Pravidla upozornění",
"alertingSearchRules": "Hledat pravidla…",
"alertingAddRule": "Vytvořit pravidlo",
"alertingColumnSource": "Zdroj",
"alertingColumnTrigger": "Spouštěč",
"alertingColumnActions": "Akce",
"alertingColumnEnabled": "Povoleno",
"alertingDeleteQuestion": "Potvrďte, prosím, zda chcete toto pravidlo upozornění smazat.",
"alertingDeleteRule": "Smazat pravidlo upozornění",
"alertingRuleDeleted": "Pravidlo upozornění bylo smazáno",
"alertingRuleSaved": "Pravidlo upozornění bylo uloženo",
"alertingRuleSavedCreatedDescription": "Vaše nové pravidlo upozornění bylo vytvořeno. Můžete ho dál upravovat na této stránce.",
"alertingRuleSavedUpdatedDescription": "Vaše změny pro toto pravidlo upozornění byly uloženy.",
"alertingEditRule": "Upravit pravidlo upozornění",
"alertingCreateRule": "Vytvořit pravidlo upozornění",
"alertingRuleCredenzaDescription": "Vyberte, co sledovat, kdy ho spustit a jak oznamovat",
"alertingRuleNamePlaceholder": "Produkční stránka je dolů",
"alertingRuleEnabled": "Pravidlo povoleno",
"alertingSectionSource": "Zdroj",
"alertingSourceType": "Typ zdroje",
"alertingSourceSite": "Lokalita",
"alertingSourceHealthCheck": "Kontrola stavu",
"alertingPickSites": "Lokality",
"alertingPickHealthChecks": "Kontroly stavu",
"alertingPickResources": "Zdroje",
"alertingAllSites": "Všechny lokality",
"alertingAllSitesDescription": "Upozornění pro jakoukoli lokalitu",
"alertingSpecificSites": "Specifické lokality",
"alertingSpecificSitesDescription": "Vyberte specifické lokality k sledování",
"alertingAllHealthChecks": "Všechny kontroly stavu",
"alertingAllHealthChecksDescription": "Upozornění pro jakoukoli kontrolu stavu",
"alertingSpecificHealthChecks": "Specifické kontroly stavu",
"alertingSpecificHealthChecksDescription": "Vyberte specifické kontroly stavu k sledování",
"alertingAllResources": "Všechny zdroje",
"alertingAllResourcesDescription": "Upozornění pro jakýkoli zdroj",
"alertingSpecificResources": "Specifické zdroje",
"alertingSpecificResourcesDescription": "Vyberte specifické zdroje k sledování",
"alertingSelectResources": "Vyberte zdroje…",
"alertingResourcesSelected": "{count} zdrojů vybráno",
"alertingResourcesEmpty": "Žádné zdroje s cíly v prvních 10 výsledcích.",
"alertingSectionTrigger": "Spouštěč",
"alertingTrigger": "Kdy upozornit",
"alertingTriggerSiteOnline": "Stránky online",
"alertingTriggerSiteOffline": "Stránky offline",
"alertingTriggerSiteToggle": "Změny stavu stránek",
"alertingTriggerHcHealthy": "Kontrola stavu je zdravá",
"alertingTriggerHcUnhealthy": "Kontrola stavu je nezdravá",
"alertingTriggerHcToggle": "Změny stavu kontroly stavu",
"alertingTriggerResourceHealthy": "Zdroj je zdravý",
"alertingTriggerResourceUnhealthy": "Zdroj je nezdravý",
"alertingTriggerResourceDegraded": "Zhoršený zdroj",
"alertingSearchHealthChecks": "Hledat kontroly stavu…",
"alertingHealthChecksEmpty": "Nejsou dostupné kontroly stavu.",
"alertingTriggerResourceToggle": "Změny stavu zdroje",
"alertingSourceResource": "Zdroj",
"alertingSectionActions": "Akce",
"alertingAddAction": "Přidat akci",
"alertingActionNotify": "Email",
"alertingActionNotifyDescription": "Odesílat emailová upozornění uživatelům nebo rolím",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Odeslání HTTP požadavku na vlastní koncový bod",
"alertingExternalIntegration": "Externí integrace",
"alertingExternalPagerDutyDescription": "Odesílat upozornění do PagerDuty pro řízení incidentů",
"alertingExternalOpsgenieDescription": "Směrujte upozornění do Opsgenie pro řízení, když je někdo na telefonu",
"alertingExternalServiceNowDescription": "Vytvářet incidenty ServiceNow z událostí upozornění",
"alertingExternalIncidentIoDescription": "Spouštět Incident.io workflowy z událostí upozornění",
"alertingActionType": "Typ akce",
"alertingNotifyUsers": "Uživatelé",
"alertingNotifyRoles": "Role",
"alertingNotifyEmails": "Emailové adresy",
"alertingEmailPlaceholder": "Přidejte e-mail a stiskněte Enter",
"alertingWebhookMethod": "HTTP metoda",
"alertingWebhookSecret": "Přihlašovací tajemství (volitelné)",
"alertingWebhookSecretPlaceholder": "HMAC tajemství",
"alertingWebhookHeaders": "Hlavičky",
"alertingAddHeader": "Přidat hlavičku",
"alertingSelectSites": "Vybrat lokality…",
"alertingSitesSelected": "{count} lokalit vybráno",
"alertingSelectHealthChecks": "Vybrat kontroly stavu…",
"alertingHealthChecksSelected": "{count} kontrol stavu vybráno",
"alertingNoHealthChecks": "Žádné cíle s povolenými kontrolami stavu",
"alertingHealthCheckStub": "Výběr zdrojů kontrol stavu ještě není propojen můžete stále konfigurovat spouštěče a akce.",
"alertingSelectUsers": "Vybrat uživatele…",
"alertingUsersSelected": "{count} uživatelů vybráno",
"alertingSelectRoles": "Vybrat role…",
"alertingRolesSelected": "{count} rolí vybráno",
"alertingSummarySites": "Lokality ({count})",
"alertingSummaryAllSites": "Všechny lokality",
"alertingSummaryHealthChecks": "Kontroly stavu ({count})",
"alertingSummaryAllHealthChecks": "Všechny kontroly stavu",
"alertingSummaryResources": "Zdroje ({count})",
"alertingSummaryAllResources": "Všechny zdroje",
"alertingErrorNameRequired": "Zadejte jméno",
"alertingErrorActionsMin": "Přidat alespoň jednu akci",
"alertingErrorPickSites": "Vyberte alespoň jednu lokalitu",
"alertingErrorPickHealthChecks": "Vyberte alespoň jednu kontrolu stavu",
"alertingErrorPickResources": "Vyberte alespoň jeden zdroj",
"alertingErrorTriggerSite": "Vyberte spouštěč lokality",
"alertingErrorTriggerHealth": "Vyberte spouštěč kontroly stavu",
"alertingErrorTriggerResource": "Vyberte spouštěč zdroje",
"alertingErrorNotifyRecipients": "Vyberte uživatele, role nebo alespoň jeden email",
"alertingConfigureSource": "Konfigurace zdroje",
"alertingConfigureTrigger": "Konfigurace spouštěče",
"alertingConfigureActions": "Konfigurace akcí",
"alertingBackToRules": "Zpět na pravidla",
"alertingRuleCooldown": "Odpočinek (sekundy)",
"alertingRuleCooldownDescription": "Minimální doba mezi opakovanými upozorněními pro stejné pravidlo. Nastavte na 0 pro spuštění pokaždé.",
"alertingDraftBadge": "Koncept - uložit pro uložení tohoto pravidla",
"alertingSidebarHint": "Kliknutím na krok na plátno ho zde upravte.",
"alertingGraphCanvasTitle": "Průběh pravidla",
"alertingGraphCanvasDescription": "Vizuální přehled o zdroji, spouštěči a akcích. Vyberte uzel k jeho editaci v panelu.",
"alertingNodeNotConfigured": "Ještě není nakonfigurováno",
"alertingNodeActionsCount": "{count, plural, one {# akce} few {# akce} many {# akcí} other {# akce}}",
"alertingNodeRoleSource": "Zdroj",
"alertingNodeRoleTrigger": "Spouštěč",
"alertingNodeRoleAction": "Akce",
"alertingTabRules": "Pravidla upozornění",
"alertingTabHealthChecks": "Kontroly stavu",
"alertingRulesBannerTitle": "Dostávat upozornění",
"alertingRulesBannerDescription": "Každé pravidlo spojuje, co sledovat (lokalita, kontrola stavu nebo zdroj), kdy ho spustit (například offline nebo nezdravé), a jak informovat váš tým emailem, webhookem nebo integracemi. Použijte tento seznam k vytvoření, povolení a správě těchto pravidel.",
"alertingHealthChecksBannerTitle": "Monitorujte zdraví a zdroje",
"alertingHealthChecksBannerDescription": "Kontroly stavu jsou HTTP nebo TCP monitory, které nastavíte jednou. Poté je můžete použít jako zdroje v pravidlech upozornění, takže budete informováni, když se cíl stane zdravým nebo nezdravým. Kontroly stavu také zde se objeví.",
"standaloneHcTableTitle": "Kontroly stavu",
"standaloneHcSearchPlaceholder": "Hledat kontroly stavu…",
"standaloneHcAddButton": "Vytvořit kontrolu stavu",
"standaloneHcCreateTitle": "Vytvořit kontrolu stavu",
"standaloneHcEditTitle": "Upravit kontrolu stavu",
"standaloneHcDescription": "Nakonfigurujte HTTP nebo TCP kontrolu stavu pro použití v pravidlech upozornění.",
"standaloneHcNameLabel": "Jméno",
"standaloneHcNamePlaceholder": "Můj HTTP Monitor",
"standaloneHcDeleteTitle": "Smazat kontrolu stavu",
"standaloneHcDeleteQuestion": "Potvrďte, prosím, zda chcete tuto kontrolu stavu smazat.",
"standaloneHcDeleted": "Kontrola stavu byla smazána",
"standaloneHcSaved": "Kontrola stavu byla uložena",
"standaloneHcColumnHealth": "Zdraví",
"standaloneHcColumnMode": "Režim",
"standaloneHcColumnTarget": "Cíl",
"standaloneHcHealthStateHealthy": "Zdravé",
"standaloneHcHealthStateUnhealthy": "Nezdravé",
"standaloneHcHealthStateUnknown": "Neznámý",
"standaloneHcFilterAnySite": "Všechny lokality",
"standaloneHcFilterAnyResource": "Všechny zdroje",
"standaloneHcFilterMode": "Režim",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Zdraví",
"standaloneHcFilterEnabled": "Povoleno",
"standaloneHcFilterEnabledOn": "Povoleno",
"standaloneHcFilterEnabledOff": "Zakázáno",
"standaloneHcFilterSiteIdFallback": "Stránka {id}",
"standaloneHcFilterResourceIdFallback": "Zdroj {id}",
"blueprints": "Plány", "blueprints": "Plány",
"blueprintsDescription": "Použít deklarativní konfigurace a zobrazit předchozí běhy", "blueprintsDescription": "Použít deklarativní konfigurace a zobrazit předchozí běhy",
"blueprintAdd": "Přidat plán", "blueprintAdd": "Přidat plán",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Vytvořte účet správce intial serveru. Pouze jeden správce serveru může existovat. Tyto přihlašovací údaje můžete kdykoliv změnit.", "initialSetupDescription": "Vytvořte účet správce intial serveru. Pouze jeden správce serveru může existovat. Tyto přihlašovací údaje můžete kdykoliv změnit.",
"createAdminAccount": "Vytvořit účet správce", "createAdminAccount": "Vytvořit účet správce",
"setupErrorCreateAdmin": "Došlo k chybě při vytváření účtu správce serveru.", "setupErrorCreateAdmin": "Došlo k chybě při vytváření účtu správce serveru.",
"certificateStatus": "Stav certifikátu", "certificateStatus": "Certifikát",
"certificateStatusAutoRefreshHint": "Stav se automaticky obnovuje.",
"loading": "Načítání", "loading": "Načítání",
"loadingAnalytics": "Načítání analytiky", "loadingAnalytics": "Načítání analytiky",
"restart": "Restartovat", "restart": "Restartovat",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Zobrazit poznámky k vydání", "pangolinUpdateAvailableReleaseNotes": "Zobrazit poznámky k vydání",
"newtUpdateAvailable": "Dostupná aktualizace", "newtUpdateAvailable": "Dostupná aktualizace",
"newtUpdateAvailableInfo": "Je k dispozici nová verze Newt. Pro nejlepší zážitek prosím aktualizujte na nejnovější verzi.", "newtUpdateAvailableInfo": "Je k dispozici nová verze Newt. Pro nejlepší zážitek prosím aktualizujte na nejnovější verzi.",
"pangolinNodeUpdateAvailableInfo": "Je k dispozici nová verze uzlu Pangolin. Pro nejlepší zážitek aktualizujte na nejnovější verzi.",
"domainPickerEnterDomain": "Doména", "domainPickerEnterDomain": "Doména",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Zadejte úplnou doménu zdroje pro zobrazení dostupných možností.", "domainPickerDescription": "Zadejte úplnou doménu zdroje pro zobrazení dostupných možností.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Konfigurace kontroly stavu", "configureHealthCheck": "Konfigurace kontroly stavu",
"configureHealthCheckDescription": "Nastavit sledování zdravotního stavu pro {target}", "configureHealthCheckDescription": "Nastavit sledování zdravotního stavu pro {target}",
"enableHealthChecks": "Povolit kontrolu stavu", "enableHealthChecks": "Povolit kontrolu stavu",
"healthCheckDisabledStateDescription": "Pokud je zakázáno, web nebude provádět zdravotní kontroly a stav bude považován za neznámý.",
"enableHealthChecksDescription": "Sledujte zdraví tohoto cíle. V případě potřeby můžete sledovat jiný cílový bod, než je cíl.", "enableHealthChecksDescription": "Sledujte zdraví tohoto cíle. V případě potřeby můžete sledovat jiný cílový bod, než je cíl.",
"healthScheme": "Způsob", "healthScheme": "Způsob",
"healthSelectScheme": "Vybrat metodu", "healthSelectScheme": "Vybrat metodu",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Interval kontroly musí být nejméně 5 sekund", "healthCheckIntervalMin": "Interval kontroly musí být nejméně 5 sekund",
"healthCheckTimeoutMin": "Časový limit musí být nejméně 1 sekunda", "healthCheckTimeoutMin": "Časový limit musí být nejméně 1 sekunda",
"healthCheckRetryMin": "Pokusy opakovat musí být alespoň 1", "healthCheckRetryMin": "Pokusy opakovat musí být alespoň 1",
"healthCheckMode": "Režim kontroly",
"healthCheckStrategy": "Strategie",
"healthCheckModeDescription": "Režim TCP ověřuje pouze připojení. Režim HTTP ověřuje HTTP odezvu.",
"healthyThreshold": "Zdravý práh",
"healthyThresholdDescription": "Počet po sobě jdoucích úspěchů vyžadovaných před označením jako zdravý.",
"unhealthyThreshold": "Nezdravý práh",
"unhealthyThresholdDescription": "Počet po sobě jdoucích selhání vyžadovaných před označením jako nezdravý.",
"healthCheckHealthyThresholdMin": "Zdravý práh musí být alespoň 1",
"healthCheckUnhealthyThresholdMin": "Nezdravý práh musí být alespoň 1",
"httpMethod": "HTTP metoda", "httpMethod": "HTTP metoda",
"selectHttpMethod": "Vyberte HTTP metodu", "selectHttpMethod": "Vyberte HTTP metodu",
"domainPickerSubdomainLabel": "Subdoména", "domainPickerSubdomainLabel": "Subdoména",
"domainPickerWildcard": "Zástupný znak",
"domainPickerWildcardPaidOnly": "Zástupné poddomény jsou placenou funkcí. Upgradujte, prosím, pro přístup k této funkci.",
"domainPickerBaseDomainLabel": "Základní doména", "domainPickerBaseDomainLabel": "Základní doména",
"domainPickerSearchDomains": "Hledat domény...", "domainPickerSearchDomains": "Hledat domény...",
"domainPickerNoDomainsFound": "Nebyly nalezeny žádné domény", "domainPickerNoDomainsFound": "Nebyly nalezeny žádné domény",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Tato adresa je součástí subsítě veřejných služeb organizace. Používá se k řešení záznamů aliasů pomocí interního rozlišení DNS.", "resourcesTableAliasAddressInfo": "Tato adresa je součástí subsítě veřejných služeb organizace. Používá se k řešení záznamů aliasů pomocí interního rozlišení DNS.",
"resourcesTableClients": "Klienti", "resourcesTableClients": "Klienti",
"resourcesTableAndOnlyAccessibleInternally": "a jsou interně přístupné pouze v případě, že jsou propojeni s klientem.", "resourcesTableAndOnlyAccessibleInternally": "a jsou interně přístupné pouze v případě, že jsou propojeni s klientem.",
"resourcesTableNoTargets": "Žádné cíle",
"resourcesTableHealthy": "Zdravé", "resourcesTableHealthy": "Zdravé",
"resourcesTableDegraded": "Rozklad", "resourcesTableDegraded": "Rozklad",
"resourcesTableOffline": "Offline", "resourcesTableUnhealthy": "Nezdravý",
"resourcesTableUnknown": "Neznámý", "resourcesTableUnknown": "Neznámý",
"resourcesTableNotMonitored": "Není sledováno", "resourcesTableNotMonitored": "Není sledováno",
"resourcesTableNoTargets": "Žádné cíle",
"editInternalResourceDialogEditClientResource": "Upravit soukromý dokument", "editInternalResourceDialogEditClientResource": "Upravit soukromý dokument",
"editInternalResourceDialogUpdateResourceProperties": "Aktualizovat konfiguraci zdroje a ovládací prvky přístupu pro {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Aktualizovat konfiguraci zdroje a ovládací prvky přístupu pro {resourceName}",
"editInternalResourceDialogResourceProperties": "Vlastnosti zdroje", "editInternalResourceDialogResourceProperties": "Vlastnosti zdroje",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Přístav", "editInternalResourceDialogModePort": "Přístav",
"editInternalResourceDialogModeHost": "Hostitel", "editInternalResourceDialogModeHost": "Hostitel",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Schéma",
"editInternalResourceDialogEnableSsl": "Povolit SSL",
"editInternalResourceDialogEnableSslDescription": "Povolit šifrování SSL/TLS pro zabezpečené HTTPS připojení k cíli.",
"editInternalResourceDialogDestination": "Místo určení", "editInternalResourceDialogDestination": "Místo určení",
"editInternalResourceDialogDestinationHostDescription": "IP adresa nebo název hostitele zdroje v síti webu.", "editInternalResourceDialogDestinationHostDescription": "IP adresa nebo název hostitele zdroje v síti webu.",
"editInternalResourceDialogDestinationIPDescription": "IP nebo název hostitele zdroje v síti webu.", "editInternalResourceDialogDestinationIPDescription": "IP nebo název hostitele zdroje v síti webu.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Jméno", "createInternalResourceDialogName": "Jméno",
"createInternalResourceDialogSite": "Lokalita", "createInternalResourceDialogSite": "Lokalita",
"selectSite": "Vybrat lokalitu...", "selectSite": "Vybrat lokalitu...",
"multiSitesSelectorSitesCount": "{count, plural, one {# web} few {# weby} many {# webů} other {# weby}}",
"noSitesFound": "Nebyly nalezeny žádné lokality.", "noSitesFound": "Nebyly nalezeny žádné lokality.",
"createInternalResourceDialogProtocol": "Protokol", "createInternalResourceDialogProtocol": "Protokol",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Přístav", "createInternalResourceDialogModePort": "Přístav",
"createInternalResourceDialogModeHost": "Hostitel", "createInternalResourceDialogModeHost": "Hostitel",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Schéma",
"createInternalResourceDialogScheme": "Schéma",
"createInternalResourceDialogEnableSsl": "Povolit SSL",
"createInternalResourceDialogEnableSslDescription": "Povolit šifrování SSL/TLS pro zabezpečené HTTPS připojení k cíli.",
"createInternalResourceDialogDestination": "Místo určení", "createInternalResourceDialogDestination": "Místo určení",
"createInternalResourceDialogDestinationHostDescription": "IP adresa nebo název hostitele zdroje v síti webu.", "createInternalResourceDialogDestinationHostDescription": "IP adresa nebo název hostitele zdroje v síti webu.",
"createInternalResourceDialogDestinationCidrDescription": "Rozsah zdrojů CIDR v síti webu.", "createInternalResourceDialogDestinationCidrDescription": "Rozsah zdrojů CIDR v síti webu.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Volitelný interní DNS alias pro tento dokument.", "createInternalResourceDialogAliasDescription": "Volitelný interní DNS alias pro tento dokument.",
"internalResourceDownstreamSchemeRequired": "HTTP metoda je vyžadována pro HTTP zdroje",
"internalResourceHttpPortRequired": "Přípoječný port je nutný pro HTTP zdroj",
"siteConfiguration": "Konfigurace", "siteConfiguration": "Konfigurace",
"siteAcceptClientConnections": "Přijmout připojení klienta", "siteAcceptClientConnections": "Přijmout připojení klienta",
"siteAcceptClientConnectionsDescription": "Povolit uživatelským zařízením a klientům přístup ke zdrojům na tomto webu. To lze později změnit.", "siteAcceptClientConnectionsDescription": "Povolit uživatelským zařízením a klientům přístup ke zdrojům na tomto webu. To lze později změnit.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Ověřeno", "domainPickerVerified": "Ověřeno",
"domainPickerUnverified": "Neověřeno", "domainPickerUnverified": "Neověřeno",
"domainPickerManual": "Ruční nastavení", "domainPickerManual": "Ruční nastavení",
"domainPickerInvalidSubdomainStructure": "Tato subdoména obsahuje neplatné znaky nebo strukturu. Bude automaticky sanitována při uložení.", "domainPickerInvalidSubdomainStructure": "Neplatné znaky budou při ukládání vyčištěny.",
"domainPickerError": "Chyba", "domainPickerError": "Chyba",
"domainPickerErrorLoadDomains": "Nepodařilo se načíst domény organizace", "domainPickerErrorLoadDomains": "Nepodařilo se načíst domény organizace",
"domainPickerErrorCheckAvailability": "Kontrola dostupnosti domény se nezdařila", "domainPickerErrorCheckAvailability": "Kontrola dostupnosti domény se nezdařila",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Chcete-li pokračovat, vyberte svého poskytovatele identity", "orgAuthChooseIdpDescription": "Chcete-li pokračovat, vyberte svého poskytovatele identity",
"orgAuthNoIdpConfigured": "Tato organizace nemá nakonfigurovány žádné poskytovatele identity. Místo toho se můžete přihlásit s vaší Pangolinovou identitou.", "orgAuthNoIdpConfigured": "Tato organizace nemá nakonfigurovány žádné poskytovatele identity. Místo toho se můžete přihlásit s vaší Pangolinovou identitou.",
"orgAuthSignInWithPangolin": "Přihlásit se pomocí Pangolinu", "orgAuthSignInWithPangolin": "Přihlásit se pomocí Pangolinu",
"orgAuthSignInToOrg": "Přihlásit se do organizace", "orgAuthSignInToOrg": "Poskytovatel identity organizace (SSO)",
"orgAuthSelectOrgTitle": "Přihlášení do organizace", "orgAuthSelectOrgTitle": "Přihlášení do organizace",
"orgAuthSelectOrgDescription": "Zadejte ID vaší organizace pro pokračování", "orgAuthSelectOrgDescription": "Zadejte ID vaší organizace pro pokračování",
"orgAuthOrgIdPlaceholder": "vaše-organizace", "orgAuthOrgIdPlaceholder": "vaše-organizace",
@@ -2429,6 +2648,7 @@
"validPassword": "Platné heslo", "validPassword": "Platné heslo",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Připojený klient",
"resourceBlocked": "Zablokované zdroje", "resourceBlocked": "Zablokované zdroje",
"droppedByRule": "Zrušeno pravidlem", "droppedByRule": "Zrušeno pravidlem",
"noSessions": "Žádné relace", "noSessions": "Žádné relace",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Přidat klienty", "editInternalResourceDialogAddClients": "Přidat klienty",
"editInternalResourceDialogDestinationLabel": "Cíl", "editInternalResourceDialogDestinationLabel": "Cíl",
"editInternalResourceDialogDestinationDescription": "Určete cílovou adresu pro interní prostředek. Může se jednat o hostname, IP adresu, nebo rozsah CIDR v závislosti na vybraném režimu. Volitelně nastavte interní DNS alias pro snazší identifikaci.", "editInternalResourceDialogDestinationDescription": "Určete cílovou adresu pro interní prostředek. Může se jednat o hostname, IP adresu, nebo rozsah CIDR v závislosti na vybraném režimu. Volitelně nastavte interní DNS alias pro snazší identifikaci.",
"internalResourceFormMultiSiteRoutingHelp": "Výběrem více webů se povolí odolné směrování a přepojení pro vysokou dostupnost.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Zjistit více",
"editInternalResourceDialogPortRestrictionsDescription": "Omezte přístup na specifické TCP/UDP porty nebo povolte/blokujte všechny porty.", "editInternalResourceDialogPortRestrictionsDescription": "Omezte přístup na specifické TCP/UDP porty nebo povolte/blokujte všechny porty.",
"createInternalResourceDialogHttpConfiguration": "Konfigurace HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Zvolte doménu, kterou klienti použijí k dosažení tohoto zdroje přes HTTP nebo HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Konfigurace HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Zvolte doménu, kterou klienti použijí k dosažení tohoto zdroje přes HTTP nebo HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Vrátíme se brzy! Naše stránka právě prochází plánovanou údrbou.", "maintenancePageMessagePlaceholder": "Vrátíme se brzy! Naše stránka právě prochází plánovanou údrbou.",
"maintenancePageMessageDescription": "Podrobná zpráva vysvětlující údržbu", "maintenancePageMessageDescription": "Podrobná zpráva vysvětlující údržbu",
"maintenancePageTimeTitle": "Odhadovaný čas dokončení (volitelný)", "maintenancePageTimeTitle": "Odhadovaný čas dokončení (volitelný)",
"privateMaintenanceScreenTitle": "Soukromá obrazovka údržby",
"privateMaintenanceScreenMessage": "Tato doména je používána na soukromém zdroji. Prosím, připojte se přes klienta Pangolin pro přístup k tomuto zdroji.",
"privateMaintenanceScreenSteps": "Jakmile se připojíte, pokud stále vidíte tuto zprávu, možná je mezipaměť DNS vašeho prohlížeče stále nasměrována na starou adresu. Abyste to opravili: úplně zavřete a znovu otevřete tuto záložku nebo prohlížeč, a poté se vraťte na tuto stránku.",
"maintenanceTime": "např. 2 hodiny, 1. listopadu v 17:00", "maintenanceTime": "např. 2 hodiny, 1. listopadu v 17:00",
"maintenanceEstimatedTimeDescription": "Kdy očekáváte, že údržba bude dokončena", "maintenanceEstimatedTimeDescription": "Kdy očekáváte, že údržba bude dokončena",
"editDomain": "Upravit doménu", "editDomain": "Upravit doménu",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Přidat cíl HTTP", "httpDestAddTitle": "Přidat cíl HTTP",
"httpDestEditDescription": "Aktualizovat konfiguraci pro tuto destinaci HTTP události", "httpDestEditDescription": "Aktualizovat konfiguraci pro tuto destinaci HTTP události",
"httpDestAddDescription": "Konfigurace nového koncového bodu HTTP pro příjem událostí vaší organizace.", "httpDestAddDescription": "Konfigurace nového koncového bodu HTTP pro příjem událostí vaší organizace.",
"S3DestEditTitle": "Upravit cíl",
"S3DestAddTitle": "Přidat S3 cíl",
"S3DestEditDescription": "Aktualizujte konfiguraci tohoto S3 cíle pro streamování událostí.",
"S3DestAddDescription": "Konfigurujte nový S3 koncový bod pro přijímání událostí vaší organizace.",
"datadogDestEditTitle": "Upravit cíl",
"datadogDestAddTitle": "Přidat Datadog cíl",
"datadogDestEditDescription": "Aktualizujte konfiguraci tohoto Datadog cíle pro streamování událostí.",
"datadogDestAddDescription": "Konfigurujte nový Datadog koncový bod pro přijímání událostí vaší organizace.",
"httpDestTabSettings": "Nastavení", "httpDestTabSettings": "Nastavení",
"httpDestTabHeaders": "Záhlaví", "httpDestTabHeaders": "Záhlaví",
"httpDestTabBody": "Tělo", "httpDestTabBody": "Tělo",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Cíl byl úspěšně vytvořen", "httpDestCreatedSuccess": "Cíl byl úspěšně vytvořen",
"httpDestUpdateFailed": "Nepodařilo se aktualizovat cíl", "httpDestUpdateFailed": "Nepodařilo se aktualizovat cíl",
"httpDestCreateFailed": "Nepodařilo se vytvořit cíl", "httpDestCreateFailed": "Nepodařilo se vytvořit cíl",
"followRedirects": "Následovat přesměrování",
"followRedirectsDescription": "Automaticky sledovat přesměrování HTTP pro požadavky.",
"alertingErrorWebhookUrl": "Zadejte platnou URL pro webhook.",
"healthCheckStrategyHttp": "Ověření připojení a kontrola stavu HTTP odpovědi.",
"healthCheckStrategyTcp": "Ověření TCP připojení, bez inspekce odpovědi.",
"healthCheckStrategySnmp": "Vytváří SNMP požadavek pro kontrolu stavu síťových zařízení a infrastruktury.",
"healthCheckStrategyIcmp": "Používá se ICMP echo požadavky (pingy) ke kontrole, zda je zdroj dosažitelný a reaguje.",
"healthCheckTabStrategy": "Strategie",
"healthCheckTabConnection": "Připojení",
"healthCheckTabAdvanced": "Pokročilé",
"healthCheckStrategyNotAvailable": "Tato strategie není dostupná. Kontaktujte prodejce pro povolení této funkce.",
"uptime30d": "Doba provozu (30d)",
"idpAddActionCreateNew": "Vytvořit nového poskytovatele identity", "idpAddActionCreateNew": "Vytvořit nového poskytovatele identity",
"idpAddActionImportFromOrg": "Importovat z jiné organizace", "idpAddActionImportFromOrg": "Importovat z jiné organizace",
"idpImportDialogTitle": "Importovat poskytovatele identity", "idpImportDialogTitle": "Importovat poskytovatele identity",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Toto nelze pro tuto organizaci vrátit.", "idpUnassociateWarning": "Toto nelze pro tuto organizaci vrátit.",
"idpUnassociatedDescription": "Poskytovatel identity byl úspěšně odpojen od této organizace", "idpUnassociatedDescription": "Poskytovatel identity byl úspěšně odpojen od této organizace",
"idpUnassociateMenu": "Odpojit", "idpUnassociateMenu": "Odpojit",
"idpDeleteAllOrgsMenu": "Odstranit" "idpDeleteAllOrgsMenu": "Odstranit",
"publicIpEndpoint": "Koncový bod",
"lastTriggeredAt": "Poslední spouštěč",
"reject": "Odmítnout",
"uptimeDaysAgo": "Před {count} dny",
"uptimeToday": "Dnes",
"uptimeNoDataAvailable": "Dostupná žádná data",
"uptimeSuffix": "doba dostupnosti",
"uptimeDowntimeSuffix": "doba nedostupnosti",
"uptimeTooltipUptimeLabel": "Doba dostupnosti",
"uptimeTooltipDowntimeLabel": "Doba nedostupnosti",
"uptimeOngoing": "probíhá",
"uptimeNoMonitoringData": "Žádné monitorovací údaje",
"uptimeNoData": "Žádná data",
"uptimeMiniBarDown": "Nedostupný",
"uptimeSectionTitle": "Doba dostupnosti",
"uptimeSectionDescription": "Dostupnost za posledních {days} dní",
"uptimeAddAlert": "Přidat upozornění",
"uptimeViewAlerts": "Zobrazit upozornění",
"uptimeCreateEmailAlert": "Vytvořit e-mailové upozornění",
"uptimeAlertDescriptionSite": "Pošleme vám upozornění e-mailem, když bude tento web offline nebo se vrátí online.",
"uptimeAlertDescriptionResource": "Pošleme vám upozornění e-mailem, když bude tento zdroj offline nebo se vrátí online.",
"uptimeAlertNamePlaceholder": "Název upozornění",
"uptimeAdditionalEmails": "Další e-maily",
"uptimeCreateAlert": "Vytvořit upozornění",
"uptimeAlertNoRecipients": "Žádní příjemci",
"uptimeAlertNoRecipientsDescription": "Přidejte prosím alespoň jednoho uživatele, roli nebo e-mailovou adresu pro upozornění.",
"uptimeAlertCreated": "Upozornění vytvořeno",
"uptimeAlertCreatedDescription": "Budete upozorněni, když se tento stav změní.",
"uptimeAlertCreateFailed": "Nepodařilo se vytvořit upozornění",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Klíč",
"webhookHeaderValuePlaceholder": "Hodnota",
"alertLabel": "Upozornění",
"domainPickerWildcardSubdomainNotAllowed": "Zástupné poddomény nejsou povoleny.",
"domainPickerWildcardCertWarning": "Zástupné zdroje mohou vyžadovat dodatečnou konfiguraci pro správnou funkci.",
"domainPickerWildcardCertWarningLink": "Zjistit více",
"health": "Zdraví",
"domainPendingErrorTitle": "Problém s ověřením"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Vertrieb kontaktieren, um diese Funktion zu aktivieren.",
"contactSalesBookDemo": "Demo vereinbaren",
"contactSalesOr": "oder",
"contactSalesContactUs": "kontaktieren Sie uns",
"setupCreate": "Organisation, Standort und Ressourcen erstellen", "setupCreate": "Organisation, Standort und Ressourcen erstellen",
"headerAuthCompatibilityInfo": "Aktivieren Sie dies, um eine 401 Nicht autorisierte Antwort zu erzwingen, wenn ein Authentifizierungs-Token fehlt. Dies ist erforderlich für Browser oder bestimmte HTTP-Bibliotheken, die keine Anmeldedaten ohne Server-Challenge senden.", "headerAuthCompatibilityInfo": "Aktivieren Sie dies, um eine 401 Nicht autorisierte Antwort zu erzwingen, wenn ein Authentifizierungs-Token fehlt. Dies ist erforderlich für Browser oder bestimmte HTTP-Bibliotheken, die keine Anmeldedaten ohne Server-Challenge senden.",
"headerAuthCompatibility": "Erweiterte Kompatibilität", "headerAuthCompatibility": "Erweiterte Kompatibilität",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
"dismiss": "Verwerfen", "dismiss": "Verwerfen",
"subscriptionViolationMessage": "Sie überschreiten Ihre Grenzen für Ihr aktuelles Paket. Korrigieren Sie das Problem, indem Sie Webseiten, Benutzer oder andere Ressourcen entfernen, um in Ihrem Paket zu bleiben.", "subscriptionViolationMessage": "Sie überschreiten Ihre Grenzen für Ihr aktuelles Paket. Korrigieren Sie das Problem, indem Sie Webseiten, Benutzer oder andere Ressourcen entfernen, um in Ihrem Paket zu bleiben.",
"trialBannerMessage": "Ihre Testversion läuft in {countdown} ab. Upgraden, um den Zugriff zu behalten.",
"trialBannerExpired": "Ihre Testversion ist abgelaufen. Jetzt upgraden, um den Zugriff wiederherzustellen.",
"trialActive": "Kostenlose Testversion aktiv",
"trialExpired": "Testversion abgelaufen",
"trialHasEnded": "Ihre Testversion ist beendet.",
"trialDaysRemaining": "{count, plural, one {# Tag übrig} other {# Tage übrig}}",
"trialDaysLeftShort": "Noch {days}d in der Testversion",
"trialGoToBilling": "Zur Rechnungsseite gehen",
"subscriptionViolationViewBilling": "Rechnung anzeigen", "subscriptionViolationViewBilling": "Rechnung anzeigen",
"componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Standorte, was das Lizenzlimit von {maxSites} Standorten überschreitet. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.", "componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Standorte, was das Lizenzlimit von {maxSites} Standorten überschreitet. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
"componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!", "componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Ich habe die Konfiguration kopiert", "siteConfirmCopy": "Ich habe die Konfiguration kopiert",
"searchSitesProgress": "Standorte durchsuchen...", "searchSitesProgress": "Standorte durchsuchen...",
"siteAdd": "Standort hinzufügen", "siteAdd": "Standort hinzufügen",
"sitesTableViewPublicResources": "Öffentliche Ressourcen anzeigen",
"sitesTableViewPrivateResources": "Private Ressourcen anzeigen",
"siteInstallNewt": "Newt installieren", "siteInstallNewt": "Newt installieren",
"siteInstallNewtDescription": "Installiere Newt auf deinem System.", "siteInstallNewtDescription": "Installiere Newt auf deinem System.",
"WgConfiguration": "WireGuard Konfiguration", "WgConfiguration": "WireGuard Konfiguration",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Der Standort wurde aktualisiert.", "siteUpdatedDescription": "Der Standort wurde aktualisiert.",
"siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren", "siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren",
"siteSettingDescription": "Standorteinstellungen konfigurieren", "siteSettingDescription": "Standorteinstellungen konfigurieren",
"siteResourcesTab": "Ressourcen",
"siteResourcesNoneOnSite": "Diese Seite hat noch keine öffentlichen oder privaten Ressourcen.",
"siteResourcesSectionPublic": "Öffentliche Ressourcen",
"siteResourcesSectionPrivate": "Private Ressourcen",
"siteResourcesSectionPublicDescription": "Ressourcen, die extern über Domains oder Ports bereitgestellt werden.",
"siteResourcesSectionPrivateDescription": "Ressourcen, die in Ihrem privaten Netzwerk über die Seite verfügbar sind.",
"siteResourcesViewAllPublic": "Alle Ressourcen anzeigen",
"siteResourcesViewAllPrivate": "Alle Ressourcen anzeigen",
"siteResourcesDialogDescription": "Überblick über öffentliche und private Ressourcen, die mit dieser Seite verbunden sind.",
"siteResourcesShowMore": "Mehr anzeigen",
"siteResourcesPermissionDenied": "Sie haben keine Berechtigung, diese Ressourcen aufzulisten.",
"siteResourcesEmptyPublic": "Noch sind keine öffentlichen Ressourcen für diese Seite vorhanden.",
"siteResourcesEmptyPrivate": "Noch sind keine privaten Ressourcen mit dieser Seite verbunden.",
"siteResourcesHowToAccess": "Zugriffsmöglichkeiten",
"siteResourcesTargetsOnSite": "Ziele auf dieser Seite",
"siteSetting": "{siteName} Einstellungen", "siteSetting": "{siteName} Einstellungen",
"siteNewtTunnel": "Newt Standort (empfohlen)", "siteNewtTunnel": "Newt Standort (empfohlen)",
"siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in jedes Netzwerk zu erstellen. Keine zusätzliche Einrichtung.", "siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in jedes Netzwerk zu erstellen. Keine zusätzliche Einrichtung.",
@@ -267,8 +296,11 @@
"orgMissing": "Organisations-ID fehlt", "orgMissing": "Organisations-ID fehlt",
"orgMissingMessage": "Einladung kann ohne Organisations-ID nicht neu generiert werden.", "orgMissingMessage": "Einladung kann ohne Organisations-ID nicht neu generiert werden.",
"accessUsersManage": "Benutzer verwalten", "accessUsersManage": "Benutzer verwalten",
"accessUserManage": "Benutzer verwalten",
"accessUsersDescription": "Benutzer mit Zugriff auf diese Organisation einladen und verwalten", "accessUsersDescription": "Benutzer mit Zugriff auf diese Organisation einladen und verwalten",
"accessUsersSearch": "Benutzer suchen...", "accessUsersSearch": "Benutzer suchen...",
"accessUsersRoleFilterCount": "{count, plural, one {# Rolle} other {# Rollen}}",
"accessUsersRoleFilterClear": "Rollenfilter löschen",
"accessUserCreate": "Benutzer erstellen", "accessUserCreate": "Benutzer erstellen",
"accessUserRemove": "Benutzer entfernen", "accessUserRemove": "Benutzer entfernen",
"username": "Benutzername", "username": "Benutzername",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpunkt", "newtEndpoint": "Endpunkt",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Geheimnis", "newtSecretKey": "Geheimnis",
"newtVersion": "Version",
"architecture": "Architektur", "architecture": "Architektur",
"sites": "Standorte", "sites": "Standorte",
"siteWgAnyClients": "Verwenden Sie jeden WireGuard-Client um sich zu verbinden. Sie müssen interne Ressourcen über die Peer-IP ansprechen.", "siteWgAnyClients": "Verwenden Sie jeden WireGuard-Client um sich zu verbinden. Sie müssen interne Ressourcen über die Peer-IP ansprechen.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Logs anzeigen", "actionViewLogs": "Logs anzeigen",
"noneSelected": "Keine ausgewählt", "noneSelected": "Keine ausgewählt",
"orgNotFound2": "Keine Organisationen gefunden.", "orgNotFound2": "Keine Organisationen gefunden.",
"search": "Suche…",
"searchPlaceholder": "Suche...", "searchPlaceholder": "Suche...",
"emptySearchOptions": "Keine Optionen gefunden", "emptySearchOptions": "Keine Optionen gefunden",
"create": "Erstellen", "create": "Erstellen",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Verwalten", "sidebarGeneral": "Verwalten",
"sidebarLogAndAnalytics": "Log & Analytik", "sidebarLogAndAnalytics": "Log & Analytik",
"sidebarBluePrints": "Blaupausen", "sidebarBluePrints": "Blaupausen",
"sidebarAlerting": "Benachrichtigung",
"sidebarHealthChecks": "Gesundheits-Checks",
"sidebarOrganization": "Organisation", "sidebarOrganization": "Organisation",
"sidebarManagement": "Management", "sidebarManagement": "Management",
"sidebarBillingAndLicenses": "Abrechnung & Lizenzen", "sidebarBillingAndLicenses": "Abrechnung & Lizenzen",
"sidebarLogsAnalytics": "Analytik", "sidebarLogsAnalytics": "Analytik",
"alertingTitle": "Benachrichtigung",
"alertingDescription": "Quellen, Auslöser und Aktionen für Benachrichtigungen festlegen",
"alertingRules": "Benachrichtigungsregeln",
"alertingSearchRules": "Suchregeln…",
"alertingAddRule": "Regel erstellen",
"alertingColumnSource": "Quelle",
"alertingColumnTrigger": "Auslöser",
"alertingColumnActions": "Aktionen",
"alertingColumnEnabled": "Aktiviert",
"alertingDeleteQuestion": "Bitte bestätigen Sie, dass Sie diese Benachrichtigungsregel löschen möchten.",
"alertingDeleteRule": "Benachrichtigungsregel löschen",
"alertingRuleDeleted": "Benachrichtigungsregel gelöscht",
"alertingRuleSaved": "Benachrichtigungsregel gespeichert",
"alertingRuleSavedCreatedDescription": "Ihre neue Benachrichtigungsregel wurde erstellt. Sie können sie auf dieser Seite weiter bearbeiten.",
"alertingRuleSavedUpdatedDescription": "Ihre Änderungen an dieser Benachrichtigungsregel wurden gespeichert.",
"alertingEditRule": "Benachrichtigungsregel bearbeiten",
"alertingCreateRule": "Benachrichtigungsregel erstellen",
"alertingRuleCredenzaDescription": "Wählen Sie aus, was beobachtet, wann ausgelöst und wie benachrichtigt werden soll",
"alertingRuleNamePlaceholder": "Produktionsseite ausgefallen",
"alertingRuleEnabled": "Regel aktiviert",
"alertingSectionSource": "Quelle",
"alertingSourceType": "Quellentyp",
"alertingSourceSite": "Standort",
"alertingSourceHealthCheck": "Gesundheits-Check",
"alertingPickSites": "Standorte",
"alertingPickHealthChecks": "Gesundheits-Checks",
"alertingPickResources": "Ressourcen",
"alertingAllSites": "Alle Standorte",
"alertingAllSitesDescription": "Benachrichtigung für jeden Standort",
"alertingSpecificSites": "Bestimmte Standorte",
"alertingSpecificSitesDescription": "Wählen Sie spezifische Standorte zur Beobachtung aus",
"alertingAllHealthChecks": "Alle Gesundheits-Checks",
"alertingAllHealthChecksDescription": "Benachrichtigung für jeden Gesundheits-Check",
"alertingSpecificHealthChecks": "Bestimmte Gesundheits-Checks",
"alertingSpecificHealthChecksDescription": "Wählen Sie spezifische Gesundheits-Checks zur Beobachtung aus",
"alertingAllResources": "Alle Ressourcen",
"alertingAllResourcesDescription": "Benachrichtigung für jede Ressource",
"alertingSpecificResources": "Spezifische Ressourcen",
"alertingSpecificResourcesDescription": "Wählen Sie spezifische Ressourcen zur Beobachtung aus",
"alertingSelectResources": "Ressourcen auswählen…",
"alertingResourcesSelected": "{count} Ressourcen ausgewählt",
"alertingResourcesEmpty": "Keine Ressourcen mit Zielen in den ersten 10 Ergebnissen.",
"alertingSectionTrigger": "Auslöser",
"alertingTrigger": "Wann benachrichtigen",
"alertingTriggerSiteOnline": "Seite online",
"alertingTriggerSiteOffline": "Seite offline",
"alertingTriggerSiteToggle": "Seitenstatus ändern",
"alertingTriggerHcHealthy": "Gesundheits-Check gesund",
"alertingTriggerHcUnhealthy": "Gesundheits-Check ungesund",
"alertingTriggerHcToggle": "Gesundheits-Check-Status ändern",
"alertingTriggerResourceHealthy": "Ressource gesund",
"alertingTriggerResourceUnhealthy": "Ressource ungesund",
"alertingTriggerResourceDegraded": "Ressource verschlechtert",
"alertingSearchHealthChecks": "Gesundheits-Checks suchen…",
"alertingHealthChecksEmpty": "Keine Gesundheits-Checks verfügbar.",
"alertingTriggerResourceToggle": "Ressourcenstatus ändern",
"alertingSourceResource": "Ressource",
"alertingSectionActions": "Aktionen",
"alertingAddAction": "Aktion hinzufügen",
"alertingActionNotify": "E-Mail",
"alertingActionNotifyDescription": "Versenden Sie E-Mail-Benachrichtigungen an Benutzer oder Rollen",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Senden Sie eine HTTP-Anfrage an einen benutzerdefinierten Endpunkt",
"alertingExternalIntegration": "Externe Integration",
"alertingExternalPagerDutyDescription": "Senden Sie Benachrichtigungen an PagerDuty für Incident Management",
"alertingExternalOpsgenieDescription": "Leiten Sie Benachrichtigungen an Opsgenie für On-Call Management",
"alertingExternalServiceNowDescription": "Erstellen Sie ServiceNow-Incidents aus Benachrichtigungsereignissen",
"alertingExternalIncidentIoDescription": "Starten Sie Incident.io-Workflows aus Benachrichtigungsereignissen",
"alertingActionType": "Aktionstyp",
"alertingNotifyUsers": "Benutzer",
"alertingNotifyRoles": "Rollen",
"alertingNotifyEmails": "E-Mail-Adressen",
"alertingEmailPlaceholder": "E-Mail hinzufügen und Enter drücken",
"alertingWebhookMethod": "HTTP-Methode",
"alertingWebhookSecret": "Signatur geheim (optional)",
"alertingWebhookSecretPlaceholder": "HMAC-Geheimnis",
"alertingWebhookHeaders": "Header",
"alertingAddHeader": "Header hinzufügen",
"alertingSelectSites": "Standorte auswählen…",
"alertingSitesSelected": "{count} Standorte ausgewählt",
"alertingSelectHealthChecks": "Gesundheits-Checks auswählen…",
"alertingHealthChecksSelected": "{count} Gesundheits-Checks ausgewählt",
"alertingNoHealthChecks": "Keine Ziele mit aktivierten Gesundheits-Checks",
"alertingHealthCheckStub": "Gesundheits-Quellenauswahl ist noch nicht verdrahtet Sie können trotzdem Auslöser und Aktionen konfigurieren.",
"alertingSelectUsers": "Benutzer auswählen…",
"alertingUsersSelected": "{count} Benutzer ausgewählt",
"alertingSelectRoles": "Rollen auswählen…",
"alertingRolesSelected": "{count} Rollen ausgewählt",
"alertingSummarySites": "Standorte ({count})",
"alertingSummaryAllSites": "Alle Standorte",
"alertingSummaryHealthChecks": "Gesundheits-Checks ({count})",
"alertingSummaryAllHealthChecks": "Alle Gesundheits-Checks",
"alertingSummaryResources": "Ressourcen ({count})",
"alertingSummaryAllResources": "Alle Ressourcen",
"alertingErrorNameRequired": "Einen Namen eingeben",
"alertingErrorActionsMin": "Mindestens eine Aktion hinzufügen",
"alertingErrorPickSites": "Wählen Sie mindestens einen Standort aus",
"alertingErrorPickHealthChecks": "Wählen Sie mindestens einen Gesundheits-Check aus",
"alertingErrorPickResources": "Wählen Sie mindestens eine Ressource aus",
"alertingErrorTriggerSite": "Wählen Sie einen Auslöser für den Standort",
"alertingErrorTriggerHealth": "Wählen Sie einen Auslöser für den Gesundheits-Check",
"alertingErrorTriggerResource": "Wählen Sie einen Auslöser für die Ressource",
"alertingErrorNotifyRecipients": "Wählen Sie Benutzer, Rollen oder mindestens eine E-Mail aus",
"alertingConfigureSource": "Quelle konfigurieren",
"alertingConfigureTrigger": "Auslöser konfigurieren",
"alertingConfigureActions": "Aktionen konfigurieren",
"alertingBackToRules": "Zurück zu den Regeln",
"alertingRuleCooldown": "Cooldown (Sekunden)",
"alertingRuleCooldownDescription": "Mindest-Zeit zwischen wiederholten Benachrichtigungen für dieselbe Regel. Auf 0 setzen, um jedes Mal auszulösen.",
"alertingDraftBadge": "Entwurf - speichern, um diese Regel zu sichern",
"alertingSidebarHint": "Klicken Sie auf einen Schritt auf der Leinwand, um ihn hier zu bearbeiten.",
"alertingGraphCanvasTitle": "Regelfluss",
"alertingGraphCanvasDescription": "Visuelle Übersicht über Quelle, Auslöser und Aktionen. Wählen Sie einen Knoten aus, um ihn im Panel zu bearbeiten.",
"alertingNodeNotConfigured": "Noch nicht konfiguriert",
"alertingNodeActionsCount": "{count, plural, one {# Aktion} other {# Aktionen}}",
"alertingNodeRoleSource": "Quelle",
"alertingNodeRoleTrigger": "Auslöser",
"alertingNodeRoleAction": "Aktion",
"alertingTabRules": "Benachrichtigungsregeln",
"alertingTabHealthChecks": "Gesundheits-Checks",
"alertingRulesBannerTitle": "Benachrichtigt werden",
"alertingRulesBannerDescription": "Jede Regel verknüpft, was beobachtet werden soll (eine Seite, ein Gesundheits-Check oder eine Ressource), wann es ausgelöst werden soll (zum Beispiel offline oder ungesund), und wie Ihr Team benachrichtigt wird, z. B. per E-Mail, Webhooks oder Integrationen. Verwenden Sie diese Liste, um diese Regeln zu erstellen, zu aktivieren und zu verwalten.",
"alertingHealthChecksBannerTitle": "Gesundheit & Ressourcen überwachen",
"alertingHealthChecksBannerDescription": "Gesundheits-Checks sind HTTP- oder TCP-Monitore, die Sie einmal definieren. Sie können sie dann als Quellen in Benachrichtigungsregeln verwenden, so dass Sie benachrichtigt werden, wenn ein Ziel gesund oder ungesund wird. Gesundheits-Checks für Ressourcen erscheinen ebenfalls hier.",
"standaloneHcTableTitle": "Gesundheits-Checks",
"standaloneHcSearchPlaceholder": "Gesundheits-Checks suchen…",
"standaloneHcAddButton": "Gesundheits-Check erstellen",
"standaloneHcCreateTitle": "Gesundheits-Check erstellen",
"standaloneHcEditTitle": "Gesundheits-Check bearbeiten",
"standaloneHcDescription": "Konfigurieren Sie einen HTTP- oder TCP-Gesundheits-Check zur Verwendung in Benachrichtigungsregeln.",
"standaloneHcNameLabel": "Name",
"standaloneHcNamePlaceholder": "Mein HTTP-Monitor",
"standaloneHcDeleteTitle": "Gesundheits-Check löschen",
"standaloneHcDeleteQuestion": "Bitte bestätigen Sie, dass Sie diesen Gesundheits-Check löschen möchten.",
"standaloneHcDeleted": "Gesundheits-Check gelöscht",
"standaloneHcSaved": "Gesundheits-Check gespeichert",
"standaloneHcColumnHealth": "Gesundheit",
"standaloneHcColumnMode": "Modus",
"standaloneHcColumnTarget": "Ziel",
"standaloneHcHealthStateHealthy": "Gesund",
"standaloneHcHealthStateUnhealthy": "Ungesund",
"standaloneHcHealthStateUnknown": "Unbekannt",
"standaloneHcFilterAnySite": "Alle Standorte",
"standaloneHcFilterAnyResource": "Alle Ressourcen",
"standaloneHcFilterMode": "Modus",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Gesundheit",
"standaloneHcFilterEnabled": "Aktiviert",
"standaloneHcFilterEnabledOn": "Aktiviert",
"standaloneHcFilterEnabledOff": "Deaktiviert",
"standaloneHcFilterSiteIdFallback": "Standort {id}",
"standaloneHcFilterResourceIdFallback": "Ressource {id}",
"blueprints": "Blaupausen", "blueprints": "Blaupausen",
"blueprintsDescription": "Deklarative Konfigurationen anwenden und vorherige Abläufe anzeigen", "blueprintsDescription": "Deklarative Konfigurationen anwenden und vorherige Abläufe anzeigen",
"blueprintAdd": "Blueprint hinzufügen", "blueprintAdd": "Blueprint hinzufügen",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Erstellen Sie das initiale Server-Admin-Konto. Es kann nur einen Server-Admin geben. Sie können diese Anmeldedaten später immer ändern.", "initialSetupDescription": "Erstellen Sie das initiale Server-Admin-Konto. Es kann nur einen Server-Admin geben. Sie können diese Anmeldedaten später immer ändern.",
"createAdminAccount": "Admin-Konto erstellen", "createAdminAccount": "Admin-Konto erstellen",
"setupErrorCreateAdmin": "Beim Erstellen des Server-Admin-Kontos ist ein Fehler aufgetreten.", "setupErrorCreateAdmin": "Beim Erstellen des Server-Admin-Kontos ist ein Fehler aufgetreten.",
"certificateStatus": "Zertifikatsstatus", "certificateStatus": "Zertifikat",
"certificateStatusAutoRefreshHint": "Der Status wird automatisch aktualisiert.",
"loading": "Laden", "loading": "Laden",
"loadingAnalytics": "Analytik wird geladen", "loadingAnalytics": "Analytik wird geladen",
"restart": "Neustart", "restart": "Neustart",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Versionshinweise anzeigen", "pangolinUpdateAvailableReleaseNotes": "Versionshinweise anzeigen",
"newtUpdateAvailable": "Update verfügbar", "newtUpdateAvailable": "Update verfügbar",
"newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.", "newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.",
"pangolinNodeUpdateAvailableInfo": "Eine neue Version von Pangolin Node ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.",
"domainPickerEnterDomain": "Domäne", "domainPickerEnterDomain": "Domäne",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Geben Sie die vollständige Domain der Ressource ein, um verfügbare Optionen zu sehen.", "domainPickerDescription": "Geben Sie die vollständige Domain der Ressource ein, um verfügbare Optionen zu sehen.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Gesundheits-Check konfigurieren", "configureHealthCheck": "Gesundheits-Check konfigurieren",
"configureHealthCheckDescription": "Richten Sie die Gesundheitsüberwachung für {target} ein", "configureHealthCheckDescription": "Richten Sie die Gesundheitsüberwachung für {target} ein",
"enableHealthChecks": "Gesundheits-Checks aktivieren", "enableHealthChecks": "Gesundheits-Checks aktivieren",
"healthCheckDisabledStateDescription": "Wenn deaktiviert, führt die Seite keine Gesundheitsprüfungen durch und der Zustand wird als unbekannt betrachtet.",
"enableHealthChecksDescription": "Überwachen Sie die Gesundheit dieses Ziels. Bei Bedarf können Sie einen anderen Endpunkt als das Ziel überwachen.", "enableHealthChecksDescription": "Überwachen Sie die Gesundheit dieses Ziels. Bei Bedarf können Sie einen anderen Endpunkt als das Ziel überwachen.",
"healthScheme": "Methode", "healthScheme": "Methode",
"healthSelectScheme": "Methode auswählen", "healthSelectScheme": "Methode auswählen",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Prüfintervall muss mindestens 5 Sekunden betragen", "healthCheckIntervalMin": "Prüfintervall muss mindestens 5 Sekunden betragen",
"healthCheckTimeoutMin": "Zeitüberschreitung muss mindestens 1 Sekunde betragen", "healthCheckTimeoutMin": "Zeitüberschreitung muss mindestens 1 Sekunde betragen",
"healthCheckRetryMin": "Wiederholungsversuche müssen mindestens 1 betragen", "healthCheckRetryMin": "Wiederholungsversuche müssen mindestens 1 betragen",
"healthCheckMode": "Überprüfungsmodus",
"healthCheckStrategy": "Strategie",
"healthCheckModeDescription": "TCP-Modus überprüft nur die Konnektivität. HTTP-Modus validiert die HTTP-Antwort.",
"healthyThreshold": "Gesundheitsschwelle",
"healthyThresholdDescription": "Erforderliche aufeinanderfolgende Erfolge, bevor als gesund markiert wird.",
"unhealthyThreshold": "Ungesunde Schwelle",
"unhealthyThresholdDescription": "Erforderliche aufeinanderfolgende Fehlschläge, bevor als ungesund markiert wird.",
"healthCheckHealthyThresholdMin": "Gesundheitsschwelle muss mindestens 1 betragen",
"healthCheckUnhealthyThresholdMin": "Ungesunde Schwelle muss mindestens 1 betragen",
"httpMethod": "HTTP-Methode", "httpMethod": "HTTP-Methode",
"selectHttpMethod": "HTTP-Methode auswählen", "selectHttpMethod": "HTTP-Methode auswählen",
"domainPickerSubdomainLabel": "Subdomain", "domainPickerSubdomainLabel": "Subdomain",
"domainPickerWildcard": "Platzhalter",
"domainPickerWildcardPaidOnly": "Wildcard-Subdomains sind ein kostenpflichtiges Feature. Bitte upgraden Sie, um auf dieses Feature zuzugreifen.",
"domainPickerBaseDomainLabel": "Basisdomain", "domainPickerBaseDomainLabel": "Basisdomain",
"domainPickerSearchDomains": "Domains suchen...", "domainPickerSearchDomains": "Domains suchen...",
"domainPickerNoDomainsFound": "Keine Domains gefunden", "domainPickerNoDomainsFound": "Keine Domains gefunden",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Diese Adresse ist Teil des Utility-Subnetzes der Organisation. Sie wird verwendet, um Alias-Einträge mit interner DNS-Auflösung aufzulösen.", "resourcesTableAliasAddressInfo": "Diese Adresse ist Teil des Utility-Subnetzes der Organisation. Sie wird verwendet, um Alias-Einträge mit interner DNS-Auflösung aufzulösen.",
"resourcesTableClients": "Clients", "resourcesTableClients": "Clients",
"resourcesTableAndOnlyAccessibleInternally": "und sind nur intern zugänglich, wenn mit einem Client verbunden.", "resourcesTableAndOnlyAccessibleInternally": "und sind nur intern zugänglich, wenn mit einem Client verbunden.",
"resourcesTableNoTargets": "Keine Ziele",
"resourcesTableHealthy": "Gesund", "resourcesTableHealthy": "Gesund",
"resourcesTableDegraded": "Degradiert", "resourcesTableDegraded": "Degradiert",
"resourcesTableOffline": "Offline", "resourcesTableUnhealthy": "Ungesund",
"resourcesTableUnknown": "Unbekannt", "resourcesTableUnknown": "Unbekannt",
"resourcesTableNotMonitored": "Nicht überwacht", "resourcesTableNotMonitored": "Nicht überwacht",
"resourcesTableNoTargets": "Keine Ziele",
"editInternalResourceDialogEditClientResource": "Private Ressource bearbeiten", "editInternalResourceDialogEditClientResource": "Private Ressource bearbeiten",
"editInternalResourceDialogUpdateResourceProperties": "Ressourcen-Konfiguration und Zugriffssteuerung für {resourceName} aktualisieren", "editInternalResourceDialogUpdateResourceProperties": "Ressourcen-Konfiguration und Zugriffssteuerung für {resourceName} aktualisieren",
"editInternalResourceDialogResourceProperties": "Ressourceneigenschaften", "editInternalResourceDialogResourceProperties": "Ressourceneigenschaften",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Port", "editInternalResourceDialogModePort": "Port",
"editInternalResourceDialogModeHost": "Host", "editInternalResourceDialogModeHost": "Host",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Schema",
"editInternalResourceDialogEnableSsl": "SSL aktivieren",
"editInternalResourceDialogEnableSslDescription": "SSL/TLS-Verschlüsselung für sichere HTTPS-Verbindungen zum Ziel aktivieren.",
"editInternalResourceDialogDestination": "Ziel", "editInternalResourceDialogDestination": "Ziel",
"editInternalResourceDialogDestinationHostDescription": "Die IP-Adresse oder der Hostname der Ressource im Netzwerk der Website.", "editInternalResourceDialogDestinationHostDescription": "Die IP-Adresse oder der Hostname der Ressource im Netzwerk der Website.",
"editInternalResourceDialogDestinationIPDescription": "Die IP-Adresse oder Hostname Adresse der Ressource im Netzwerk der Website.", "editInternalResourceDialogDestinationIPDescription": "Die IP-Adresse oder Hostname Adresse der Ressource im Netzwerk der Website.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Name", "createInternalResourceDialogName": "Name",
"createInternalResourceDialogSite": "Standort", "createInternalResourceDialogSite": "Standort",
"selectSite": "Standort auswählen...", "selectSite": "Standort auswählen...",
"multiSitesSelectorSitesCount": "{count, plural, one {# Standort} other {# Standorte}}",
"noSitesFound": "Keine Standorte gefunden.", "noSitesFound": "Keine Standorte gefunden.",
"createInternalResourceDialogProtocol": "Protokoll", "createInternalResourceDialogProtocol": "Protokoll",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Port", "createInternalResourceDialogModePort": "Port",
"createInternalResourceDialogModeHost": "Host", "createInternalResourceDialogModeHost": "Host",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Schema",
"createInternalResourceDialogScheme": "Schema",
"createInternalResourceDialogEnableSsl": "SSL aktivieren",
"createInternalResourceDialogEnableSslDescription": "SSL/TLS-Verschlüsselung für sichere HTTPS-Verbindungen zum Ziel aktivieren.",
"createInternalResourceDialogDestination": "Ziel", "createInternalResourceDialogDestination": "Ziel",
"createInternalResourceDialogDestinationHostDescription": "Die IP-Adresse oder der Hostname der Ressource im Netzwerk der Website.", "createInternalResourceDialogDestinationHostDescription": "Die IP-Adresse oder der Hostname der Ressource im Netzwerk der Website.",
"createInternalResourceDialogDestinationCidrDescription": "Der CIDR-Bereich der Ressource im Netzwerk der Website.", "createInternalResourceDialogDestinationCidrDescription": "Der CIDR-Bereich der Ressource im Netzwerk der Website.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Ein optionaler interner DNS-Alias für diese Ressource.", "createInternalResourceDialogAliasDescription": "Ein optionaler interner DNS-Alias für diese Ressource.",
"internalResourceDownstreamSchemeRequired": "Schema ist für HTTP-Ressourcen erforderlich",
"internalResourceHttpPortRequired": "Zielport ist für HTTP-Ressourcen erforderlich",
"siteConfiguration": "Konfiguration", "siteConfiguration": "Konfiguration",
"siteAcceptClientConnections": "Clientverbindungen akzeptieren", "siteAcceptClientConnections": "Clientverbindungen akzeptieren",
"siteAcceptClientConnectionsDescription": "Erlaube Benutzer-Geräten und Clients Zugriff auf Ressourcen auf diesem Standort. Dies kann später geändert werden.", "siteAcceptClientConnectionsDescription": "Erlaube Benutzer-Geräten und Clients Zugriff auf Ressourcen auf diesem Standort. Dies kann später geändert werden.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Verifiziert", "domainPickerVerified": "Verifiziert",
"domainPickerUnverified": "Nicht verifiziert", "domainPickerUnverified": "Nicht verifiziert",
"domainPickerManual": "Manuell", "domainPickerManual": "Manuell",
"domainPickerInvalidSubdomainStructure": "Diese Subdomain enthält ungültige Zeichen oder Struktur. Sie wird beim Speichern automatisch bereinigt.", "domainPickerInvalidSubdomainStructure": "Ungültige Zeichen werden beim Speichern bereinigt.",
"domainPickerError": "Fehler", "domainPickerError": "Fehler",
"domainPickerErrorLoadDomains": "Fehler beim Laden der Organisations-Domains", "domainPickerErrorLoadDomains": "Fehler beim Laden der Organisations-Domains",
"domainPickerErrorCheckAvailability": "Fehler beim Prüfen der Domain-Verfügbarkeit", "domainPickerErrorCheckAvailability": "Fehler beim Prüfen der Domain-Verfügbarkeit",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Wähle deinen Identitätsanbieter um fortzufahren", "orgAuthChooseIdpDescription": "Wähle deinen Identitätsanbieter um fortzufahren",
"orgAuthNoIdpConfigured": "Diese Organisation hat keine Identitätsanbieter konfiguriert. Sie können sich stattdessen mit Ihrer Pangolin-Identität anmelden.", "orgAuthNoIdpConfigured": "Diese Organisation hat keine Identitätsanbieter konfiguriert. Sie können sich stattdessen mit Ihrer Pangolin-Identität anmelden.",
"orgAuthSignInWithPangolin": "Mit Pangolin anmelden", "orgAuthSignInWithPangolin": "Mit Pangolin anmelden",
"orgAuthSignInToOrg": "Bei einer Organisation anmelden", "orgAuthSignInToOrg": "Organisations-Identitätsanbieter (SSO)",
"orgAuthSelectOrgTitle": "Organisations-Anmeldung", "orgAuthSelectOrgTitle": "Organisations-Anmeldung",
"orgAuthSelectOrgDescription": "Geben Sie Ihre Organisations-ID ein, um fortzufahren", "orgAuthSelectOrgDescription": "Geben Sie Ihre Organisations-ID ein, um fortzufahren",
"orgAuthOrgIdPlaceholder": "Ihre Organisation", "orgAuthOrgIdPlaceholder": "Ihre Organisation",
@@ -2429,6 +2648,7 @@
"validPassword": "Gültiges Passwort", "validPassword": "Gültiges Passwort",
"validEmail": "Gültige E-Mail-Adresse", "validEmail": "Gültige E-Mail-Adresse",
"validSSO": "Gültige SSO-Anmeldung", "validSSO": "Gültige SSO-Anmeldung",
"connectedClient": "Verbundenes Gerät",
"resourceBlocked": "Ressource blockiert", "resourceBlocked": "Ressource blockiert",
"droppedByRule": "Abgelegt durch Regel", "droppedByRule": "Abgelegt durch Regel",
"noSessions": "Keine Sitzungen", "noSessions": "Keine Sitzungen",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Clients hinzufügen", "editInternalResourceDialogAddClients": "Clients hinzufügen",
"editInternalResourceDialogDestinationLabel": "Ziel", "editInternalResourceDialogDestinationLabel": "Ziel",
"editInternalResourceDialogDestinationDescription": "Geben Sie die Zieladresse für die interne Ressource an. Dies kann ein Hostname, eine IP-Adresse oder ein CIDR-Bereich sein, abhängig vom gewählten Modus. Legen Sie optional einen internen DNS-Alias für eine vereinfachte Identifizierung fest.", "editInternalResourceDialogDestinationDescription": "Geben Sie die Zieladresse für die interne Ressource an. Dies kann ein Hostname, eine IP-Adresse oder ein CIDR-Bereich sein, abhängig vom gewählten Modus. Legen Sie optional einen internen DNS-Alias für eine vereinfachte Identifizierung fest.",
"internalResourceFormMultiSiteRoutingHelp": "Durch die Auswahl mehrerer Seiten wird ein ausfallsicheres Routing und Failover für hohe Verfügbarkeit ermöglicht.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Mehr erfahren",
"editInternalResourceDialogPortRestrictionsDescription": "Den Zugriff auf bestimmte TCP/UDP-Ports beschränken oder alle Ports erlauben/blockieren.", "editInternalResourceDialogPortRestrictionsDescription": "Den Zugriff auf bestimmte TCP/UDP-Ports beschränken oder alle Ports erlauben/blockieren.",
"createInternalResourceDialogHttpConfiguration": "HTTP-Konfiguration",
"createInternalResourceDialogHttpConfigurationDescription": "Wählen Sie die Domain, die Clients verwenden, um über HTTP oder HTTPS auf diese Ressource zuzugreifen.",
"editInternalResourceDialogHttpConfiguration": "HTTP-Konfiguration",
"editInternalResourceDialogHttpConfigurationDescription": "Wählen Sie die Domain, die Clients verwenden, um über HTTP oder HTTPS auf diese Ressource zuzugreifen.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Wir sind bald wieder da! Unsere Seite wird derzeit planmäßig gewartet.", "maintenancePageMessagePlaceholder": "Wir sind bald wieder da! Unsere Seite wird derzeit planmäßig gewartet.",
"maintenancePageMessageDescription": "Detaillierte Meldung zur Erklärung der Wartung", "maintenancePageMessageDescription": "Detaillierte Meldung zur Erklärung der Wartung",
"maintenancePageTimeTitle": "Geschätzte Abschlusszeit (Optional)", "maintenancePageTimeTitle": "Geschätzte Abschlusszeit (Optional)",
"privateMaintenanceScreenTitle": "Privater Platzhalterschirm",
"privateMaintenanceScreenMessage": "Diese Domain wird auf einer privaten Ressource verwendet. Bitte verbinden Sie sich mit dem Pangolin-Client, um auf diese Ressource zuzugreifen.",
"privateMaintenanceScreenSteps": "Sobald verbunden, wenn Sie diese Nachricht weiterhin sehen, zeigt der DNS-Cache Ihres Browsers möglicherweise noch auf die alte Adresse. Um dies zu beheben: Schließen Sie diesen Tab vollständig und öffnen Sie ihn erneut oder starten Sie Ihren Browser neu und rufen Sie dann diese Seite erneut auf.",
"maintenanceTime": "z.B.: 2 Stunden, Nov 1 um 17:00 Uhr", "maintenanceTime": "z.B.: 2 Stunden, Nov 1 um 17:00 Uhr",
"maintenanceEstimatedTimeDescription": "Wann Sie den Abschluss der Wartung erwarten", "maintenanceEstimatedTimeDescription": "Wann Sie den Abschluss der Wartung erwarten",
"editDomain": "Domain bearbeiten", "editDomain": "Domain bearbeiten",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "HTTP-Ziel hinzufügen", "httpDestAddTitle": "HTTP-Ziel hinzufügen",
"httpDestEditDescription": "Aktualisiere die Konfiguration für dieses HTTP-Streaming-Ziel.", "httpDestEditDescription": "Aktualisiere die Konfiguration für dieses HTTP-Streaming-Ziel.",
"httpDestAddDescription": "Konfigurieren Sie einen neuen HTTP-Endpunkt, um die Ereignisse Ihrer Organisation zu empfangen.", "httpDestAddDescription": "Konfigurieren Sie einen neuen HTTP-Endpunkt, um die Ereignisse Ihrer Organisation zu empfangen.",
"S3DestEditTitle": "Ziel bearbeiten",
"S3DestAddTitle": "S3-Ziel hinzufügen",
"S3DestEditDescription": "Konfiguration für dieses S3-Ereignis-Streamingziel aktualisieren.",
"S3DestAddDescription": "Neuen S3-Endpunkt konfigurieren, um die Ereignisse Ihrer Organisation zu erhalten.",
"datadogDestEditTitle": "Ziel bearbeiten",
"datadogDestAddTitle": "Datadog-Ziel hinzufügen",
"datadogDestEditDescription": "Konfiguration für dieses Datadog-Ereignis-Streamingziel aktualisieren.",
"datadogDestAddDescription": "Neuen Datadog-Endpunkt konfigurieren, um die Ereignisse Ihrer Organisation zu erhalten.",
"httpDestTabSettings": "Einstellungen", "httpDestTabSettings": "Einstellungen",
"httpDestTabHeaders": "Kopfzeilen", "httpDestTabHeaders": "Kopfzeilen",
"httpDestTabBody": "Körper", "httpDestTabBody": "Körper",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Ziel erfolgreich erstellt", "httpDestCreatedSuccess": "Ziel erfolgreich erstellt",
"httpDestUpdateFailed": "Fehler beim Aktualisieren des Ziels", "httpDestUpdateFailed": "Fehler beim Aktualisieren des Ziels",
"httpDestCreateFailed": "Fehler beim Erstellen des Ziels", "httpDestCreateFailed": "Fehler beim Erstellen des Ziels",
"followRedirects": "Weiterleitungen folgen",
"followRedirectsDescription": "HTTP-Weiterleitungen für Anfragen automatisch folgen.",
"alertingErrorWebhookUrl": "Bitte geben Sie eine gültige URL für das Webhook ein.",
"healthCheckStrategyHttp": "Prüft die Konnektivität und den HTTP-Antwort-Status.",
"healthCheckStrategyTcp": "Verifiziert nur die TCP-Konnektivität, ohne die Antwort zu überprüfen.",
"healthCheckStrategySnmp": "Stellt eine SNMP-Get-Anfrage, um die Gesundheit von Netzwerkgeräten und Infrastruktur zu überprüfen.",
"healthCheckStrategyIcmp": "Verwendet ICMP-Echo-Anfragen (Pings), um zu überprüfen, ob eine Ressource erreichbar und reaktionsfähig ist.",
"healthCheckTabStrategy": "Strategie",
"healthCheckTabConnection": "Verbindung",
"healthCheckTabAdvanced": "Fortgeschritten",
"healthCheckStrategyNotAvailable": "Diese Strategie ist nicht verfügbar. Bitte kontaktieren Sie den Vertrieb, um diese Funktion zu aktivieren.",
"uptime30d": "Betriebszeit (30 Tage)",
"idpAddActionCreateNew": "Neuen Identitätsanbieter erstellen", "idpAddActionCreateNew": "Neuen Identitätsanbieter erstellen",
"idpAddActionImportFromOrg": "Von einer anderen Organisation importieren", "idpAddActionImportFromOrg": "Von einer anderen Organisation importieren",
"idpImportDialogTitle": "Identitätsanbieter importieren", "idpImportDialogTitle": "Identitätsanbieter importieren",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Dies kann für diese Organisation nicht rückgängig gemacht werden.", "idpUnassociateWarning": "Dies kann für diese Organisation nicht rückgängig gemacht werden.",
"idpUnassociatedDescription": "Identitätsanbieter erfolgreich von dieser Organisation gelöst", "idpUnassociatedDescription": "Identitätsanbieter erfolgreich von dieser Organisation gelöst",
"idpUnassociateMenu": "Verknüpfung aufheben", "idpUnassociateMenu": "Verknüpfung aufheben",
"idpDeleteAllOrgsMenu": "Löschen" "idpDeleteAllOrgsMenu": "Löschen",
"publicIpEndpoint": "Endpunkt",
"lastTriggeredAt": "Letzter Auslöser",
"reject": "Zurückweisen",
"uptimeDaysAgo": "vor {count} Tagen",
"uptimeToday": "Heute",
"uptimeNoDataAvailable": "Keine Daten verfügbar",
"uptimeSuffix": "Betriebzeit",
"uptimeDowntimeSuffix": "Ausfallzeit",
"uptimeTooltipUptimeLabel": "Betriebszeit",
"uptimeTooltipDowntimeLabel": "Ausfallzeit",
"uptimeOngoing": "im Gange",
"uptimeNoMonitoringData": "Keine Überwachungsdaten",
"uptimeNoData": "Keine Daten",
"uptimeMiniBarDown": "Unten",
"uptimeSectionTitle": "Betriebszeit",
"uptimeSectionDescription": "Verfügbarkeit in den letzten {days} Tagen",
"uptimeAddAlert": "Warnmeldung hinzufügen",
"uptimeViewAlerts": "Warnungen anzeigen",
"uptimeCreateEmailAlert": "E-Mail Alarm erstellen",
"uptimeAlertDescriptionSite": "Werde per E-Mail benachrichtigt, wenn diese Seite offline oder wieder online ist.",
"uptimeAlertDescriptionResource": "Werde per E-Mail benachrichtigt, wenn diese Ressource offline oder wieder online ist.",
"uptimeAlertNamePlaceholder": "Alarmname",
"uptimeAdditionalEmails": "Zusätzliche E-Mails",
"uptimeCreateAlert": "Alarm erstellen",
"uptimeAlertNoRecipients": "Kein Empfänger",
"uptimeAlertNoRecipientsDescription": "Bitte fügen Sie mindestens einen Benutzer, eine Rolle oder eine E-Mail zur Benachrichtigung hinzu.",
"uptimeAlertCreated": "Alarm erstellt",
"uptimeAlertCreatedDescription": "Sie werden benachrichtigt, wenn dieser Status sich ändert",
"uptimeAlertCreateFailed": "Fehler beim Erstellen der Benachrichtigung",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Schlüssel",
"webhookHeaderValuePlaceholder": "Wert",
"alertLabel": "Alarm",
"domainPickerWildcardSubdomainNotAllowed": "Wildcard-Subdomains sind nicht erlaubt.",
"domainPickerWildcardCertWarning": "Wildcard-Ressourcen erfordern möglicherweise zusätzliche Konfigurationen, um ordnungsgemäß zu funktionieren.",
"domainPickerWildcardCertWarningLink": "Mehr erfahren",
"health": "Gesundheit",
"domainPendingErrorTitle": "Verifizierungsproblem"
} }

View File

@@ -23,6 +23,14 @@
"componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.", "componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.",
"dismiss": "Dismiss", "dismiss": "Dismiss",
"subscriptionViolationMessage": "You're beyond your limits for your current plan. Correct the problem by removing sites, users, or other resources to stay within your plan.", "subscriptionViolationMessage": "You're beyond your limits for your current plan. Correct the problem by removing sites, users, or other resources to stay within your plan.",
"trialBannerMessage": "Your trial expires in {countdown}. Upgrade to keep access.",
"trialBannerExpired": "Your trial has expired. Upgrade now to restore access.",
"trialActive": "Free Trial Active",
"trialExpired": "Trial Expired",
"trialHasEnded": "Your trial has ended.",
"trialDaysRemaining": "{count, plural, one {# day remaining} other {# days remaining}}",
"trialDaysLeftShort": "{days}d left in trial",
"trialGoToBilling": "Go to billing page",
"subscriptionViolationViewBilling": "View billing", "subscriptionViolationViewBilling": "View billing",
"componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.",
"componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!", "componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!",
@@ -85,6 +93,8 @@
"siteConfirmCopy": "I have copied the config", "siteConfirmCopy": "I have copied the config",
"searchSitesProgress": "Search sites...", "searchSitesProgress": "Search sites...",
"siteAdd": "Add Site", "siteAdd": "Add Site",
"sitesTableViewPublicResources": "View Public Resources",
"sitesTableViewPrivateResources": "View Private Resources",
"siteInstallNewt": "Install Site", "siteInstallNewt": "Install Site",
"siteInstallNewtDescription": "Install the site connector for your system", "siteInstallNewtDescription": "Install the site connector for your system",
"WgConfiguration": "WireGuard Configuration", "WgConfiguration": "WireGuard Configuration",
@@ -102,6 +112,21 @@
"siteUpdatedDescription": "The site has been updated.", "siteUpdatedDescription": "The site has been updated.",
"siteGeneralDescription": "Configure the general settings for this site", "siteGeneralDescription": "Configure the general settings for this site",
"siteSettingDescription": "Configure the settings on the site", "siteSettingDescription": "Configure the settings on the site",
"siteResourcesTab": "Resources",
"siteResourcesNoneOnSite": "This site has no public or private resources yet.",
"siteResourcesSectionPublic": "Public Resources",
"siteResourcesSectionPrivate": "Private Resources",
"siteResourcesSectionPublicDescription": "Resources exposed externally through domains or ports.",
"siteResourcesSectionPrivateDescription": "Resources available on your private network through the site.",
"siteResourcesViewAllPublic": "View all resources",
"siteResourcesViewAllPrivate": "View all resources",
"siteResourcesDialogDescription": "Overview of public and private resources associated with this site.",
"siteResourcesShowMore": "Show more",
"siteResourcesPermissionDenied": "You do not have permission to list these resources.",
"siteResourcesEmptyPublic": "No public resources target this site yet.",
"siteResourcesEmptyPrivate": "No private resources are associated with this site yet.",
"siteResourcesHowToAccess": "How to access",
"siteResourcesTargetsOnSite": "Targets on this site",
"siteSetting": "{siteName} Settings", "siteSetting": "{siteName} Settings",
"siteNewtTunnel": "Newt Site (Recommended)", "siteNewtTunnel": "Newt Site (Recommended)",
"siteNewtTunnelDescription": "Easiest way to create an entrypoint into any network. No extra setup.", "siteNewtTunnelDescription": "Easiest way to create an entrypoint into any network. No extra setup.",
@@ -271,8 +296,11 @@
"orgMissing": "Organization ID Missing", "orgMissing": "Organization ID Missing",
"orgMissingMessage": "Unable to regenerate invitation without an organization ID.", "orgMissingMessage": "Unable to regenerate invitation without an organization ID.",
"accessUsersManage": "Manage Users", "accessUsersManage": "Manage Users",
"accessUserManage": "Manage User",
"accessUsersDescription": "Invite and manage users with access to this organization", "accessUsersDescription": "Invite and manage users with access to this organization",
"accessUsersSearch": "Search users...", "accessUsersSearch": "Search users...",
"accessUsersRoleFilterCount": "{count, plural, one {# role} other {# roles}}",
"accessUsersRoleFilterClear": "Clear role filters",
"accessUserCreate": "Create User", "accessUserCreate": "Create User",
"accessUserRemove": "Remove User", "accessUserRemove": "Remove User",
"username": "Username", "username": "Username",
@@ -735,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Secret", "newtSecretKey": "Secret",
"newtVersion": "Version",
"architecture": "Architecture", "architecture": "Architecture",
"sites": "Sites", "sites": "Sites",
"siteWgAnyClients": "Use any WireGuard client to connect. You will have to address internal resources using the peer IP.", "siteWgAnyClients": "Use any WireGuard client to connect. You will have to address internal resources using the peer IP.",
@@ -1347,6 +1376,7 @@
"sidebarLogAndAnalytics": "Log & Analytics", "sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Blueprints", "sidebarBluePrints": "Blueprints",
"sidebarAlerting": "Alerting", "sidebarAlerting": "Alerting",
"sidebarHealthChecks": "Health checks",
"sidebarOrganization": "Organization", "sidebarOrganization": "Organization",
"sidebarManagement": "Management", "sidebarManagement": "Management",
"sidebarBillingAndLicenses": "Billing & Licenses", "sidebarBillingAndLicenses": "Billing & Licenses",
@@ -1360,13 +1390,15 @@
"alertingColumnTrigger": "Trigger", "alertingColumnTrigger": "Trigger",
"alertingColumnActions": "Actions", "alertingColumnActions": "Actions",
"alertingColumnEnabled": "Enabled", "alertingColumnEnabled": "Enabled",
"alertingDeleteQuestion": "Delete this alert rule? This cannot be undone.", "alertingDeleteQuestion": "Please confirm you want to delete this alert rule.",
"alertingDeleteRule": "Delete alert rule", "alertingDeleteRule": "Delete alert rule",
"alertingRuleDeleted": "Alert rule deleted", "alertingRuleDeleted": "Alert rule deleted",
"alertingRuleSaved": "Alert rule saved", "alertingRuleSaved": "Alert rule saved",
"alertingEditRule": "Edit alert rule", "alertingRuleSavedCreatedDescription": "Your new alert rule was created. You can keep editing it on this page.",
"alertingCreateRule": "Create alert rule", "alertingRuleSavedUpdatedDescription": "Your changes to this alert rule were saved.",
"alertingRuleCredenzaDescription": "Choose what to watch, when to fire, and how to notify your team.", "alertingEditRule": "Edit Alert Rule",
"alertingCreateRule": "Create Alert Rule",
"alertingRuleCredenzaDescription": "Choose what to watch, when to fire, and how to notify",
"alertingRuleNamePlaceholder": "Production site down", "alertingRuleNamePlaceholder": "Production site down",
"alertingRuleEnabled": "Rule enabled", "alertingRuleEnabled": "Rule enabled",
"alertingSectionSource": "Source", "alertingSectionSource": "Source",
@@ -1386,7 +1418,7 @@
"alertingSpecificHealthChecksDescription": "Choose specific health checks to watch", "alertingSpecificHealthChecksDescription": "Choose specific health checks to watch",
"alertingAllResources": "All Resources", "alertingAllResources": "All Resources",
"alertingAllResourcesDescription": "Alert fires for any resource", "alertingAllResourcesDescription": "Alert fires for any resource",
"alertingSpecificResources": "Specific resources", "alertingSpecificResources": "Specific Resources",
"alertingSpecificResourcesDescription": "Choose specific resources to watch", "alertingSpecificResourcesDescription": "Choose specific resources to watch",
"alertingSelectResources": "Select resources…", "alertingSelectResources": "Select resources…",
"alertingResourcesSelected": "{count} resources selected", "alertingResourcesSelected": "{count} resources selected",
@@ -1401,12 +1433,13 @@
"alertingTriggerHcToggle": "Health check status changes", "alertingTriggerHcToggle": "Health check status changes",
"alertingTriggerResourceHealthy": "Resource healthy", "alertingTriggerResourceHealthy": "Resource healthy",
"alertingTriggerResourceUnhealthy": "Resource unhealthy", "alertingTriggerResourceUnhealthy": "Resource unhealthy",
"alertingTriggerResourceDegraded": "Resource degraded",
"alertingSearchHealthChecks": "Search health checks…", "alertingSearchHealthChecks": "Search health checks…",
"alertingHealthChecksEmpty": "No health checks available.", "alertingHealthChecksEmpty": "No health checks available.",
"alertingTriggerResourceToggle": "Resource status changes", "alertingTriggerResourceToggle": "Resource status changes",
"alertingSourceResource": "Resource", "alertingSourceResource": "Resource",
"alertingSectionActions": "Actions", "alertingSectionActions": "Actions",
"alertingAddAction": "Add action", "alertingAddAction": "Add Action",
"alertingActionNotify": "Email", "alertingActionNotify": "Email",
"alertingActionNotifyDescription": "Send email notifications to users or roles", "alertingActionNotifyDescription": "Send email notifications to users or roles",
"alertingActionWebhook": "Webhook", "alertingActionWebhook": "Webhook",
@@ -1437,8 +1470,11 @@
"alertingSelectRoles": "Select roles…", "alertingSelectRoles": "Select roles…",
"alertingRolesSelected": "{count} roles selected", "alertingRolesSelected": "{count} roles selected",
"alertingSummarySites": "Sites ({count})", "alertingSummarySites": "Sites ({count})",
"alertingSummaryAllSites": "All sites",
"alertingSummaryHealthChecks": "Health checks ({count})", "alertingSummaryHealthChecks": "Health checks ({count})",
"alertingSummaryAllHealthChecks": "All health checks",
"alertingSummaryResources": "Resources ({count})", "alertingSummaryResources": "Resources ({count})",
"alertingSummaryAllResources": "All resources",
"alertingErrorNameRequired": "Enter a name", "alertingErrorNameRequired": "Enter a name",
"alertingErrorActionsMin": "Add at least one action", "alertingErrorActionsMin": "Add at least one action",
"alertingErrorPickSites": "Select at least one site", "alertingErrorPickSites": "Select at least one site",
@@ -1452,6 +1488,8 @@
"alertingConfigureTrigger": "Configure Trigger", "alertingConfigureTrigger": "Configure Trigger",
"alertingConfigureActions": "Configure Actions", "alertingConfigureActions": "Configure Actions",
"alertingBackToRules": "Back to Rules", "alertingBackToRules": "Back to Rules",
"alertingRuleCooldown": "Cooldown (seconds)",
"alertingRuleCooldownDescription": "Minimum time between repeated alerts for the same rule. Set to 0 to fire every time.",
"alertingDraftBadge": "Draft - save to store this rule", "alertingDraftBadge": "Draft - save to store this rule",
"alertingSidebarHint": "Click a step on the canvas to edit it here.", "alertingSidebarHint": "Click a step on the canvas to edit it here.",
"alertingGraphCanvasTitle": "Rule Flow", "alertingGraphCanvasTitle": "Rule Flow",
@@ -1463,6 +1501,10 @@
"alertingNodeRoleAction": "Action", "alertingNodeRoleAction": "Action",
"alertingTabRules": "Alert Rules", "alertingTabRules": "Alert Rules",
"alertingTabHealthChecks": "Health Checks", "alertingTabHealthChecks": "Health Checks",
"alertingRulesBannerTitle": "Get Notified",
"alertingRulesBannerDescription": "Each rule ties together what to watch (a site, health check, or resource), when to fire (for example offline or unhealthy), and how to notify your team via email, webhooks, or integrations. Use this list to create, enable, and manage those rules.",
"alertingHealthChecksBannerTitle": "Monitor Health & Resources",
"alertingHealthChecksBannerDescription": "Health checks are HTTP or TCP monitors you define once. You can then use them as sources in alert rules so you get notified when a target becomes healthy or unhealthy. Health checks on resources also appear here.",
"standaloneHcTableTitle": "Health Checks", "standaloneHcTableTitle": "Health Checks",
"standaloneHcSearchPlaceholder": "Search health checks…", "standaloneHcSearchPlaceholder": "Search health checks…",
"standaloneHcAddButton": "Create Health Check", "standaloneHcAddButton": "Create Health Check",
@@ -1472,12 +1514,28 @@
"standaloneHcNameLabel": "Name", "standaloneHcNameLabel": "Name",
"standaloneHcNamePlaceholder": "My HTTP Monitor", "standaloneHcNamePlaceholder": "My HTTP Monitor",
"standaloneHcDeleteTitle": "Delete health check", "standaloneHcDeleteTitle": "Delete health check",
"standaloneHcDeleteQuestion": "Delete this health check? This cannot be undone.", "standaloneHcDeleteQuestion": "Please confirm you want to delete this health check.",
"standaloneHcDeleted": "Health check deleted", "standaloneHcDeleted": "Health check deleted",
"standaloneHcSaved": "Health check saved", "standaloneHcSaved": "Health check saved",
"standaloneHcColumnHealth": "Health", "standaloneHcColumnHealth": "Health",
"standaloneHcColumnMode": "Mode", "standaloneHcColumnMode": "Mode",
"standaloneHcColumnTarget": "Target", "standaloneHcColumnTarget": "Target",
"standaloneHcHealthStateHealthy": "Healthy",
"standaloneHcHealthStateUnhealthy": "Unhealthy",
"standaloneHcHealthStateUnknown": "Unknown",
"standaloneHcFilterAnySite": "All sites",
"standaloneHcFilterAnyResource": "All resources",
"standaloneHcFilterMode": "Mode",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Health",
"standaloneHcFilterEnabled": "Enabled",
"standaloneHcFilterEnabledOn": "Enabled",
"standaloneHcFilterEnabledOff": "Disabled",
"standaloneHcFilterSiteIdFallback": "Site {id}",
"standaloneHcFilterResourceIdFallback": "Resource {id}",
"blueprints": "Blueprints", "blueprints": "Blueprints",
"blueprintsDescription": "Apply declarative configurations and view previous runs", "blueprintsDescription": "Apply declarative configurations and view previous runs",
"blueprintAdd": "Add Blueprint", "blueprintAdd": "Add Blueprint",
@@ -1539,7 +1597,8 @@
"initialSetupDescription": "Create the intial server admin account. Only one server admin can exist. You can always change these credentials later.", "initialSetupDescription": "Create the intial server admin account. Only one server admin can exist. You can always change these credentials later.",
"createAdminAccount": "Create Admin Account", "createAdminAccount": "Create Admin Account",
"setupErrorCreateAdmin": "An error occurred while creating the server admin account.", "setupErrorCreateAdmin": "An error occurred while creating the server admin account.",
"certificateStatus": "Certificate Status", "certificateStatus": "Certificate",
"certificateStatusAutoRefreshHint": "Status refreshes automatically.",
"loading": "Loading", "loading": "Loading",
"loadingAnalytics": "Loading Analytics", "loadingAnalytics": "Loading Analytics",
"restart": "Restart", "restart": "Restart",
@@ -1608,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "View Release Notes", "pangolinUpdateAvailableReleaseNotes": "View Release Notes",
"newtUpdateAvailable": "Update Available", "newtUpdateAvailable": "Update Available",
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.", "newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.",
"pangolinNodeUpdateAvailableInfo": "A new version of Pangolin Node is available. Please update to the latest version for the best experience.",
"domainPickerEnterDomain": "Domain", "domainPickerEnterDomain": "Domain",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Enter the full domain of the resource to see available options.", "domainPickerDescription": "Enter the full domain of the resource to see available options.",
@@ -1847,6 +1907,7 @@
"configureHealthCheck": "Configure Health Check", "configureHealthCheck": "Configure Health Check",
"configureHealthCheckDescription": "Set up health monitoring for {target}", "configureHealthCheckDescription": "Set up health monitoring for {target}",
"enableHealthChecks": "Enable Health Checks", "enableHealthChecks": "Enable Health Checks",
"healthCheckDisabledStateDescription": "When disabled, the site will not perform health checks and the state will be considered unknown.",
"enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.", "enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.",
"healthScheme": "Method", "healthScheme": "Method",
"healthSelectScheme": "Select Method", "healthSelectScheme": "Select Method",
@@ -1908,6 +1969,8 @@
"httpMethod": "Scheme", "httpMethod": "Scheme",
"selectHttpMethod": "Select scheme", "selectHttpMethod": "Select scheme",
"domainPickerSubdomainLabel": "Subdomain", "domainPickerSubdomainLabel": "Subdomain",
"domainPickerWildcard": "Wildcard",
"domainPickerWildcardPaidOnly": "Wildcard subdomains are a paid feature. Please upgrade to access this feature.",
"domainPickerBaseDomainLabel": "Base Domain", "domainPickerBaseDomainLabel": "Base Domain",
"domainPickerSearchDomains": "Search domains...", "domainPickerSearchDomains": "Search domains...",
"domainPickerNoDomainsFound": "No domains found", "domainPickerNoDomainsFound": "No domains found",
@@ -1933,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "This address is part of the organization's utility subnet. It's used to resolve alias records using internal DNS resolution.", "resourcesTableAliasAddressInfo": "This address is part of the organization's utility subnet. It's used to resolve alias records using internal DNS resolution.",
"resourcesTableClients": "Clients", "resourcesTableClients": "Clients",
"resourcesTableAndOnlyAccessibleInternally": "and are only accessible internally when connected with a client.", "resourcesTableAndOnlyAccessibleInternally": "and are only accessible internally when connected with a client.",
"resourcesTableNoTargets": "No targets",
"resourcesTableHealthy": "Healthy", "resourcesTableHealthy": "Healthy",
"resourcesTableDegraded": "Degraded", "resourcesTableDegraded": "Degraded",
"resourcesTableOffline": "Offline", "resourcesTableUnhealthy": "Unhealthy",
"resourcesTableUnknown": "Unknown", "resourcesTableUnknown": "Unknown",
"resourcesTableNotMonitored": "Not monitored", "resourcesTableNotMonitored": "Not monitored",
"resourcesTableNoTargets": "No targets",
"editInternalResourceDialogEditClientResource": "Edit Private Resource", "editInternalResourceDialogEditClientResource": "Edit Private Resource",
"editInternalResourceDialogUpdateResourceProperties": "Update the resource configuration and access controls for {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Update the resource configuration and access controls for {resourceName}",
"editInternalResourceDialogResourceProperties": "Resource Properties", "editInternalResourceDialogResourceProperties": "Resource Properties",
@@ -2279,7 +2342,7 @@
"domainPickerVerified": "Verified", "domainPickerVerified": "Verified",
"domainPickerUnverified": "Unverified", "domainPickerUnverified": "Unverified",
"domainPickerManual": "Manual", "domainPickerManual": "Manual",
"domainPickerInvalidSubdomainStructure": "This subdomain contains invalid characters or structure. It will be sanitized automatically when you save.", "domainPickerInvalidSubdomainStructure": "Invalid characters will be sanitized when saved.",
"domainPickerError": "Error", "domainPickerError": "Error",
"domainPickerErrorLoadDomains": "Failed to load organization domains", "domainPickerErrorLoadDomains": "Failed to load organization domains",
"domainPickerErrorCheckAvailability": "Failed to check domain availability", "domainPickerErrorCheckAvailability": "Failed to check domain availability",
@@ -2292,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Choose your identity provider to continue", "orgAuthChooseIdpDescription": "Choose your identity provider to continue",
"orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.", "orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.",
"orgAuthSignInWithPangolin": "Sign in with Pangolin", "orgAuthSignInWithPangolin": "Sign in with Pangolin",
"orgAuthSignInToOrg": "Sign in to an organization", "orgAuthSignInToOrg": "Organization Identity Provider (SSO)",
"orgAuthSelectOrgTitle": "Organization Sign In", "orgAuthSelectOrgTitle": "Organization Sign In",
"orgAuthSelectOrgDescription": "Enter your organization ID to continue", "orgAuthSelectOrgDescription": "Enter your organization ID to continue",
"orgAuthOrgIdPlaceholder": "your-organization", "orgAuthOrgIdPlaceholder": "your-organization",
@@ -2824,6 +2887,8 @@
"editInternalResourceDialogAddClients": "Add Clients", "editInternalResourceDialogAddClients": "Add Clients",
"editInternalResourceDialogDestinationLabel": "Destination", "editInternalResourceDialogDestinationLabel": "Destination",
"editInternalResourceDialogDestinationDescription": "Choose where this resource runs and how clients reach it. Selecting multiple sites will create a high availability resource that can be accessed from any of the selected sites.", "editInternalResourceDialogDestinationDescription": "Choose where this resource runs and how clients reach it. Selecting multiple sites will create a high availability resource that can be accessed from any of the selected sites.",
"internalResourceFormMultiSiteRoutingHelp": "Selecting multiple sites enables resilient routing and failover for high availability.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Learn more",
"editInternalResourceDialogPortRestrictionsDescription": "Restrict access to specific TCP/UDP ports or allow/block all ports.", "editInternalResourceDialogPortRestrictionsDescription": "Restrict access to specific TCP/UDP ports or allow/block all ports.",
"createInternalResourceDialogHttpConfiguration": "HTTP configuration", "createInternalResourceDialogHttpConfiguration": "HTTP configuration",
"createInternalResourceDialogHttpConfigurationDescription": "Choose the domain clients will use to reach this resource over HTTP or HTTPS.", "createInternalResourceDialogHttpConfigurationDescription": "Choose the domain clients will use to reach this resource over HTTP or HTTPS.",
@@ -2869,6 +2934,7 @@
"maintenancePageTimeTitle": "Estimated Completion Time (Optional)", "maintenancePageTimeTitle": "Estimated Completion Time (Optional)",
"privateMaintenanceScreenTitle": "Private Placeholder Screen", "privateMaintenanceScreenTitle": "Private Placeholder Screen",
"privateMaintenanceScreenMessage": "This domain is being used on a private resource. Please connect using the Pangolin client to access this resource.", "privateMaintenanceScreenMessage": "This domain is being used on a private resource. Please connect using the Pangolin client to access this resource.",
"privateMaintenanceScreenSteps": "Once connected, if you are still seeing this message your browser's DNS cache may still point to the old address. To fix this: fully close and reopen this tab, or your browser, then navigate back to this page.",
"maintenanceTime": "e.g., 2 hours, Nov 1 at 5:00 PM", "maintenanceTime": "e.g., 2 hours, Nov 1 at 5:00 PM",
"maintenanceEstimatedTimeDescription": "When you expect maintenance to be completed", "maintenanceEstimatedTimeDescription": "When you expect maintenance to be completed",
"editDomain": "Edit Domain", "editDomain": "Edit Domain",
@@ -3101,5 +3167,42 @@
"idpUnassociatedDescription": "Identity provider unassociated from this organization successfully", "idpUnassociatedDescription": "Identity provider unassociated from this organization successfully",
"idpUnassociateMenu": "Unassociate", "idpUnassociateMenu": "Unassociate",
"idpDeleteAllOrgsMenu": "Delete", "idpDeleteAllOrgsMenu": "Delete",
"publicIpEndpoint": "Endpoint" "publicIpEndpoint": "Endpoint",
"lastTriggeredAt": "Last Trigger",
"reject": "Reject",
"uptimeDaysAgo": "{count} days ago",
"uptimeToday": "Today",
"uptimeNoDataAvailable": "No data available",
"uptimeSuffix": "uptime",
"uptimeDowntimeSuffix": "downtime",
"uptimeTooltipUptimeLabel": "Uptime",
"uptimeTooltipDowntimeLabel": "Downtime",
"uptimeOngoing": "ongoing",
"uptimeNoMonitoringData": "No monitoring data",
"uptimeNoData": "No data",
"uptimeMiniBarDown": "Down",
"uptimeSectionTitle": "Uptime",
"uptimeSectionDescription": "Availability over the last {days} days",
"uptimeAddAlert": "Add Alert",
"uptimeViewAlerts": "View Alerts",
"uptimeCreateEmailAlert": "Create Email Alert",
"uptimeAlertDescriptionSite": "Get notified by email when this site goes offline or comes back online.",
"uptimeAlertDescriptionResource": "Get notified by email when this resource goes offline or comes back online.",
"uptimeAlertNamePlaceholder": "Alert name",
"uptimeAdditionalEmails": "Additional Emails",
"uptimeCreateAlert": "Create Alert",
"uptimeAlertNoRecipients": "No recipients",
"uptimeAlertNoRecipientsDescription": "Please add at least one user, role, or email to notify.",
"uptimeAlertCreated": "Alert created",
"uptimeAlertCreatedDescription": "You will be notified when this changes status.",
"uptimeAlertCreateFailed": "Failed to create alert",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Key",
"webhookHeaderValuePlaceholder": "Value",
"alertLabel": "Alert",
"domainPickerWildcardSubdomainNotAllowed": "Wildcard subdomains are not allowed.",
"domainPickerWildcardCertWarning": "Wildcard resources may require additional configuration to work properly.",
"domainPickerWildcardCertWarningLink": "Learn more",
"health": "Health",
"domainPendingErrorTitle": "Verification Issue"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Contacta ventas para habilitar esta función.",
"contactSalesBookDemo": "Reservar una demostración",
"contactSalesOr": "o",
"contactSalesContactUs": "contáctenos",
"setupCreate": "Crear la organización, el sitio y los recursos", "setupCreate": "Crear la organización, el sitio y los recursos",
"headerAuthCompatibilityInfo": "Habilite esto para forzar una respuesta 401 no autorizada cuando falte un token de autenticación. Esto es necesario para navegadores o bibliotecas HTTP específicas que no envían credenciales sin un desafío del servidor.", "headerAuthCompatibilityInfo": "Habilite esto para forzar una respuesta 401 no autorizada cuando falte un token de autenticación. Esto es necesario para navegadores o bibliotecas HTTP específicas que no envían credenciales sin un desafío del servidor.",
"headerAuthCompatibility": "Compatibilidad extendida", "headerAuthCompatibility": "Compatibilidad extendida",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Se han detectado claves de licencia inválidas o caducadas. Siga los términos de licencia para seguir usando todas las características.", "componentsInvalidKey": "Se han detectado claves de licencia inválidas o caducadas. Siga los términos de licencia para seguir usando todas las características.",
"dismiss": "Descartar", "dismiss": "Descartar",
"subscriptionViolationMessage": "Estás más allá de tus límites para tu plan actual. Corrija el problema eliminando sitios, usuarios u otros recursos para permanecer dentro de tu plan.", "subscriptionViolationMessage": "Estás más allá de tus límites para tu plan actual. Corrija el problema eliminando sitios, usuarios u otros recursos para permanecer dentro de tu plan.",
"trialBannerMessage": "Su prueba expira en {countdown}. Actualice para mantener el acceso.",
"trialBannerExpired": "Su prueba ha expirado. Actualice ahora para restaurar el acceso.",
"trialActive": "Prueba gratuita activa",
"trialExpired": "Prueba expirada",
"trialHasEnded": "Su prueba ha terminado.",
"trialDaysRemaining": "{count, plural, one {# día restante} other {# días restantes}}",
"trialDaysLeftShort": "Quedan {days}d en la prueba",
"trialGoToBilling": "Ir a la página de facturación",
"subscriptionViolationViewBilling": "Ver facturación", "subscriptionViolationViewBilling": "Ver facturación",
"componentsLicenseViolation": "Violación de la Licencia: Este servidor está usando sitios {usedSites} que exceden su límite de licencias de sitios {maxSites} . Siga los términos de licencia para seguir usando todas las características.", "componentsLicenseViolation": "Violación de la Licencia: Este servidor está usando sitios {usedSites} que exceden su límite de licencias de sitios {maxSites} . Siga los términos de licencia para seguir usando todas las características.",
"componentsSupporterMessage": "¡Gracias por apoyar a Pangolin como {tier}!", "componentsSupporterMessage": "¡Gracias por apoyar a Pangolin como {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "He copiado la configuración", "siteConfirmCopy": "He copiado la configuración",
"searchSitesProgress": "Buscar sitios...", "searchSitesProgress": "Buscar sitios...",
"siteAdd": "Añadir sitio", "siteAdd": "Añadir sitio",
"sitesTableViewPublicResources": "Ver Recursos Públicos",
"sitesTableViewPrivateResources": "Ver Recursos Privados",
"siteInstallNewt": "Instalar Newt", "siteInstallNewt": "Instalar Newt",
"siteInstallNewtDescription": "Recibe Newt corriendo en tu sistema", "siteInstallNewtDescription": "Recibe Newt corriendo en tu sistema",
"WgConfiguration": "Configuración de Wirex Guard", "WgConfiguration": "Configuración de Wirex Guard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "El sitio ha sido actualizado.", "siteUpdatedDescription": "El sitio ha sido actualizado.",
"siteGeneralDescription": "Configurar la configuración general de este sitio", "siteGeneralDescription": "Configurar la configuración general de este sitio",
"siteSettingDescription": "Configurar los ajustes en el sitio", "siteSettingDescription": "Configurar los ajustes en el sitio",
"siteResourcesTab": "Recursos",
"siteResourcesNoneOnSite": "Este sitio aún no tiene recursos públicos o privados.",
"siteResourcesSectionPublic": "Recursos Públicos",
"siteResourcesSectionPrivate": "Recursos Privados",
"siteResourcesSectionPublicDescription": "Recursos expuestos externamente a través de dominios o puertos.",
"siteResourcesSectionPrivateDescription": "Recursos disponibles en tu red privada a través del sitio.",
"siteResourcesViewAllPublic": "Ver todos los recursos",
"siteResourcesViewAllPrivate": "Ver todos los recursos",
"siteResourcesDialogDescription": "Descripción general de los recursos públicos y privados asociados con este sitio.",
"siteResourcesShowMore": "Mostrar más",
"siteResourcesPermissionDenied": "No tienes permiso para listar estos recursos.",
"siteResourcesEmptyPublic": "Aún no hay recursos públicos apuntando a este sitio.",
"siteResourcesEmptyPrivate": "Aún no hay recursos privados asociados con este sitio.",
"siteResourcesHowToAccess": "Cómo acceder",
"siteResourcesTargetsOnSite": "Objetivos en este sitio",
"siteSetting": "Ajustes {siteName}", "siteSetting": "Ajustes {siteName}",
"siteNewtTunnel": "Sitio nuevo (recomendado)", "siteNewtTunnel": "Sitio nuevo (recomendado)",
"siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en cualquier red. Sin configuración extra.", "siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en cualquier red. Sin configuración extra.",
@@ -267,8 +296,11 @@
"orgMissing": "Falta el ID de la organización", "orgMissing": "Falta el ID de la organización",
"orgMissingMessage": "No se puede regenerar la invitación sin el ID de la organización.", "orgMissingMessage": "No se puede regenerar la invitación sin el ID de la organización.",
"accessUsersManage": "Administrar usuarios", "accessUsersManage": "Administrar usuarios",
"accessUserManage": "Administrar usuario",
"accessUsersDescription": "Invitar y administrar usuarios con acceso a esta organización", "accessUsersDescription": "Invitar y administrar usuarios con acceso a esta organización",
"accessUsersSearch": "Buscar usuarios...", "accessUsersSearch": "Buscar usuarios...",
"accessUsersRoleFilterCount": "{count, plural, one {# rol} other {# roles}}",
"accessUsersRoleFilterClear": "Borrar filtros de rol",
"accessUserCreate": "Crear usuario", "accessUserCreate": "Crear usuario",
"accessUserRemove": "Eliminar usuario", "accessUserRemove": "Eliminar usuario",
"username": "Usuario", "username": "Usuario",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Secreto", "newtSecretKey": "Secreto",
"newtVersion": "Versión",
"architecture": "Arquitectura", "architecture": "Arquitectura",
"sites": "Sitios", "sites": "Sitios",
"siteWgAnyClients": "Usa cualquier cliente de Wirex para conectarte. Tendrás que dirigirte a los recursos internos usando la IP de compañeros.", "siteWgAnyClients": "Usa cualquier cliente de Wirex para conectarte. Tendrás que dirigirte a los recursos internos usando la IP de compañeros.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Ver registros", "actionViewLogs": "Ver registros",
"noneSelected": "Ninguno seleccionado", "noneSelected": "Ninguno seleccionado",
"orgNotFound2": "No se encontraron organizaciones.", "orgNotFound2": "No se encontraron organizaciones.",
"search": "Buscar…",
"searchPlaceholder": "Buscar...", "searchPlaceholder": "Buscar...",
"emptySearchOptions": "No se encontraron opciones", "emptySearchOptions": "No se encontraron opciones",
"create": "Crear", "create": "Crear",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Gestionar", "sidebarGeneral": "Gestionar",
"sidebarLogAndAnalytics": "Registro y análisis", "sidebarLogAndAnalytics": "Registro y análisis",
"sidebarBluePrints": "Planos", "sidebarBluePrints": "Planos",
"sidebarAlerting": "Alertas",
"sidebarHealthChecks": "Chequeos de salud",
"sidebarOrganization": "Organización", "sidebarOrganization": "Organización",
"sidebarManagement": "Gestión", "sidebarManagement": "Gestión",
"sidebarBillingAndLicenses": "Facturación y licencias", "sidebarBillingAndLicenses": "Facturación y licencias",
"sidebarLogsAnalytics": "Analíticas", "sidebarLogsAnalytics": "Analíticas",
"alertingTitle": "Alertas",
"alertingDescription": "Definir fuentes, disparadores y acciones para notificaciones",
"alertingRules": "Reglas de alerta",
"alertingSearchRules": "Buscar reglas…",
"alertingAddRule": "Crear regla",
"alertingColumnSource": "Fuente",
"alertingColumnTrigger": "Disparador",
"alertingColumnActions": "Acciones",
"alertingColumnEnabled": "Activado",
"alertingDeleteQuestion": "Por favor, confirme que desea eliminar esta regla de alerta.",
"alertingDeleteRule": "Eliminar regla de alerta",
"alertingRuleDeleted": "Regla de alerta eliminada",
"alertingRuleSaved": "Regla de alerta guardada",
"alertingRuleSavedCreatedDescription": "Tu nueva regla de alerta fue creada. Puedes seguir editándola en esta página.",
"alertingRuleSavedUpdatedDescription": "Tus cambios a esta regla de alerta fueron guardados.",
"alertingEditRule": "Editar regla de alerta",
"alertingCreateRule": "Crear regla de alerta",
"alertingRuleCredenzaDescription": "Elija qué observar, cuándo disparar y cómo notificar",
"alertingRuleNamePlaceholder": "Sitio de producción caído",
"alertingRuleEnabled": "Regla habilitada",
"alertingSectionSource": "Fuente",
"alertingSourceType": "Tipo de fuente",
"alertingSourceSite": "Sitio",
"alertingSourceHealthCheck": "Chequeo de salud",
"alertingPickSites": "Sitios",
"alertingPickHealthChecks": "Chequeos de salud",
"alertingPickResources": "Recursos",
"alertingAllSites": "Todos los sitios",
"alertingAllSitesDescription": "Las alertas se activan para cualquier sitio",
"alertingSpecificSites": "Sitios específicos",
"alertingSpecificSitesDescription": "Escoja sitios específicos para observar",
"alertingAllHealthChecks": "Todos los chequeos de salud",
"alertingAllHealthChecksDescription": "Las alertas se activan para cualquier chequeo de salud",
"alertingSpecificHealthChecks": "Chequeos de salud específicos",
"alertingSpecificHealthChecksDescription": "Elija chequeos de salud específicos para observar",
"alertingAllResources": "Todos los recursos",
"alertingAllResourcesDescription": "Las alertas se activan para cualquier recurso",
"alertingSpecificResources": "Recursos específicos",
"alertingSpecificResourcesDescription": "Elija recursos específicos para observar",
"alertingSelectResources": "Seleccionar recursos…",
"alertingResourcesSelected": "{count} recursos seleccionados",
"alertingResourcesEmpty": "No hay recursos con objetivos en los primeros 10 resultados.",
"alertingSectionTrigger": "Disparador",
"alertingTrigger": "Cuándo alertar",
"alertingTriggerSiteOnline": "Sitio en línea",
"alertingTriggerSiteOffline": "Sitio fuera de línea",
"alertingTriggerSiteToggle": "El estado del sitio cambia",
"alertingTriggerHcHealthy": "Chequeo de salud saludable",
"alertingTriggerHcUnhealthy": "Chequeo de salud no saludable",
"alertingTriggerHcToggle": "El estado del chequeo de salud cambia",
"alertingTriggerResourceHealthy": "Recurso saludable",
"alertingTriggerResourceUnhealthy": "Recurso no saludable",
"alertingTriggerResourceDegraded": "Recurso degradado",
"alertingSearchHealthChecks": "Buscar chequeos de salud…",
"alertingHealthChecksEmpty": "No hay chequeos de salud disponibles.",
"alertingTriggerResourceToggle": "El estado del recurso cambia",
"alertingSourceResource": "Recurso",
"alertingSectionActions": "Acciones",
"alertingAddAction": "Añadir acción",
"alertingActionNotify": "E-mail",
"alertingActionNotifyDescription": "Enviar notificaciones por correo electrónico a usuarios o roles",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Enviar una solicitud HTTP a un punto final personalizado",
"alertingExternalIntegration": "Integración externa",
"alertingExternalPagerDutyDescription": "Enviar alertas a PagerDuty para gestión de incidentes",
"alertingExternalOpsgenieDescription": "Dirigir alertas a Opsgenie para gestión de llamadas",
"alertingExternalServiceNowDescription": "Crear incidentes de ServiceNow a partir de eventos de alerta",
"alertingExternalIncidentIoDescription": "Activar flujos de trabajo de Incident.io a partir de eventos de alerta",
"alertingActionType": "Tipo de acción",
"alertingNotifyUsers": "Usuarios",
"alertingNotifyRoles": "Roles",
"alertingNotifyEmails": "Direcciones de correo electrónico",
"alertingEmailPlaceholder": "Añadir email y presionar Enter",
"alertingWebhookMethod": "Método HTTP",
"alertingWebhookSecret": "Firma secreta (opcional)",
"alertingWebhookSecretPlaceholder": "Secreto HMAC",
"alertingWebhookHeaders": "Encabezados",
"alertingAddHeader": "Añadir encabezado",
"alertingSelectSites": "Seleccionar sitios…",
"alertingSitesSelected": "{count} sitios seleccionados",
"alertingSelectHealthChecks": "Seleccionar chequeos de salud…",
"alertingHealthChecksSelected": "{count} chequeos de salud seleccionados",
"alertingNoHealthChecks": "No hay objetivos con chequeos de salud habilitados",
"alertingHealthCheckStub": "La selección de chequeo de salud no está conectada aún - todavía puede configurar disparadores y acciones.",
"alertingSelectUsers": "Seleccionar usuarios…",
"alertingUsersSelected": "{count} usuarios seleccionados",
"alertingSelectRoles": "Seleccionar roles…",
"alertingRolesSelected": "{count} roles seleccionados",
"alertingSummarySites": "Sitios ({count})",
"alertingSummaryAllSites": "Todos los sitios",
"alertingSummaryHealthChecks": "Chequeos de salud ({count})",
"alertingSummaryAllHealthChecks": "Todos los chequeos de salud",
"alertingSummaryResources": "Recursos ({count})",
"alertingSummaryAllResources": "Todos los recursos",
"alertingErrorNameRequired": "Introduce un nombre",
"alertingErrorActionsMin": "Añada al menos una acción",
"alertingErrorPickSites": "Seleccione al menos un sitio",
"alertingErrorPickHealthChecks": "Seleccione al menos un chequeo de salud",
"alertingErrorPickResources": "Seleccione al menos un recurso",
"alertingErrorTriggerSite": "Elija un disparador de sitio",
"alertingErrorTriggerHealth": "Elija un disparador de chequeo de salud",
"alertingErrorTriggerResource": "Elija un disparador de recurso",
"alertingErrorNotifyRecipients": "Elija usuarios, roles o al menos un correo electrónico",
"alertingConfigureSource": "Configurar fuente",
"alertingConfigureTrigger": "Configurar disparador",
"alertingConfigureActions": "Configurar acciones",
"alertingBackToRules": "Volver a las reglas",
"alertingRuleCooldown": "Tiempo de espera (segundos)",
"alertingRuleCooldownDescription": "Tiempo mínimo entre alertas repetidas para la misma regla. Establezca en 0 para disparar cada vez.",
"alertingDraftBadge": "Borrador - guardarlo para almacenar esta regla",
"alertingSidebarHint": "Haga clic en un paso en el lienzo para editarlo aquí.",
"alertingGraphCanvasTitle": "Flujo de regla",
"alertingGraphCanvasDescription": "Visión general visual de fuente, disparador y acciones. Selecciona un nodo para editarlo en el panel.",
"alertingNodeNotConfigured": "Aún no configurado",
"alertingNodeActionsCount": "{count, plural, one {# acción} other {# acciones}}",
"alertingNodeRoleSource": "Fuente",
"alertingNodeRoleTrigger": "Disparador",
"alertingNodeRoleAction": "Acción",
"alertingTabRules": "Reglas de Alerta",
"alertingTabHealthChecks": "Chequeos de salud",
"alertingRulesBannerTitle": "Obtenga notificaciones",
"alertingRulesBannerDescription": "Cada regla vincula lo que se debe observar (un sitio, chequeo de salud o recurso), cuándo disparar (por ejemplo, fuera de línea o no saludable), y cómo notificar a su equipo vía email, webhooks o integraciones. Use esta lista para crear, habilitar y administrar esas reglas.",
"alertingHealthChecksBannerTitle": "Monitorear Salud y Recursos",
"alertingHealthChecksBannerDescription": "Los chequeos de salud son monitores HTTP o TCP que define una vez. Luego puede usarlos como fuentes en reglas de alerta para que se le notifique cuando un objetivo se vuelva saludable o no saludable. Los chequeos de salud en recursos también aparecen aquí.",
"standaloneHcTableTitle": "Chequeos de salud",
"standaloneHcSearchPlaceholder": "Buscar chequeos de salud…",
"standaloneHcAddButton": "Crear chequeo de salud",
"standaloneHcCreateTitle": "Crear chequeo de salud",
"standaloneHcEditTitle": "Editar chequeo de salud",
"standaloneHcDescription": "Configurar un chequeo de salud HTTP o TCP para usar en reglas de alerta.",
"standaloneHcNameLabel": "Nombre",
"standaloneHcNamePlaceholder": "Mi monitor HTTP",
"standaloneHcDeleteTitle": "Eliminar chequeo de salud",
"standaloneHcDeleteQuestion": "Por favor, confirme que desea eliminar este chequeo de salud.",
"standaloneHcDeleted": "Chequeo de salud eliminado",
"standaloneHcSaved": "Chequeo de salud guardado",
"standaloneHcColumnHealth": "Salud",
"standaloneHcColumnMode": "Modo",
"standaloneHcColumnTarget": "Destino",
"standaloneHcHealthStateHealthy": "Saludable",
"standaloneHcHealthStateUnhealthy": "No saludable",
"standaloneHcHealthStateUnknown": "Desconocido",
"standaloneHcFilterAnySite": "Todos los sitios",
"standaloneHcFilterAnyResource": "Todos los recursos",
"standaloneHcFilterMode": "Modo",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Salud",
"standaloneHcFilterEnabled": "Activado",
"standaloneHcFilterEnabledOn": "Activado",
"standaloneHcFilterEnabledOff": "Deshabilitado",
"standaloneHcFilterSiteIdFallback": "Sitio {id}",
"standaloneHcFilterResourceIdFallback": "Recurso {id}",
"blueprints": "Planos", "blueprints": "Planos",
"blueprintsDescription": "Aplicar configuraciones declarativas y ver ejecuciones anteriores", "blueprintsDescription": "Aplicar configuraciones declarativas y ver ejecuciones anteriores",
"blueprintAdd": "Añadir plano", "blueprintAdd": "Añadir plano",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Cree la cuenta de administrador del servidor inicial. Solo puede existir un administrador del servidor. Siempre puede cambiar estas credenciales más tarde.", "initialSetupDescription": "Cree la cuenta de administrador del servidor inicial. Solo puede existir un administrador del servidor. Siempre puede cambiar estas credenciales más tarde.",
"createAdminAccount": "Crear cuenta de administrador", "createAdminAccount": "Crear cuenta de administrador",
"setupErrorCreateAdmin": "Se produjo un error al crear la cuenta de administrador del servidor.", "setupErrorCreateAdmin": "Se produjo un error al crear la cuenta de administrador del servidor.",
"certificateStatus": "Estado del certificado", "certificateStatus": "Certificado",
"certificateStatusAutoRefreshHint": "El estado se actualiza automáticamente.",
"loading": "Cargando", "loading": "Cargando",
"loadingAnalytics": "Cargando analíticas", "loadingAnalytics": "Cargando analíticas",
"restart": "Reiniciar", "restart": "Reiniciar",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Ver notas de lanzamiento", "pangolinUpdateAvailableReleaseNotes": "Ver notas de lanzamiento",
"newtUpdateAvailable": "Nueva actualización disponible", "newtUpdateAvailable": "Nueva actualización disponible",
"newtUpdateAvailableInfo": "Hay una nueva versión de Newt disponible. Actualice a la última versión para la mejor experiencia.", "newtUpdateAvailableInfo": "Hay una nueva versión de Newt disponible. Actualice a la última versión para la mejor experiencia.",
"pangolinNodeUpdateAvailableInfo": "Hay una nueva versión de Pangolin Node disponible. Actualice a la última versión para la mejor experiencia.",
"domainPickerEnterDomain": "Dominio", "domainPickerEnterDomain": "Dominio",
"domainPickerPlaceholder": "miapp.ejemplo.com", "domainPickerPlaceholder": "miapp.ejemplo.com",
"domainPickerDescription": "Ingresa el dominio completo del recurso para ver las opciones disponibles.", "domainPickerDescription": "Ingresa el dominio completo del recurso para ver las opciones disponibles.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Configurar Chequeo de Salud", "configureHealthCheck": "Configurar Chequeo de Salud",
"configureHealthCheckDescription": "Configura la monitorización de salud para {target}", "configureHealthCheckDescription": "Configura la monitorización de salud para {target}",
"enableHealthChecks": "Activar Chequeos de Salud", "enableHealthChecks": "Activar Chequeos de Salud",
"healthCheckDisabledStateDescription": "Cuando está deshabilitado, el sitio no realizará comprobaciones de salud y el estado se considerará desconocido.",
"enableHealthChecksDescription": "Controlar la salud de este objetivo. Puedes supervisar un punto final diferente al objetivo si es necesario.", "enableHealthChecksDescription": "Controlar la salud de este objetivo. Puedes supervisar un punto final diferente al objetivo si es necesario.",
"healthScheme": "Método", "healthScheme": "Método",
"healthSelectScheme": "Seleccionar método", "healthSelectScheme": "Seleccionar método",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "El intervalo de comprobación debe ser de al menos 5 segundos", "healthCheckIntervalMin": "El intervalo de comprobación debe ser de al menos 5 segundos",
"healthCheckTimeoutMin": "El tiempo de espera debe ser de al menos 1 segundo", "healthCheckTimeoutMin": "El tiempo de espera debe ser de al menos 1 segundo",
"healthCheckRetryMin": "Los intentos de reintento deben ser de al menos 1", "healthCheckRetryMin": "Los intentos de reintento deben ser de al menos 1",
"healthCheckMode": "Modo de chequeo",
"healthCheckStrategy": "Estrategia",
"healthCheckModeDescription": "El modo TCP verifica solo la conectividad. El modo HTTP valida la respuesta HTTP.",
"healthyThreshold": "Umbral Saludable",
"healthyThresholdDescription": "Éxitos consecutivos requeridos antes de marcar como saludable.",
"unhealthyThreshold": "Umbral No Saludable",
"unhealthyThresholdDescription": "Fallos consecutivos requeridos antes de marcar como no saludable.",
"healthCheckHealthyThresholdMin": "El umbral saludable debe ser al menos 1",
"healthCheckUnhealthyThresholdMin": "El umbral no saludable debe ser al menos 1",
"httpMethod": "Método HTTP", "httpMethod": "Método HTTP",
"selectHttpMethod": "Seleccionar método HTTP", "selectHttpMethod": "Seleccionar método HTTP",
"domainPickerSubdomainLabel": "Subdominio", "domainPickerSubdomainLabel": "Subdominio",
"domainPickerWildcard": "Comodín",
"domainPickerWildcardPaidOnly": "Los subdominios comodín son una característica paga. Por favor, mejora tu plan para acceder a esta característica.",
"domainPickerBaseDomainLabel": "Dominio base", "domainPickerBaseDomainLabel": "Dominio base",
"domainPickerSearchDomains": "Buscar dominios...", "domainPickerSearchDomains": "Buscar dominios...",
"domainPickerNoDomainsFound": "No se encontraron dominios", "domainPickerNoDomainsFound": "No se encontraron dominios",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Esta dirección es parte de la subred de utilidad de la organización. Se utiliza para resolver registros de alias usando resolución DNS interna.", "resourcesTableAliasAddressInfo": "Esta dirección es parte de la subred de utilidad de la organización. Se utiliza para resolver registros de alias usando resolución DNS interna.",
"resourcesTableClients": "Clientes", "resourcesTableClients": "Clientes",
"resourcesTableAndOnlyAccessibleInternally": "y solo son accesibles internamente cuando se conectan con un cliente.", "resourcesTableAndOnlyAccessibleInternally": "y solo son accesibles internamente cuando se conectan con un cliente.",
"resourcesTableNoTargets": "Sin objetivos",
"resourcesTableHealthy": "Saludable", "resourcesTableHealthy": "Saludable",
"resourcesTableDegraded": "Degrado", "resourcesTableDegraded": "Degrado",
"resourcesTableOffline": "Desconectado", "resourcesTableUnhealthy": "No saludable",
"resourcesTableUnknown": "Desconocido", "resourcesTableUnknown": "Desconocido",
"resourcesTableNotMonitored": "No supervisado", "resourcesTableNotMonitored": "No supervisado",
"resourcesTableNoTargets": "Sin objetivos",
"editInternalResourceDialogEditClientResource": "Editar recurso privado", "editInternalResourceDialogEditClientResource": "Editar recurso privado",
"editInternalResourceDialogUpdateResourceProperties": "Actualizar la configuración del recurso y los controles de acceso para {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Actualizar la configuración del recurso y los controles de acceso para {resourceName}",
"editInternalResourceDialogResourceProperties": "Propiedades del recurso", "editInternalResourceDialogResourceProperties": "Propiedades del recurso",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Puerto", "editInternalResourceDialogModePort": "Puerto",
"editInternalResourceDialogModeHost": "Anfitrión", "editInternalResourceDialogModeHost": "Anfitrión",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Esquema",
"editInternalResourceDialogEnableSsl": "Activar SSL",
"editInternalResourceDialogEnableSslDescription": "Habilitar cifrado SSL/TLS para conexiones HTTPS seguras al destino.",
"editInternalResourceDialogDestination": "Destino", "editInternalResourceDialogDestination": "Destino",
"editInternalResourceDialogDestinationHostDescription": "La dirección IP o nombre de host del recurso en la red del sitio.", "editInternalResourceDialogDestinationHostDescription": "La dirección IP o nombre de host del recurso en la red del sitio.",
"editInternalResourceDialogDestinationIPDescription": "La dirección IP o nombre de host del recurso en la red del sitio.", "editInternalResourceDialogDestinationIPDescription": "La dirección IP o nombre de host del recurso en la red del sitio.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Nombre", "createInternalResourceDialogName": "Nombre",
"createInternalResourceDialogSite": "Sitio", "createInternalResourceDialogSite": "Sitio",
"selectSite": "Seleccionar sitio...", "selectSite": "Seleccionar sitio...",
"multiSitesSelectorSitesCount": "{count, plural, one {# sitio} other {# sitios}}",
"noSitesFound": "Sitios no encontrados.", "noSitesFound": "Sitios no encontrados.",
"createInternalResourceDialogProtocol": "Protocolo", "createInternalResourceDialogProtocol": "Protocolo",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Puerto", "createInternalResourceDialogModePort": "Puerto",
"createInternalResourceDialogModeHost": "Anfitrión", "createInternalResourceDialogModeHost": "Anfitrión",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Esquema",
"createInternalResourceDialogScheme": "Esquema",
"createInternalResourceDialogEnableSsl": "Activar SSL",
"createInternalResourceDialogEnableSslDescription": "Habilitar cifrado SSL/TLS para conexiones HTTPS seguras al destino.",
"createInternalResourceDialogDestination": "Destino", "createInternalResourceDialogDestination": "Destino",
"createInternalResourceDialogDestinationHostDescription": "La dirección IP o nombre de host del recurso en la red del sitio.", "createInternalResourceDialogDestinationHostDescription": "La dirección IP o nombre de host del recurso en la red del sitio.",
"createInternalResourceDialogDestinationCidrDescription": "El rango CIDR del recurso en la red del sitio.", "createInternalResourceDialogDestinationCidrDescription": "El rango CIDR del recurso en la red del sitio.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Un alias DNS interno opcional para este recurso.", "createInternalResourceDialogAliasDescription": "Un alias DNS interno opcional para este recurso.",
"internalResourceDownstreamSchemeRequired": "Se requiere el método para recursos HTTP",
"internalResourceHttpPortRequired": "Se requiere el puerto de destino para recursos HTTP",
"siteConfiguration": "Configuración", "siteConfiguration": "Configuración",
"siteAcceptClientConnections": "Aceptar conexiones de clientes", "siteAcceptClientConnections": "Aceptar conexiones de clientes",
"siteAcceptClientConnectionsDescription": "Permitir a los dispositivos de usuario y clientes acceder a los recursos de este sitio. Esto se puede cambiar más tarde.", "siteAcceptClientConnectionsDescription": "Permitir a los dispositivos de usuario y clientes acceder a los recursos de este sitio. Esto se puede cambiar más tarde.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Verificado", "domainPickerVerified": "Verificado",
"domainPickerUnverified": "Sin verificar", "domainPickerUnverified": "Sin verificar",
"domainPickerManual": "Manual", "domainPickerManual": "Manual",
"domainPickerInvalidSubdomainStructure": "Este subdominio contiene caracteres o estructura no válidos. Se limpiará automáticamente al guardar.", "domainPickerInvalidSubdomainStructure": "Los caracteres inválidos serán saneados al guardar.",
"domainPickerError": "Error", "domainPickerError": "Error",
"domainPickerErrorLoadDomains": "Error al cargar los dominios de la organización", "domainPickerErrorLoadDomains": "Error al cargar los dominios de la organización",
"domainPickerErrorCheckAvailability": "No se pudo comprobar la disponibilidad del dominio", "domainPickerErrorCheckAvailability": "No se pudo comprobar la disponibilidad del dominio",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Elige tu proveedor de identidad para continuar", "orgAuthChooseIdpDescription": "Elige tu proveedor de identidad para continuar",
"orgAuthNoIdpConfigured": "Esta organización no tiene ningún proveedor de identidad configurado. En su lugar puedes iniciar sesión con tu identidad de Pangolin.", "orgAuthNoIdpConfigured": "Esta organización no tiene ningún proveedor de identidad configurado. En su lugar puedes iniciar sesión con tu identidad de Pangolin.",
"orgAuthSignInWithPangolin": "Iniciar sesión con Pangolin", "orgAuthSignInWithPangolin": "Iniciar sesión con Pangolin",
"orgAuthSignInToOrg": "Iniciar sesión en una organización", "orgAuthSignInToOrg": "Proveedor de identidad de la organización (SSO)",
"orgAuthSelectOrgTitle": "Inicio de sesión de organización", "orgAuthSelectOrgTitle": "Inicio de sesión de organización",
"orgAuthSelectOrgDescription": "Ingrese el ID de su organización para continuar", "orgAuthSelectOrgDescription": "Ingrese el ID de su organización para continuar",
"orgAuthOrgIdPlaceholder": "tu-organización", "orgAuthOrgIdPlaceholder": "tu-organización",
@@ -2429,6 +2648,7 @@
"validPassword": "Contraseña válida", "validPassword": "Contraseña válida",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Cliente conectado",
"resourceBlocked": "Recurso bloqueado", "resourceBlocked": "Recurso bloqueado",
"droppedByRule": "Soltado por regla", "droppedByRule": "Soltado por regla",
"noSessions": "No hay sesiones", "noSessions": "No hay sesiones",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Agregar clientes", "editInternalResourceDialogAddClients": "Agregar clientes",
"editInternalResourceDialogDestinationLabel": "Destino", "editInternalResourceDialogDestinationLabel": "Destino",
"editInternalResourceDialogDestinationDescription": "Especifique la dirección de destino para el recurso interno. Puede ser un nombre de host, dirección IP o rango CIDR dependiendo del modo seleccionado. Opcionalmente establezca un alias DNS interno para una identificación más fácil.", "editInternalResourceDialogDestinationDescription": "Especifique la dirección de destino para el recurso interno. Puede ser un nombre de host, dirección IP o rango CIDR dependiendo del modo seleccionado. Opcionalmente establezca un alias DNS interno para una identificación más fácil.",
"internalResourceFormMultiSiteRoutingHelp": "Seleccionar múltiples sitios habilita el enrutamiento resistente y la conmutación por error para alta disponibilidad.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Más información",
"editInternalResourceDialogPortRestrictionsDescription": "Restringir el acceso a puertos TCP/UDP específicos o permitir/bloquear todos los puertos.", "editInternalResourceDialogPortRestrictionsDescription": "Restringir el acceso a puertos TCP/UDP específicos o permitir/bloquear todos los puertos.",
"createInternalResourceDialogHttpConfiguration": "Configuración HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Elija el dominio que los clientes usarán para alcanzar este recurso a través de HTTP o HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Configuración HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Elija el dominio que los clientes usarán para alcanzar este recurso a través de HTTP o HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "¡Volveremos pronto! Nuestro sitio está actualmente en mantenimiento programado.", "maintenancePageMessagePlaceholder": "¡Volveremos pronto! Nuestro sitio está actualmente en mantenimiento programado.",
"maintenancePageMessageDescription": "Mensaje detallado explicando el mantenimiento", "maintenancePageMessageDescription": "Mensaje detallado explicando el mantenimiento",
"maintenancePageTimeTitle": "Tiempo estimado de finalización (Opcional)", "maintenancePageTimeTitle": "Tiempo estimado de finalización (Opcional)",
"privateMaintenanceScreenTitle": "Pantalla de marcador de posición privada",
"privateMaintenanceScreenMessage": "Este dominio se está utilizando en un recurso privado. Conéctese usando el cliente Pangolin para acceder a este recurso.",
"privateMaintenanceScreenSteps": "Una vez conectado, si sigues viendo este mensaje, la caché de DNS de tu navegador puede seguir apuntando a la dirección antigua. Para solucionarlo: cierra por completo y vuelve a abrir esta pestaña o tu navegador, luego regresa a esta página.",
"maintenanceTime": "Ej., 2 horas, 1 de noviembre a las 5:00 PM", "maintenanceTime": "Ej., 2 horas, 1 de noviembre a las 5:00 PM",
"maintenanceEstimatedTimeDescription": "Cuando espera que el mantenimiento esté terminado", "maintenanceEstimatedTimeDescription": "Cuando espera que el mantenimiento esté terminado",
"editDomain": "Editar dominio", "editDomain": "Editar dominio",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Añadir destino HTTP", "httpDestAddTitle": "Añadir destino HTTP",
"httpDestEditDescription": "Actualizar la configuración para este destino de transmisión de eventos 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.", "httpDestAddDescription": "Configure un nuevo extremo HTTP para recibir los eventos de su organización.",
"S3DestEditTitle": "Editar destino",
"S3DestAddTitle": "Añadir destino S3",
"S3DestEditDescription": "Actualice la configuración para este destino de transmisión de eventos S3.",
"S3DestAddDescription": "Configure un nuevo punto final S3 para recibir los eventos de su organización.",
"datadogDestEditTitle": "Editar destino",
"datadogDestAddTitle": "Añadir destino Datadog",
"datadogDestEditDescription": "Actualice la configuración para este destino de transmisión de eventos Datadog.",
"datadogDestAddDescription": "Configure un nuevo punto final de Datadog para recibir los eventos de su organización.",
"httpDestTabSettings": "Ajustes", "httpDestTabSettings": "Ajustes",
"httpDestTabHeaders": "Encabezados", "httpDestTabHeaders": "Encabezados",
"httpDestTabBody": "Cuerpo", "httpDestTabBody": "Cuerpo",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Destino creado correctamente", "httpDestCreatedSuccess": "Destino creado correctamente",
"httpDestUpdateFailed": "Error al actualizar destino", "httpDestUpdateFailed": "Error al actualizar destino",
"httpDestCreateFailed": "Error al crear el destino", "httpDestCreateFailed": "Error al crear el destino",
"followRedirects": "Seguir redirecciones",
"followRedirectsDescription": "Seguir automáticamente las redirecciones HTTP para solicitudes.",
"alertingErrorWebhookUrl": "Por favor, introduzca una URL válida para el webhook.",
"healthCheckStrategyHttp": "Valida la conectividad y verifica el estado de respuesta HTTP.",
"healthCheckStrategyTcp": "Verifica la conectividad TCP solamente, sin inspeccionar la respuesta.",
"healthCheckStrategySnmp": "Realiza una solicitud SNMP get para verificar la salud de dispositivos y la infraestructura de red.",
"healthCheckStrategyIcmp": "Usa solicitudes de eco ICMP (pings) para verificar si un recurso es alcanzable y receptivo.",
"healthCheckTabStrategy": "Estrategia",
"healthCheckTabConnection": "Conexión",
"healthCheckTabAdvanced": "Avanzado",
"healthCheckStrategyNotAvailable": "Esta estrategia no está disponible. Contacte ventas para habilitar esta funcionalidad.",
"uptime30d": "Tiempo de actividad (30d)",
"idpAddActionCreateNew": "Crear nuevo proveedor de identidad", "idpAddActionCreateNew": "Crear nuevo proveedor de identidad",
"idpAddActionImportFromOrg": "Importar de otra organización", "idpAddActionImportFromOrg": "Importar de otra organización",
"idpImportDialogTitle": "Importar Proveedor de Identidad", "idpImportDialogTitle": "Importar Proveedor de Identidad",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Esto no se puede deshacer para esta organización.", "idpUnassociateWarning": "Esto no se puede deshacer para esta organización.",
"idpUnassociatedDescription": "Proveedor de identidad desasociado de esta organización con éxito", "idpUnassociatedDescription": "Proveedor de identidad desasociado de esta organización con éxito",
"idpUnassociateMenu": "Desasociar", "idpUnassociateMenu": "Desasociar",
"idpDeleteAllOrgsMenu": "Eliminar" "idpDeleteAllOrgsMenu": "Eliminar",
"publicIpEndpoint": "Punto final",
"lastTriggeredAt": "Último disparo",
"reject": "Rechazar",
"uptimeDaysAgo": "Hace {count} días",
"uptimeToday": "Hoy",
"uptimeNoDataAvailable": "No hay datos disponibles",
"uptimeSuffix": "disponibilidad",
"uptimeDowntimeSuffix": "tiempo de inactividad",
"uptimeTooltipUptimeLabel": "Disponibilidad",
"uptimeTooltipDowntimeLabel": "Tiempo de inactividad",
"uptimeOngoing": "en curso",
"uptimeNoMonitoringData": "No hay datos de monitoreo",
"uptimeNoData": "Sin datos",
"uptimeMiniBarDown": "Caído",
"uptimeSectionTitle": "Disponibilidad",
"uptimeSectionDescription": "Disponibilidad durante los últimos {days} días",
"uptimeAddAlert": "Agregar alerta",
"uptimeViewAlerts": "Ver alertas",
"uptimeCreateEmailAlert": "Crear alerta de correo electrónico",
"uptimeAlertDescriptionSite": "Recibe notificaciones por correo electrónico cuando este sitio esté fuera de línea o vuelva en línea.",
"uptimeAlertDescriptionResource": "Recibe notificaciones por correo electrónico cuando este recurso esté fuera de línea o vuelva en línea.",
"uptimeAlertNamePlaceholder": "Nombre de la alerta",
"uptimeAdditionalEmails": "Emails adicionales",
"uptimeCreateAlert": "Crear alerta",
"uptimeAlertNoRecipients": "Sin destinatarios",
"uptimeAlertNoRecipientsDescription": "Por favor, agrega al menos un usuario, rol o correo electrónico para notificación.",
"uptimeAlertCreated": "Alerta creada",
"uptimeAlertCreatedDescription": "Serás notificado cuando cambie de estado.",
"uptimeAlertCreateFailed": "Error al crear la alerta",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Clave",
"webhookHeaderValuePlaceholder": "Valor",
"alertLabel": "Alerta",
"domainPickerWildcardSubdomainNotAllowed": "No se permiten subdominios comodín.",
"domainPickerWildcardCertWarning": "Los recursos comodín pueden requerir configuración adicional para funcionar correctamente.",
"domainPickerWildcardCertWarningLink": "Más información",
"health": "Salud",
"domainPendingErrorTitle": "Problema de verificación"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Contactez le service commercial pour activer cette fonctionnalité.",
"contactSalesBookDemo": "Réserver une démo",
"contactSalesOr": "ou",
"contactSalesContactUs": "contactez-nous",
"setupCreate": "Créer l'organisation, le site et les ressources", "setupCreate": "Créer l'organisation, le site et les ressources",
"headerAuthCompatibilityInfo": "Activez ceci pour forcer une réponse 401 Unauthorized lorsque le jeton d'authentification est manquant. Cela est nécessaire pour les navigateurs ou les bibliothèques HTTP spécifiques qui n'envoient pas de credentials sans un challenge du serveur.", "headerAuthCompatibilityInfo": "Activez ceci pour forcer une réponse 401 Unauthorized lorsque le jeton d'authentification est manquant. Cela est nécessaire pour les navigateurs ou les bibliothèques HTTP spécifiques qui n'envoient pas de credentials sans un challenge du serveur.",
"headerAuthCompatibility": "Compatibilité étendue", "headerAuthCompatibility": "Compatibilité étendue",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Veuillez respecter les conditions de licence pour continuer à utiliser toutes les fonctionnalités.", "componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Veuillez respecter les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
"dismiss": "Rejeter", "dismiss": "Rejeter",
"subscriptionViolationMessage": "Vous dépassez vos limites pour votre forfait actuel. Corrigez le problème en supprimant des sites, des utilisateurs ou d'autres ressources pour rester dans votre forfait.", "subscriptionViolationMessage": "Vous dépassez vos limites pour votre forfait actuel. Corrigez le problème en supprimant des sites, des utilisateurs ou d'autres ressources pour rester dans votre forfait.",
"trialBannerMessage": "Votre essai expire dans {countdown}. Passez à l'abonnement pour garder l'accès.",
"trialBannerExpired": "Votre essai a expiré. Passez à l'abonnement maintenant pour restaurer l'accès.",
"trialActive": "Essai gratuit actif",
"trialExpired": "Essai expiré",
"trialHasEnded": "Votre essai est terminé.",
"trialDaysRemaining": "{count, plural, one {# jour restant} other {# jours restants}}",
"trialDaysLeftShort": "{days}j restants dans l'essai",
"trialGoToBilling": "Aller à la page de facturation",
"subscriptionViolationViewBilling": "Voir la facturation", "subscriptionViolationViewBilling": "Voir la facturation",
"componentsLicenseViolation": "Violation de licence : ce serveur utilise {usedSites} nœuds, ce qui dépasse la limite autorisée de {maxSites} nœuds. Respectez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.", "componentsLicenseViolation": "Violation de licence : ce serveur utilise {usedSites} nœuds, ce qui dépasse la limite autorisée de {maxSites} nœuds. Respectez les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
"componentsSupporterMessage": "Merci de soutenir Pangolin en tant que {tier}!", "componentsSupporterMessage": "Merci de soutenir Pangolin en tant que {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "J'ai copié la configuration", "siteConfirmCopy": "J'ai copié la configuration",
"searchSitesProgress": "Rechercher des nœuds...", "searchSitesProgress": "Rechercher des nœuds...",
"siteAdd": "Ajouter un nœud", "siteAdd": "Ajouter un nœud",
"sitesTableViewPublicResources": "Voir les ressources publiques",
"sitesTableViewPrivateResources": "Voir les ressources privées",
"siteInstallNewt": "Installer Newt", "siteInstallNewt": "Installer Newt",
"siteInstallNewtDescription": "Faites fonctionner Newt sur votre système", "siteInstallNewtDescription": "Faites fonctionner Newt sur votre système",
"WgConfiguration": "Configuration WireGuard", "WgConfiguration": "Configuration WireGuard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Le nœud a été mis à jour.", "siteUpdatedDescription": "Le nœud a été mis à jour.",
"siteGeneralDescription": "Configurer les paramètres par défaut de ce nœud", "siteGeneralDescription": "Configurer les paramètres par défaut de ce nœud",
"siteSettingDescription": "Configurer les paramètres du site", "siteSettingDescription": "Configurer les paramètres du site",
"siteResourcesTab": "Ressources",
"siteResourcesNoneOnSite": "Ce site n'a pas encore de ressources publiques ou privées.",
"siteResourcesSectionPublic": "Ressources publiques",
"siteResourcesSectionPrivate": "Ressources privées",
"siteResourcesSectionPublicDescription": "Ressources exposées à l'extérieur via des domaines ou des ports.",
"siteResourcesSectionPrivateDescription": "Ressources disponibles sur votre réseau privé via le site.",
"siteResourcesViewAllPublic": "Voir toutes les ressources",
"siteResourcesViewAllPrivate": "Voir toutes les ressources",
"siteResourcesDialogDescription": "Aperçu des ressources publiques et privées associées à ce site.",
"siteResourcesShowMore": "Afficher plus",
"siteResourcesPermissionDenied": "Vous n'avez pas la permission de lister ces ressources.",
"siteResourcesEmptyPublic": "Aucune ressource publique ne cible encore ce site.",
"siteResourcesEmptyPrivate": "Aucune ressource privée n'est encore associée à ce site.",
"siteResourcesHowToAccess": "Comment accéder",
"siteResourcesTargetsOnSite": "Cibles sur ce site",
"siteSetting": "Paramètres de {siteName}", "siteSetting": "Paramètres de {siteName}",
"siteNewtTunnel": "Site Newt (Recommandé)", "siteNewtTunnel": "Site Newt (Recommandé)",
"siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans n'importe quel réseau. Pas de configuration supplémentaire.", "siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans n'importe quel réseau. Pas de configuration supplémentaire.",
@@ -267,8 +296,11 @@
"orgMissing": "ID d'organisation manquant", "orgMissing": "ID d'organisation manquant",
"orgMissingMessage": "Impossible de régénérer l'invitation sans un ID d'organisation.", "orgMissingMessage": "Impossible de régénérer l'invitation sans un ID d'organisation.",
"accessUsersManage": "Gérer les utilisateurs", "accessUsersManage": "Gérer les utilisateurs",
"accessUserManage": "Gérer l'utilisateur",
"accessUsersDescription": "Inviter et gérer les utilisateurs ayant accès à cette organisation", "accessUsersDescription": "Inviter et gérer les utilisateurs ayant accès à cette organisation",
"accessUsersSearch": "Chercher des utilisateurs...", "accessUsersSearch": "Chercher des utilisateurs...",
"accessUsersRoleFilterCount": "{count, plural, one {# rôle} other {# rôles}}",
"accessUsersRoleFilterClear": "Effacer les filtres de rôle",
"accessUserCreate": "Créer un utilisateur", "accessUserCreate": "Créer un utilisateur",
"accessUserRemove": "Supprimer un utilisateur", "accessUserRemove": "Supprimer un utilisateur",
"username": "Nom d'utilisateur", "username": "Nom d'utilisateur",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Secrète", "newtSecretKey": "Secrète",
"newtVersion": "Version",
"architecture": "Architecture", "architecture": "Architecture",
"sites": "Nœuds", "sites": "Nœuds",
"siteWgAnyClients": "Utilisez n'importe quel client WireGuard pour vous connecter. Vous devrez adresser des ressources internes en utilisant l'adresse IP du pair.", "siteWgAnyClients": "Utilisez n'importe quel client WireGuard pour vous connecter. Vous devrez adresser des ressources internes en utilisant l'adresse IP du pair.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Voir les logs", "actionViewLogs": "Voir les logs",
"noneSelected": "Aucune sélection", "noneSelected": "Aucune sélection",
"orgNotFound2": "Aucune organisation trouvée.", "orgNotFound2": "Aucune organisation trouvée.",
"search": "Rechercher…",
"searchPlaceholder": "Recherche...", "searchPlaceholder": "Recherche...",
"emptySearchOptions": "Aucune option trouvée", "emptySearchOptions": "Aucune option trouvée",
"create": "Créer", "create": "Créer",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Gérer", "sidebarGeneral": "Gérer",
"sidebarLogAndAnalytics": "Journaux & Analytiques", "sidebarLogAndAnalytics": "Journaux & Analytiques",
"sidebarBluePrints": "Configs", "sidebarBluePrints": "Configs",
"sidebarAlerting": "Alertes",
"sidebarHealthChecks": "Vérifications de l'état de santé",
"sidebarOrganization": "Organisation", "sidebarOrganization": "Organisation",
"sidebarManagement": "Gestion", "sidebarManagement": "Gestion",
"sidebarBillingAndLicenses": "Facturation & Licences", "sidebarBillingAndLicenses": "Facturation & Licences",
"sidebarLogsAnalytics": "Analyses", "sidebarLogsAnalytics": "Analyses",
"alertingTitle": "Alertes",
"alertingDescription": "Définissez des sources, des déclencheurs et des actions pour les notifications",
"alertingRules": "Règles d'alerte",
"alertingSearchRules": "Rechercher des règles…",
"alertingAddRule": "Créer une règle",
"alertingColumnSource": "Source",
"alertingColumnTrigger": "Déclencheur",
"alertingColumnActions": "Actions",
"alertingColumnEnabled": "Activé",
"alertingDeleteQuestion": "Veuillez confirmer que vous souhaitez supprimer cette règle d'alerte.",
"alertingDeleteRule": "Supprimer la règle d'alerte",
"alertingRuleDeleted": "Règle d'alerte supprimée",
"alertingRuleSaved": "Règle d'alerte enregistrée",
"alertingRuleSavedCreatedDescription": "Votre nouvelle règle d'alerte a été créée. Vous pouvez continuer à la modifier sur cette page.",
"alertingRuleSavedUpdatedDescription": "Vos modifications apportées à cette règle d'alerte ont été enregistrées.",
"alertingEditRule": "Modifier la règle d'alerte",
"alertingCreateRule": "Créer une règle d'alerte",
"alertingRuleCredenzaDescription": "Choisissez ce qu'il faut surveiller, quand la déclencher et comment notifier",
"alertingRuleNamePlaceholder": "Site de production hors ligne",
"alertingRuleEnabled": "Règle activée",
"alertingSectionSource": "Source",
"alertingSourceType": "Type de source",
"alertingSourceSite": "Nœud",
"alertingSourceHealthCheck": "Vérification de l'état de santé",
"alertingPickSites": "Nœuds",
"alertingPickHealthChecks": "Vérifications de l'état de santé",
"alertingPickResources": "Ressources",
"alertingAllSites": "Tous les nœuds",
"alertingAllSitesDescription": "Les alertes se déclenchent pour n'importe quel nœud",
"alertingSpecificSites": "Nœuds spécifiques",
"alertingSpecificSitesDescription": "Choisissez des nœuds spécifiques à surveiller",
"alertingAllHealthChecks": "Toutes les vérifications de l'état de santé",
"alertingAllHealthChecksDescription": "Les alertes se déclenchent pour n'importe quelle vérification de l'état de santé",
"alertingSpecificHealthChecks": "Vérifications de l'état de santé spécifiques",
"alertingSpecificHealthChecksDescription": "Choisissez des vérifications de l'état de santé spécifiques à surveiller",
"alertingAllResources": "Toutes les ressources",
"alertingAllResourcesDescription": "Les alertes se déclenchent pour n'importe quelle ressource",
"alertingSpecificResources": "Ressources spécifiques",
"alertingSpecificResourcesDescription": "Choisissez des ressources spécifiques à surveiller",
"alertingSelectResources": "Sélectionner des ressources…",
"alertingResourcesSelected": "{count} ressources sélectionnées",
"alertingResourcesEmpty": "Aucune ressource avec des cibles dans les 10 premiers résultats.",
"alertingSectionTrigger": "Déclencheur",
"alertingTrigger": "Quand alerter",
"alertingTriggerSiteOnline": "Site en ligne",
"alertingTriggerSiteOffline": "Site hors ligne",
"alertingTriggerSiteToggle": "Les changements d'état du site",
"alertingTriggerHcHealthy": "Vérification de l'état de santé sain",
"alertingTriggerHcUnhealthy": "Vérification de l'état de santé non sain",
"alertingTriggerHcToggle": "Les changements d'état de la vérification de l'état de santé",
"alertingTriggerResourceHealthy": "Ressource saine",
"alertingTriggerResourceUnhealthy": "Ressource non saine",
"alertingTriggerResourceDegraded": "Ressource dégradée",
"alertingSearchHealthChecks": "Rechercher des vérifications de l'état de santé…",
"alertingHealthChecksEmpty": "Aucune vérification de l'état de santé disponible.",
"alertingTriggerResourceToggle": "Les changements d'état de la ressource",
"alertingSourceResource": "Ressource",
"alertingSectionActions": "Actions",
"alertingAddAction": "Ajouter une action",
"alertingActionNotify": "Adresse mail",
"alertingActionNotifyDescription": "Envoyez des notifications par e-mail aux utilisateurs ou aux rôles",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Envoyez une requête HTTP à un point de terminaison personnalisé",
"alertingExternalIntegration": "Intégration externe",
"alertingExternalPagerDutyDescription": "Envoyer des alertes à PagerDuty pour la gestion des incidents",
"alertingExternalOpsgenieDescription": "Diriger les alertes vers Opsgenie pour la gestion des appels",
"alertingExternalServiceNowDescription": "Créer des incidents ServiceNow à partir des événements d'alerte",
"alertingExternalIncidentIoDescription": "Déclencher des flux de travail Incident.io à partir d'événements d'alerte",
"alertingActionType": "Type d'action",
"alertingNotifyUsers": "Utilisateurs",
"alertingNotifyRoles": "Rôles",
"alertingNotifyEmails": "Adresses e-mail",
"alertingEmailPlaceholder": "Ajoutez un e-mail et appuyez sur Entrée",
"alertingWebhookMethod": "Méthode HTTP",
"alertingWebhookSecret": "Secret de signature (facultatif)",
"alertingWebhookSecretPlaceholder": "Secret HMAC",
"alertingWebhookHeaders": "En-têtes",
"alertingAddHeader": "Ajouter un en-tête",
"alertingSelectSites": "Sélectionner des sites…",
"alertingSitesSelected": "{count} sites sélectionnés",
"alertingSelectHealthChecks": "Sélectionner des vérifications de l'état de santé…",
"alertingHealthChecksSelected": "{count} vérifications de santé sélectionnées",
"alertingNoHealthChecks": "Aucune cible avec des vérifications de l'état de santé activées",
"alertingHealthCheckStub": "La sélection de la source de vérification de l'état de santé n'est pas encore câblée - vous pouvez toujours configurer les déclencheurs et les actions.",
"alertingSelectUsers": "Sélectionner des utilisateurs…",
"alertingUsersSelected": "{count} utilisateurs sélectionnés",
"alertingSelectRoles": "Sélectionner des rôles…",
"alertingRolesSelected": "{count} rôles sélectionnés",
"alertingSummarySites": "Sites ({count})",
"alertingSummaryAllSites": "Tous les nœuds",
"alertingSummaryHealthChecks": "Vérifications de l'état de santé ({count})",
"alertingSummaryAllHealthChecks": "Toutes les vérifications de l'état de santé",
"alertingSummaryResources": "Ressources ({count})",
"alertingSummaryAllResources": "Toutes les ressources",
"alertingErrorNameRequired": "Entrer un nom",
"alertingErrorActionsMin": "Ajoutez au moins une action",
"alertingErrorPickSites": "Sélectionnez au moins un site",
"alertingErrorPickHealthChecks": "Sélectionnez au moins une vérification de l'état de santé",
"alertingErrorPickResources": "Sélectionnez au moins une ressource",
"alertingErrorTriggerSite": "Choisissez un déclencheur de site",
"alertingErrorTriggerHealth": "Choisissez un déclencheur de vérification de l'état de santé",
"alertingErrorTriggerResource": "Choisissez un déclencheur de ressource",
"alertingErrorNotifyRecipients": "Choisissez des utilisateurs, des rôles ou au moins un e-mail",
"alertingConfigureSource": "Configurer la source",
"alertingConfigureTrigger": "Configurer le déclencheur",
"alertingConfigureActions": "Configurer les actions",
"alertingBackToRules": "Retour aux règles",
"alertingRuleCooldown": "Temps de repos (secondes)",
"alertingRuleCooldownDescription": "Temps minimum entre les alertes répétées pour la même règle. Réglez sur 0 pour déclencher à chaque fois.",
"alertingDraftBadge": "Brouillon - enregistrez pour stocker cette règle",
"alertingSidebarHint": "Cliquez sur une étape dans la vue d'ensemble pour la modifier ici.",
"alertingGraphCanvasTitle": "Flux de règle",
"alertingGraphCanvasDescription": "Vue d'ensemble visuelle de la source, du déclencheur et des actions. Sélectionnez un nœud pour le modifier dans le panneau.",
"alertingNodeNotConfigured": "Pas encore configuré",
"alertingNodeActionsCount": "{count, plural, one {# action} other {# actions}}",
"alertingNodeRoleSource": "Source",
"alertingNodeRoleTrigger": "Déclencheur",
"alertingNodeRoleAction": "Action",
"alertingTabRules": "Règles d'alerte",
"alertingTabHealthChecks": "Vérifications de l'état de santé",
"alertingRulesBannerTitle": "Soyez averti",
"alertingRulesBannerDescription": "Chaque règle associe ce qu'il faut surveiller (un site, une vérification de l'état de santé ou une ressource), quand l'exécuter (par exemple, hors ligne ou non saine), et comment notifier votre équipe par e-mail, webhooks ou intégrations. Utilisez cette liste pour créer, activer et gérer ces règles.",
"alertingHealthChecksBannerTitle": "Surveiller la santé et les ressources",
"alertingHealthChecksBannerDescription": "Les vérifications de l'état de santé sont des moniteurs HTTP ou TCP que vous définissez une fois. Vous pouvez ensuite les utiliser comme sources dans les règles d'alerte pour être averti lorsqu'une cible devient saine ou non saine. Les vérifications de l'état de santé sur les ressources apparaissent également ici.",
"standaloneHcTableTitle": "Vérifications de l'état de santé",
"standaloneHcSearchPlaceholder": "Rechercher des vérifications de l'état de santé…",
"standaloneHcAddButton": "Créer une vérification de l'état de santé",
"standaloneHcCreateTitle": "Créer une vérification de l'état de santé",
"standaloneHcEditTitle": "Modifier la vérification de l'état de santé",
"standaloneHcDescription": "Configurez une vérification HTTP ou TCP de l'état de santé pour une utilisation dans les règles d'alerte.",
"standaloneHcNameLabel": "Nom",
"standaloneHcNamePlaceholder": "Mon moniteur HTTP",
"standaloneHcDeleteTitle": "Supprimer la vérification de l'état de santé",
"standaloneHcDeleteQuestion": "Veuillez confirmer que você souhaitez supprimer cette vérification de l'état de santé.",
"standaloneHcDeleted": "Vérification de l'état de santé supprimée",
"standaloneHcSaved": "Vérification de l'état de santé enregistrée",
"standaloneHcColumnHealth": "Santé",
"standaloneHcColumnMode": "Mode",
"standaloneHcColumnTarget": "Cible",
"standaloneHcHealthStateHealthy": "Sain",
"standaloneHcHealthStateUnhealthy": "En mauvaise santé",
"standaloneHcHealthStateUnknown": "Inconnu",
"standaloneHcFilterAnySite": "Tous les nœuds",
"standaloneHcFilterAnyResource": "Toutes les ressources",
"standaloneHcFilterMode": "Mode",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Santé",
"standaloneHcFilterEnabled": "Activé",
"standaloneHcFilterEnabledOn": "Activé",
"standaloneHcFilterEnabledOff": "Désactivé",
"standaloneHcFilterSiteIdFallback": "Site {id}",
"standaloneHcFilterResourceIdFallback": "Ressource {id}",
"blueprints": "Configs", "blueprints": "Configs",
"blueprintsDescription": "Appliquer les configurations déclaratives et afficher les exécutions précédentes", "blueprintsDescription": "Appliquer les configurations déclaratives et afficher les exécutions précédentes",
"blueprintAdd": "Ajouter une Config", "blueprintAdd": "Ajouter une Config",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Créer le compte administrateur du serveur initial. Un seul administrateur serveur peut exister. Vous pouvez toujours changer ces informations d'identification plus tard.", "initialSetupDescription": "Créer le compte administrateur du serveur initial. Un seul administrateur serveur peut exister. Vous pouvez toujours changer ces informations d'identification plus tard.",
"createAdminAccount": "Créer un compte administrateur", "createAdminAccount": "Créer un compte administrateur",
"setupErrorCreateAdmin": "Une erreur s'est produite lors de la création du compte administrateur du serveur.", "setupErrorCreateAdmin": "Une erreur s'est produite lors de la création du compte administrateur du serveur.",
"certificateStatus": "Statut du certificat", "certificateStatus": "Certificat",
"certificateStatusAutoRefreshHint": "L'état se rafraîchit automatiquement.",
"loading": "Chargement", "loading": "Chargement",
"loadingAnalytics": "Chargement de l'analyse", "loadingAnalytics": "Chargement de l'analyse",
"restart": "Redémarrer", "restart": "Redémarrer",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Voir les notes de publication", "pangolinUpdateAvailableReleaseNotes": "Voir les notes de publication",
"newtUpdateAvailable": "Mise à jour disponible", "newtUpdateAvailable": "Mise à jour disponible",
"newtUpdateAvailableInfo": "Une nouvelle version de Newt est disponible. Veuillez mettre à jour vers la dernière version pour une meilleure expérience.", "newtUpdateAvailableInfo": "Une nouvelle version de Newt est disponible. Veuillez mettre à jour vers la dernière version pour une meilleure expérience.",
"pangolinNodeUpdateAvailableInfo": "Une nouvelle version de Pangolin Node est disponible. Veuillez mettre à jour vers la dernière version pour une meilleure expérience.",
"domainPickerEnterDomain": "Domaine", "domainPickerEnterDomain": "Domaine",
"domainPickerPlaceholder": "monapp.exemple.com", "domainPickerPlaceholder": "monapp.exemple.com",
"domainPickerDescription": "Entrez le domaine complet de la ressource pour voir les options disponibles.", "domainPickerDescription": "Entrez le domaine complet de la ressource pour voir les options disponibles.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Configurer la vérification de l'état de santé", "configureHealthCheck": "Configurer la vérification de l'état de santé",
"configureHealthCheckDescription": "Configurer la surveillance de la santé pour {target}", "configureHealthCheckDescription": "Configurer la surveillance de la santé pour {target}",
"enableHealthChecks": "Activer les vérifications de santé", "enableHealthChecks": "Activer les vérifications de santé",
"healthCheckDisabledStateDescription": "Lorsqu'il est désactivé, le site ne procédera pas aux vérifications de santé et l'état sera considéré comme inconnu.",
"enableHealthChecksDescription": "Surveiller la vie de cette cible. Vous pouvez surveiller un point de terminaison différent de la cible si nécessaire.", "enableHealthChecksDescription": "Surveiller la vie de cette cible. Vous pouvez surveiller un point de terminaison différent de la cible si nécessaire.",
"healthScheme": "Méthode", "healthScheme": "Méthode",
"healthSelectScheme": "Sélectionnez la méthode", "healthSelectScheme": "Sélectionnez la méthode",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "L'intervalle de vérification doit être d'au moins 5 secondes", "healthCheckIntervalMin": "L'intervalle de vérification doit être d'au moins 5 secondes",
"healthCheckTimeoutMin": "Le délai doit être d'au moins 1 seconde", "healthCheckTimeoutMin": "Le délai doit être d'au moins 1 seconde",
"healthCheckRetryMin": "Les tentatives de réessai doivent être d'au moins 1", "healthCheckRetryMin": "Les tentatives de réessai doivent être d'au moins 1",
"healthCheckMode": "Mode de vérification",
"healthCheckStrategy": "Stratégie",
"healthCheckModeDescription": "Le mode TCP vérifie uniquement la connectivité. Le mode HTTP valide la réponse HTTP.",
"healthyThreshold": "Seuil de santé",
"healthyThresholdDescription": "Succès consécutifs requis avant de marquer comme sain.",
"unhealthyThreshold": "Seuil de non-santé",
"unhealthyThresholdDescription": "Echecs consécutifs requis avant de signaler comme non sain.",
"healthCheckHealthyThresholdMin": "Le seuil de santé doit être d'au moins 1",
"healthCheckUnhealthyThresholdMin": "Le seuil de non-santé doit être d'au moins 1",
"httpMethod": "Méthode HTTP", "httpMethod": "Méthode HTTP",
"selectHttpMethod": "Sélectionnez la méthode HTTP", "selectHttpMethod": "Sélectionnez la méthode HTTP",
"domainPickerSubdomainLabel": "Sous-domaine", "domainPickerSubdomainLabel": "Sous-domaine",
"domainPickerWildcard": "Joker",
"domainPickerWildcardPaidOnly": "Les sous-domaines Joker sont une fonctionnalité payante. Veuillez mettre à niveau pour accéder à cette fonctionnalité.",
"domainPickerBaseDomainLabel": "Domaine de base", "domainPickerBaseDomainLabel": "Domaine de base",
"domainPickerSearchDomains": "Rechercher des domaines...", "domainPickerSearchDomains": "Rechercher des domaines...",
"domainPickerNoDomainsFound": "Aucun domaine trouvé", "domainPickerNoDomainsFound": "Aucun domaine trouvé",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Cette adresse fait partie du sous-réseau utilitaire de l'organisation. Elle est utilisée pour résoudre les enregistrements d'alias en utilisant une résolution DNS interne.", "resourcesTableAliasAddressInfo": "Cette adresse fait partie du sous-réseau utilitaire de l'organisation. Elle est utilisée pour résoudre les enregistrements d'alias en utilisant une résolution DNS interne.",
"resourcesTableClients": "Clients", "resourcesTableClients": "Clients",
"resourcesTableAndOnlyAccessibleInternally": "et sont uniquement accessibles en interne lorsqu'elles sont connectées avec un client.", "resourcesTableAndOnlyAccessibleInternally": "et sont uniquement accessibles en interne lorsqu'elles sont connectées avec un client.",
"resourcesTableNoTargets": "Aucune cible",
"resourcesTableHealthy": "Sain", "resourcesTableHealthy": "Sain",
"resourcesTableDegraded": "Dégradé", "resourcesTableDegraded": "Dégradé",
"resourcesTableOffline": "Hors ligne", "resourcesTableUnhealthy": "En mauvaise santé",
"resourcesTableUnknown": "Inconnu", "resourcesTableUnknown": "Inconnu",
"resourcesTableNotMonitored": "Non-monitoré", "resourcesTableNotMonitored": "Non-monitoré",
"resourcesTableNoTargets": "Aucune cible",
"editInternalResourceDialogEditClientResource": "Modifier une ressource privée", "editInternalResourceDialogEditClientResource": "Modifier une ressource privée",
"editInternalResourceDialogUpdateResourceProperties": "Mettre à jour la configuration de la ressource et les contrôles d'accès pour {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Mettre à jour la configuration de la ressource et les contrôles d'accès pour {resourceName}",
"editInternalResourceDialogResourceProperties": "Propriétés de la ressource", "editInternalResourceDialogResourceProperties": "Propriétés de la ressource",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Port", "editInternalResourceDialogModePort": "Port",
"editInternalResourceDialogModeHost": "Hôte", "editInternalResourceDialogModeHost": "Hôte",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Méthode HTTP",
"editInternalResourceDialogEnableSsl": "Activer SSL",
"editInternalResourceDialogEnableSslDescription": "Activer le cryptage SSL/TLS pour des connexions HTTPS sécurisées vers la destination.",
"editInternalResourceDialogDestination": "Destination", "editInternalResourceDialogDestination": "Destination",
"editInternalResourceDialogDestinationHostDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.", "editInternalResourceDialogDestinationHostDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.",
"editInternalResourceDialogDestinationIPDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.", "editInternalResourceDialogDestinationIPDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Nom", "createInternalResourceDialogName": "Nom",
"createInternalResourceDialogSite": "Site", "createInternalResourceDialogSite": "Site",
"selectSite": "Sélectionner un site...", "selectSite": "Sélectionner un site...",
"multiSitesSelectorSitesCount": "{count, plural, one {# site} other {# sites}}",
"noSitesFound": "Aucun site trouvé.", "noSitesFound": "Aucun site trouvé.",
"createInternalResourceDialogProtocol": "Protocole", "createInternalResourceDialogProtocol": "Protocole",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Port", "createInternalResourceDialogModePort": "Port",
"createInternalResourceDialogModeHost": "Hôte", "createInternalResourceDialogModeHost": "Hôte",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Méthode HTTP",
"createInternalResourceDialogScheme": "Méthode HTTP",
"createInternalResourceDialogEnableSsl": "Activer SSL",
"createInternalResourceDialogEnableSslDescription": "Activer le cryptage SSL/TLS pour des connexions HTTPS sécurisées vers la destination.",
"createInternalResourceDialogDestination": "Destination", "createInternalResourceDialogDestination": "Destination",
"createInternalResourceDialogDestinationHostDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.", "createInternalResourceDialogDestinationHostDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.",
"createInternalResourceDialogDestinationCidrDescription": "La gamme CIDR de la ressource sur le réseau du site.", "createInternalResourceDialogDestinationCidrDescription": "La gamme CIDR de la ressource sur le réseau du site.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Un alias DNS interne optionnel pour cette ressource.", "createInternalResourceDialogAliasDescription": "Un alias DNS interne optionnel pour cette ressource.",
"internalResourceDownstreamSchemeRequired": "Un schéma est requis pour les ressources HTTP",
"internalResourceHttpPortRequired": "Le port de destination est requis pour les ressources HTTP",
"siteConfiguration": "Configuration", "siteConfiguration": "Configuration",
"siteAcceptClientConnections": "Accepter les connexions client", "siteAcceptClientConnections": "Accepter les connexions client",
"siteAcceptClientConnectionsDescription": "Autoriser les utilisateurs et les clients à accéder aux ressources de ce site. Cela peut être modifié plus tard.", "siteAcceptClientConnectionsDescription": "Autoriser les utilisateurs et les clients à accéder aux ressources de ce site. Cela peut être modifié plus tard.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Vérifié", "domainPickerVerified": "Vérifié",
"domainPickerUnverified": "Non vérifié", "domainPickerUnverified": "Non vérifié",
"domainPickerManual": "Manuel", "domainPickerManual": "Manuel",
"domainPickerInvalidSubdomainStructure": "Ce sous-domaine contient des caractères ou une structure non valide. Il sera automatiquement nettoyé lorsque vous enregistrez.", "domainPickerInvalidSubdomainStructure": "Les caractères invalides seront nettoyés lors de l'enregistrement.",
"domainPickerError": "Erreur", "domainPickerError": "Erreur",
"domainPickerErrorLoadDomains": "Impossible de charger les domaines de l'organisation", "domainPickerErrorLoadDomains": "Impossible de charger les domaines de l'organisation",
"domainPickerErrorCheckAvailability": "Impossible de vérifier la disponibilité du domaine", "domainPickerErrorCheckAvailability": "Impossible de vérifier la disponibilité du domaine",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Choisissez votre fournisseur d'identité pour continuer", "orgAuthChooseIdpDescription": "Choisissez votre fournisseur d'identité pour continuer",
"orgAuthNoIdpConfigured": "Cette organisation n'a aucun fournisseur d'identité configuré. Vous pouvez vous connecter avec votre identité Pangolin à la place.", "orgAuthNoIdpConfigured": "Cette organisation n'a aucun fournisseur d'identité configuré. Vous pouvez vous connecter avec votre identité Pangolin à la place.",
"orgAuthSignInWithPangolin": "Se connecter avec Pangolin", "orgAuthSignInWithPangolin": "Se connecter avec Pangolin",
"orgAuthSignInToOrg": "Se connecter à une organisation", "orgAuthSignInToOrg": "Fournisseur d'identité d'organisation (SSO)",
"orgAuthSelectOrgTitle": "Connexion à l'organisation", "orgAuthSelectOrgTitle": "Connexion à l'organisation",
"orgAuthSelectOrgDescription": "Entrez votre identifiant d'organisation pour continuer", "orgAuthSelectOrgDescription": "Entrez votre identifiant d'organisation pour continuer",
"orgAuthOrgIdPlaceholder": "votre-organisation", "orgAuthOrgIdPlaceholder": "votre-organisation",
@@ -2429,6 +2648,7 @@
"validPassword": "Mot de passe valide", "validPassword": "Mot de passe valide",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Client connecté",
"resourceBlocked": "Ressource bloquée", "resourceBlocked": "Ressource bloquée",
"droppedByRule": "Abandonné par la règle", "droppedByRule": "Abandonné par la règle",
"noSessions": "Aucune session", "noSessions": "Aucune session",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Ajouter des clients", "editInternalResourceDialogAddClients": "Ajouter des clients",
"editInternalResourceDialogDestinationLabel": "Destination", "editInternalResourceDialogDestinationLabel": "Destination",
"editInternalResourceDialogDestinationDescription": "Indiquez l'adresse de destination pour la ressource interne. Cela peut être un nom d'hôte, une adresse IP ou une plage CIDR selon le mode sélectionné. Définissez éventuellement un alias DNS interne pour une identification plus facile.", "editInternalResourceDialogDestinationDescription": "Indiquez l'adresse de destination pour la ressource interne. Cela peut être un nom d'hôte, une adresse IP ou une plage CIDR selon le mode sélectionné. Définissez éventuellement un alias DNS interne pour une identification plus facile.",
"internalResourceFormMultiSiteRoutingHelp": "La sélection de plusieurs sites permet un routage résilient et un basculement pour une haute disponibilité.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "En savoir plus",
"editInternalResourceDialogPortRestrictionsDescription": "Restreindre l'accès à des ports TCP/UDP spécifiques ou autoriser/bloquer tous les ports.", "editInternalResourceDialogPortRestrictionsDescription": "Restreindre l'accès à des ports TCP/UDP spécifiques ou autoriser/bloquer tous les ports.",
"createInternalResourceDialogHttpConfiguration": "Configuration HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Choisissez le domaine que les clients utiliseront pour atteindre cette ressource via HTTP ou HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Configuration HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Choisissez le domaine que les clients utiliseront pour atteindre cette ressource via HTTP ou HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Nous serons bientôt de retour ! Notre site est actuellement en maintenance planifiée.", "maintenancePageMessagePlaceholder": "Nous serons bientôt de retour ! Notre site est actuellement en maintenance planifiée.",
"maintenancePageMessageDescription": "Message détaillé expliquant la maintenance", "maintenancePageMessageDescription": "Message détaillé expliquant la maintenance",
"maintenancePageTimeTitle": "Temps d'achèvement estimé (facultatif)", "maintenancePageTimeTitle": "Temps d'achèvement estimé (facultatif)",
"privateMaintenanceScreenTitle": "Écran de maintien de service privé",
"privateMaintenanceScreenMessage": "Ce domaine est utilisé sur une ressource privée. Veuillez vous connecter à l'aide du client Pangolin pour accéder à cette ressource.",
"privateMaintenanceScreenSteps": "Une fois connecté, si vous voyez toujours ce message, le cache DNS de votre navigateur peut toujours pointer vers l'ancienne adresse. Pour résoudre cela : fermez complètement et rouvrez cet onglet, ou votre navigateur, puis retournez sur cette page.",
"maintenanceTime": "par exemple, 2 heures, le 1er nov. à 17:00", "maintenanceTime": "par exemple, 2 heures, le 1er nov. à 17:00",
"maintenanceEstimatedTimeDescription": "Quand vous attendez que la maintenance soit terminée", "maintenanceEstimatedTimeDescription": "Quand vous attendez que la maintenance soit terminée",
"editDomain": "Modifier le domaine", "editDomain": "Modifier le domaine",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Ajouter une destination HTTP", "httpDestAddTitle": "Ajouter une destination HTTP",
"httpDestEditDescription": "Mettre à jour la configuration pour cette destination de streaming d'événements 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.", "httpDestAddDescription": "Configurez un nouveau point de terminaison HTTP pour recevoir les événements de votre organisation.",
"S3DestEditTitle": "Modifier la destination",
"S3DestAddTitle": "Ajouter une destination S3",
"S3DestEditDescription": "Mettre à jour la configuration de cette destination de diffusion d'événements S3.",
"S3DestAddDescription": "Configurer un nouveau point de terminaison S3 pour recevoir les événements de votre organisation.",
"datadogDestEditTitle": "Modifier la destination",
"datadogDestAddTitle": "Ajouter une destination Datadog",
"datadogDestEditDescription": "Mettre à jour la configuration de cette destination de diffusion d'événements Datadog.",
"datadogDestAddDescription": "Configurer un nouveau point de terminaison Datadog pour recevoir les événements de votre organisation.",
"httpDestTabSettings": "Réglages", "httpDestTabSettings": "Réglages",
"httpDestTabHeaders": "En-têtes", "httpDestTabHeaders": "En-têtes",
"httpDestTabBody": "Corps", "httpDestTabBody": "Corps",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Destination créée avec succès", "httpDestCreatedSuccess": "Destination créée avec succès",
"httpDestUpdateFailed": "Impossible de mettre à jour la destination", "httpDestUpdateFailed": "Impossible de mettre à jour la destination",
"httpDestCreateFailed": "Impossible de créer la destination", "httpDestCreateFailed": "Impossible de créer la destination",
"followRedirects": "Suivre les redirections",
"followRedirectsDescription": "Suivre automatiquement les redirections HTTP pour les requêtes.",
"alertingErrorWebhookUrl": "Veuillez entrer une URL valide pour le webhook.",
"healthCheckStrategyHttp": "Valide la connectivité et vérifie le statut de la réponse HTTP.",
"healthCheckStrategyTcp": "Vérifie uniquement la connectivité TCP, sans inspecter la réponse.",
"healthCheckStrategySnmp": "Effectue une requête SNMP pour vérifier la santé des dispositifs et de l'infrastructure réseau.",
"healthCheckStrategyIcmp": "Utilise des requêtes écho ICMP (pings) pour vérifier si une ressource est accessible et réactive.",
"healthCheckTabStrategy": "Stratégie",
"healthCheckTabConnection": "Connexion",
"healthCheckTabAdvanced": "Avancé",
"healthCheckStrategyNotAvailable": "Cette stratégie n'est pas disponible. Veuillez contacter le service commercial pour activer cette fonctionnalité.",
"uptime30d": "Disponibilité (30j)",
"idpAddActionCreateNew": "Créer un nouveau fournisseur d'identité", "idpAddActionCreateNew": "Créer un nouveau fournisseur d'identité",
"idpAddActionImportFromOrg": "Importer d'une autre organisation", "idpAddActionImportFromOrg": "Importer d'une autre organisation",
"idpImportDialogTitle": "Importer le fournisseur d'identité", "idpImportDialogTitle": "Importer le fournisseur d'identité",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Cela ne peut pas être annulé pour cette organisation.", "idpUnassociateWarning": "Cela ne peut pas être annulé pour cette organisation.",
"idpUnassociatedDescription": "Fournisseur d'identités dissocié de cette organisation avec succès", "idpUnassociatedDescription": "Fournisseur d'identités dissocié de cette organisation avec succès",
"idpUnassociateMenu": "Dissocier", "idpUnassociateMenu": "Dissocier",
"idpDeleteAllOrgsMenu": "Supprimer" "idpDeleteAllOrgsMenu": "Supprimer",
"publicIpEndpoint": "Point de terminaison",
"lastTriggeredAt": "Dernier déclenchement",
"reject": "Rejeter",
"uptimeDaysAgo": "Il y a {count} jours",
"uptimeToday": "Aujourd'hui",
"uptimeNoDataAvailable": "Aucune donnée disponible",
"uptimeSuffix": "disponibilité",
"uptimeDowntimeSuffix": "indisponibilité",
"uptimeTooltipUptimeLabel": "Disponibilité",
"uptimeTooltipDowntimeLabel": "Indisponibilité",
"uptimeOngoing": "en cours",
"uptimeNoMonitoringData": "Pas de données de surveillance",
"uptimeNoData": "Aucune donnée",
"uptimeMiniBarDown": "Non disponible",
"uptimeSectionTitle": "Disponibilité",
"uptimeSectionDescription": "Disponibilité sur les {days} derniers jours",
"uptimeAddAlert": "Ajouter une alerte",
"uptimeViewAlerts": "Voir les alertes",
"uptimeCreateEmailAlert": "Créer une alerte par e-mail",
"uptimeAlertDescriptionSite": "Recevez un e-mail lorsque ce site est hors ligne ou revient en ligne.",
"uptimeAlertDescriptionResource": "Recevez un e-mail lorsque cette ressource est hors ligne ou revient en ligne.",
"uptimeAlertNamePlaceholder": "Nom de l'alerte",
"uptimeAdditionalEmails": "E-mails supplémentaires",
"uptimeCreateAlert": "Créer une alerte",
"uptimeAlertNoRecipients": "Aucun destinataire",
"uptimeAlertNoRecipientsDescription": "Veuillez ajouter au moins un utilisateur, rôle ou e-mail à notifier.",
"uptimeAlertCreated": "Alerte créé",
"uptimeAlertCreatedDescription": "Vous serez notifié lorsque ce statut changera.",
"uptimeAlertCreateFailed": "Échec de la création de l'alerte",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Clé",
"webhookHeaderValuePlaceholder": "Valeur",
"alertLabel": "Alerte",
"domainPickerWildcardSubdomainNotAllowed": "Les sous-domaines Joker ne sont pas autorisés.",
"domainPickerWildcardCertWarning": "Les ressources Joker peuvent nécessiter une configuration supplémentaire pour fonctionner correctement.",
"domainPickerWildcardCertWarningLink": "En savoir plus",
"health": "Santé",
"domainPendingErrorTitle": "Problème de vérification"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Contatta le vendite per abilitare questa funzionalità.",
"contactSalesBookDemo": "Prenota una demo",
"contactSalesOr": "o",
"contactSalesContactUs": "contattaci",
"setupCreate": "Creare l'organizzazione, il sito e le risorse", "setupCreate": "Creare l'organizzazione, il sito e le risorse",
"headerAuthCompatibilityInfo": "Abilita questa funzionalità per forzare una risposta 401 Unauthorized quando manca un token di autenticazione. Questo è richiesto per browser o librerie HTTP specifiche che non inviano credenziali senza una sfida del server.", "headerAuthCompatibilityInfo": "Abilita questa funzionalità per forzare una risposta 401 Unauthorized quando manca un token di autenticazione. Questo è richiesto per browser o librerie HTTP specifiche che non inviano credenziali senza una sfida del server.",
"headerAuthCompatibility": "Compatibilità estesa", "headerAuthCompatibility": "Compatibilità estesa",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Rilevata chiave di licenza non valida o scaduta. Segui i termini di licenza per continuare a utilizzare tutte le funzionalità.", "componentsInvalidKey": "Rilevata chiave di licenza non valida o scaduta. Segui i termini di licenza per continuare a utilizzare tutte le funzionalità.",
"dismiss": "Ignora", "dismiss": "Ignora",
"subscriptionViolationMessage": "Hai superato i tuoi limiti per il tuo piano attuale. Correggi il problema rimuovendo siti, utenti o altre risorse per rimanere all'interno del tuo piano.", "subscriptionViolationMessage": "Hai superato i tuoi limiti per il tuo piano attuale. Correggi il problema rimuovendo siti, utenti o altre risorse per rimanere all'interno del tuo piano.",
"trialBannerMessage": "Il tuo periodo di prova scade tra {countdown}. Aggiorna per mantenere l'accesso.",
"trialBannerExpired": "Il tuo periodo di prova è scaduto. Aggiorna ora per ripristinare l'accesso.",
"trialActive": "Prova Gratuita Attiva",
"trialExpired": "Prova scaduta",
"trialHasEnded": "La tua prova è terminata.",
"trialDaysRemaining": "{count, plural, one {# giorno rimanente} other {# giorni rimanenti}}",
"trialDaysLeftShort": "{days}g rimasti nella prova",
"trialGoToBilling": "Vai alla pagina di fatturazione",
"subscriptionViolationViewBilling": "Visualizza fatturazione", "subscriptionViolationViewBilling": "Visualizza fatturazione",
"componentsLicenseViolation": "Violazione della licenza: Questo server sta usando i siti {usedSites} che superano il suo limite concesso in licenza per i siti {maxSites} . Segui i termini di licenza per continuare a usare tutte le funzionalità.", "componentsLicenseViolation": "Violazione della licenza: Questo server sta usando i siti {usedSites} che superano il suo limite concesso in licenza per i siti {maxSites} . Segui i termini di licenza per continuare a usare tutte le funzionalità.",
"componentsSupporterMessage": "Grazie per aver supportato Pangolin come {tier}!", "componentsSupporterMessage": "Grazie per aver supportato Pangolin come {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Ho copiato la configurazione", "siteConfirmCopy": "Ho copiato la configurazione",
"searchSitesProgress": "Cerca siti...", "searchSitesProgress": "Cerca siti...",
"siteAdd": "Aggiungi Sito", "siteAdd": "Aggiungi Sito",
"sitesTableViewPublicResources": "Visualizza Risorse Pubbliche",
"sitesTableViewPrivateResources": "Visualizza Risorse Private",
"siteInstallNewt": "Installa Newt", "siteInstallNewt": "Installa Newt",
"siteInstallNewtDescription": "Esegui Newt sul tuo sistema", "siteInstallNewtDescription": "Esegui Newt sul tuo sistema",
"WgConfiguration": "Configurazione WireGuard", "WgConfiguration": "Configurazione WireGuard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Il sito è stato aggiornato.", "siteUpdatedDescription": "Il sito è stato aggiornato.",
"siteGeneralDescription": "Configura le impostazioni generali per questo sito", "siteGeneralDescription": "Configura le impostazioni generali per questo sito",
"siteSettingDescription": "Configura le impostazioni del sito", "siteSettingDescription": "Configura le impostazioni del sito",
"siteResourcesTab": "Risorse",
"siteResourcesNoneOnSite": "Questo sito non ha ancora risorse pubbliche o private.",
"siteResourcesSectionPublic": "Risorse Pubbliche",
"siteResourcesSectionPrivate": "Risorse Private",
"siteResourcesSectionPublicDescription": "Risorse esposte esternamente attraverso domini o porte.",
"siteResourcesSectionPrivateDescription": "Risorse disponibili sulla tua rete privata tramite il sito.",
"siteResourcesViewAllPublic": "Visualizza tutte le risorse",
"siteResourcesViewAllPrivate": "Visualizza tutte le risorse",
"siteResourcesDialogDescription": "Panoramica delle risorse pubbliche e private associate a questo sito.",
"siteResourcesShowMore": "Mostra Altro",
"siteResourcesPermissionDenied": "Non hai il permesso di elencare queste risorse.",
"siteResourcesEmptyPublic": "Ancora nessuna risorsa pubblica punta a questo sito.",
"siteResourcesEmptyPrivate": "Ancora nessuna risorsa privata è associata a questo sito.",
"siteResourcesHowToAccess": "Come accedere",
"siteResourcesTargetsOnSite": "Obiettivi su questo sito",
"siteSetting": "Impostazioni del sito {siteName}", "siteSetting": "Impostazioni del sito {siteName}",
"siteNewtTunnel": "Nuovo Sito (Consigliato)", "siteNewtTunnel": "Nuovo Sito (Consigliato)",
"siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint in qualsiasi rete. Nessuna configurazione aggiuntiva.", "siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint in qualsiasi rete. Nessuna configurazione aggiuntiva.",
@@ -267,8 +296,11 @@
"orgMissing": "ID Organizzazione Mancante", "orgMissing": "ID Organizzazione Mancante",
"orgMissingMessage": "Impossibile rigenerare l'invito senza un ID organizzazione.", "orgMissingMessage": "Impossibile rigenerare l'invito senza un ID organizzazione.",
"accessUsersManage": "Gestisci Utenti", "accessUsersManage": "Gestisci Utenti",
"accessUserManage": "Gestisci Utente",
"accessUsersDescription": "Invita e gestisci gli utenti con accesso a questa organizzazione", "accessUsersDescription": "Invita e gestisci gli utenti con accesso a questa organizzazione",
"accessUsersSearch": "Cerca utenti...", "accessUsersSearch": "Cerca utenti...",
"accessUsersRoleFilterCount": "{count, plural, one {# ruolo} other {# ruoli}}",
"accessUsersRoleFilterClear": "Cancella filtri ruolo",
"accessUserCreate": "Crea Utente", "accessUserCreate": "Crea Utente",
"accessUserRemove": "Rimuovi Utente", "accessUserRemove": "Rimuovi Utente",
"username": "Nome utente", "username": "Nome utente",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Segreto", "newtSecretKey": "Segreto",
"newtVersion": "Versione",
"architecture": "Architettura", "architecture": "Architettura",
"sites": "Siti", "sites": "Siti",
"siteWgAnyClients": "Usa qualsiasi client WireGuard per connetterti. Dovrai indirizzare le risorse interne utilizzando l'IP del peer.", "siteWgAnyClients": "Usa qualsiasi client WireGuard per connetterti. Dovrai indirizzare le risorse interne utilizzando l'IP del peer.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Visualizza Log", "actionViewLogs": "Visualizza Log",
"noneSelected": "Nessuna selezione", "noneSelected": "Nessuna selezione",
"orgNotFound2": "Nessuna organizzazione trovata.", "orgNotFound2": "Nessuna organizzazione trovata.",
"search": "Cerca…",
"searchPlaceholder": "Cerca...", "searchPlaceholder": "Cerca...",
"emptySearchOptions": "Nessuna opzione trovata", "emptySearchOptions": "Nessuna opzione trovata",
"create": "Crea", "create": "Crea",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Gestisci", "sidebarGeneral": "Gestisci",
"sidebarLogAndAnalytics": "Log & Analytics", "sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Progetti", "sidebarBluePrints": "Progetti",
"sidebarAlerting": "Allerta",
"sidebarHealthChecks": "Controlli di salute",
"sidebarOrganization": "Organizzazione", "sidebarOrganization": "Organizzazione",
"sidebarManagement": "Gestione", "sidebarManagement": "Gestione",
"sidebarBillingAndLicenses": "Fatturazione E Licenze", "sidebarBillingAndLicenses": "Fatturazione E Licenze",
"sidebarLogsAnalytics": "Analisi", "sidebarLogsAnalytics": "Analisi",
"alertingTitle": "Allerta",
"alertingDescription": "Definisci fonti, trigger e azioni per le notifiche",
"alertingRules": "Regole di allerta",
"alertingSearchRules": "Cerca regole…",
"alertingAddRule": "Crea Regola",
"alertingColumnSource": "Fonte",
"alertingColumnTrigger": "Trigger",
"alertingColumnActions": "Azioni",
"alertingColumnEnabled": "Abilitato",
"alertingDeleteQuestion": "Si prega di confermare di voler eliminare questa regola di allerta.",
"alertingDeleteRule": "Elimina regola di allerta",
"alertingRuleDeleted": "Regola di allerta eliminata",
"alertingRuleSaved": "Regola di allerta salvata",
"alertingRuleSavedCreatedDescription": "La tua nuova regola di allerta è stata creata. Puoi continuare a modificarla su questa pagina.",
"alertingRuleSavedUpdatedDescription": "Le modifiche a questa regola di allerta sono state salvate.",
"alertingEditRule": "Modifica Regola di Allerta",
"alertingCreateRule": "Crea Regola di Allerta",
"alertingRuleCredenzaDescription": "Scegli cosa monitorare, quando attivare e come notificare",
"alertingRuleNamePlaceholder": "Sito di produzione giù",
"alertingRuleEnabled": "Regola abilitata",
"alertingSectionSource": "Fonte",
"alertingSourceType": "Tipo Di Fonte",
"alertingSourceSite": "Sito",
"alertingSourceHealthCheck": "Controllo di Salute",
"alertingPickSites": "Siti",
"alertingPickHealthChecks": "Controlli di Salute",
"alertingPickResources": "Risorse",
"alertingAllSites": "Tutti i Siti",
"alertingAllSitesDescription": "L'allerta scatta per qualsiasi sito",
"alertingSpecificSites": "Siti Specifici",
"alertingSpecificSitesDescription": "Scegli siti specifici da monitorare",
"alertingAllHealthChecks": "Tutti i Controlli di Salute",
"alertingAllHealthChecksDescription": "L'allerta scatta per qualsiasi controllo di salute",
"alertingSpecificHealthChecks": "Controlli di Salute Specifici",
"alertingSpecificHealthChecksDescription": "Scegli controlli di salute specifici da monitorare",
"alertingAllResources": "Tutte le Risorse",
"alertingAllResourcesDescription": "L'allerta scatta per qualsiasi risorsa",
"alertingSpecificResources": "Risorse Specifiche",
"alertingSpecificResourcesDescription": "Scegli risorse specifiche da monitorare",
"alertingSelectResources": "Seleziona risorse…",
"alertingResourcesSelected": "{count} risorse selezionate",
"alertingResourcesEmpty": "Nessuna risorsa con target nei primi 10 risultati.",
"alertingSectionTrigger": "Trigger",
"alertingTrigger": "Quando allertare",
"alertingTriggerSiteOnline": "Sito online",
"alertingTriggerSiteOffline": "Sito offline",
"alertingTriggerSiteToggle": "I cambiamenti di stato del sito",
"alertingTriggerHcHealthy": "Controllo di Salute Sano",
"alertingTriggerHcUnhealthy": "Controllo di Salute Non Sano",
"alertingTriggerHcToggle": "I cambiamenti di stato del controllo di salute",
"alertingTriggerResourceHealthy": "Risorsa in buona salute",
"alertingTriggerResourceUnhealthy": "Risorsa in cattiva salute",
"alertingTriggerResourceDegraded": "Risorsa degradata",
"alertingSearchHealthChecks": "Cerca controlli di salute…",
"alertingHealthChecksEmpty": "Nessun controllo di salute disponibile.",
"alertingTriggerResourceToggle": "Variazioni di stato della risorsa",
"alertingSourceResource": "Fonte",
"alertingSectionActions": "Azioni",
"alertingAddAction": "Aggiungi Azione",
"alertingActionNotify": "Email",
"alertingActionNotifyDescription": "Invia notifiche email agli utenti o ai ruoli",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Invia una richiesta HTTP a un endpoint personalizzato",
"alertingExternalIntegration": "Integrazione esterna",
"alertingExternalPagerDutyDescription": "Invia avvisi a PagerDuty per la gestione degli incidenti",
"alertingExternalOpsgenieDescription": "Indirizza avvisi a Opsgenie per la gestione delle chiamate",
"alertingExternalServiceNowDescription": "Crea incidenti ServiceNow dagli eventi di allerta",
"alertingExternalIncidentIoDescription": "Attiva i flussi di lavoro di Incident.io dagli eventi di allerta",
"alertingActionType": "Tipo di azione",
"alertingNotifyUsers": "Utenti",
"alertingNotifyRoles": "Ruoli",
"alertingNotifyEmails": "Indirizzi email",
"alertingEmailPlaceholder": "Aggiungi email e premi Invio",
"alertingWebhookMethod": "Metodo HTTP",
"alertingWebhookSecret": "Segreto di firma (opzionale)",
"alertingWebhookSecretPlaceholder": "Segreto HMAC",
"alertingWebhookHeaders": "Intestazioni",
"alertingAddHeader": "Aggiungi intestazione",
"alertingSelectSites": "Seleziona siti…",
"alertingSitesSelected": "{count} siti selezionati",
"alertingSelectHealthChecks": "Seleziona controlli di salute…",
"alertingHealthChecksSelected": "{count} controlli di salute selezionati",
"alertingNoHealthChecks": "Nessun obiettivo con controlli di salute abilitati",
"alertingHealthCheckStub": "Selezione fonte controllo di salute non ancora collegata - puoi comunque configurare trigger e azioni.",
"alertingSelectUsers": "Seleziona utenti…",
"alertingUsersSelected": "{count} utenti selezionati",
"alertingSelectRoles": "Seleziona ruoli…",
"alertingRolesSelected": "{count} ruoli selezionati",
"alertingSummarySites": "Siti ({count})",
"alertingSummaryAllSites": "Tutti i siti",
"alertingSummaryHealthChecks": "Controlli di Salute ({count})",
"alertingSummaryAllHealthChecks": "Tutti i controlli di salute",
"alertingSummaryResources": "Risorse ({count})",
"alertingSummaryAllResources": "Tutte le risorse",
"alertingErrorNameRequired": "Inserisci un nome",
"alertingErrorActionsMin": "Aggiungi almeno un'azione",
"alertingErrorPickSites": "Seleziona almeno un sito",
"alertingErrorPickHealthChecks": "Seleziona almeno un controllo di salute",
"alertingErrorPickResources": "Seleziona almeno una risorsa",
"alertingErrorTriggerSite": "Scegli un trigger sito",
"alertingErrorTriggerHealth": "Scegli un trigger controllo di salute",
"alertingErrorTriggerResource": "Scegli un trigger risorsa",
"alertingErrorNotifyRecipients": "Seleziona utenti, ruoli o almeno un indirizzo email",
"alertingConfigureSource": "Configura Fonte",
"alertingConfigureTrigger": "Configura Trigger",
"alertingConfigureActions": "Configura Azioni",
"alertingBackToRules": "Torna alle Regole",
"alertingRuleCooldown": "Tempo di riposo (secondi)",
"alertingRuleCooldownDescription": "Tempo minimo tra avvisi ripetuti per la stessa regola. Imposta a 0 per attivare ogni volta.",
"alertingDraftBadge": "Bozza - salva per memorizzare questa regola",
"alertingSidebarHint": "Clicca su un passaggio nella tela per modificarlo qui.",
"alertingGraphCanvasTitle": "Flusso della regola",
"alertingGraphCanvasDescription": "Panoramica visiva di fonte, trigger e azioni. Seleziona un nodo per modificarlo nel pannello.",
"alertingNodeNotConfigured": "Non ancora configurato",
"alertingNodeActionsCount": "{count, plural, one {# azione} other {# azioni}}",
"alertingNodeRoleSource": "Fonte",
"alertingNodeRoleTrigger": "Trigger",
"alertingNodeRoleAction": "Azione",
"alertingTabRules": "Regole di Allerta",
"alertingTabHealthChecks": "Controlli di Salute",
"alertingRulesBannerTitle": "Ricevi Notifiche",
"alertingRulesBannerDescription": "Ogni regola collega ciò che monitorare (un sito, controllo di salute o risorsa), quando attivare (ad esempio offline o non sano) e come notificare il tuo team via email, webhook o integrazioni. Usa questo elenco per creare, abilitare e gestire queste regole.",
"alertingHealthChecksBannerTitle": "Monitora Salute & Risorse",
"alertingHealthChecksBannerDescription": "I controlli di salute sono monitor HTTP o TCP che definisci una volta. Puoi poi usarli come fonti nelle regole di allerta così ricevi avvisi quando un obiettivo diventa sano o non sano. I controlli di salute sulle risorse appaiono anche qui.",
"standaloneHcTableTitle": "Controlli di Salute",
"standaloneHcSearchPlaceholder": "Cerca controlli di salute…",
"standaloneHcAddButton": "Crea Controllo di Salute",
"standaloneHcCreateTitle": "Crea Controllo di Salute",
"standaloneHcEditTitle": "Modifica Controllo di Salute",
"standaloneHcDescription": "Configura un controllo di salute HTTP o TCP da utilizzare nelle regole di allerta.",
"standaloneHcNameLabel": "Nome",
"standaloneHcNamePlaceholder": "Il mio Monitor HTTP",
"standaloneHcDeleteTitle": "Elimina controllo di salute",
"standaloneHcDeleteQuestion": "Si prega di confermare di voler eliminare questo controllo di integrità.",
"standaloneHcDeleted": "Controllo di salute eliminato",
"standaloneHcSaved": "Controllo di salute salvato",
"standaloneHcColumnHealth": "Salute",
"standaloneHcColumnMode": "Modalità",
"standaloneHcColumnTarget": "Target",
"standaloneHcHealthStateHealthy": "Sano",
"standaloneHcHealthStateUnhealthy": "Non Sano",
"standaloneHcHealthStateUnknown": "Sconosciuto",
"standaloneHcFilterAnySite": "Tutti i siti",
"standaloneHcFilterAnyResource": "Tutte le risorse",
"standaloneHcFilterMode": "Modalità",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Salute",
"standaloneHcFilterEnabled": "Abilitato",
"standaloneHcFilterEnabledOn": "Abilitato",
"standaloneHcFilterEnabledOff": "Disabilitato",
"standaloneHcFilterSiteIdFallback": "Sito {id}",
"standaloneHcFilterResourceIdFallback": "Risorsa {id}",
"blueprints": "Progetti", "blueprints": "Progetti",
"blueprintsDescription": "Applica le configurazioni dichiarative e visualizza le partite precedenti", "blueprintsDescription": "Applica le configurazioni dichiarative e visualizza le partite precedenti",
"blueprintAdd": "Aggiungi Progetto", "blueprintAdd": "Aggiungi Progetto",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Crea l'account amministratore del server iniziale. Può esistere solo un amministratore del server. È sempre possibile modificare queste credenziali in seguito.", "initialSetupDescription": "Crea l'account amministratore del server iniziale. Può esistere solo un amministratore del server. È sempre possibile modificare queste credenziali in seguito.",
"createAdminAccount": "Crea Account Admin", "createAdminAccount": "Crea Account Admin",
"setupErrorCreateAdmin": "Si è verificato un errore durante la creazione dell'account amministratore del server.", "setupErrorCreateAdmin": "Si è verificato un errore durante la creazione dell'account amministratore del server.",
"certificateStatus": "Stato del Certificato", "certificateStatus": "Certificato",
"certificateStatusAutoRefreshHint": "Lo stato si aggiorna automaticamente.",
"loading": "Caricamento", "loading": "Caricamento",
"loadingAnalytics": "Caricamento Delle Analisi", "loadingAnalytics": "Caricamento Delle Analisi",
"restart": "Riavvia", "restart": "Riavvia",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Visualizza Note Di Rilascio", "pangolinUpdateAvailableReleaseNotes": "Visualizza Note Di Rilascio",
"newtUpdateAvailable": "Aggiornamento Disponibile", "newtUpdateAvailable": "Aggiornamento Disponibile",
"newtUpdateAvailableInfo": "È disponibile una nuova versione di Newt. Si prega di aggiornare all'ultima versione per la migliore esperienza.", "newtUpdateAvailableInfo": "È disponibile una nuova versione di Newt. Si prega di aggiornare all'ultima versione per la migliore esperienza.",
"pangolinNodeUpdateAvailableInfo": "È disponibile una nuova versione di Pangolin Node. Si prega di aggiornare all'ultima versione per la migliore esperienza.",
"domainPickerEnterDomain": "Dominio", "domainPickerEnterDomain": "Dominio",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Inserisci il dominio completo della risorsa per vedere le opzioni disponibili.", "domainPickerDescription": "Inserisci il dominio completo della risorsa per vedere le opzioni disponibili.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Configura Controllo Salute", "configureHealthCheck": "Configura Controllo Salute",
"configureHealthCheckDescription": "Imposta il monitoraggio della salute per {target}", "configureHealthCheckDescription": "Imposta il monitoraggio della salute per {target}",
"enableHealthChecks": "Abilita i Controlli di Salute", "enableHealthChecks": "Abilita i Controlli di Salute",
"healthCheckDisabledStateDescription": "Quando disabilitato, il sito non eseguirà controlli di integrità e lo stato sarà considerato sconosciuto.",
"enableHealthChecksDescription": "Monitorare lo stato di salute di questo obiettivo. Se necessario, è possibile monitorare un endpoint diverso da quello del bersaglio.", "enableHealthChecksDescription": "Monitorare lo stato di salute di questo obiettivo. Se necessario, è possibile monitorare un endpoint diverso da quello del bersaglio.",
"healthScheme": "Metodo", "healthScheme": "Metodo",
"healthSelectScheme": "Seleziona Metodo", "healthSelectScheme": "Seleziona Metodo",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "L'intervallo del controllo deve essere almeno di 5 secondi", "healthCheckIntervalMin": "L'intervallo del controllo deve essere almeno di 5 secondi",
"healthCheckTimeoutMin": "Il timeout deve essere di almeno 1 secondo", "healthCheckTimeoutMin": "Il timeout deve essere di almeno 1 secondo",
"healthCheckRetryMin": "I tentativi di riprova devono essere almeno 1", "healthCheckRetryMin": "I tentativi di riprova devono essere almeno 1",
"healthCheckMode": "Verifica Modalità",
"healthCheckStrategy": "Strategia",
"healthCheckModeDescription": "La modalità TCP verifica solo la connettività. La modalità HTTP valida la risposta HTTP.",
"healthyThreshold": "Soglia di salute",
"healthyThresholdDescription": "Successi consecutivi necessari prima di contrassegnare come sano.",
"unhealthyThreshold": "Soglia non sana",
"unhealthyThresholdDescription": "Fallimenti consecutivi richiesti prima di contrassegnare come non sano.",
"healthCheckHealthyThresholdMin": "La soglia di salute deve essere almeno 1",
"healthCheckUnhealthyThresholdMin": "La soglia non sana deve essere almeno 1",
"httpMethod": "Metodo HTTP", "httpMethod": "Metodo HTTP",
"selectHttpMethod": "Seleziona metodo HTTP", "selectHttpMethod": "Seleziona metodo HTTP",
"domainPickerSubdomainLabel": "Sottodominio", "domainPickerSubdomainLabel": "Sottodominio",
"domainPickerWildcard": "Jolly",
"domainPickerWildcardPaidOnly": "Sotto-domini wildcard sono una funzione a pagamento. Si prega di aggiornare per accedere a questa funzione.",
"domainPickerBaseDomainLabel": "Dominio Base", "domainPickerBaseDomainLabel": "Dominio Base",
"domainPickerSearchDomains": "Cerca domini...", "domainPickerSearchDomains": "Cerca domini...",
"domainPickerNoDomainsFound": "Nessun dominio trovato", "domainPickerNoDomainsFound": "Nessun dominio trovato",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Questo indirizzo fa parte della subnet di utilità dell'organizzazione. È usato per risolvere i record alias usando la risoluzione DNS interna.", "resourcesTableAliasAddressInfo": "Questo indirizzo fa parte della subnet di utilità dell'organizzazione. È usato per risolvere i record alias usando la risoluzione DNS interna.",
"resourcesTableClients": "Client", "resourcesTableClients": "Client",
"resourcesTableAndOnlyAccessibleInternally": "e sono accessibili solo internamente quando connessi con un client.", "resourcesTableAndOnlyAccessibleInternally": "e sono accessibili solo internamente quando connessi con un client.",
"resourcesTableNoTargets": "Nessun obiettivo",
"resourcesTableHealthy": "Sano", "resourcesTableHealthy": "Sano",
"resourcesTableDegraded": "Degraded", "resourcesTableDegraded": "Degraded",
"resourcesTableOffline": "Offline", "resourcesTableUnhealthy": "Non Sano",
"resourcesTableUnknown": "Sconosciuto", "resourcesTableUnknown": "Sconosciuto",
"resourcesTableNotMonitored": "Non monitorato", "resourcesTableNotMonitored": "Non monitorato",
"resourcesTableNoTargets": "Nessun obiettivo",
"editInternalResourceDialogEditClientResource": "Modifica Risorse Private", "editInternalResourceDialogEditClientResource": "Modifica Risorse Private",
"editInternalResourceDialogUpdateResourceProperties": "Aggiorna la configurazione delle risorse e i controlli di accesso per {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Aggiorna la configurazione delle risorse e i controlli di accesso per {resourceName}",
"editInternalResourceDialogResourceProperties": "Proprietà della Risorsa", "editInternalResourceDialogResourceProperties": "Proprietà della Risorsa",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Porta", "editInternalResourceDialogModePort": "Porta",
"editInternalResourceDialogModeHost": "Host", "editInternalResourceDialogModeHost": "Host",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Metodo HTTP",
"editInternalResourceDialogEnableSsl": "Abilitare SSL",
"editInternalResourceDialogEnableSslDescription": "Abilita la crittografia SSL/TLS per connessioni HTTPS sicure alla destinazione.",
"editInternalResourceDialogDestination": "Destinazione", "editInternalResourceDialogDestination": "Destinazione",
"editInternalResourceDialogDestinationHostDescription": "L'indirizzo IP o il nome host della risorsa nella rete del sito.", "editInternalResourceDialogDestinationHostDescription": "L'indirizzo IP o il nome host della risorsa nella rete del sito.",
"editInternalResourceDialogDestinationIPDescription": "L'indirizzo IP o hostname della risorsa nella rete del sito.", "editInternalResourceDialogDestinationIPDescription": "L'indirizzo IP o hostname della risorsa nella rete del sito.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Nome", "createInternalResourceDialogName": "Nome",
"createInternalResourceDialogSite": "Sito", "createInternalResourceDialogSite": "Sito",
"selectSite": "Seleziona sito...", "selectSite": "Seleziona sito...",
"multiSitesSelectorSitesCount": "{count, plural, one {# sito} other {# siti}}",
"noSitesFound": "Nessun sito trovato.", "noSitesFound": "Nessun sito trovato.",
"createInternalResourceDialogProtocol": "Protocollo", "createInternalResourceDialogProtocol": "Protocollo",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Porta", "createInternalResourceDialogModePort": "Porta",
"createInternalResourceDialogModeHost": "Host", "createInternalResourceDialogModeHost": "Host",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Metodo HTTP",
"createInternalResourceDialogScheme": "Metodo HTTP",
"createInternalResourceDialogEnableSsl": "Abilitare SSL",
"createInternalResourceDialogEnableSslDescription": "Abilita la crittografia SSL/TLS per connessioni HTTPS sicure alla destinazione.",
"createInternalResourceDialogDestination": "Destinazione", "createInternalResourceDialogDestination": "Destinazione",
"createInternalResourceDialogDestinationHostDescription": "L'indirizzo IP o il nome host della risorsa nella rete del sito.", "createInternalResourceDialogDestinationHostDescription": "L'indirizzo IP o il nome host della risorsa nella rete del sito.",
"createInternalResourceDialogDestinationCidrDescription": "La gamma CIDR della risorsa sulla rete del sito.", "createInternalResourceDialogDestinationCidrDescription": "La gamma CIDR della risorsa sulla rete del sito.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Un alias DNS interno opzionale per questa risorsa.", "createInternalResourceDialogAliasDescription": "Un alias DNS interno opzionale per questa risorsa.",
"internalResourceDownstreamSchemeRequired": "Il metodo è richiesto per risorse HTTP",
"internalResourceHttpPortRequired": "Porta di destinazione richiesta per risorse HTTP",
"siteConfiguration": "Configurazione", "siteConfiguration": "Configurazione",
"siteAcceptClientConnections": "Accetta Connessioni Client", "siteAcceptClientConnections": "Accetta Connessioni Client",
"siteAcceptClientConnectionsDescription": "Consenti ai dispositivi utente e ai client di accedere alle risorse di questo sito. Questo può essere modificato in seguito.", "siteAcceptClientConnectionsDescription": "Consenti ai dispositivi utente e ai client di accedere alle risorse di questo sito. Questo può essere modificato in seguito.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Verificato", "domainPickerVerified": "Verificato",
"domainPickerUnverified": "Non Verificato", "domainPickerUnverified": "Non Verificato",
"domainPickerManual": "Manuale", "domainPickerManual": "Manuale",
"domainPickerInvalidSubdomainStructure": "Questo sottodominio contiene caratteri o struttura non validi. Sarà sanificato automaticamente quando si salva.", "domainPickerInvalidSubdomainStructure": "I caratteri non validi saranno sanitizzati quando salvati.",
"domainPickerError": "Errore", "domainPickerError": "Errore",
"domainPickerErrorLoadDomains": "Impossibile caricare i domini dell'organizzazione", "domainPickerErrorLoadDomains": "Impossibile caricare i domini dell'organizzazione",
"domainPickerErrorCheckAvailability": "Impossibile verificare la disponibilità del dominio", "domainPickerErrorCheckAvailability": "Impossibile verificare la disponibilità del dominio",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Scegli il tuo provider di identità per continuare", "orgAuthChooseIdpDescription": "Scegli il tuo provider di identità per continuare",
"orgAuthNoIdpConfigured": "Questa organizzazione non ha nessun provider di identità configurato. Puoi accedere con la tua identità Pangolin.", "orgAuthNoIdpConfigured": "Questa organizzazione non ha nessun provider di identità configurato. Puoi accedere con la tua identità Pangolin.",
"orgAuthSignInWithPangolin": "Accedi con Pangolino", "orgAuthSignInWithPangolin": "Accedi con Pangolino",
"orgAuthSignInToOrg": "Accedi a un'organizzazione", "orgAuthSignInToOrg": "Provider di identità dell'organizzazione (SSO)",
"orgAuthSelectOrgTitle": "Accesso Organizzazione", "orgAuthSelectOrgTitle": "Accesso Organizzazione",
"orgAuthSelectOrgDescription": "Inserisci l'ID dell'organizzazione per continuare", "orgAuthSelectOrgDescription": "Inserisci l'ID dell'organizzazione per continuare",
"orgAuthOrgIdPlaceholder": "la-tua-organizzazione", "orgAuthOrgIdPlaceholder": "la-tua-organizzazione",
@@ -2429,6 +2648,7 @@
"validPassword": "Password Valida", "validPassword": "Password Valida",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Cliente Connesso",
"resourceBlocked": "Risorsa Bloccata", "resourceBlocked": "Risorsa Bloccata",
"droppedByRule": "Eliminato dalla regola", "droppedByRule": "Eliminato dalla regola",
"noSessions": "Nessuna Sessione", "noSessions": "Nessuna Sessione",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Aggiungi Clienti", "editInternalResourceDialogAddClients": "Aggiungi Clienti",
"editInternalResourceDialogDestinationLabel": "Destinazione", "editInternalResourceDialogDestinationLabel": "Destinazione",
"editInternalResourceDialogDestinationDescription": "Specifica l'indirizzo di destinazione per la risorsa interna. Può essere un hostname, indirizzo IP o un intervallo CIDR a seconda della modalità selezionata. Opzionalmente imposta un alias DNS interno per una più facile identificazione.", "editInternalResourceDialogDestinationDescription": "Specifica l'indirizzo di destinazione per la risorsa interna. Può essere un hostname, indirizzo IP o un intervallo CIDR a seconda della modalità selezionata. Opzionalmente imposta un alias DNS interno per una più facile identificazione.",
"internalResourceFormMultiSiteRoutingHelp": "Selezionare più siti consente un routing resiliente e Failover per alta disponibilità.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Scopri di più",
"editInternalResourceDialogPortRestrictionsDescription": "Limita l'accesso a porte TCP/UDP specifiche o consenti/blocca tutte le porte.", "editInternalResourceDialogPortRestrictionsDescription": "Limita l'accesso a porte TCP/UDP specifiche o consenti/blocca tutte le porte.",
"createInternalResourceDialogHttpConfiguration": "Configurazione HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Scegli il dominio che i clienti utilizzeranno per accedere a questa risorsa tramite HTTP o HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Configurazione HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Scegli il dominio che i clienti utilizzeranno per accedere a questa risorsa tramite HTTP o HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Torneremo presto! Il nostro sito è attualmente in manutenzione programmata.", "maintenancePageMessagePlaceholder": "Torneremo presto! Il nostro sito è attualmente in manutenzione programmata.",
"maintenancePageMessageDescription": "Messaggio dettagliato che spiega la manutenzione", "maintenancePageMessageDescription": "Messaggio dettagliato che spiega la manutenzione",
"maintenancePageTimeTitle": "Tempo di Completamento Stimato (Opzionale)", "maintenancePageTimeTitle": "Tempo di Completamento Stimato (Opzionale)",
"privateMaintenanceScreenTitle": "Schermo segnaposto privato",
"privateMaintenanceScreenMessage": "Questo dominio è utilizzato su una risorsa privata. Connettiti usando il client Pangolin per accedere a questa risorsa.",
"privateMaintenanceScreenSteps": "Una volta connesso, se ancora visualizzi questo messaggio, la cache DNS del tuo browser potrebbe ancora puntare al vecchio indirizzo. Per risolvere: chiudi e riapri completamente questa scheda o il tuo browser, quindi torna su questa pagina.",
"maintenanceTime": "es. 2 ore, 1 novembre alle 17:00", "maintenanceTime": "es. 2 ore, 1 novembre alle 17:00",
"maintenanceEstimatedTimeDescription": "Quando prevedi che la manutenzione sarà completata", "maintenanceEstimatedTimeDescription": "Quando prevedi che la manutenzione sarà completata",
"editDomain": "Modifica Dominio", "editDomain": "Modifica Dominio",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Aggiungi Destinazione HTTP", "httpDestAddTitle": "Aggiungi Destinazione HTTP",
"httpDestEditDescription": "Aggiorna la configurazione per questa destinazione di streaming di eventi 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.", "httpDestAddDescription": "Configura un nuovo endpoint HTTP per ricevere gli eventi della tua organizzazione.",
"S3DestEditTitle": "Modifica Destinazione",
"S3DestAddTitle": "Aggiungi Destinazione S3",
"S3DestEditDescription": "Aggiorna la configurazione per questa destinazione di streaming eventi S3.",
"S3DestAddDescription": "Configura un nuovo endpoint S3 per ricevere gli eventi della tua organizzazione.",
"datadogDestEditTitle": "Modifica Destinazione",
"datadogDestAddTitle": "Aggiungi Destinazione Datadog",
"datadogDestEditDescription": "Aggiorna la configurazione per questa destinazione di streaming eventi Datadog.",
"datadogDestAddDescription": "Configura un nuovo endpoint Datadog per ricevere gli eventi della tua organizzazione.",
"httpDestTabSettings": "Impostazioni", "httpDestTabSettings": "Impostazioni",
"httpDestTabHeaders": "Intestazioni", "httpDestTabHeaders": "Intestazioni",
"httpDestTabBody": "Corpo", "httpDestTabBody": "Corpo",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Destinazione creata con successo", "httpDestCreatedSuccess": "Destinazione creata con successo",
"httpDestUpdateFailed": "Impossibile aggiornare la destinazione", "httpDestUpdateFailed": "Impossibile aggiornare la destinazione",
"httpDestCreateFailed": "Impossibile creare la destinazione", "httpDestCreateFailed": "Impossibile creare la destinazione",
"followRedirects": "Segui i reindirizzamenti",
"followRedirectsDescription": "Segui automaticamente i reindirizzamenti HTTP per le richieste.",
"alertingErrorWebhookUrl": "Inserisci un URL valido per il webhook.",
"healthCheckStrategyHttp": "Convalida la connettività e controlla lo stato della risposta HTTP.",
"healthCheckStrategyTcp": "Verifica solo la connettività TCP, senza controllare la risposta.",
"healthCheckStrategySnmp": "Effettua una richiesta SNMP per controllare la salute di dispositivi di rete e infrastrutture.",
"healthCheckStrategyIcmp": "Utilizza richieste ICMP echo (ping) per verificare se una risorsa è raggiungibile e reattiva.",
"healthCheckTabStrategy": "Strategia",
"healthCheckTabConnection": "Connessione",
"healthCheckTabAdvanced": "Avanzato",
"healthCheckStrategyNotAvailable": "Questa strategia non è disponibile. Contatta le vendite per abilitare questa funzionalità.",
"uptime30d": "Uptime (30d)",
"idpAddActionCreateNew": "Crea nuovo provider di identità", "idpAddActionCreateNew": "Crea nuovo provider di identità",
"idpAddActionImportFromOrg": "Importa da un'altra organizzazione", "idpAddActionImportFromOrg": "Importa da un'altra organizzazione",
"idpImportDialogTitle": "Importa Provider di Identità", "idpImportDialogTitle": "Importa Provider di Identità",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Questo non può essere annullato per questa organizzazione.", "idpUnassociateWarning": "Questo non può essere annullato per questa organizzazione.",
"idpUnassociatedDescription": "Provider di identità disassociato con successo da questa organizzazione", "idpUnassociatedDescription": "Provider di identità disassociato con successo da questa organizzazione",
"idpUnassociateMenu": "Disassocia", "idpUnassociateMenu": "Disassocia",
"idpDeleteAllOrgsMenu": "Elimina" "idpDeleteAllOrgsMenu": "Elimina",
"publicIpEndpoint": "Endpoint",
"lastTriggeredAt": "Ultimo trigger",
"reject": "Rifiuta",
"uptimeDaysAgo": "{count} giorni fa",
"uptimeToday": "Oggi",
"uptimeNoDataAvailable": "Nessun dato disponibile",
"uptimeSuffix": "tempo di attività",
"uptimeDowntimeSuffix": "tempo di inattività",
"uptimeTooltipUptimeLabel": "Tempo di attività",
"uptimeTooltipDowntimeLabel": "Tempo di inattività",
"uptimeOngoing": "in corso",
"uptimeNoMonitoringData": "Nessun dato di monitoraggio",
"uptimeNoData": "Nessun dato",
"uptimeMiniBarDown": "Giù",
"uptimeSectionTitle": "Tempo di attività",
"uptimeSectionDescription": "Disponibilità negli ultimi {days} giorni",
"uptimeAddAlert": "Aggiungi Avviso",
"uptimeViewAlerts": "Visualizza Avvisi",
"uptimeCreateEmailAlert": "Crea Avviso Email",
"uptimeAlertDescriptionSite": "Ricevi notifica via email quando questo sito va offline o torna online.",
"uptimeAlertDescriptionResource": "Ricevi notifica via email quando questa risorsa va offline o torna online.",
"uptimeAlertNamePlaceholder": "Nome avviso",
"uptimeAdditionalEmails": "Email aggiuntive",
"uptimeCreateAlert": "Crea Avviso",
"uptimeAlertNoRecipients": "Nessun destinatario",
"uptimeAlertNoRecipientsDescription": "Si prega di aggiungere almeno un utente, ruolo o e-mail da notificare.",
"uptimeAlertCreated": "Avviso creato",
"uptimeAlertCreatedDescription": "Riceverai una notifica quando questo cambia stato.",
"uptimeAlertCreateFailed": "Errore nella creazione dell'avviso",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Chiave",
"webhookHeaderValuePlaceholder": "Valore",
"alertLabel": "Avviso",
"domainPickerWildcardSubdomainNotAllowed": "I sottodomini wildcard non sono permessi.",
"domainPickerWildcardCertWarning": "Le risorse wildcard potrebbero richiedere configurazioni aggiuntive per funzionare correttamente.",
"domainPickerWildcardCertWarningLink": "Scopri di più",
"health": "Salute",
"domainPendingErrorTitle": "Problema di Verifica"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "이 기능을 활성화하려면 영업팀에 연락하세요.",
"contactSalesBookDemo": "데모 예약하기",
"contactSalesOr": "또는",
"contactSalesContactUs": "문의하기",
"setupCreate": "조직, 사이트 및 리소스를 생성합니다.", "setupCreate": "조직, 사이트 및 리소스를 생성합니다.",
"headerAuthCompatibilityInfo": "인증 토큰이 없을 때 401 Unauthorized 응답을 강제하도록 설정합니다. 서버 챌린지 없이 자격 증명을 제공하지 않는 브라우저나 특정 HTTP 라이브러리에 필요합니다.", "headerAuthCompatibilityInfo": "인증 토큰이 없을 때 401 Unauthorized 응답을 강제하도록 설정합니다. 서버 챌린지 없이 자격 증명을 제공하지 않는 브라우저나 특정 HTTP 라이브러리에 필요합니다.",
"headerAuthCompatibility": "확장된 호환성", "headerAuthCompatibility": "확장된 호환성",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "유효하지 않거나 만료된 라이센스 키가 감지되었습니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.", "componentsInvalidKey": "유효하지 않거나 만료된 라이센스 키가 감지되었습니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.",
"dismiss": "해제", "dismiss": "해제",
"subscriptionViolationMessage": "현재 계획의 한계를 초과했습니다. 사이트, 사용자 또는 기타 리소스를 제거하여 계획 내에 머물도록 해결하세요.", "subscriptionViolationMessage": "현재 계획의 한계를 초과했습니다. 사이트, 사용자 또는 기타 리소스를 제거하여 계획 내에 머물도록 해결하세요.",
"trialBannerMessage": "시험 사용 기간이 {countdown} 안에 만료됩니다. 업그레이드하여 액세스를 유지하세요.",
"trialBannerExpired": "시험 사용 기간이 만료되었습니다. 지금 업그레이드하여 액세스를 복구하세요.",
"trialActive": "무료 체험 활성화됨",
"trialExpired": "체험 만료됨",
"trialHasEnded": "시험 사용 기간이 종료되었습니다.",
"trialDaysRemaining": "{count, plural, other {#일 남음}}",
"trialDaysLeftShort": "시험 사용 기간 종료까지 {days}일 남음",
"trialGoToBilling": "청구 페이지로 이동",
"subscriptionViolationViewBilling": "청구 보기", "subscriptionViolationViewBilling": "청구 보기",
"componentsLicenseViolation": "라이센스 위반: 이 서버는 {usedSites} 사이트를 사용하고 있으며, 이는 {maxSites} 사이트의 라이센스 한도를 초과합니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.", "componentsLicenseViolation": "라이센스 위반: 이 서버는 {usedSites} 사이트를 사용하고 있으며, 이는 {maxSites} 사이트의 라이센스 한도를 초과합니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.",
"componentsSupporterMessage": "{tier}로 판골린을 지원해 주셔서 감사합니다!", "componentsSupporterMessage": "{tier}로 판골린을 지원해 주셔서 감사합니다!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "구성을 복사했습니다.", "siteConfirmCopy": "구성을 복사했습니다.",
"searchSitesProgress": "사이트 검색...", "searchSitesProgress": "사이트 검색...",
"siteAdd": "사이트 추가", "siteAdd": "사이트 추가",
"sitesTableViewPublicResources": "공용 리소스 보기",
"sitesTableViewPrivateResources": "개인 리소스 보기",
"siteInstallNewt": "Newt 설치", "siteInstallNewt": "Newt 설치",
"siteInstallNewtDescription": "시스템에서 Newt 실행하기", "siteInstallNewtDescription": "시스템에서 Newt 실행하기",
"WgConfiguration": "WireGuard 구성", "WgConfiguration": "WireGuard 구성",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "사이트가 업데이트되었습니다.", "siteUpdatedDescription": "사이트가 업데이트되었습니다.",
"siteGeneralDescription": "이 사이트에 대한 일반 설정을 구성하세요.", "siteGeneralDescription": "이 사이트에 대한 일반 설정을 구성하세요.",
"siteSettingDescription": "사이트에서 설정을 구성하세요.", "siteSettingDescription": "사이트에서 설정을 구성하세요.",
"siteResourcesTab": "리소스",
"siteResourcesNoneOnSite": "이 사이트에는 아직 공용 또는 개인 리소스가 없습니다.",
"siteResourcesSectionPublic": "공용 리소스",
"siteResourcesSectionPrivate": "개인 리소스",
"siteResourcesSectionPublicDescription": "도메인이나 포트를 통해 외부에 노출되는 리소스.",
"siteResourcesSectionPrivateDescription": "사이트를 통해 개인 네트워크에서 사용할 수 있는 리소스.",
"siteResourcesViewAllPublic": "모든 리소스 보기",
"siteResourcesViewAllPrivate": "모든 리소스 보기",
"siteResourcesDialogDescription": "이 사이트와 연관된 공용 및 개인 리소스의 개요.",
"siteResourcesShowMore": "더 보기",
"siteResourcesPermissionDenied": "이 리소스를 나열할 권한이 없습니다.",
"siteResourcesEmptyPublic": "이 사이트에는 아직 대상 공용 리소스가 없습니다.",
"siteResourcesEmptyPrivate": "이 사이트와 연결된 개인 리소스가 아직 없습니다.",
"siteResourcesHowToAccess": "액세스 방법",
"siteResourcesTargetsOnSite": "이 사이트의 대상",
"siteSetting": "{siteName} 설정", "siteSetting": "{siteName} 설정",
"siteNewtTunnel": "뉴트 사이트 (추천)", "siteNewtTunnel": "뉴트 사이트 (추천)",
"siteNewtTunnelDescription": "네트워크의 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.", "siteNewtTunnelDescription": "네트워크의 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.",
@@ -267,8 +296,11 @@
"orgMissing": "조직 ID가 누락되었습니다", "orgMissing": "조직 ID가 누락되었습니다",
"orgMissingMessage": "조직 ID 없이 초대장을 재생성할 수 없습니다.", "orgMissingMessage": "조직 ID 없이 초대장을 재생성할 수 없습니다.",
"accessUsersManage": "사용자 관리", "accessUsersManage": "사용자 관리",
"accessUserManage": "사용자 관리",
"accessUsersDescription": "이 조직에 액세스할 사용자 초대 및 관리", "accessUsersDescription": "이 조직에 액세스할 사용자 초대 및 관리",
"accessUsersSearch": "사용자 검색...", "accessUsersSearch": "사용자 검색...",
"accessUsersRoleFilterCount": "{count, plural, other {# 역할}}",
"accessUsersRoleFilterClear": "역할 필터 지우기",
"accessUserCreate": "사용자 생성", "accessUserCreate": "사용자 생성",
"accessUserRemove": "사용자 제거", "accessUserRemove": "사용자 제거",
"username": "사용자 이름", "username": "사용자 이름",
@@ -731,6 +763,7 @@
"newtEndpoint": "엔드포인트", "newtEndpoint": "엔드포인트",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "비밀", "newtSecretKey": "비밀",
"newtVersion": "버전",
"architecture": "아키텍처", "architecture": "아키텍처",
"sites": "사이트", "sites": "사이트",
"siteWgAnyClients": "WireGuard 클라이언트를 사용하여 연결하십시오. 피어 IP를 사용하여 내부 리소스에 접근해야 합니다.", "siteWgAnyClients": "WireGuard 클라이언트를 사용하여 연결하십시오. 피어 IP를 사용하여 내부 리소스에 접근해야 합니다.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "로그 보기", "actionViewLogs": "로그 보기",
"noneSelected": "선택된 항목 없음", "noneSelected": "선택된 항목 없음",
"orgNotFound2": "조직이 없습니다.", "orgNotFound2": "조직이 없습니다.",
"search": "검색…",
"searchPlaceholder": "검색...", "searchPlaceholder": "검색...",
"emptySearchOptions": "옵션이 없습니다", "emptySearchOptions": "옵션이 없습니다",
"create": "생성", "create": "생성",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "관리", "sidebarGeneral": "관리",
"sidebarLogAndAnalytics": "로그 & 통계", "sidebarLogAndAnalytics": "로그 & 통계",
"sidebarBluePrints": "청사진", "sidebarBluePrints": "청사진",
"sidebarAlerting": "알림",
"sidebarHealthChecks": "상태 확인",
"sidebarOrganization": "조직", "sidebarOrganization": "조직",
"sidebarManagement": "관리", "sidebarManagement": "관리",
"sidebarBillingAndLicenses": "결제 및 라이선스", "sidebarBillingAndLicenses": "결제 및 라이선스",
"sidebarLogsAnalytics": "분석", "sidebarLogsAnalytics": "분석",
"alertingTitle": "알림",
"alertingDescription": "알림에 대한 소스, 트리거 및 작업 정의",
"alertingRules": "알림 규칙",
"alertingSearchRules": "규칙 검색…",
"alertingAddRule": "규칙 생성",
"alertingColumnSource": "소스",
"alertingColumnTrigger": "트리거",
"alertingColumnActions": "작업",
"alertingColumnEnabled": "활성화됨",
"alertingDeleteQuestion": "이 알림 규칙을 삭제하겠습니까.",
"alertingDeleteRule": "알림 규칙 삭제",
"alertingRuleDeleted": "알림 규칙 삭제됨",
"alertingRuleSaved": "알림 규칙 저장됨",
"alertingRuleSavedCreatedDescription": "새 알림 규칙이 생성되었습니다. 이 페이지에서 계속 편집할 수 있습니다.",
"alertingRuleSavedUpdatedDescription": "이 알림 규칙에 대한 변경 사항이 저장되었습니다.",
"alertingEditRule": "알림 규칙 편집",
"alertingCreateRule": "알림 규칙 생성",
"alertingRuleCredenzaDescription": "무엇을 감시할지, 언제 알릴지, 어떻게 알릴지를 선택하세요.",
"alertingRuleNamePlaceholder": "프로덕션 사이트 중단",
"alertingRuleEnabled": "규칙 활성화됨",
"alertingSectionSource": "소스",
"alertingSourceType": "소스 유형",
"alertingSourceSite": "사이트",
"alertingSourceHealthCheck": "상태 확인",
"alertingPickSites": "사이트들",
"alertingPickHealthChecks": "상태 확인들",
"alertingPickResources": "리소스들",
"alertingAllSites": "모든 사이트",
"alertingAllSitesDescription": "모든 사이트에서 알림 발동",
"alertingSpecificSites": "특정 사이트",
"alertingSpecificSitesDescription": "감시할 특정 사이트를 선택하세요",
"alertingAllHealthChecks": "모든 상태 확인",
"alertingAllHealthChecksDescription": "모든 상태 확인에 대한 알림 발동",
"alertingSpecificHealthChecks": "특정 상태 확인",
"alertingSpecificHealthChecksDescription": "감시할 특정 상태 확인을 선택하세요",
"alertingAllResources": "모든 리소스",
"alertingAllResourcesDescription": "모든 리소스에 대한 알림 발동",
"alertingSpecificResources": "특정 리소스",
"alertingSpecificResourcesDescription": "감시할 특정 리소스를 선택하세요",
"alertingSelectResources": "리소스 선택…",
"alertingResourcesSelected": "{count}개의 리소스 선택됨",
"alertingResourcesEmpty": "앞 10개의 결과에서 타겟이 있는 리소스 없음.",
"alertingSectionTrigger": "트리거",
"alertingTrigger": "언제 알림을 받을지",
"alertingTriggerSiteOnline": "사이트 온라인",
"alertingTriggerSiteOffline": "사이트 오프라인",
"alertingTriggerSiteToggle": "사이트 상태 변경",
"alertingTriggerHcHealthy": "상태 확인 정상",
"alertingTriggerHcUnhealthy": "상태 확인 비정상",
"alertingTriggerHcToggle": "상태 확인 상태 변경",
"alertingTriggerResourceHealthy": "리소스 정상",
"alertingTriggerResourceUnhealthy": "리소스 비정상",
"alertingTriggerResourceDegraded": "리소스 열화",
"alertingSearchHealthChecks": "상태 확인 검색…",
"alertingHealthChecksEmpty": "사용 가능한 상태 확인이 없습니다.",
"alertingTriggerResourceToggle": "리소스 상태 변경",
"alertingSourceResource": "리소스",
"alertingSectionActions": "작업",
"alertingAddAction": "작업 추가",
"alertingActionNotify": "이메일",
"alertingActionNotifyDescription": "사용자 또는 역할에게 이메일 알림 전송",
"alertingActionWebhook": "웹훅",
"alertingActionWebhookDescription": "사용자 정의 엔드포인트로 HTTP 요청 보내기",
"alertingExternalIntegration": "외부 통합",
"alertingExternalPagerDutyDescription": "사고 관리를 위해 PagerDuty에 알림 보내기",
"alertingExternalOpsgenieDescription": "대기 중인 관리자로 Opsgenie에 알림 보내기",
"alertingExternalServiceNowDescription": "알림 이벤트로 ServiceNow 사고 생성",
"alertingExternalIncidentIoDescription": "알림 이벤트로 Incident.io 워크플로우 트리거",
"alertingActionType": "작업 유형",
"alertingNotifyUsers": "사용자들",
"alertingNotifyRoles": "역할들",
"alertingNotifyEmails": "이메일 주소들",
"alertingEmailPlaceholder": "이메일 추가 후 Enter 키를 누르세요",
"alertingWebhookMethod": "HTTP 메소드",
"alertingWebhookSecret": "서명 비밀 (선택 사항)",
"alertingWebhookSecretPlaceholder": "HMAC 비밀",
"alertingWebhookHeaders": "헤더들",
"alertingAddHeader": "헤더 추가",
"alertingSelectSites": "사이트 선택…",
"alertingSitesSelected": "{count}개의 사이트 선택됨",
"alertingSelectHealthChecks": "상태 확인 선택…",
"alertingHealthChecksSelected": "{count}개의 상태 확인 선택됨",
"alertingNoHealthChecks": "활성화된 상태 확인이 있는 타겟 없음",
"alertingHealthCheckStub": "상태 확인 소스 선택은 아직 연결되지 않았습니다 - 트리거 및 작업을 계속 구성할 수 있습니다.",
"alertingSelectUsers": "사용자 선택…",
"alertingUsersSelected": "{count}명의 사용자 선택됨",
"alertingSelectRoles": "역할 선택…",
"alertingRolesSelected": "{count}개의 역할 선택됨",
"alertingSummarySites": "사이트 ({count})",
"alertingSummaryAllSites": "모든 사이트",
"alertingSummaryHealthChecks": "상태 확인 ({count})",
"alertingSummaryAllHealthChecks": "모든 상태 확인",
"alertingSummaryResources": "리소스 ({count})",
"alertingSummaryAllResources": "모든 리소스",
"alertingErrorNameRequired": "이름을 입력하세요",
"alertingErrorActionsMin": "최소한 하나의 작업 추가",
"alertingErrorPickSites": "최소한 하나의 사이트 선택",
"alertingErrorPickHealthChecks": "최소한 하나의 상태 확인 선택",
"alertingErrorPickResources": "최소한 하나의 리소스 선택",
"alertingErrorTriggerSite": "사이트 트리거 선택",
"alertingErrorTriggerHealth": "상태 확인 트리거 선택",
"alertingErrorTriggerResource": "리소스 트리거 선택",
"alertingErrorNotifyRecipients": "사용자, 역할 또는 최소 하나의 이메일 선택",
"alertingConfigureSource": "소스 구성",
"alertingConfigureTrigger": "트리거 구성",
"alertingConfigureActions": "작업 구성",
"alertingBackToRules": "규칙으로 돌아가기",
"alertingRuleCooldown": "냉각 시간 (초)",
"alertingRuleCooldownDescription": "같은 규칙에 대해 반복된 알림 사이의 최소 시간. 매번 발생하려면 0으로 설정하세요.",
"alertingDraftBadge": "초안 - 이 규칙을 저장하려면 저장",
"alertingSidebarHint": "여기에서 편집하려면 캔버스의 단계를 클릭하세요.",
"alertingGraphCanvasTitle": "규칙 흐름",
"alertingGraphCanvasDescription": "소스, 트리거 및 작업의 시각적 개요입니다. 노드를 선택하여 패널에서 수정할 수 있습니다.",
"alertingNodeNotConfigured": "아직 구성되지 않음",
"alertingNodeActionsCount": "{count, plural, other {# 작업}}",
"alertingNodeRoleSource": "소스",
"alertingNodeRoleTrigger": "트리거",
"alertingNodeRoleAction": "작업",
"alertingTabRules": "알림 규칙",
"alertingTabHealthChecks": "상태 확인",
"alertingRulesBannerTitle": "알림 받기",
"alertingRulesBannerDescription": "각 규칙은 무엇을 감시할지(사이트, 상태 확인, 리소스), 언제 발동할지(예: 오프라인 또는 비정상), 이메일, 웹훅 또는 통합을 통해 팀에 어떻게 알릴지를 연결합니다. 이 목록을 사용하여 규칙을 생성, 활성화 및 관리하세요.",
"alertingHealthChecksBannerTitle": "건강 및 리소스 모니터링",
"alertingHealthChecksBannerDescription": "상태 확인은 한 번 정의한 HTTP 또는 TCP 모니터링입니다. 그런 다음 이를 알림 규칙의 소스로 사용하여 타겟이 정상 또는 비정상이 되었을 때 알림을 받을 수 있습니다. 리소스의 상태 확인도 여기에 나타납니다.",
"standaloneHcTableTitle": "상태 확인",
"standaloneHcSearchPlaceholder": "상태 확인 검색…",
"standaloneHcAddButton": "상태 확인 생성",
"standaloneHcCreateTitle": "상태 확인 생성",
"standaloneHcEditTitle": "상태 확인 편집",
"standaloneHcDescription": "알림 규칙에 사용할 HTTP 또는 TCP 상태 확인을 구성하세요.",
"standaloneHcNameLabel": "이름",
"standaloneHcNamePlaceholder": "나의 HTTP 모니터",
"standaloneHcDeleteTitle": "상태 확인 삭제",
"standaloneHcDeleteQuestion": "이 상태 확인을 삭제하겠습니까.",
"standaloneHcDeleted": "상태 확인 삭제됨",
"standaloneHcSaved": "상태 확인 저장됨",
"standaloneHcColumnHealth": "건강",
"standaloneHcColumnMode": "모드",
"standaloneHcColumnTarget": "타겟",
"standaloneHcHealthStateHealthy": "정상",
"standaloneHcHealthStateUnhealthy": "비정상",
"standaloneHcHealthStateUnknown": "알 수 없음",
"standaloneHcFilterAnySite": "모든 사이트",
"standaloneHcFilterAnyResource": "모든 리소스",
"standaloneHcFilterMode": "모드",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "핑",
"standaloneHcFilterHealth": "건강",
"standaloneHcFilterEnabled": "활성화됨",
"standaloneHcFilterEnabledOn": "활성화됨",
"standaloneHcFilterEnabledOff": "비활성화됨",
"standaloneHcFilterSiteIdFallback": "사이트 {id}",
"standaloneHcFilterResourceIdFallback": "리소스 {id}",
"blueprints": "청사진", "blueprints": "청사진",
"blueprintsDescription": "선언적 구성을 적용하고 이전 실행을 봅니다", "blueprintsDescription": "선언적 구성을 적용하고 이전 실행을 봅니다",
"blueprintAdd": "청사진 추가", "blueprintAdd": "청사진 추가",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "초기 서버 관리자 계정을 생성하세요. 서버 관리자 계정은 하나만 존재할 수 있습니다. 이러한 자격 증명은 나중에 언제든지 변경할 수 있습니다.", "initialSetupDescription": "초기 서버 관리자 계정을 생성하세요. 서버 관리자 계정은 하나만 존재할 수 있습니다. 이러한 자격 증명은 나중에 언제든지 변경할 수 있습니다.",
"createAdminAccount": "관리자 계정 생성", "createAdminAccount": "관리자 계정 생성",
"setupErrorCreateAdmin": "서버 관리자 계정을 생성하는 동안 오류가 발생했습니다.", "setupErrorCreateAdmin": "서버 관리자 계정을 생성하는 동안 오류가 발생했습니다.",
"certificateStatus": "인증서 상태", "certificateStatus": "인증서",
"certificateStatusAutoRefreshHint": "상태가 자동으로 새로 고쳐집니다.",
"loading": "로딩 중", "loading": "로딩 중",
"loadingAnalytics": "분석 로딩 중", "loadingAnalytics": "분석 로딩 중",
"restart": "재시작", "restart": "재시작",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "릴리스 노트 보기", "pangolinUpdateAvailableReleaseNotes": "릴리스 노트 보기",
"newtUpdateAvailable": "업데이트 가능", "newtUpdateAvailable": "업데이트 가능",
"newtUpdateAvailableInfo": "뉴트의 새 버전이 출시되었습니다. 최상의 경험을 위해 최신 버전으로 업데이트하세요.", "newtUpdateAvailableInfo": "뉴트의 새 버전이 출시되었습니다. 최상의 경험을 위해 최신 버전으로 업데이트하세요.",
"pangolinNodeUpdateAvailableInfo": "Pangolin Node의 새 버전이 출시되었습니다. 최상의 경험을 위해 최신 버전으로 업데이트하세요.",
"domainPickerEnterDomain": "도메인", "domainPickerEnterDomain": "도메인",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "리소스의 전체 도메인을 입력하여 사용 가능한 옵션을 확인하십시오.", "domainPickerDescription": "리소스의 전체 도메인을 입력하여 사용 가능한 옵션을 확인하십시오.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "상태 확인 설정", "configureHealthCheck": "상태 확인 설정",
"configureHealthCheckDescription": "{target}에 대한 상태 모니터링 설정", "configureHealthCheckDescription": "{target}에 대한 상태 모니터링 설정",
"enableHealthChecks": "상태 확인 활성화", "enableHealthChecks": "상태 확인 활성화",
"healthCheckDisabledStateDescription": "비활성화되면 이 사이트가 상태 확인을 수행하지 않으며 상태가 알 수 없는 것으로 간주됩니다.",
"enableHealthChecksDescription": "이 대상을 모니터링하여 건강 상태를 확인하세요. 필요에 따라 대상과 다른 엔드포인트를 모니터링할 수 있습니다.", "enableHealthChecksDescription": "이 대상을 모니터링하여 건강 상태를 확인하세요. 필요에 따라 대상과 다른 엔드포인트를 모니터링할 수 있습니다.",
"healthScheme": "방법", "healthScheme": "방법",
"healthSelectScheme": "방법 선택", "healthSelectScheme": "방법 선택",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "확인 간격은 최소 5초여야 합니다.", "healthCheckIntervalMin": "확인 간격은 최소 5초여야 합니다.",
"healthCheckTimeoutMin": "시간 초과는 최소 1초여야 합니다.", "healthCheckTimeoutMin": "시간 초과는 최소 1초여야 합니다.",
"healthCheckRetryMin": "재시도 횟수는 최소 1회여야 합니다.", "healthCheckRetryMin": "재시도 횟수는 최소 1회여야 합니다.",
"healthCheckMode": "확인 모드",
"healthCheckStrategy": "전략",
"healthCheckModeDescription": "TCP 모드는 연결성만 확인합니다. HTTP 모드는 HTTP 응답을 확인합니다.",
"healthyThreshold": "건강 임계값",
"healthyThresholdDescription": "정상으로 표시되기 전에 연속 성공이 필요합니다.",
"unhealthyThreshold": "비정상 임계값",
"unhealthyThresholdDescription": "비정상으로 표시되기 전에 연속 실패가 필요합니다.",
"healthCheckHealthyThresholdMin": "정상 임계값은 최소 1 이상이어야 합니다",
"healthCheckUnhealthyThresholdMin": "비정상 임계값은 최소 1 이상이어야 합니다",
"httpMethod": "HTTP 메소드", "httpMethod": "HTTP 메소드",
"selectHttpMethod": "HTTP 메소드 선택", "selectHttpMethod": "HTTP 메소드 선택",
"domainPickerSubdomainLabel": "서브도메인", "domainPickerSubdomainLabel": "서브도메인",
"domainPickerWildcard": "와일드카드",
"domainPickerWildcardPaidOnly": "와일드카드 서브도메인은 유료 기능입니다. 이 기능에 액세스하려면 업그레이드하세요.",
"domainPickerBaseDomainLabel": "기본 도메인", "domainPickerBaseDomainLabel": "기본 도메인",
"domainPickerSearchDomains": "도메인 검색...", "domainPickerSearchDomains": "도메인 검색...",
"domainPickerNoDomainsFound": "찾을 수 없는 도메인이 없습니다", "domainPickerNoDomainsFound": "찾을 수 없는 도메인이 없습니다",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "이 주소는 조직의 유틸리티 서브넷의 일부로, 내부 DNS 해석을 사용하여 별칭 레코드를 해석하는 데 사용됩니다.", "resourcesTableAliasAddressInfo": "이 주소는 조직의 유틸리티 서브넷의 일부로, 내부 DNS 해석을 사용하여 별칭 레코드를 해석하는 데 사용됩니다.",
"resourcesTableClients": "클라이언트", "resourcesTableClients": "클라이언트",
"resourcesTableAndOnlyAccessibleInternally": "클라이언트와 연결되었을 때만 내부적으로 접근 가능합니다.", "resourcesTableAndOnlyAccessibleInternally": "클라이언트와 연결되었을 때만 내부적으로 접근 가능합니다.",
"resourcesTableNoTargets": "대상 없음",
"resourcesTableHealthy": "정상", "resourcesTableHealthy": "정상",
"resourcesTableDegraded": "저하됨", "resourcesTableDegraded": "저하됨",
"resourcesTableOffline": "오프라인", "resourcesTableUnhealthy": "비정상",
"resourcesTableUnknown": "알 수 없음", "resourcesTableUnknown": "알 수 없음",
"resourcesTableNotMonitored": "모니터링되지 않음", "resourcesTableNotMonitored": "모니터링되지 않음",
"resourcesTableNoTargets": "대상 없음",
"editInternalResourceDialogEditClientResource": "비공개 리소스 수정", "editInternalResourceDialogEditClientResource": "비공개 리소스 수정",
"editInternalResourceDialogUpdateResourceProperties": "{resourceName}의 리소스 속성과 대상 구성을 업데이트하세요", "editInternalResourceDialogUpdateResourceProperties": "{resourceName}의 리소스 속성과 대상 구성을 업데이트하세요",
"editInternalResourceDialogResourceProperties": "리소스 속성", "editInternalResourceDialogResourceProperties": "리소스 속성",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "포트", "editInternalResourceDialogModePort": "포트",
"editInternalResourceDialogModeHost": "호스트", "editInternalResourceDialogModeHost": "호스트",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "스킴",
"editInternalResourceDialogEnableSsl": "SSL 활성화",
"editInternalResourceDialogEnableSslDescription": "목적지로의 안전한 HTTPS 연결을 위한 SSL/TLS 암호화 활성화.",
"editInternalResourceDialogDestination": "대상지", "editInternalResourceDialogDestination": "대상지",
"editInternalResourceDialogDestinationHostDescription": "사이트 네트워크의 자원 IP 주소입니다.", "editInternalResourceDialogDestinationHostDescription": "사이트 네트워크의 자원 IP 주소입니다.",
"editInternalResourceDialogDestinationIPDescription": "사이트 네트워크의 자원 IP 또는 호스트 네임 주소입니다.", "editInternalResourceDialogDestinationIPDescription": "사이트 네트워크의 자원 IP 또는 호스트 네임 주소입니다.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "이름", "createInternalResourceDialogName": "이름",
"createInternalResourceDialogSite": "사이트", "createInternalResourceDialogSite": "사이트",
"selectSite": "사이트 선택...", "selectSite": "사이트 선택...",
"multiSitesSelectorSitesCount": "{count, plural, other {# 사이트}}",
"noSitesFound": "사이트를 찾을 수 없습니다.", "noSitesFound": "사이트를 찾을 수 없습니다.",
"createInternalResourceDialogProtocol": "프로토콜", "createInternalResourceDialogProtocol": "프로토콜",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "포트", "createInternalResourceDialogModePort": "포트",
"createInternalResourceDialogModeHost": "호스트", "createInternalResourceDialogModeHost": "호스트",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "스킴",
"createInternalResourceDialogScheme": "스킴",
"createInternalResourceDialogEnableSsl": "SSL 활성화",
"createInternalResourceDialogEnableSslDescription": "목적지로의 안전한 HTTPS 연결을 위한 SSL/TLS 암호화 활성화.",
"createInternalResourceDialogDestination": "대상지", "createInternalResourceDialogDestination": "대상지",
"createInternalResourceDialogDestinationHostDescription": "사이트 네트워크의 자원 IP 주소입니다.", "createInternalResourceDialogDestinationHostDescription": "사이트 네트워크의 자원 IP 주소입니다.",
"createInternalResourceDialogDestinationCidrDescription": "사이트 네트워크의 자원 IP 주소입니다.", "createInternalResourceDialogDestinationCidrDescription": "사이트 네트워크의 자원 IP 주소입니다.",
"createInternalResourceDialogAlias": "별칭", "createInternalResourceDialogAlias": "별칭",
"createInternalResourceDialogAliasDescription": "이 리소스에 대한 선택적 내부 DNS 별칭입니다.", "createInternalResourceDialogAliasDescription": "이 리소스에 대한 선택적 내부 DNS 별칭입니다.",
"internalResourceDownstreamSchemeRequired": "HTTP 리소스에 스킴이 필요합니다",
"internalResourceHttpPortRequired": "HTTP 리소스에 목적지 포트가 필요합니다",
"siteConfiguration": "설정", "siteConfiguration": "설정",
"siteAcceptClientConnections": "클라이언트 연결 허용", "siteAcceptClientConnections": "클라이언트 연결 허용",
"siteAcceptClientConnectionsDescription": "사용자 장치와 클라이언트가 이 사이트의 리소스에 접근할 수 있도록 허용하세요. 나중에 변경할 수 있습니다.", "siteAcceptClientConnectionsDescription": "사용자 장치와 클라이언트가 이 사이트의 리소스에 접근할 수 있도록 허용하세요. 나중에 변경할 수 있습니다.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "검증됨", "domainPickerVerified": "검증됨",
"domainPickerUnverified": "검증되지 않음", "domainPickerUnverified": "검증되지 않음",
"domainPickerManual": "수동", "domainPickerManual": "수동",
"domainPickerInvalidSubdomainStructure": "이 하위 도메인은 잘못된 문자 또는 구조를 포함하고 있습니다. 저장 시 자동으로 정리됩니다.", "domainPickerInvalidSubdomainStructure": "잘못된 문자는 저장 시 새니타이즈됩니다.",
"domainPickerError": "오류", "domainPickerError": "오류",
"domainPickerErrorLoadDomains": "조직 도메인 로드 실패", "domainPickerErrorLoadDomains": "조직 도메인 로드 실패",
"domainPickerErrorCheckAvailability": "도메인 가용성 확인 실패", "domainPickerErrorCheckAvailability": "도메인 가용성 확인 실패",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "계속하려면 신원 공급자를 선택하세요.", "orgAuthChooseIdpDescription": "계속하려면 신원 공급자를 선택하세요.",
"orgAuthNoIdpConfigured": "이 조직은 구성된 신원 공급자가 없습니다. 대신 Pangolin 아이덴티티로 로그인할 수 있습니다.", "orgAuthNoIdpConfigured": "이 조직은 구성된 신원 공급자가 없습니다. 대신 Pangolin 아이덴티티로 로그인할 수 있습니다.",
"orgAuthSignInWithPangolin": "Pangolin으로 로그인", "orgAuthSignInWithPangolin": "Pangolin으로 로그인",
"orgAuthSignInToOrg": "조직에 로그인", "orgAuthSignInToOrg": "조직 아이덴티티 제공자 (SSO)",
"orgAuthSelectOrgTitle": "조직 로그인", "orgAuthSelectOrgTitle": "조직 로그인",
"orgAuthSelectOrgDescription": "계속하려면 조직 ID를 입력하십시오.", "orgAuthSelectOrgDescription": "계속하려면 조직 ID를 입력하십시오.",
"orgAuthOrgIdPlaceholder": "your-organization", "orgAuthOrgIdPlaceholder": "your-organization",
@@ -2429,6 +2648,7 @@
"validPassword": "유효한 비밀번호", "validPassword": "유효한 비밀번호",
"validEmail": "유효한 이메일", "validEmail": "유효한 이메일",
"validSSO": "유효한 SSO", "validSSO": "유효한 SSO",
"connectedClient": "연결된 클라이언트",
"resourceBlocked": "리소스 차단됨", "resourceBlocked": "리소스 차단됨",
"droppedByRule": "룰에 의해 드롭됨", "droppedByRule": "룰에 의해 드롭됨",
"noSessions": "세션 없음", "noSessions": "세션 없음",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "클라이언트 추가", "editInternalResourceDialogAddClients": "클라이언트 추가",
"editInternalResourceDialogDestinationLabel": "대상지", "editInternalResourceDialogDestinationLabel": "대상지",
"editInternalResourceDialogDestinationDescription": "내부 리소스의 목적지 주소를 지정하세요. 선택한 모드에 따라 이 주소는 호스트명, IP 주소, 또는 CIDR 범위가 될 수 있습니다. 더욱 쉽게 식별할 수 있도록 내부 DNS 별칭을 설정할 수 있습니다.", "editInternalResourceDialogDestinationDescription": "내부 리소스의 목적지 주소를 지정하세요. 선택한 모드에 따라 이 주소는 호스트명, IP 주소, 또는 CIDR 범위가 될 수 있습니다. 더욱 쉽게 식별할 수 있도록 내부 DNS 별칭을 설정할 수 있습니다.",
"internalResourceFormMultiSiteRoutingHelp": "다중 사이트를 선택하면 높은 가용성을 위해 회복력 있는 라우팅 및 페일오버가 가능해집니다.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "자세히 알아보기",
"editInternalResourceDialogPortRestrictionsDescription": "특정 TCP/UDP 포트에 대한 접근을 제한하거나 모든 포트를 허용/차단하십시오.", "editInternalResourceDialogPortRestrictionsDescription": "특정 TCP/UDP 포트에 대한 접근을 제한하거나 모든 포트를 허용/차단하십시오.",
"createInternalResourceDialogHttpConfiguration": "HTTP 구성",
"createInternalResourceDialogHttpConfigurationDescription": "이 리소스에 HTTP 또는 HTTPS로 도달하기 위한 도메인을 선택하세요.",
"editInternalResourceDialogHttpConfiguration": "HTTP 구성",
"editInternalResourceDialogHttpConfigurationDescription": "이 리소스에 HTTP 또는 HTTPS로 도달하기 위한 도메인을 선택하세요.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "곧 돌아오겠습니다! 사이트는 현재 예정된 유지보수를 진행 중입니다.", "maintenancePageMessagePlaceholder": "곧 돌아오겠습니다! 사이트는 현재 예정된 유지보수를 진행 중입니다.",
"maintenancePageMessageDescription": "유지보수를 설명하는 상세 메시지", "maintenancePageMessageDescription": "유지보수를 설명하는 상세 메시지",
"maintenancePageTimeTitle": "예상 완료 시간(선택 사항)", "maintenancePageTimeTitle": "예상 완료 시간(선택 사항)",
"privateMaintenanceScreenTitle": "프라이빗 플레이스홀더 화면",
"privateMaintenanceScreenMessage": "이 도메인은 개인 리소스에서 사용 중입니다. Pangolin 클라이언트를 사용하여 이 리소스에 액세스하세요.",
"privateMaintenanceScreenSteps": "연결된 후에도 이 메시지가 보이면 브라우저의 DNS 캐시가 여전히 이전 주소를 가리킬 수 있습니다. 이를 해결하려면 이 탭이나 브라우저를 완전히 닫고 다시 열고 이 페이지로 돌아가세요.",
"maintenanceTime": "예: 2시간, 11월 1일 오후 5시", "maintenanceTime": "예: 2시간, 11월 1일 오후 5시",
"maintenanceEstimatedTimeDescription": "유지보수가 완료될 것으로 예상되는 시간", "maintenanceEstimatedTimeDescription": "유지보수가 완료될 것으로 예상되는 시간",
"editDomain": "도메인 수정", "editDomain": "도메인 수정",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "HTTP 대상지 추가", "httpDestAddTitle": "HTTP 대상지 추가",
"httpDestEditDescription": "이 HTTP 이벤트 스트리밍 대상지의 구성을 업데이트하세요.", "httpDestEditDescription": "이 HTTP 이벤트 스트리밍 대상지의 구성을 업데이트하세요.",
"httpDestAddDescription": "조직의 이벤트 수신을 위한 새로운 HTTP 엔드포인트를 구성하세요.", "httpDestAddDescription": "조직의 이벤트 수신을 위한 새로운 HTTP 엔드포인트를 구성하세요.",
"S3DestEditTitle": "대상지 수정",
"S3DestAddTitle": "S3 대상지 추가",
"S3DestEditDescription": "이 S3 이벤트 스트리밍 대상지의 구성을 업데이트하세요.",
"S3DestAddDescription": "조직의 이벤트를 받기 위한 새로운 S3 엔드포인트를 구성하세요.",
"datadogDestEditTitle": "대상지 수정",
"datadogDestAddTitle": "Datadog 대상지 추가",
"datadogDestEditDescription": "이 Datadog 이벤트 스트리밍 대상지의 구성을 업데이트하세요.",
"datadogDestAddDescription": "조직의 이벤트를 받기 위한 새로운 Datadog 엔드포인트를 구성하세요.",
"httpDestTabSettings": "설정", "httpDestTabSettings": "설정",
"httpDestTabHeaders": "헤더", "httpDestTabHeaders": "헤더",
"httpDestTabBody": "본문", "httpDestTabBody": "본문",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "대상지가 성공적으로 생성되었습니다", "httpDestCreatedSuccess": "대상지가 성공적으로 생성되었습니다",
"httpDestUpdateFailed": "대상지를 업데이트하는 데 실패했습니다", "httpDestUpdateFailed": "대상지를 업데이트하는 데 실패했습니다",
"httpDestCreateFailed": "대상지를 생성하는 데 실패했습니다", "httpDestCreateFailed": "대상지를 생성하는 데 실패했습니다",
"followRedirects": "리디렉션 따라가기",
"followRedirectsDescription": "요청에 대해 HTTP 리디렉션을 자동으로 따라갑니다.",
"alertingErrorWebhookUrl": "웹훅의 유효한 URL을 입력하세요.",
"healthCheckStrategyHttp": "연결성을 확인하고 HTTP 응답 상태를 확인합니다.",
"healthCheckStrategyTcp": "응답을 검사하지 않고 TCP 연결성만 확인합니다.",
"healthCheckStrategySnmp": "네트워크 장비 및 인프라의 상태를 확인하기 위해 SNMP get 요청을 보냅니다.",
"healthCheckStrategyIcmp": "ICMP 에코 요청(핑)을 사용하여 리소스에 대한 접근 가능성을 확인합니다.",
"healthCheckTabStrategy": "전략",
"healthCheckTabConnection": "연결",
"healthCheckTabAdvanced": "고급",
"healthCheckStrategyNotAvailable": "이 전략은 사용할 수 없습니다. 기능을 활성화하려면 영업팀에 문의하세요.",
"uptime30d": "업타임 (30일)",
"idpAddActionCreateNew": "새로운 아이덴티티 공급자 생성", "idpAddActionCreateNew": "새로운 아이덴티티 공급자 생성",
"idpAddActionImportFromOrg": "다른 조직에서 가져오기", "idpAddActionImportFromOrg": "다른 조직에서 가져오기",
"idpImportDialogTitle": "아이덴티티 공급자 가져오기", "idpImportDialogTitle": "아이덴티티 공급자 가져오기",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "이 조직에서 이것은 되돌릴 수 없습니다.", "idpUnassociateWarning": "이 조직에서 이것은 되돌릴 수 없습니다.",
"idpUnassociatedDescription": "아이덴티티 공급자가 이 조직에서 성공적으로 연관 해제되었습니다", "idpUnassociatedDescription": "아이덴티티 공급자가 이 조직에서 성공적으로 연관 해제되었습니다",
"idpUnassociateMenu": "연관 해제", "idpUnassociateMenu": "연관 해제",
"idpDeleteAllOrgsMenu": "삭제" "idpDeleteAllOrgsMenu": "삭제",
"publicIpEndpoint": "엔드포인트",
"lastTriggeredAt": "마지막 트리거",
"reject": "거부",
"uptimeDaysAgo": "{count}일 전",
"uptimeToday": "오늘",
"uptimeNoDataAvailable": "데이터가 없습니다",
"uptimeSuffix": "가동 시간",
"uptimeDowntimeSuffix": "다운타임",
"uptimeTooltipUptimeLabel": "가동 시간",
"uptimeTooltipDowntimeLabel": "다운타임",
"uptimeOngoing": "진행 중",
"uptimeNoMonitoringData": "모니터링 데이터 없음",
"uptimeNoData": "데이터 없음",
"uptimeMiniBarDown": "중단됨",
"uptimeSectionTitle": "가동 시간",
"uptimeSectionDescription": "지난 {days}일 동안의 가용성",
"uptimeAddAlert": "알림 추가",
"uptimeViewAlerts": "알림 보기",
"uptimeCreateEmailAlert": "이메일 알림 생성",
"uptimeAlertDescriptionSite": "이 사이트가 오프라인 되거나 다시 온라인 될 때 이메일로 알림을 받습니다.",
"uptimeAlertDescriptionResource": "이 리소스가 오프라인 되거나 다시 온라인 될 때 이메일로 알림을 받습니다.",
"uptimeAlertNamePlaceholder": "알림 이름",
"uptimeAdditionalEmails": "추가 이메일",
"uptimeCreateAlert": "알림 생성",
"uptimeAlertNoRecipients": "수신자 없음",
"uptimeAlertNoRecipientsDescription": "통지를 받을 사용자, 역할 또는 이메일을 최소 한 개 추가하세요.",
"uptimeAlertCreated": "알림 생성됨",
"uptimeAlertCreatedDescription": "상태가 변경되면 통지를 받습니다.",
"uptimeAlertCreateFailed": "알림 생성 실패",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "키",
"webhookHeaderValuePlaceholder": "값",
"alertLabel": "알림",
"domainPickerWildcardSubdomainNotAllowed": "와일드카드 서브도메인은 허용되지 않습니다.",
"domainPickerWildcardCertWarning": "와일드카드 리소스는 올바르게 작동하려면 추가 구성이 필요할 수 있습니다.",
"domainPickerWildcardCertWarningLink": "자세히 알아보기",
"health": "건강",
"domainPendingErrorTitle": "확인 문제"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Kontakt salgsavdelingen for å aktivere denne funksjonen.",
"contactSalesBookDemo": "Bestill en demo",
"contactSalesOr": "eller",
"contactSalesContactUs": "kontakt oss",
"setupCreate": "Opprett organisasjonen, nettstedet og ressursene", "setupCreate": "Opprett organisasjonen, nettstedet og ressursene",
"headerAuthCompatibilityInfo": "Aktiver dette for å tvinge frem en 401 Uautorisert-respons når en autentiseringstoken mangler. Dette kreves for nettlesere eller spesifikke HTTP-biblioteker som ikke sender legitimasjon uten en serverutfordring.", "headerAuthCompatibilityInfo": "Aktiver dette for å tvinge frem en 401 Uautorisert-respons når en autentiseringstoken mangler. Dette kreves for nettlesere eller spesifikke HTTP-biblioteker som ikke sender legitimasjon uten en serverutfordring.",
"headerAuthCompatibility": "Utvidet kompatibilitet", "headerAuthCompatibility": "Utvidet kompatibilitet",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Ugyldig eller utgått lisensnøkkel oppdaget. Følg lisensvilkårene for å fortsette å kunne bruke alle funksjonene.", "componentsInvalidKey": "Ugyldig eller utgått lisensnøkkel oppdaget. Følg lisensvilkårene for å fortsette å kunne bruke alle funksjonene.",
"dismiss": "Avvis", "dismiss": "Avvis",
"subscriptionViolationMessage": "Du er utenfor grensen for gjeldende plan. Rett problemet ved å fjerne nettsteder, brukere eller andre ressurser for å bli innenfor planen din.", "subscriptionViolationMessage": "Du er utenfor grensen for gjeldende plan. Rett problemet ved å fjerne nettsteder, brukere eller andre ressurser for å bli innenfor planen din.",
"trialBannerMessage": "Din prøveperiode utløper om {countdown}. Oppgrader for å beholde tilgangen.",
"trialBannerExpired": "Prøveperioden din har utløpt. Oppgrader nå for å gjenopprette tilgangen.",
"trialActive": "Gratis prøveversjon aktiv",
"trialExpired": "Prøveperioden er utløpt",
"trialHasEnded": "Din prøveperiode har avsluttet.",
"trialDaysRemaining": "{count, plural, one {# dag igjen} other {# dager igjen}}",
"trialDaysLeftShort": "{days}d igjen av prøveperioden",
"trialGoToBilling": "Gå til faktureringssiden",
"subscriptionViolationViewBilling": "Vis fakturering", "subscriptionViolationViewBilling": "Vis fakturering",
"componentsLicenseViolation": "Lisens Brudd: Denne serveren bruker {usedSites} områder som overskrider den lisensierte grenser av {maxSites} områder. Følg lisensvilkårene for å fortsette å kunne bruke alle funksjonene.", "componentsLicenseViolation": "Lisens Brudd: Denne serveren bruker {usedSites} områder som overskrider den lisensierte grenser av {maxSites} områder. Følg lisensvilkårene for å fortsette å kunne bruke alle funksjonene.",
"componentsSupporterMessage": "Takk for at du støtter Pangolin som en {tier}!", "componentsSupporterMessage": "Takk for at du støtter Pangolin som en {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Jeg har kopiert konfigurasjonen", "siteConfirmCopy": "Jeg har kopiert konfigurasjonen",
"searchSitesProgress": "Søker i områder...", "searchSitesProgress": "Søker i områder...",
"siteAdd": "Legg til område", "siteAdd": "Legg til område",
"sitesTableViewPublicResources": "Vis offentlige ressurser",
"sitesTableViewPrivateResources": "Vis private ressurser",
"siteInstallNewt": "Installer Newt", "siteInstallNewt": "Installer Newt",
"siteInstallNewtDescription": "Få Newt til å kjøre på systemet ditt", "siteInstallNewtDescription": "Få Newt til å kjøre på systemet ditt",
"WgConfiguration": "WireGuard Konfigurasjon", "WgConfiguration": "WireGuard Konfigurasjon",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Området har blitt oppdatert.", "siteUpdatedDescription": "Området har blitt oppdatert.",
"siteGeneralDescription": "Konfigurer de generelle innstillingene for dette området", "siteGeneralDescription": "Konfigurer de generelle innstillingene for dette området",
"siteSettingDescription": "Konfigurere innstillingene på nettstedet", "siteSettingDescription": "Konfigurere innstillingene på nettstedet",
"siteResourcesTab": "Ressurser",
"siteResourcesNoneOnSite": "Dette nettstedet har ingen offentlige eller private ressurser enda.",
"siteResourcesSectionPublic": "Offentlige ressurser",
"siteResourcesSectionPrivate": "Private ressurser",
"siteResourcesSectionPublicDescription": "Ressurser eksponert eksternt gjennom domener eller porter.",
"siteResourcesSectionPrivateDescription": "Ressurser tilgjengelig på ditt private nettverk gjennom nettstedet.",
"siteResourcesViewAllPublic": "Vis alle ressurser",
"siteResourcesViewAllPrivate": "Vis alle ressurser",
"siteResourcesDialogDescription": "Oversikt over offentlige og private ressurser assosiert med dette nettstedet.",
"siteResourcesShowMore": "Vis mer",
"siteResourcesPermissionDenied": "Du har ikke tillatelse til å liste opp disse ressursene.",
"siteResourcesEmptyPublic": "Ingen offentlige ressurser retter seg mot dette nettstedet enda.",
"siteResourcesEmptyPrivate": "Ingen private ressurser er assosiert med dette nettstedet enda.",
"siteResourcesHowToAccess": "Hvordan få tilgang",
"siteResourcesTargetsOnSite": "Mål på dette nettstedet",
"siteSetting": "{siteName} Innstillinger", "siteSetting": "{siteName} Innstillinger",
"siteNewtTunnel": "Nyhetsnettsted (anbefalt)", "siteNewtTunnel": "Nyhetsnettsted (anbefalt)",
"siteNewtTunnelDescription": "Lekkeste måte å lage et inngangspunkt til ethvert nettverk. Ingen ekstra oppsett på.", "siteNewtTunnelDescription": "Lekkeste måte å lage et inngangspunkt til ethvert nettverk. Ingen ekstra oppsett på.",
@@ -267,8 +296,11 @@
"orgMissing": "Organisasjons-ID Mangler", "orgMissing": "Organisasjons-ID Mangler",
"orgMissingMessage": "Kan ikke regenerere invitasjon uten en organisasjons-ID.", "orgMissingMessage": "Kan ikke regenerere invitasjon uten en organisasjons-ID.",
"accessUsersManage": "Administrer brukere", "accessUsersManage": "Administrer brukere",
"accessUserManage": "Administrer brukere",
"accessUsersDescription": "Inviter og behandle brukere med tilgang til denne organisasjonen", "accessUsersDescription": "Inviter og behandle brukere med tilgang til denne organisasjonen",
"accessUsersSearch": "Søk etter brukere...", "accessUsersSearch": "Søk etter brukere...",
"accessUsersRoleFilterCount": "{count, plural, one {# rolle} other {# roller}}",
"accessUsersRoleFilterClear": "Fjern rollesøkefiltre",
"accessUserCreate": "Opprett bruker", "accessUserCreate": "Opprett bruker",
"accessUserRemove": "Fjern bruker", "accessUserRemove": "Fjern bruker",
"username": "Brukernavn", "username": "Brukernavn",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Sikkerhetsnøkkel", "newtSecretKey": "Sikkerhetsnøkkel",
"newtVersion": "Versjon",
"architecture": "Arkitektur", "architecture": "Arkitektur",
"sites": "Områder", "sites": "Områder",
"siteWgAnyClients": "Bruk hvilken som helst WireGuard klient til å koble til. Du må adressere interne ressurser ved hjelp av peer IP.", "siteWgAnyClients": "Bruk hvilken som helst WireGuard klient til å koble til. Du må adressere interne ressurser ved hjelp av peer IP.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Vis logger", "actionViewLogs": "Vis logger",
"noneSelected": "Ingen valgt", "noneSelected": "Ingen valgt",
"orgNotFound2": "Ingen organisasjoner funnet.", "orgNotFound2": "Ingen organisasjoner funnet.",
"search": "Søk…",
"searchPlaceholder": "Søk...", "searchPlaceholder": "Søk...",
"emptySearchOptions": "Ingen valg funnet", "emptySearchOptions": "Ingen valg funnet",
"create": "Opprett", "create": "Opprett",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Administrer", "sidebarGeneral": "Administrer",
"sidebarLogAndAnalytics": "Logg og analyser", "sidebarLogAndAnalytics": "Logg og analyser",
"sidebarBluePrints": "Tegninger", "sidebarBluePrints": "Tegninger",
"sidebarAlerting": "Varsling",
"sidebarHealthChecks": "Helsekontroller",
"sidebarOrganization": "Organisasjon", "sidebarOrganization": "Organisasjon",
"sidebarManagement": "Administrasjon", "sidebarManagement": "Administrasjon",
"sidebarBillingAndLicenses": "Fakturering & lisenser", "sidebarBillingAndLicenses": "Fakturering & lisenser",
"sidebarLogsAnalytics": "Analyser", "sidebarLogsAnalytics": "Analyser",
"alertingTitle": "Varsling",
"alertingDescription": "Definer kilder, triggere og handlinger for varsler",
"alertingRules": "Varslingsregler",
"alertingSearchRules": "Søk i regler…",
"alertingAddRule": "Opprett regel",
"alertingColumnSource": "Kilde",
"alertingColumnTrigger": "Utløser",
"alertingColumnActions": "Handlinger",
"alertingColumnEnabled": "Aktivert",
"alertingDeleteQuestion": "Vennligst bekreft at du vil slette denne varslingsregelen.",
"alertingDeleteRule": "Slett varslingsregel",
"alertingRuleDeleted": "Varslingsregel slettet",
"alertingRuleSaved": "Varslingsregel lagret",
"alertingRuleSavedCreatedDescription": "Din nye varslingsregel ble opprettet. Du kan fortsette å redigere den på denne siden.",
"alertingRuleSavedUpdatedDescription": "Endringene dine i denne varslingsregelen ble lagret.",
"alertingEditRule": "Rediger varslingsregel",
"alertingCreateRule": "Opprett varslingsregel",
"alertingRuleCredenzaDescription": "Velg hva som skal overvåkes, når det skal varsles, og hvordan du vil bli informert",
"alertingRuleNamePlaceholder": "Produksjonsside nede",
"alertingRuleEnabled": "Regel aktivert",
"alertingSectionSource": "Kilde",
"alertingSourceType": "Kildetype",
"alertingSourceSite": "Område",
"alertingSourceHealthCheck": "Helsekontroll",
"alertingPickSites": "Områder",
"alertingPickHealthChecks": "Helsekontroller",
"alertingPickResources": "Ressurser",
"alertingAllSites": "Alle områder",
"alertingAllSitesDescription": "Varsler for alle områder",
"alertingSpecificSites": "Spesifikke områder",
"alertingSpecificSitesDescription": "Velg spesifikke områder for overvåking",
"alertingAllHealthChecks": "Alle helsekontroller",
"alertingAllHealthChecksDescription": "Varsler for alle helsekontroller",
"alertingSpecificHealthChecks": "Spesifikke helsekontroller",
"alertingSpecificHealthChecksDescription": "Velg spesifikke helsekontroller for overvåking",
"alertingAllResources": "Alle ressurser",
"alertingAllResourcesDescription": "Varsler for alle ressurser",
"alertingSpecificResources": "Spesifikke ressurser",
"alertingSpecificResourcesDescription": "Velg spesifikke ressurser for overvåking",
"alertingSelectResources": "Velg ressurser…",
"alertingResourcesSelected": "{count} ressurser valgt",
"alertingResourcesEmpty": "Ingen ressurser med mål i de første 10 resultatene.",
"alertingSectionTrigger": "Utløser",
"alertingTrigger": "Når skal det varsles",
"alertingTriggerSiteOnline": "Nettsted er online",
"alertingTriggerSiteOffline": "Nettsted er offline",
"alertingTriggerSiteToggle": "Endringer i nettstedstatus",
"alertingTriggerHcHealthy": "Helsekontroll sunn",
"alertingTriggerHcUnhealthy": "Helsekontroll usunn",
"alertingTriggerHcToggle": "Endringer i helsekontrollstatus",
"alertingTriggerResourceHealthy": "Ressurs sunn",
"alertingTriggerResourceUnhealthy": "Ressurs usunn",
"alertingTriggerResourceDegraded": "Ressurs forringet",
"alertingSearchHealthChecks": "Søk i helsekontroller…",
"alertingHealthChecksEmpty": "Ingen tilgjengelige helsekontroller.",
"alertingTriggerResourceToggle": "Endringer i ressursstatus",
"alertingSourceResource": "Ressurs",
"alertingSectionActions": "Handlinger",
"alertingAddAction": "Legg til handling",
"alertingActionNotify": "E-post",
"alertingActionNotifyDescription": "Send e-postvarsler til brukere eller roller",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Send en HTTP-forespørsel til et tilpasset endepunkt",
"alertingExternalIntegration": "Ekstern integrasjon",
"alertingExternalPagerDutyDescription": "Send varsler til PagerDuty for hendelseshåndtering",
"alertingExternalOpsgenieDescription": "Rute varsler til Opsgenie for vakt håndtering",
"alertingExternalServiceNowDescription": "Opprett ServiceNow hendelser fra varslingseventer",
"alertingExternalIncidentIoDescription": "Utløs Incident.io arbeidsflyter fra varsels begivenheter",
"alertingActionType": "Handlings type",
"alertingNotifyUsers": "Brukere",
"alertingNotifyRoles": "Roller",
"alertingNotifyEmails": "E-postadresser",
"alertingEmailPlaceholder": "Legg til e-post og trykk Enter",
"alertingWebhookMethod": "HTTP-metode",
"alertingWebhookSecret": "Signeringshemmelig (valgfritt)",
"alertingWebhookSecretPlaceholder": "HMAC-hemmelig",
"alertingWebhookHeaders": "Overskrifter",
"alertingAddHeader": "Legg til header",
"alertingSelectSites": "Velg områder…",
"alertingSitesSelected": "{count} områder valgt",
"alertingSelectHealthChecks": "Velg helsekontroller…",
"alertingHealthChecksSelected": "{count} helsekontroller valgt",
"alertingNoHealthChecks": "Ingen mål med helsekontroller aktivert",
"alertingHealthCheckStub": "Valg av helsekontrollkilde er ikke sluttført ennå - du kan fortsatt konfigurere triggere og handlinger.",
"alertingSelectUsers": "Velg brukere…",
"alertingUsersSelected": "{count} brukere valgt",
"alertingSelectRoles": "Velg roller…",
"alertingRolesSelected": "{count} roller valgt",
"alertingSummarySites": "Områder ({count})",
"alertingSummaryAllSites": "Alle områder",
"alertingSummaryHealthChecks": "Helsekontroller ({count})",
"alertingSummaryAllHealthChecks": "Alle helsekoner",
"alertingSummaryResources": "Ressurser ({count})",
"alertingSummaryAllResources": "Alle ressurser",
"alertingErrorNameRequired": "Skriv inn et navn",
"alertingErrorActionsMin": "Legg til minst én handling",
"alertingErrorPickSites": "Velg minst ett område",
"alertingErrorPickHealthChecks": "Velg minst én helsekontroll",
"alertingErrorPickResources": "Velg minst én ressurs",
"alertingErrorTriggerSite": "Velg en triggetjeneste for nettsted",
"alertingErrorTriggerHealth": "Velg en triggetjeneste for helsekontroll",
"alertingErrorTriggerResource": "Velg en triggetjeneste for ressurs",
"alertingErrorNotifyRecipients": "Velg brukere, roller, eller minst én e-post",
"alertingConfigureSource": "Konfigurer kilde",
"alertingConfigureTrigger": "Konfigurer trigger",
"alertingConfigureActions": "Konfigurer handlinger",
"alertingBackToRules": "Tilbake til regler",
"alertingRuleCooldown": "Nedkjøling (sekunder)",
"alertingRuleCooldownDescription": "Minimum tid mellom gjentatte varsler for samme regel. Sett til 0 for å skyte hver gang.",
"alertingDraftBadge": "Utkast - lagre for å lagre denne regelen",
"alertingSidebarHint": "Klikk på et steg på lerretet for å redigere det her.",
"alertingGraphCanvasTitle": "Regel Flyt",
"alertingGraphCanvasDescription": "Visuell oversikt over kilde, trigger og handlinger. Velg en node for å redigere den i panelet.",
"alertingNodeNotConfigured": "Ikke konfigurert ennå",
"alertingNodeActionsCount": "{count, plural, one {# handling} other {# handlinger}}",
"alertingNodeRoleSource": "Kilde",
"alertingNodeRoleTrigger": "Utløser",
"alertingNodeRoleAction": "Handling",
"alertingTabRules": "Varslingsregler",
"alertingTabHealthChecks": "Helsekontroller",
"alertingRulesBannerTitle": "Bli varslet",
"alertingRulesBannerDescription": "Hver regel binder sammen hva som skal overvåkes (et område, helsekontroll eller ressurs), når det skal varsles (for eksempel offline eller usunn), og hvordan varsle teamet ditt via e-post, webhooks eller integrasjoner. Bruk denne listen for å opprette, aktivere og administrere disse reglene.",
"alertingHealthChecksBannerTitle": "Overvåk helse & ressurser",
"alertingHealthChecksBannerDescription": "Helsekontroller er HTTP- eller TCP-monitorer du definerer én gang. Du kan deretter bruke dem som kilder i varslingsregler slik at du blir varslet når et mål blir sunt eller usunt. Helsekontroller på ressurser vises også her.",
"standaloneHcTableTitle": "Helsekontroller",
"standaloneHcSearchPlaceholder": "Søk i helsekontroller…",
"standaloneHcAddButton": "Opprett helsekontroll",
"standaloneHcCreateTitle": "Opprett helsekontroll",
"standaloneHcEditTitle": "Rediger helsekontroll",
"standaloneHcDescription": "Konfigurer en HTTP- eller TCP-helsekontroll for bruk i varslingsregler.",
"standaloneHcNameLabel": "Navn",
"standaloneHcNamePlaceholder": "Min HTTP-monitor",
"standaloneHcDeleteTitle": "Slett helsekontroll",
"standaloneHcDeleteQuestion": "Vennligst bekreft at du vil slette denne helsekontrollen.",
"standaloneHcDeleted": "Helsekontroll slettet",
"standaloneHcSaved": "Helsekontroll lagret",
"standaloneHcColumnHealth": "Helse",
"standaloneHcColumnMode": "Modus",
"standaloneHcColumnTarget": "Mål",
"standaloneHcHealthStateHealthy": "Sunn",
"standaloneHcHealthStateUnhealthy": "Usunn",
"standaloneHcHealthStateUnknown": "Ukjent",
"standaloneHcFilterAnySite": "Alle områder",
"standaloneHcFilterAnyResource": "Alle ressurser",
"standaloneHcFilterMode": "Modus",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Helse",
"standaloneHcFilterEnabled": "Aktivert",
"standaloneHcFilterEnabledOn": "Aktivert",
"standaloneHcFilterEnabledOff": "Deaktivert",
"standaloneHcFilterSiteIdFallback": "Område {id}",
"standaloneHcFilterResourceIdFallback": "Ressurs {id}",
"blueprints": "Tegninger", "blueprints": "Tegninger",
"blueprintsDescription": "Bruk deklarative konfigurasjoner og vis tidligere kjøringer", "blueprintsDescription": "Bruk deklarative konfigurasjoner og vis tidligere kjøringer",
"blueprintAdd": "Legg til blåkopi", "blueprintAdd": "Legg til blåkopi",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Opprett den første serveradministratorkontoen. Det kan bare finnes én serveradministrator. Du kan alltid endre denne påloggingsinformasjonen senere.", "initialSetupDescription": "Opprett den første serveradministratorkontoen. Det kan bare finnes én serveradministrator. Du kan alltid endre denne påloggingsinformasjonen senere.",
"createAdminAccount": "Opprett administratorkonto", "createAdminAccount": "Opprett administratorkonto",
"setupErrorCreateAdmin": "En feil oppstod under opprettelsen av serveradministratorkontoen.", "setupErrorCreateAdmin": "En feil oppstod under opprettelsen av serveradministratorkontoen.",
"certificateStatus": "Sertifikatstatus", "certificateStatus": "Sertifikat",
"certificateStatusAutoRefreshHint": "Status oppdateres automatisk.",
"loading": "Laster inn", "loading": "Laster inn",
"loadingAnalytics": "Laster inn analyser", "loadingAnalytics": "Laster inn analyser",
"restart": "Start på nytt", "restart": "Start på nytt",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Se utgivelsesnotater", "pangolinUpdateAvailableReleaseNotes": "Se utgivelsesnotater",
"newtUpdateAvailable": "Oppdatering tilgjengelig", "newtUpdateAvailable": "Oppdatering tilgjengelig",
"newtUpdateAvailableInfo": "En ny versjon av Newt er tilgjengelig. Vennligst oppdater til den nyeste versjonen for den beste opplevelsen.", "newtUpdateAvailableInfo": "En ny versjon av Newt er tilgjengelig. Vennligst oppdater til den nyeste versjonen for den beste opplevelsen.",
"pangolinNodeUpdateAvailableInfo": "En ny versjon av Pangolin Node er tilgjengelig. Vennligst oppdater til den nyeste versjonen for den beste opplevelsen.",
"domainPickerEnterDomain": "Domene", "domainPickerEnterDomain": "Domene",
"domainPickerPlaceholder": "minapp.eksempel.no", "domainPickerPlaceholder": "minapp.eksempel.no",
"domainPickerDescription": "Skriv inn hele domenet til ressursen for å se tilgjengelige alternativer.", "domainPickerDescription": "Skriv inn hele domenet til ressursen for å se tilgjengelige alternativer.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Konfigurer Helsekontroll", "configureHealthCheck": "Konfigurer Helsekontroll",
"configureHealthCheckDescription": "Sett opp helsekontroll for {target}", "configureHealthCheckDescription": "Sett opp helsekontroll for {target}",
"enableHealthChecks": "Aktiver Helsekontroller", "enableHealthChecks": "Aktiver Helsekontroller",
"healthCheckDisabledStateDescription": "Når deaktivert, vil ikke nettstedet utføre helsekontroller, og tilstanden vil anses som ukjent.",
"enableHealthChecksDescription": "Overvåk helsen til dette målet. Du kan overvåke et annet endepunkt enn målet hvis nødvendig.", "enableHealthChecksDescription": "Overvåk helsen til dette målet. Du kan overvåke et annet endepunkt enn målet hvis nødvendig.",
"healthScheme": "Metode", "healthScheme": "Metode",
"healthSelectScheme": "Velg metode", "healthSelectScheme": "Velg metode",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Sjekkeintervallet må være minst 5 sekunder", "healthCheckIntervalMin": "Sjekkeintervallet må være minst 5 sekunder",
"healthCheckTimeoutMin": "Timeout må være minst 1 sekund", "healthCheckTimeoutMin": "Timeout må være minst 1 sekund",
"healthCheckRetryMin": "Forsøk på nytt må være minst 1", "healthCheckRetryMin": "Forsøk på nytt må være minst 1",
"healthCheckMode": "Sjekk modus",
"healthCheckStrategy": "Strategi",
"healthCheckModeDescription": "TCP-modus verifiserer kun tilkobling. HTTP-modus validerer HTTP-responsen.",
"healthyThreshold": "Sunnhets terskel",
"healthyThresholdDescription": "Suksesser på rad som kreves før man markerer som sunn.",
"unhealthyThreshold": "Usunn terskel",
"unhealthyThresholdDescription": "Feil på rad som kreves før man markerer som usunn.",
"healthCheckHealthyThresholdMin": "Sunnhet terskel må være minst 1",
"healthCheckUnhealthyThresholdMin": "Usunn terskel må være minst 1",
"httpMethod": "HTTP-metode", "httpMethod": "HTTP-metode",
"selectHttpMethod": "Velg HTTP-metode", "selectHttpMethod": "Velg HTTP-metode",
"domainPickerSubdomainLabel": "Underdomene", "domainPickerSubdomainLabel": "Underdomene",
"domainPickerWildcard": "Jokertegn",
"domainPickerWildcardPaidOnly": "Jokertegnsubdomener er en betalt funksjon. Vennligst oppgrader for å få tilgang til denne funksjonen.",
"domainPickerBaseDomainLabel": "Grunndomene", "domainPickerBaseDomainLabel": "Grunndomene",
"domainPickerSearchDomains": "Søk i domener...", "domainPickerSearchDomains": "Søk i domener...",
"domainPickerNoDomainsFound": "Ingen domener funnet", "domainPickerNoDomainsFound": "Ingen domener funnet",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Denne adressen er en del av organisasjonens undernettverk. Den brukes til å løse aliasposter ved hjelp av intern DNS-oppløsning.", "resourcesTableAliasAddressInfo": "Denne adressen er en del av organisasjonens undernettverk. Den brukes til å løse aliasposter ved hjelp av intern DNS-oppløsning.",
"resourcesTableClients": "Klienter", "resourcesTableClients": "Klienter",
"resourcesTableAndOnlyAccessibleInternally": "og er kun tilgjengelig internt når de er koblet til med en klient.", "resourcesTableAndOnlyAccessibleInternally": "og er kun tilgjengelig internt når de er koblet til med en klient.",
"resourcesTableNoTargets": "Ingen mål",
"resourcesTableHealthy": "Frisk", "resourcesTableHealthy": "Frisk",
"resourcesTableDegraded": "Nedgradert", "resourcesTableDegraded": "Nedgradert",
"resourcesTableOffline": "Frakoblet", "resourcesTableUnhealthy": "Usunn",
"resourcesTableUnknown": "Ukjent", "resourcesTableUnknown": "Ukjent",
"resourcesTableNotMonitored": "Ikke overvåket", "resourcesTableNotMonitored": "Ikke overvåket",
"resourcesTableNoTargets": "Ingen mål",
"editInternalResourceDialogEditClientResource": "Rediger Private Ressurser", "editInternalResourceDialogEditClientResource": "Rediger Private Ressurser",
"editInternalResourceDialogUpdateResourceProperties": "Oppdater ressurskonfigurasjonen og få tilgangskontroller for {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Oppdater ressurskonfigurasjonen og få tilgangskontroller for {resourceName}",
"editInternalResourceDialogResourceProperties": "Ressursegenskaper", "editInternalResourceDialogResourceProperties": "Ressursegenskaper",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Port", "editInternalResourceDialogModePort": "Port",
"editInternalResourceDialogModeHost": "Vert", "editInternalResourceDialogModeHost": "Vert",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Skjema",
"editInternalResourceDialogEnableSsl": "Aktiver SSL",
"editInternalResourceDialogEnableSslDescription": "Aktiver SSL/TLS-kryptering for sikre HTTPS-tilkoblinger til destinasjonen.",
"editInternalResourceDialogDestination": "Destinasjon", "editInternalResourceDialogDestination": "Destinasjon",
"editInternalResourceDialogDestinationHostDescription": "IP-adressen eller vertsnavnet til ressursen på nettstedets nettverk.", "editInternalResourceDialogDestinationHostDescription": "IP-adressen eller vertsnavnet til ressursen på nettstedets nettverk.",
"editInternalResourceDialogDestinationIPDescription": "IP eller vertsnavn til ressursen på nettstedets nettverk.", "editInternalResourceDialogDestinationIPDescription": "IP eller vertsnavn til ressursen på nettstedets nettverk.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Navn", "createInternalResourceDialogName": "Navn",
"createInternalResourceDialogSite": "Område", "createInternalResourceDialogSite": "Område",
"selectSite": "Velg område...", "selectSite": "Velg område...",
"multiSitesSelectorSitesCount": "{count, plural, one {# sted} other {# steder}}",
"noSitesFound": "Ingen områder funnet.", "noSitesFound": "Ingen områder funnet.",
"createInternalResourceDialogProtocol": "Protokoll", "createInternalResourceDialogProtocol": "Protokoll",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Port", "createInternalResourceDialogModePort": "Port",
"createInternalResourceDialogModeHost": "Vert", "createInternalResourceDialogModeHost": "Vert",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Skjema",
"createInternalResourceDialogScheme": "Skjema",
"createInternalResourceDialogEnableSsl": "Aktiver SSL",
"createInternalResourceDialogEnableSslDescription": "Aktiver SSL/TLS-kryptering for sikre HTTPS-tilkoblinger til destinasjonen.",
"createInternalResourceDialogDestination": "Destinasjon", "createInternalResourceDialogDestination": "Destinasjon",
"createInternalResourceDialogDestinationHostDescription": "IP-adressen eller vertsnavnet til ressursen på nettstedets nettverk.", "createInternalResourceDialogDestinationHostDescription": "IP-adressen eller vertsnavnet til ressursen på nettstedets nettverk.",
"createInternalResourceDialogDestinationCidrDescription": "CIDR-rekkevidden til ressursen på nettstedets nettverk.", "createInternalResourceDialogDestinationCidrDescription": "CIDR-rekkevidden til ressursen på nettstedets nettverk.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Et valgfritt internt DNS-alias for denne ressursen.", "createInternalResourceDialogAliasDescription": "Et valgfritt internt DNS-alias for denne ressursen.",
"internalResourceDownstreamSchemeRequired": "Skjema er påkrevd for HTTP-ressurser",
"internalResourceHttpPortRequired": "Destinasjonsport er nødvendig for HTTP-ressurser",
"siteConfiguration": "Konfigurasjon", "siteConfiguration": "Konfigurasjon",
"siteAcceptClientConnections": "Godta klientforbindelser", "siteAcceptClientConnections": "Godta klientforbindelser",
"siteAcceptClientConnectionsDescription": "Tillat brukere og klienter å få tilgang til ressurser på denne siden. Dette kan endres senere.", "siteAcceptClientConnectionsDescription": "Tillat brukere og klienter å få tilgang til ressurser på denne siden. Dette kan endres senere.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Bekreftet", "domainPickerVerified": "Bekreftet",
"domainPickerUnverified": "Uverifisert", "domainPickerUnverified": "Uverifisert",
"domainPickerManual": "Manuell", "domainPickerManual": "Manuell",
"domainPickerInvalidSubdomainStructure": "Dette underdomenet inneholder ugyldige tegn eller struktur. Det vil automatisk bli utsatt når du lagrer.", "domainPickerInvalidSubdomainStructure": "Ugyldige tegn vil bli sanitert når de er lagret.",
"domainPickerError": "Feil", "domainPickerError": "Feil",
"domainPickerErrorLoadDomains": "Kan ikke laste organisasjonens domener", "domainPickerErrorLoadDomains": "Kan ikke laste organisasjonens domener",
"domainPickerErrorCheckAvailability": "Kunne ikke kontrollere domenetilgjengelighet", "domainPickerErrorCheckAvailability": "Kunne ikke kontrollere domenetilgjengelighet",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Velg din identitet leverandør for å fortsette", "orgAuthChooseIdpDescription": "Velg din identitet leverandør for å fortsette",
"orgAuthNoIdpConfigured": "Denne organisasjonen har ikke noen identitetstjeneste konfigurert. Du kan i stedet logge inn med Pangolin identiteten din.", "orgAuthNoIdpConfigured": "Denne organisasjonen har ikke noen identitetstjeneste konfigurert. Du kan i stedet logge inn med Pangolin identiteten din.",
"orgAuthSignInWithPangolin": "Logg inn med Pangolin", "orgAuthSignInWithPangolin": "Logg inn med Pangolin",
"orgAuthSignInToOrg": "Logg inn på en organisasjon", "orgAuthSignInToOrg": "Organisasjonens identitetsleverandør (SSO)",
"orgAuthSelectOrgTitle": "Organisasjonsinnlogging", "orgAuthSelectOrgTitle": "Organisasjonsinnlogging",
"orgAuthSelectOrgDescription": "Skriv inn organisasjons-ID-en din for å fortsette", "orgAuthSelectOrgDescription": "Skriv inn organisasjons-ID-en din for å fortsette",
"orgAuthOrgIdPlaceholder": "din-organisasjon", "orgAuthOrgIdPlaceholder": "din-organisasjon",
@@ -2429,6 +2648,7 @@
"validPassword": "Gyldig passord", "validPassword": "Gyldig passord",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Tilkoblet klient",
"resourceBlocked": "Ressurs blokkert", "resourceBlocked": "Ressurs blokkert",
"droppedByRule": "Legg i regelen", "droppedByRule": "Legg i regelen",
"noSessions": "Ingen økter", "noSessions": "Ingen økter",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Legg til klienter", "editInternalResourceDialogAddClients": "Legg til klienter",
"editInternalResourceDialogDestinationLabel": "Destinasjon", "editInternalResourceDialogDestinationLabel": "Destinasjon",
"editInternalResourceDialogDestinationDescription": "Spesifiser destinasjonsadressen for den interne ressursen. Dette kan være et vertsnavn, IP-adresse eller CIDR-sjikt avhengig av valgt modus. Valgfrie oppsett av intern DNS-alias for enklere identifikasjon.", "editInternalResourceDialogDestinationDescription": "Spesifiser destinasjonsadressen for den interne ressursen. Dette kan være et vertsnavn, IP-adresse eller CIDR-sjikt avhengig av valgt modus. Valgfrie oppsett av intern DNS-alias for enklere identifikasjon.",
"internalResourceFormMultiSiteRoutingHelp": "Valg av flere nettsteder muliggjør motstandskraftig ruting og failover for høy tilgjengelighet.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Lær mer",
"editInternalResourceDialogPortRestrictionsDescription": "Begrens tilgang til spesifikke TCP/UDP-porter eller tillate/blokkere alle porter.", "editInternalResourceDialogPortRestrictionsDescription": "Begrens tilgang til spesifikke TCP/UDP-porter eller tillate/blokkere alle porter.",
"createInternalResourceDialogHttpConfiguration": "HTTP-konfigurasjon",
"createInternalResourceDialogHttpConfigurationDescription": "Velg domenet klienter vil bruke for å nå denne ressursen via HTTP eller HTTPS.",
"editInternalResourceDialogHttpConfiguration": "HTTP-konfigurasjon",
"editInternalResourceDialogHttpConfigurationDescription": "Velg domenet klienter vil bruke for å nå denne ressursen via HTTP eller HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Vi kommer snart tilbake! Vårt nettsted gjennomgår for øyeblikket planlagt vedlikehold.", "maintenancePageMessagePlaceholder": "Vi kommer snart tilbake! Vårt nettsted gjennomgår for øyeblikket planlagt vedlikehold.",
"maintenancePageMessageDescription": "Detaljert beskjed som forklarer vedlikeholdet", "maintenancePageMessageDescription": "Detaljert beskjed som forklarer vedlikeholdet",
"maintenancePageTimeTitle": "Estimert ferdigstillelsestid (Valgfritt)", "maintenancePageTimeTitle": "Estimert ferdigstillelsestid (Valgfritt)",
"privateMaintenanceScreenTitle": "Privat plassholder skjerm",
"privateMaintenanceScreenMessage": "Dette domenet brukes på en privatressurs. Koble til ved å bruke Pangolin-klienten for å få tilgang til denne ressursen.",
"privateMaintenanceScreenSteps": "Når du er koblet til, hvis du fortsatt ser denne meldingen, peker kanskje DNS-cachen til nettleseren din fortsatt til den gamle adressen. For å rette på dette: lukk og åpne denne fanen eller nettleseren på nytt, og naviger deretter tilbake til denne siden.",
"maintenanceTime": "f.eks. 2 timer, 1. november kl. 17:00", "maintenanceTime": "f.eks. 2 timer, 1. november kl. 17:00",
"maintenanceEstimatedTimeDescription": "Når du forventer at vedlikeholdet er ferdigstilt", "maintenanceEstimatedTimeDescription": "Når du forventer at vedlikeholdet er ferdigstilt",
"editDomain": "Rediger domene", "editDomain": "Rediger domene",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Legg til HTTP-destinasjon", "httpDestAddTitle": "Legg til HTTP-destinasjon",
"httpDestEditDescription": "Oppdater konfigurasjonen for denne HTTP-hendelsesstrømmedestinasjonen.", "httpDestEditDescription": "Oppdater konfigurasjonen for denne HTTP-hendelsesstrømmedestinasjonen.",
"httpDestAddDescription": "Konfigurer et nytt HTTP endepunkt for å motta organisasjonens hendelser.", "httpDestAddDescription": "Konfigurer et nytt HTTP endepunkt for å motta organisasjonens hendelser.",
"S3DestEditTitle": "Rediger destinasjon",
"S3DestAddTitle": "Legg til S3 destinasjon",
"S3DestEditDescription": "Oppdatere konfigurasjonen for denne S3-hendelsesstrømmingsdestinasjonen.",
"S3DestAddDescription": "Konfigurer et nytt S3-endepunkt for å motta organisasjonens hendelser.",
"datadogDestEditTitle": "Rediger destinasjon",
"datadogDestAddTitle": "Legg til Datadog destinasjon",
"datadogDestEditDescription": "Oppdatere konfigurasjonen for denne Datadog-hendelsesstrømmingsdestinasjonen.",
"datadogDestAddDescription": "Konfigurer et nytt Datadog-endepunkt for å motta organisasjonens hendelser.",
"httpDestTabSettings": "Innstillinger", "httpDestTabSettings": "Innstillinger",
"httpDestTabHeaders": "Overskrifter", "httpDestTabHeaders": "Overskrifter",
"httpDestTabBody": "Innhold", "httpDestTabBody": "Innhold",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Målet er opprettet", "httpDestCreatedSuccess": "Målet er opprettet",
"httpDestUpdateFailed": "Kunne ikke oppdatere destinasjon", "httpDestUpdateFailed": "Kunne ikke oppdatere destinasjon",
"httpDestCreateFailed": "Kan ikke opprette mål", "httpDestCreateFailed": "Kan ikke opprette mål",
"followRedirects": "Følg videresendinger",
"followRedirectsDescription": "Følg automatisk HTTP-videresendinger for forespørsler.",
"alertingErrorWebhookUrl": "Vennligst skriv inn en gyldig URL for webhooken.",
"healthCheckStrategyHttp": "Validerer tilkobling og sjekker HTTP-responsstatus.",
"healthCheckStrategyTcp": "Bekrefter kun TCP-tilkobling, uten å inspisere responsen.",
"healthCheckStrategySnmp": "Utfører en SNMP get-forespørsel for å sjekke helsen til nettverksenheter og infrastruktur.",
"healthCheckStrategyIcmp": "Bruker ICMP ekko forespørsler (ping) for å sjekke om en ressurs er tilgjengelig og responsiv.",
"healthCheckTabStrategy": "Strategi",
"healthCheckTabConnection": "Tilkobling",
"healthCheckTabAdvanced": "Avansert",
"healthCheckStrategyNotAvailable": "Denne strategien er ikke tilgjengelig. Vennligst kontakt salgsavdelingen for å aktivere denne funksjonen.",
"uptime30d": "Oppetid (30d)",
"idpAddActionCreateNew": "Opprett ny identitetsleverandør", "idpAddActionCreateNew": "Opprett ny identitetsleverandør",
"idpAddActionImportFromOrg": "Importer fra en annen organisasjon", "idpAddActionImportFromOrg": "Importer fra en annen organisasjon",
"idpImportDialogTitle": "Importer identitetsleverandør", "idpImportDialogTitle": "Importer identitetsleverandør",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Dette kan ikke angres for denne organisasjonen.", "idpUnassociateWarning": "Dette kan ikke angres for denne organisasjonen.",
"idpUnassociatedDescription": "Identitetsleverandør er vellykket frakoblet fra denne organisasjonen", "idpUnassociatedDescription": "Identitetsleverandør er vellykket frakoblet fra denne organisasjonen",
"idpUnassociateMenu": "Frakoble", "idpUnassociateMenu": "Frakoble",
"idpDeleteAllOrgsMenu": "Slett" "idpDeleteAllOrgsMenu": "Slett",
"publicIpEndpoint": "Endepunkt",
"lastTriggeredAt": "Siste utløste",
"reject": "Avvis",
"uptimeDaysAgo": "{count} days ago",
"uptimeToday": "I dag",
"uptimeNoDataAvailable": "Ingen data tilgjengelig",
"uptimeSuffix": "oppetid",
"uptimeDowntimeSuffix": "nedetid",
"uptimeTooltipUptimeLabel": "Oppetid",
"uptimeTooltipDowntimeLabel": "Nedetid",
"uptimeOngoing": "pågående",
"uptimeNoMonitoringData": "Ingen overvåkingsdata",
"uptimeNoData": "Ingen data",
"uptimeMiniBarDown": "Nede",
"uptimeSectionTitle": "Oppetid",
"uptimeSectionDescription": "Tilgjengelighet de siste {days} dagene",
"uptimeAddAlert": "Legg til varsling",
"uptimeViewAlerts": "Vis varsler",
"uptimeCreateEmailAlert": "Opprett e-postvarsel",
"uptimeAlertDescriptionSite": "Få beskjed på e-post når dette nettstedet går offline eller kommer tilbake online.",
"uptimeAlertDescriptionResource": "Få beskjed på e-post når denne ressursen går offline eller kommer tilbake online.",
"uptimeAlertNamePlaceholder": "Varslingsnavn",
"uptimeAdditionalEmails": "Flere e-poster",
"uptimeCreateAlert": "Opprett varsling",
"uptimeAlertNoRecipients": "Ingen mottakere",
"uptimeAlertNoRecipientsDescription": "Vennligst legg til minst én bruker, rolle, eller e-post for å varsle.",
"uptimeAlertCreated": "Varsel opprettet",
"uptimeAlertCreatedDescription": "Du vil bli varslet når dette endrer status.",
"uptimeAlertCreateFailed": "Kunne ikke opprette varsel",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Nøkkel",
"webhookHeaderValuePlaceholder": "Verdi",
"alertLabel": "Varsel",
"domainPickerWildcardSubdomainNotAllowed": "Jokertegnsubdomener er ikke tillatt.",
"domainPickerWildcardCertWarning": "Jokertegnressurser kan kreve ekstra konfigurasjon for å fungere skikkelig.",
"domainPickerWildcardCertWarningLink": "Lær mer",
"health": "Helse",
"domainPendingErrorTitle": "Verifiseringsproblem"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Neem contact op met de verkoopafdeling om deze functie in te schakelen.",
"contactSalesBookDemo": "Boek een demo",
"contactSalesOr": "of",
"contactSalesContactUs": "neem contact met ons op",
"setupCreate": "Maak de organisatie, site en bronnen aan", "setupCreate": "Maak de organisatie, site en bronnen aan",
"headerAuthCompatibilityInfo": "Schakel dit in om een 401 Niet Geautoriseerd antwoord af te dwingen wanneer een authenticatietoken ontbreekt. Dit is vereist voor browsers of specifieke HTTP-bibliotheken die geen referenties verzenden zonder een serveruitdaging.", "headerAuthCompatibilityInfo": "Schakel dit in om een 401 Niet Geautoriseerd antwoord af te dwingen wanneer een authenticatietoken ontbreekt. Dit is vereist voor browsers of specifieke HTTP-bibliotheken die geen referenties verzenden zonder een serveruitdaging.",
"headerAuthCompatibility": "Uitgebreide compatibiliteit", "headerAuthCompatibility": "Uitgebreide compatibiliteit",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Ongeldige of verlopen licentiesleutels gedetecteerd. Volg de licentievoorwaarden om alle functies te blijven gebruiken.", "componentsInvalidKey": "Ongeldige of verlopen licentiesleutels gedetecteerd. Volg de licentievoorwaarden om alle functies te blijven gebruiken.",
"dismiss": "Uitschakelen", "dismiss": "Uitschakelen",
"subscriptionViolationMessage": "U overschrijdt uw huidige abonnement. Corrigeer het probleem door sites, gebruikers of andere bronnen te verwijderen om binnen uw plan te blijven.", "subscriptionViolationMessage": "U overschrijdt uw huidige abonnement. Corrigeer het probleem door sites, gebruikers of andere bronnen te verwijderen om binnen uw plan te blijven.",
"trialBannerMessage": "Uw proefversie verloopt over {countdown}. Upgrade om toegang te behouden.",
"trialBannerExpired": "Uw proefperiode is verlopen. Upgrade nu om toegang te herstellen.",
"trialActive": "Gratis proefversie actief",
"trialExpired": "Proefversie verlopen",
"trialHasEnded": "Uw proefperiode is geëindigd.",
"trialDaysRemaining": "{count, plural, one {# dag resterend} other {# dagen resterend}}",
"trialDaysLeftShort": "{days}d over in proefversie",
"trialGoToBilling": "Ga naar factureringspagina",
"subscriptionViolationViewBilling": "Facturering bekijken", "subscriptionViolationViewBilling": "Facturering bekijken",
"componentsLicenseViolation": "Licentie overtreding: Deze server gebruikt {usedSites} sites die de gelicentieerde limiet van {maxSites} sites overschrijden. Volg de licentievoorwaarden om door te gaan met het gebruik van alle functies.", "componentsLicenseViolation": "Licentie overtreding: Deze server gebruikt {usedSites} sites die de gelicentieerde limiet van {maxSites} sites overschrijden. Volg de licentievoorwaarden om door te gaan met het gebruik van alle functies.",
"componentsSupporterMessage": "Bedankt voor het ondersteunen van Pangolin als {tier}!", "componentsSupporterMessage": "Bedankt voor het ondersteunen van Pangolin als {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Ik heb de configuratie gekopieerd", "siteConfirmCopy": "Ik heb de configuratie gekopieerd",
"searchSitesProgress": "Sites zoeken...", "searchSitesProgress": "Sites zoeken...",
"siteAdd": "Site toevoegen", "siteAdd": "Site toevoegen",
"sitesTableViewPublicResources": "Openbare bronnen bekijken",
"sitesTableViewPrivateResources": "Privébronnen bekijken",
"siteInstallNewt": "Installeer Newt", "siteInstallNewt": "Installeer Newt",
"siteInstallNewtDescription": "Laat Newt draaien op uw systeem", "siteInstallNewtDescription": "Laat Newt draaien op uw systeem",
"WgConfiguration": "WireGuard Configuratie", "WgConfiguration": "WireGuard Configuratie",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "De site is bijgewerkt.", "siteUpdatedDescription": "De site is bijgewerkt.",
"siteGeneralDescription": "Algemene instellingen voor deze site configureren", "siteGeneralDescription": "Algemene instellingen voor deze site configureren",
"siteSettingDescription": "Configureer de instellingen van de site", "siteSettingDescription": "Configureer de instellingen van de site",
"siteResourcesTab": "Bronnen",
"siteResourcesNoneOnSite": "Deze site heeft nog geen openbare of privébronnen.",
"siteResourcesSectionPublic": "Openbare bronnen",
"siteResourcesSectionPrivate": "Privébronnen",
"siteResourcesSectionPublicDescription": "Bronnen extern blootgesteld via domeinen of poorten.",
"siteResourcesSectionPrivateDescription": "Bronnen beschikbaar op uw privénetwerk via de site.",
"siteResourcesViewAllPublic": "Bekijk alle bronnen",
"siteResourcesViewAllPrivate": "Bekijk alle bronnen",
"siteResourcesDialogDescription": "Overzicht van openbare en privébronnen die geassocieerd zijn met deze site.",
"siteResourcesShowMore": "Meer weergeven",
"siteResourcesPermissionDenied": "U heeft geen toestemming om deze bronnen te vermelden.",
"siteResourcesEmptyPublic": "Geen openbare bronnen richten zich nog op deze site.",
"siteResourcesEmptyPrivate": "Er zijn nog geen privébronnen gekoppeld aan deze site.",
"siteResourcesHowToAccess": "Hoe te openen",
"siteResourcesTargetsOnSite": "Doelen op deze site",
"siteSetting": "{siteName} instellingen", "siteSetting": "{siteName} instellingen",
"siteNewtTunnel": "Nieuwste site (Aanbevolen)", "siteNewtTunnel": "Nieuwste site (Aanbevolen)",
"siteNewtTunnelDescription": "Makkelijkste manier om een ingangspunt in een netwerk te maken. Geen extra opzet.", "siteNewtTunnelDescription": "Makkelijkste manier om een ingangspunt in een netwerk te maken. Geen extra opzet.",
@@ -267,8 +296,11 @@
"orgMissing": "Organisatie-ID ontbreekt", "orgMissing": "Organisatie-ID ontbreekt",
"orgMissingMessage": "Niet in staat om de uitnodiging te regenereren zonder organisatie-ID.", "orgMissingMessage": "Niet in staat om de uitnodiging te regenereren zonder organisatie-ID.",
"accessUsersManage": "Gebruikers beheren", "accessUsersManage": "Gebruikers beheren",
"accessUserManage": "Beheer gebruiker",
"accessUsersDescription": "Nodig uit en beheer gebruikers met toegang tot deze organisatie", "accessUsersDescription": "Nodig uit en beheer gebruikers met toegang tot deze organisatie",
"accessUsersSearch": "Gebruikers zoeken...", "accessUsersSearch": "Gebruikers zoeken...",
"accessUsersRoleFilterCount": "{count, plural, one {# rol} other {# rollen}}",
"accessUsersRoleFilterClear": "Rolfilters wissen",
"accessUserCreate": "Gebruiker aanmaken", "accessUserCreate": "Gebruiker aanmaken",
"accessUserRemove": "Gebruiker verwijderen", "accessUserRemove": "Gebruiker verwijderen",
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Geheim", "newtSecretKey": "Geheim",
"newtVersion": "Versie",
"architecture": "Architectuur", "architecture": "Architectuur",
"sites": "Sites", "sites": "Sites",
"siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je zult interne bronnen moeten aanspreken met behulp van de peer IP.", "siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je zult interne bronnen moeten aanspreken met behulp van de peer IP.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Logboeken bekijken", "actionViewLogs": "Logboeken bekijken",
"noneSelected": "Niet geselecteerd", "noneSelected": "Niet geselecteerd",
"orgNotFound2": "Geen organisaties gevonden.", "orgNotFound2": "Geen organisaties gevonden.",
"search": "Zoeken…",
"searchPlaceholder": "Zoeken...", "searchPlaceholder": "Zoeken...",
"emptySearchOptions": "Geen opties gevonden", "emptySearchOptions": "Geen opties gevonden",
"create": "Aanmaken", "create": "Aanmaken",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Beheren", "sidebarGeneral": "Beheren",
"sidebarLogAndAnalytics": "Log & Analytics", "sidebarLogAndAnalytics": "Log & Analytics",
"sidebarBluePrints": "Blauwdrukken", "sidebarBluePrints": "Blauwdrukken",
"sidebarAlerting": "Waarschuwingen",
"sidebarHealthChecks": "Gezondheidscontroles",
"sidebarOrganization": "Organisatie", "sidebarOrganization": "Organisatie",
"sidebarManagement": "Beheer", "sidebarManagement": "Beheer",
"sidebarBillingAndLicenses": "Facturatie & Licenties", "sidebarBillingAndLicenses": "Facturatie & Licenties",
"sidebarLogsAnalytics": "Analyses", "sidebarLogsAnalytics": "Analyses",
"alertingTitle": "Waarschuwingen",
"alertingDescription": "Definieer bronnen, triggers en acties voor meldingen",
"alertingRules": "Waarschuwingsregels",
"alertingSearchRules": "Zoek regels…",
"alertingAddRule": "Regel aanmaken",
"alertingColumnSource": "Bron",
"alertingColumnTrigger": "Trigger",
"alertingColumnActions": "Acties",
"alertingColumnEnabled": "Ingeschakeld",
"alertingDeleteQuestion": "Bevestig alstublieft dat u deze waarschuwingsregel wilt verwijderen.",
"alertingDeleteRule": "Verwijder waarschuwingsregel",
"alertingRuleDeleted": "Waarschuwingsregel verwijderd",
"alertingRuleSaved": "Waarschuwingsregel opgeslagen",
"alertingRuleSavedCreatedDescription": "Uw nieuwe waarschuwingsregel is aangemaakt. U kunt deze op deze pagina blijven bewerken.",
"alertingRuleSavedUpdatedDescription": "Uw wijzigingen in deze waarschuwingsregel zijn opgeslagen.",
"alertingEditRule": "Bewerk waarschuwingsregel",
"alertingCreateRule": "Waarschuwingsregel aanmaken",
"alertingRuleCredenzaDescription": "Kies wat te bekijken, wanneer het moet gebeuren en hoe te waarschuwen",
"alertingRuleNamePlaceholder": "Productiesite offline",
"alertingRuleEnabled": "Regel ingeschakeld",
"alertingSectionSource": "Bron",
"alertingSourceType": "Brontype",
"alertingSourceSite": "Site",
"alertingSourceHealthCheck": "Gezondheidscontrole",
"alertingPickSites": "Sites",
"alertingPickHealthChecks": "Gezondheidscontroles",
"alertingPickResources": "Bronnen",
"alertingAllSites": "Alle sites",
"alertingAllSitesDescription": "Waarschuwing voor elke site",
"alertingSpecificSites": "Specifieke sites",
"alertingSpecificSitesDescription": "Kies specifieke sites om in de gaten te houden",
"alertingAllHealthChecks": "Alle Gezondheidscontroles",
"alertingAllHealthChecksDescription": "Waarschuwing voor elke gezondheidscontrole",
"alertingSpecificHealthChecks": "Specifieke Gezondheidscontroles",
"alertingSpecificHealthChecksDescription": "Kies specifieke gezondheidscontroles om in de gaten te houden",
"alertingAllResources": "Alle bronnen",
"alertingAllResourcesDescription": "Waarschuwing voor elke bron",
"alertingSpecificResources": "Specifieke bronnen",
"alertingSpecificResourcesDescription": "Kies specifieke bronnen om in de gaten te houden",
"alertingSelectResources": "Selecteer bronnen…",
"alertingResourcesSelected": "{count} bronnen geselecteerd",
"alertingResourcesEmpty": "Geen bronnen met doelen in de eerste 10 resultaten.",
"alertingSectionTrigger": "Trigger",
"alertingTrigger": "Wanneer te waarschuwen",
"alertingTriggerSiteOnline": "Site online",
"alertingTriggerSiteOffline": "Site offline",
"alertingTriggerSiteToggle": "Site status wijzigt",
"alertingTriggerHcHealthy": "Gezondheidscontrole gezond",
"alertingTriggerHcUnhealthy": "Gezondheidscontrole ongezond",
"alertingTriggerHcToggle": "Gezondheidscontrole status verandert",
"alertingTriggerResourceHealthy": "Bron gezond",
"alertingTriggerResourceUnhealthy": "Bron ongezond",
"alertingTriggerResourceDegraded": "Bron gedegradeerd",
"alertingSearchHealthChecks": "Zoek gezondheidscontroles…",
"alertingHealthChecksEmpty": "Geen gezondheidscontroles beschikbaar.",
"alertingTriggerResourceToggle": "Bronstatus wijzigt",
"alertingSourceResource": "Bron",
"alertingSectionActions": "Acties",
"alertingAddAction": "Actie toevoegen",
"alertingActionNotify": "E-mail",
"alertingActionNotifyDescription": "Stuur e-mailmeldingen naar gebruikers of rollen",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Stuur een HTTP-verzoek naar een aangepast eindpunt",
"alertingExternalIntegration": "Externe integratie",
"alertingExternalPagerDutyDescription": "Stuur waarschuwingen naar PagerDuty voor incidentbeheer",
"alertingExternalOpsgenieDescription": "Routeer waarschuwingen naar Opsgenie voor wachtdienstbeheer",
"alertingExternalServiceNowDescription": "Maak ServiceNow-incidenten aan vanuit waarschuwingsgebeurtenissen",
"alertingExternalIncidentIoDescription": "Trigger Incident.io workflows van waarschuwingsgebeurtenissen",
"alertingActionType": "Actietype",
"alertingNotifyUsers": "Gebruikers",
"alertingNotifyRoles": "Rollen",
"alertingNotifyEmails": "E-mailadressen",
"alertingEmailPlaceholder": "Voeg e-mail toe en druk op Enter",
"alertingWebhookMethod": "HTTP-methode",
"alertingWebhookSecret": "Ondertekengeheim (optioneel)",
"alertingWebhookSecretPlaceholder": "HMAC-geheim",
"alertingWebhookHeaders": "Headers",
"alertingAddHeader": "Header toevoegen",
"alertingSelectSites": "Selecteer sites…",
"alertingSitesSelected": "{count} sites geselecteerd",
"alertingSelectHealthChecks": "Selecteer gezondheidscontroles…",
"alertingHealthChecksSelected": "{count} gezondheidscontroles geselecteerd",
"alertingNoHealthChecks": "Geen doelen met ingeschakelde gezondheidscontroles",
"alertingHealthCheckStub": "Gezondheidscontrole brondeselectie is nog niet gekoppeld - u kunt nog steeds triggers en acties configureren.",
"alertingSelectUsers": "Selecteer gebruikers…",
"alertingUsersSelected": "{count} gebruikers geselecteerd",
"alertingSelectRoles": "Selecteer rollen…",
"alertingRolesSelected": "{count} rollen geselecteerd",
"alertingSummarySites": "Sites ({count})",
"alertingSummaryAllSites": "Alle sites",
"alertingSummaryHealthChecks": "Gezondheidscontroles ({count})",
"alertingSummaryAllHealthChecks": "Alle gezondheidscontroles",
"alertingSummaryResources": "Bronnen ({count})",
"alertingSummaryAllResources": "Alle bronnen",
"alertingErrorNameRequired": "Voer een naam in",
"alertingErrorActionsMin": "Voeg minimaal één actie toe",
"alertingErrorPickSites": "Selecteer minimaal één site",
"alertingErrorPickHealthChecks": "Selecteer minimaal één gezondheidscontrole",
"alertingErrorPickResources": "Selecteer minimaal één bron",
"alertingErrorTriggerSite": "Kies een site-trigger",
"alertingErrorTriggerHealth": "Kies een gezondheidscontrole-trigger",
"alertingErrorTriggerResource": "Kies een bron-trigger",
"alertingErrorNotifyRecipients": "Kies gebruikers, rollen of ten minste één e-mail",
"alertingConfigureSource": "Bron configureren",
"alertingConfigureTrigger": "Trigger configureren",
"alertingConfigureActions": "Acties configureren",
"alertingBackToRules": "Terug naar regels",
"alertingRuleCooldown": "Aflkoelperiode (seconden)",
"alertingRuleCooldownDescription": "Minimale tijd tussen herhaalwaarschuwingen voor dezelfde regel. Zet op 0 om elke keer te laten vuren.",
"alertingDraftBadge": "Concept - opslaan om deze regel op te slaan",
"alertingSidebarHint": "Klik op een stap in het canvas om deze hier te bewerken.",
"alertingGraphCanvasTitle": "Regelstroom",
"alertingGraphCanvasDescription": "Visueel overzicht van bron, trigger en acties. Selecteer een node om deze in het paneel te bewerken.",
"alertingNodeNotConfigured": "Nog niet geconfigureerd",
"alertingNodeActionsCount": "{count, plural, one {# actie} other {# acties}}",
"alertingNodeRoleSource": "Bron",
"alertingNodeRoleTrigger": "Trigger",
"alertingNodeRoleAction": "Actie",
"alertingTabRules": "Waarschuwingsregels",
"alertingTabHealthChecks": "Gezondheidscontroles",
"alertingRulesBannerTitle": "Meldingen ontvangen",
"alertingRulesBannerDescription": "Elke regel koppelt wat te bekijken (een site, gezondheidscontrole of bron), wanneer te vuren (bijvoorbeeld offline of ongezond), en hoe uw team te waarschuwen via e-mail, webhooks of integraties. Gebruik deze lijst om die regels te maken, in te schakelen en te beheren.",
"alertingHealthChecksBannerTitle": "Gezondheid & bronnen bewaken",
"alertingHealthChecksBannerDescription": "Gezondheidscontroles zijn HTTP- of TCP-monitoren die u één keer definieert. U kunt ze vervolgens als bronnen in waarschuwingsregels gebruiken, zodat u meldingen krijgt wanneer een doelwit gezond of ongezond wordt. Gezondheidscontroles van bronnen verschijnen ook hier.",
"standaloneHcTableTitle": "Gezondheidscontroles",
"standaloneHcSearchPlaceholder": "Zoek gezondheidscontroles…",
"standaloneHcAddButton": "Gezondheidscontrole aanmaken",
"standaloneHcCreateTitle": "Gezondheidscontrole aanmaken",
"standaloneHcEditTitle": "Gezondheidscontrole bewerken",
"standaloneHcDescription": "Configureer een HTTP- of TCP-gezondheidscontrole voor gebruik in waarschuwingsregels.",
"standaloneHcNameLabel": "Naam",
"standaloneHcNamePlaceholder": "Mijn HTTP-monitor",
"standaloneHcDeleteTitle": "Gezondheidscontrole verwijderen",
"standaloneHcDeleteQuestion": "Bevestig alstublieft dat u deze gezondheidscontrole wilt verwijderen.",
"standaloneHcDeleted": "Gezondheidscontrole verwijderd",
"standaloneHcSaved": "Gezondheidscontrole opgeslagen",
"standaloneHcColumnHealth": "Gezondheid",
"standaloneHcColumnMode": "Modus",
"standaloneHcColumnTarget": "Doelwit",
"standaloneHcHealthStateHealthy": "Gezond",
"standaloneHcHealthStateUnhealthy": "Ongezond",
"standaloneHcHealthStateUnknown": "Onbekend",
"standaloneHcFilterAnySite": "Alle sites",
"standaloneHcFilterAnyResource": "Alle bronnen",
"standaloneHcFilterMode": "Modus",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Gezondheid",
"standaloneHcFilterEnabled": "Ingeschakeld",
"standaloneHcFilterEnabledOn": "Ingeschakeld",
"standaloneHcFilterEnabledOff": "Uitgeschakeld",
"standaloneHcFilterSiteIdFallback": "Site {id}",
"standaloneHcFilterResourceIdFallback": "Bron {id}",
"blueprints": "Blauwdrukken", "blueprints": "Blauwdrukken",
"blueprintsDescription": "Gebruik declaratieve configuraties en bekijk vorige uitvoeringen.", "blueprintsDescription": "Gebruik declaratieve configuraties en bekijk vorige uitvoeringen.",
"blueprintAdd": "Blauwdruk toevoegen", "blueprintAdd": "Blauwdruk toevoegen",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Maak het eerste serverbeheeraccount aan. Er kan slechts één serverbeheerder bestaan. U kunt deze inloggegevens later altijd wijzigen.", "initialSetupDescription": "Maak het eerste serverbeheeraccount aan. Er kan slechts één serverbeheerder bestaan. U kunt deze inloggegevens later altijd wijzigen.",
"createAdminAccount": "Maak een beheeraccount aan", "createAdminAccount": "Maak een beheeraccount aan",
"setupErrorCreateAdmin": "Er is een fout opgetreden bij het maken van het serverbeheerdersaccount.", "setupErrorCreateAdmin": "Er is een fout opgetreden bij het maken van het serverbeheerdersaccount.",
"certificateStatus": "Certificaatstatus", "certificateStatus": "Certificaat",
"certificateStatusAutoRefreshHint": "Status ververst automatisch.",
"loading": "Bezig met laden", "loading": "Bezig met laden",
"loadingAnalytics": "Laden van Analytics", "loadingAnalytics": "Laden van Analytics",
"restart": "Herstarten", "restart": "Herstarten",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Uitgaveopmerkingen bekijken", "pangolinUpdateAvailableReleaseNotes": "Uitgaveopmerkingen bekijken",
"newtUpdateAvailable": "Update beschikbaar", "newtUpdateAvailable": "Update beschikbaar",
"newtUpdateAvailableInfo": "Er is een nieuwe versie van Newt beschikbaar. Update naar de nieuwste versie voor de beste ervaring.", "newtUpdateAvailableInfo": "Er is een nieuwe versie van Newt beschikbaar. Update naar de nieuwste versie voor de beste ervaring.",
"pangolinNodeUpdateAvailableInfo": "Er is een nieuwe versie van Pangolin Node beschikbaar. Update naar de nieuwste versie voor de beste ervaring.",
"domainPickerEnterDomain": "Domein", "domainPickerEnterDomain": "Domein",
"domainPickerPlaceholder": "mijnapp.voorbeeld.nl", "domainPickerPlaceholder": "mijnapp.voorbeeld.nl",
"domainPickerDescription": "Voer de volledige domein van de bron in om beschikbare opties te zien.", "domainPickerDescription": "Voer de volledige domein van de bron in om beschikbare opties te zien.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Configureer Gezondheidscontrole", "configureHealthCheck": "Configureer Gezondheidscontrole",
"configureHealthCheckDescription": "Stel gezondheid monitor voor {target} in", "configureHealthCheckDescription": "Stel gezondheid monitor voor {target} in",
"enableHealthChecks": "Inschakelen Gezondheidscontroles", "enableHealthChecks": "Inschakelen Gezondheidscontroles",
"healthCheckDisabledStateDescription": "Wanneer uitgeschakeld, zal de site geen gezondheidscontroles uitvoeren en wordt de staat als onbekend beschouwd.",
"enableHealthChecksDescription": "Controleer de gezondheid van dit doel. U kunt een ander eindpunt monitoren dan het doel indien vereist.", "enableHealthChecksDescription": "Controleer de gezondheid van dit doel. U kunt een ander eindpunt monitoren dan het doel indien vereist.",
"healthScheme": "Methode", "healthScheme": "Methode",
"healthSelectScheme": "Selecteer methode", "healthSelectScheme": "Selecteer methode",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Controle interval moet minimaal 5 seconden zijn", "healthCheckIntervalMin": "Controle interval moet minimaal 5 seconden zijn",
"healthCheckTimeoutMin": "Timeout moet minimaal 1 seconde zijn", "healthCheckTimeoutMin": "Timeout moet minimaal 1 seconde zijn",
"healthCheckRetryMin": "Herhaal pogingen moet minimaal 1 zijn", "healthCheckRetryMin": "Herhaal pogingen moet minimaal 1 zijn",
"healthCheckMode": "Controlemodus",
"healthCheckStrategy": "Strategie",
"healthCheckModeDescription": "TCP-modus verifieert alleen connectiviteit. HTTP-modus valideert de HTTP-respons.",
"healthyThreshold": "Gezonde drempel",
"healthyThresholdDescription": "Opeenvolgende successen vereist voordat gemarkeerd wordt als gezond.",
"unhealthyThreshold": "Ongezonde drempel",
"unhealthyThresholdDescription": "Opeenvolgende fouten vereist voordat gemarkeerd wordt als ongezond.",
"healthCheckHealthyThresholdMin": "Gezonde drempel moet minimaal 1 zijn",
"healthCheckUnhealthyThresholdMin": "Ongezonde drempel moet minimaal 1 zijn",
"httpMethod": "HTTP-methode", "httpMethod": "HTTP-methode",
"selectHttpMethod": "Selecteer HTTP-methode", "selectHttpMethod": "Selecteer HTTP-methode",
"domainPickerSubdomainLabel": "Subdomein", "domainPickerSubdomainLabel": "Subdomein",
"domainPickerWildcard": "Wildcard",
"domainPickerWildcardPaidOnly": "Wildcard-subdomeinen zijn een betaalde functie. Upgrade om deze functie te gebruiken.",
"domainPickerBaseDomainLabel": "Basisdomein", "domainPickerBaseDomainLabel": "Basisdomein",
"domainPickerSearchDomains": "Zoek domeinen...", "domainPickerSearchDomains": "Zoek domeinen...",
"domainPickerNoDomainsFound": "Geen domeinen gevonden", "domainPickerNoDomainsFound": "Geen domeinen gevonden",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Dit adres is onderdeel van het hulpprogramma subnet van de organisatie. Het wordt gebruikt om aliasrecords op te lossen met behulp van interne DNS-resolutie.", "resourcesTableAliasAddressInfo": "Dit adres is onderdeel van het hulpprogramma subnet van de organisatie. Het wordt gebruikt om aliasrecords op te lossen met behulp van interne DNS-resolutie.",
"resourcesTableClients": "Clienten", "resourcesTableClients": "Clienten",
"resourcesTableAndOnlyAccessibleInternally": "en zijn alleen intern toegankelijk wanneer verbonden met een client.", "resourcesTableAndOnlyAccessibleInternally": "en zijn alleen intern toegankelijk wanneer verbonden met een client.",
"resourcesTableNoTargets": "Geen doelen",
"resourcesTableHealthy": "Gezond", "resourcesTableHealthy": "Gezond",
"resourcesTableDegraded": "Verminderde", "resourcesTableDegraded": "Verminderde",
"resourcesTableOffline": "Offline", "resourcesTableUnhealthy": "Ongezond",
"resourcesTableUnknown": "onbekend", "resourcesTableUnknown": "onbekend",
"resourcesTableNotMonitored": "Niet gecontroleerd", "resourcesTableNotMonitored": "Niet gecontroleerd",
"resourcesTableNoTargets": "Geen doelen",
"editInternalResourceDialogEditClientResource": "Privépagina bewerken", "editInternalResourceDialogEditClientResource": "Privépagina bewerken",
"editInternalResourceDialogUpdateResourceProperties": "Update de resource configuratie en access control voor {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Update de resource configuratie en access control voor {resourceName}",
"editInternalResourceDialogResourceProperties": "Bron eigenschappen", "editInternalResourceDialogResourceProperties": "Bron eigenschappen",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Poort", "editInternalResourceDialogModePort": "Poort",
"editInternalResourceDialogModeHost": "Hostnaam", "editInternalResourceDialogModeHost": "Hostnaam",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Schema",
"editInternalResourceDialogEnableSsl": "SSL inschakelen",
"editInternalResourceDialogEnableSslDescription": "Schakel SSL/TLS-encryptie in voor beveiligde HTTPS-verbindingen met de bestemming.",
"editInternalResourceDialogDestination": "Bestemming", "editInternalResourceDialogDestination": "Bestemming",
"editInternalResourceDialogDestinationHostDescription": "Het IP-adres of de hostnaam van de bron op het netwerk van de site.", "editInternalResourceDialogDestinationHostDescription": "Het IP-adres of de hostnaam van de bron op het netwerk van de site.",
"editInternalResourceDialogDestinationIPDescription": "Het IP of hostnaam adres van de bron op het netwerk van de site.", "editInternalResourceDialogDestinationIPDescription": "Het IP of hostnaam adres van de bron op het netwerk van de site.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Naam", "createInternalResourceDialogName": "Naam",
"createInternalResourceDialogSite": "Site", "createInternalResourceDialogSite": "Site",
"selectSite": "Selecteer site...", "selectSite": "Selecteer site...",
"multiSitesSelectorSitesCount": "{count, plural, one {# site} other {# sites}}",
"noSitesFound": "Geen sites gevonden.", "noSitesFound": "Geen sites gevonden.",
"createInternalResourceDialogProtocol": "Protocol", "createInternalResourceDialogProtocol": "Protocol",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Poort", "createInternalResourceDialogModePort": "Poort",
"createInternalResourceDialogModeHost": "Hostnaam", "createInternalResourceDialogModeHost": "Hostnaam",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Schema",
"createInternalResourceDialogScheme": "Schema",
"createInternalResourceDialogEnableSsl": "SSL inschakelen",
"createInternalResourceDialogEnableSslDescription": "Schakel SSL/TLS-encryptie in voor beveiligde HTTPS-verbindingen met de bestemming.",
"createInternalResourceDialogDestination": "Bestemming", "createInternalResourceDialogDestination": "Bestemming",
"createInternalResourceDialogDestinationHostDescription": "Het IP-adres of de hostnaam van de bron op het netwerk van de site.", "createInternalResourceDialogDestinationHostDescription": "Het IP-adres of de hostnaam van de bron op het netwerk van de site.",
"createInternalResourceDialogDestinationCidrDescription": "Het CIDR-bereik van het document op het netwerk van de site.", "createInternalResourceDialogDestinationCidrDescription": "Het CIDR-bereik van het document op het netwerk van de site.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Een optionele interne DNS-alias voor dit document.", "createInternalResourceDialogAliasDescription": "Een optionele interne DNS-alias voor dit document.",
"internalResourceDownstreamSchemeRequired": "Schema is vereist voor HTTP-bronnen",
"internalResourceHttpPortRequired": "Bestemmingspoort is vereist voor HTTP-bronnen",
"siteConfiguration": "Configuratie", "siteConfiguration": "Configuratie",
"siteAcceptClientConnections": "Accepteer clientverbindingen", "siteAcceptClientConnections": "Accepteer clientverbindingen",
"siteAcceptClientConnectionsDescription": "Sta gebruikersapparaten en clients toegang toe tot bronnen op deze site. Dit kan later worden gewijzigd.", "siteAcceptClientConnectionsDescription": "Sta gebruikersapparaten en clients toegang toe tot bronnen op deze site. Dit kan later worden gewijzigd.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Geverifieerd", "domainPickerVerified": "Geverifieerd",
"domainPickerUnverified": "Ongeverifieerd", "domainPickerUnverified": "Ongeverifieerd",
"domainPickerManual": "Handleiding", "domainPickerManual": "Handleiding",
"domainPickerInvalidSubdomainStructure": "Dit subdomein bevat ongeldige tekens of structuur. Het zal automatisch worden gesaneerd wanneer u opslaat.", "domainPickerInvalidSubdomainStructure": "Ongeldige tekens worden gesaneerd bij het opslaan.",
"domainPickerError": "Foutmelding", "domainPickerError": "Foutmelding",
"domainPickerErrorLoadDomains": "Fout bij het laden van organisatiedomeinen", "domainPickerErrorLoadDomains": "Fout bij het laden van organisatiedomeinen",
"domainPickerErrorCheckAvailability": "Kan domein beschikbaarheid niet controleren", "domainPickerErrorCheckAvailability": "Kan domein beschikbaarheid niet controleren",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Kies uw identiteitsprovider om door te gaan", "orgAuthChooseIdpDescription": "Kies uw identiteitsprovider om door te gaan",
"orgAuthNoIdpConfigured": "Deze organisatie heeft geen identiteitsproviders geconfigureerd. Je kunt in plaats daarvan inloggen met je Pangolin-identiteit.", "orgAuthNoIdpConfigured": "Deze organisatie heeft geen identiteitsproviders geconfigureerd. Je kunt in plaats daarvan inloggen met je Pangolin-identiteit.",
"orgAuthSignInWithPangolin": "Log in met Pangolin", "orgAuthSignInWithPangolin": "Log in met Pangolin",
"orgAuthSignInToOrg": "Log in bij een organisatie", "orgAuthSignInToOrg": "Organisatie Identiteitsprovider (SSO)",
"orgAuthSelectOrgTitle": "Organisatie Inloggen", "orgAuthSelectOrgTitle": "Organisatie Inloggen",
"orgAuthSelectOrgDescription": "Voer je organisatie-ID in om verder te gaan", "orgAuthSelectOrgDescription": "Voer je organisatie-ID in om verder te gaan",
"orgAuthOrgIdPlaceholder": "jouw-organisatie", "orgAuthOrgIdPlaceholder": "jouw-organisatie",
@@ -2429,6 +2648,7 @@
"validPassword": "Geldig wachtwoord", "validPassword": "Geldig wachtwoord",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Verbonden Client",
"resourceBlocked": "Bron geblokkeerd", "resourceBlocked": "Bron geblokkeerd",
"droppedByRule": "Achtergelaten door regel", "droppedByRule": "Achtergelaten door regel",
"noSessions": "Geen sessies", "noSessions": "Geen sessies",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Clienten toevoegen", "editInternalResourceDialogAddClients": "Clienten toevoegen",
"editInternalResourceDialogDestinationLabel": "Bestemming", "editInternalResourceDialogDestinationLabel": "Bestemming",
"editInternalResourceDialogDestinationDescription": "Specificeer het bestemmingsadres voor de interne bron. Dit kan een hostnaam, IP-adres of CIDR-bereik zijn, afhankelijk van de geselecteerde modus. Stel optioneel een interne DNS-alias in voor eenvoudigere identificatie.", "editInternalResourceDialogDestinationDescription": "Specificeer het bestemmingsadres voor de interne bron. Dit kan een hostnaam, IP-adres of CIDR-bereik zijn, afhankelijk van de geselecteerde modus. Stel optioneel een interne DNS-alias in voor eenvoudigere identificatie.",
"internalResourceFormMultiSiteRoutingHelp": "Selecteren van meerdere sites maakt veerkrachtige routing en failover mogelijk voor hoge beschikbaarheid.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Meer informatie",
"editInternalResourceDialogPortRestrictionsDescription": "Beperk toegang tot specifieke TCP/UDP-poorten of sta alle poorten toe/blokkeer.", "editInternalResourceDialogPortRestrictionsDescription": "Beperk toegang tot specifieke TCP/UDP-poorten of sta alle poorten toe/blokkeer.",
"createInternalResourceDialogHttpConfiguration": "HTTP-configuratie",
"createInternalResourceDialogHttpConfigurationDescription": "Kies het domein dat cliënten zullen gebruiken om deze bron via HTTP of HTTPS te bereiken.",
"editInternalResourceDialogHttpConfiguration": "HTTP-configuratie",
"editInternalResourceDialogHttpConfigurationDescription": "Kies het domein dat cliënten zullen gebruiken om deze bron via HTTP of HTTPS te bereiken.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "We keren snel terug! Onze site ondergaat momenteel gepland onderhoud.", "maintenancePageMessagePlaceholder": "We keren snel terug! Onze site ondergaat momenteel gepland onderhoud.",
"maintenancePageMessageDescription": "Gedetailleerd bericht dat het onderhoud uitlegt", "maintenancePageMessageDescription": "Gedetailleerd bericht dat het onderhoud uitlegt",
"maintenancePageTimeTitle": "Geschatte voltooiingstijd (optioneel)", "maintenancePageTimeTitle": "Geschatte voltooiingstijd (optioneel)",
"privateMaintenanceScreenTitle": "Privéscherm maintenance screen",
"privateMaintenanceScreenMessage": "Dit domein wordt gebruikt op een privébron. Verbind met de Pangolin client om toegang te krijgen tot deze bron.",
"privateMaintenanceScreenSteps": "Eenmaal verbonden, als u dit bericht nog steeds ziet, kan het DNS-cache van uw browser nog steeds naar het oude adres wijzen. Om dit te corrigeren: sluit en heropen dit tabblad, of uw browser, dan navigeer weer naar deze pagina.",
"maintenanceTime": "bijv. 2 uur, 1 nov om 17:00", "maintenanceTime": "bijv. 2 uur, 1 nov om 17:00",
"maintenanceEstimatedTimeDescription": "Wanneer u verwacht dat het onderhoud voltooid is", "maintenanceEstimatedTimeDescription": "Wanneer u verwacht dat het onderhoud voltooid is",
"editDomain": "Domein bewerken", "editDomain": "Domein bewerken",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Voeg HTTP bestemming toe", "httpDestAddTitle": "Voeg HTTP bestemming toe",
"httpDestEditDescription": "Werk de configuratie voor deze HTTP-event streaming bestemming bij.", "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.", "httpDestAddDescription": "Configureer een nieuw HTTP-eindpunt om de gebeurtenissen van uw organisatie te ontvangen.",
"S3DestEditTitle": "Bestemming bewerken",
"S3DestAddTitle": "S3-bestemming toevoegen",
"S3DestEditDescription": "Werk de configuratie bij voor deze S3-gebeurtenisstreamingbestemming.",
"S3DestAddDescription": "Configureer een nieuw S3-eindpunt om de gebeurtenissen van uw organisatie te ontvangen.",
"datadogDestEditTitle": "Bestemming bewerken",
"datadogDestAddTitle": "Datadog-bestemming toevoegen",
"datadogDestEditDescription": "Werk de configuratie bij voor deze Datadog-gebeurtenisstreamingbestemming.",
"datadogDestAddDescription": "Configureer een nieuw Datadog-eindpunt om de gebeurtenissen van uw organisatie te ontvangen.",
"httpDestTabSettings": "Instellingen", "httpDestTabSettings": "Instellingen",
"httpDestTabHeaders": "Kopteksten", "httpDestTabHeaders": "Kopteksten",
"httpDestTabBody": "Lichaam", "httpDestTabBody": "Lichaam",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Bestemming succesvol aangemaakt", "httpDestCreatedSuccess": "Bestemming succesvol aangemaakt",
"httpDestUpdateFailed": "Bijwerken bestemming mislukt", "httpDestUpdateFailed": "Bijwerken bestemming mislukt",
"httpDestCreateFailed": "Aanmaken bestemming mislukt", "httpDestCreateFailed": "Aanmaken bestemming mislukt",
"followRedirects": "Volg omleidingen",
"followRedirectsDescription": "Volg automatisch HTTP-omleidingen voor verzoeken.",
"alertingErrorWebhookUrl": "Voer een geldige URL voor de webhook in.",
"healthCheckStrategyHttp": "Valideert connectiviteit en controleert de HTTP-responsstatus.",
"healthCheckStrategyTcp": "Verifieert alleen TCP-connectiviteit zonder de respons te inspecteren.",
"healthCheckStrategySnmp": "Maakt een SNMP-verzoek om de gezondheid van netwerkapparaten en infrastructuur te controleren.",
"healthCheckStrategyIcmp": "Gebruikt ICMP-verzoeken (pings) om te controleren of een bron bereikbaar en responsief is.",
"healthCheckTabStrategy": "Strategie",
"healthCheckTabConnection": "Verbinding",
"healthCheckTabAdvanced": "Geavanceerd",
"healthCheckStrategyNotAvailable": "Deze strategie is niet beschikbaar. Neem contact op met sales om deze functie in te schakelen.",
"uptime30d": "Beschikbaarheid (30d)",
"idpAddActionCreateNew": "Nieuwe identiteitsprovider aanmaken", "idpAddActionCreateNew": "Nieuwe identiteitsprovider aanmaken",
"idpAddActionImportFromOrg": "Importeer vanuit een andere organisatie", "idpAddActionImportFromOrg": "Importeer vanuit een andere organisatie",
"idpImportDialogTitle": "Importeer Identiteitsprovider", "idpImportDialogTitle": "Importeer Identiteitsprovider",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Dit kan niet ongedaan worden gemaakt voor deze organisatie.", "idpUnassociateWarning": "Dit kan niet ongedaan worden gemaakt voor deze organisatie.",
"idpUnassociatedDescription": "Identiteitsprovider succesvol losgekoppeld van deze organisatie", "idpUnassociatedDescription": "Identiteitsprovider succesvol losgekoppeld van deze organisatie",
"idpUnassociateMenu": "Ontkoppelen", "idpUnassociateMenu": "Ontkoppelen",
"idpDeleteAllOrgsMenu": "Verwijderen" "idpDeleteAllOrgsMenu": "Verwijderen",
"publicIpEndpoint": "Eindpunt",
"lastTriggeredAt": "Laatste Trigger",
"reject": "Afwijzen",
"uptimeDaysAgo": "{count} dagen geleden",
"uptimeToday": "Vandaag",
"uptimeNoDataAvailable": "Geen gegevens beschikbaar",
"uptimeSuffix": "werktijd",
"uptimeDowntimeSuffix": "uitvaltijd",
"uptimeTooltipUptimeLabel": "Werktijd",
"uptimeTooltipDowntimeLabel": "Uitvaltijd",
"uptimeOngoing": "lopend",
"uptimeNoMonitoringData": "Geen monitoringgegevens",
"uptimeNoData": "Geen gegevens",
"uptimeMiniBarDown": "Onder",
"uptimeSectionTitle": "Werktijd",
"uptimeSectionDescription": "Beschikbaarheid over de laatste {days} dagen",
"uptimeAddAlert": "Alarm toevoegen",
"uptimeViewAlerts": "Meldingen bekijken",
"uptimeCreateEmailAlert": "E-mailalert aanmaken",
"uptimeAlertDescriptionSite": "Ontvang een e-mailbericht wanneer deze site offline gaat of weer online komt.",
"uptimeAlertDescriptionResource": "Ontvang een e-mailbericht wanneer deze bron offline gaat of weer online komt.",
"uptimeAlertNamePlaceholder": "Waarschuwingsnaam",
"uptimeAdditionalEmails": "Extra e-mails",
"uptimeCreateAlert": "Alarm aanmaken",
"uptimeAlertNoRecipients": "Geen ontvangers",
"uptimeAlertNoRecipientsDescription": "Voeg ten minste één gebruiker, rol of e-mail toe om te melden.",
"uptimeAlertCreated": "Alarm aangemaakt",
"uptimeAlertCreatedDescription": "U wordt op de hoogte gebracht wanneer dit van status verandert.",
"uptimeAlertCreateFailed": "Kon alarm niet aanmaken",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Sleutel",
"webhookHeaderValuePlaceholder": "Waarde",
"alertLabel": "Waarschuwing",
"domainPickerWildcardSubdomainNotAllowed": "Wildcard-subdomeinen zijn niet toegestaan.",
"domainPickerWildcardCertWarning": "Wildcard-bronnen hebben mogelijk extra configuratie nodig om correct te werken.",
"domainPickerWildcardCertWarningLink": "Meer informatie",
"health": "Gezondheid",
"domainPendingErrorTitle": "Verificatieprobleem"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Skontaktuj się z działem sprzedaży, aby włączyć tę funkcję.",
"contactSalesBookDemo": "Umów się na demo",
"contactSalesOr": "lub",
"contactSalesContactUs": "skontaktuj się z nami",
"setupCreate": "Utwórz organizację, witrynę i zasoby", "setupCreate": "Utwórz organizację, witrynę i zasoby",
"headerAuthCompatibilityInfo": "Włącz to, aby wymusić odpowiedź Unauthorized 401, gdy brakuje tokena uwierzytelniania. Jest to wymagane dla przeglądarek lub określonych bibliotek HTTP, które nie wysyłają poświadczeń bez wyzwania serwera.", "headerAuthCompatibilityInfo": "Włącz to, aby wymusić odpowiedź Unauthorized 401, gdy brakuje tokena uwierzytelniania. Jest to wymagane dla przeglądarek lub określonych bibliotek HTTP, które nie wysyłają poświadczeń bez wyzwania serwera.",
"headerAuthCompatibility": "Rozszerzona kompatybilność", "headerAuthCompatibility": "Rozszerzona kompatybilność",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Wykryto nieprawidłowe lub wygasłe klucze licencyjne. Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.", "componentsInvalidKey": "Wykryto nieprawidłowe lub wygasłe klucze licencyjne. Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.",
"dismiss": "Odrzuć", "dismiss": "Odrzuć",
"subscriptionViolationMessage": "Nie masz ograniczeń dla aktualnego planu. Popraw problem poprzez usunięcie stron, użytkowników lub innych zasobów, aby pozostać w swoim planie.", "subscriptionViolationMessage": "Nie masz ograniczeń dla aktualnego planu. Popraw problem poprzez usunięcie stron, użytkowników lub innych zasobów, aby pozostać w swoim planie.",
"trialBannerMessage": "Twój okres próbny wygasa za {countdown}. Uaktualnij, aby zachować dostęp.",
"trialBannerExpired": "Twój okres próbny wygasł. Uaktualnij teraz, aby przywrócić dostęp.",
"trialActive": "Okres próbny aktywny",
"trialExpired": "Okres próbny wygasł",
"trialHasEnded": "Twój okres próbny dobiegł końca.",
"trialDaysRemaining": "{count, plural, one {# dzień pozostaje} few {# dni pozostają} many {# dni pozostaje} other {# dni pozostają}}",
"trialDaysLeftShort": "Pozostało {days}d próbny",
"trialGoToBilling": "Przejdź do strony rozliczeń",
"subscriptionViolationViewBilling": "Zobacz rozliczenie", "subscriptionViolationViewBilling": "Zobacz rozliczenie",
"componentsLicenseViolation": "Naruszenie licencji: Ten serwer używa stron {usedSites} , które przekraczają limit licencyjny stron {maxSites} . Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.", "componentsLicenseViolation": "Naruszenie licencji: Ten serwer używa stron {usedSites} , które przekraczają limit licencyjny stron {maxSites} . Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.",
"componentsSupporterMessage": "Dziękujemy za wsparcie Pangolina jako {tier}!", "componentsSupporterMessage": "Dziękujemy za wsparcie Pangolina jako {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Skopiowałem konfigurację", "siteConfirmCopy": "Skopiowałem konfigurację",
"searchSitesProgress": "Szukaj witryn...", "searchSitesProgress": "Szukaj witryn...",
"siteAdd": "Dodaj witrynę", "siteAdd": "Dodaj witrynę",
"sitesTableViewPublicResources": "Zobacz zasoby publiczne",
"sitesTableViewPrivateResources": "Zobacz zasoby prywatne",
"siteInstallNewt": "Zainstaluj Newt", "siteInstallNewt": "Zainstaluj Newt",
"siteInstallNewtDescription": "Uruchom Newt w swoim systemie", "siteInstallNewtDescription": "Uruchom Newt w swoim systemie",
"WgConfiguration": "Konfiguracja WireGuard", "WgConfiguration": "Konfiguracja WireGuard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Strona została zaktualizowana.", "siteUpdatedDescription": "Strona została zaktualizowana.",
"siteGeneralDescription": "Skonfiguruj ustawienia ogólne dla tej witryny", "siteGeneralDescription": "Skonfiguruj ustawienia ogólne dla tej witryny",
"siteSettingDescription": "Skonfiguruj ustawienia na stronie", "siteSettingDescription": "Skonfiguruj ustawienia na stronie",
"siteResourcesTab": "Zasoby",
"siteResourcesNoneOnSite": "Ta strona nie ma jeszcze żadnych zasobów publicznych ani prywatnych.",
"siteResourcesSectionPublic": "Zasoby publiczne",
"siteResourcesSectionPrivate": "Zasoby prywatne",
"siteResourcesSectionPublicDescription": "Zasoby eksponowane zewnętrznie przez domeny lub porty.",
"siteResourcesSectionPrivateDescription": "Zasoby dostępne w twojej prywatnej sieci przez stronę.",
"siteResourcesViewAllPublic": "Zobacz wszystkie zasoby",
"siteResourcesViewAllPrivate": "Zobacz wszystkie zasoby",
"siteResourcesDialogDescription": "Przegląd zasobów publicznych i prywatnych związanych z tą stroną.",
"siteResourcesShowMore": "Pokaż więcej",
"siteResourcesPermissionDenied": "Nie masz uprawnień do wyświetlania tych zasobów.",
"siteResourcesEmptyPublic": "Brak publicznych zasobów powiązanych z tą stroną.",
"siteResourcesEmptyPrivate": "Brak prywatnych zasobów powiązanych z tą stroną.",
"siteResourcesHowToAccess": "Jak uzyskać dostęp",
"siteResourcesTargetsOnSite": "Cele na tej stronie",
"siteSetting": "Ustawienia {siteName}", "siteSetting": "Ustawienia {siteName}",
"siteNewtTunnel": "Newt Site (Rekomendowane)", "siteNewtTunnel": "Newt Site (Rekomendowane)",
"siteNewtTunnelDescription": "Najprostszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.", "siteNewtTunnelDescription": "Najprostszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.",
@@ -267,8 +296,11 @@
"orgMissing": "Brak ID organizacji", "orgMissing": "Brak ID organizacji",
"orgMissingMessage": "Nie można ponownie wygenerować zaproszenia bez ID organizacji.", "orgMissingMessage": "Nie można ponownie wygenerować zaproszenia bez ID organizacji.",
"accessUsersManage": "Zarządzaj użytkownikami", "accessUsersManage": "Zarządzaj użytkownikami",
"accessUserManage": "Zarządzaj użytkownikiem",
"accessUsersDescription": "Zaproś użytkowników z dostępem do tej organizacji i zarządzaj nimi", "accessUsersDescription": "Zaproś użytkowników z dostępem do tej organizacji i zarządzaj nimi",
"accessUsersSearch": "Szukaj użytkowników...", "accessUsersSearch": "Szukaj użytkowników...",
"accessUsersRoleFilterCount": "{count, plural, one {# rola} few {# role} many {# ról} other {# ról}}",
"accessUsersRoleFilterClear": "Wyczyść filtry ról",
"accessUserCreate": "Utwórz użytkownika", "accessUserCreate": "Utwórz użytkownika",
"accessUserRemove": "Usuń użytkownika", "accessUserRemove": "Usuń użytkownika",
"username": "Nazwa użytkownika", "username": "Nazwa użytkownika",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Sekret", "newtSecretKey": "Sekret",
"newtVersion": "Wersja",
"architecture": "Architektura", "architecture": "Architektura",
"sites": "Witryny", "sites": "Witryny",
"siteWgAnyClients": "Użyj dowolnego klienta WireGuard, aby się połączyć. Będziesz musiał przekierować wewnętrzne zasoby za pomocą adresu IP.", "siteWgAnyClients": "Użyj dowolnego klienta WireGuard, aby się połączyć. Będziesz musiał przekierować wewnętrzne zasoby za pomocą adresu IP.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Zobacz dzienniki", "actionViewLogs": "Zobacz dzienniki",
"noneSelected": "Nie wybrano", "noneSelected": "Nie wybrano",
"orgNotFound2": "Nie znaleziono organizacji.", "orgNotFound2": "Nie znaleziono organizacji.",
"search": "Szukaj…",
"searchPlaceholder": "Szukaj...", "searchPlaceholder": "Szukaj...",
"emptySearchOptions": "Nie znaleziono opcji", "emptySearchOptions": "Nie znaleziono opcji",
"create": "Utwórz", "create": "Utwórz",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Zarządzaj", "sidebarGeneral": "Zarządzaj",
"sidebarLogAndAnalytics": "Dziennik & Analityka", "sidebarLogAndAnalytics": "Dziennik & Analityka",
"sidebarBluePrints": "Schematy", "sidebarBluePrints": "Schematy",
"sidebarAlerting": "Alarmowanie",
"sidebarHealthChecks": "Kontrole zdrowia",
"sidebarOrganization": "Organizacja", "sidebarOrganization": "Organizacja",
"sidebarManagement": "Zarządzanie", "sidebarManagement": "Zarządzanie",
"sidebarBillingAndLicenses": "Płatność i licencje", "sidebarBillingAndLicenses": "Płatność i licencje",
"sidebarLogsAnalytics": "Analityka", "sidebarLogsAnalytics": "Analityka",
"alertingTitle": "Alarmowanie",
"alertingDescription": "Zdefiniuj źródła, ustawienia, i działania dla powiadomień",
"alertingRules": "Reguły alarmowe",
"alertingSearchRules": "Szukaj reguł…",
"alertingAddRule": "Utwórz Regułę",
"alertingColumnSource": "Źródło",
"alertingColumnTrigger": "Ustawienie",
"alertingColumnActions": "Akcje",
"alertingColumnEnabled": "Włączone",
"alertingDeleteQuestion": "Potwierdź, że chcesz usunąć tę regułę alarmową.",
"alertingDeleteRule": "Usuń regułę alarmową",
"alertingRuleDeleted": "Reguła alarmowa usunięta",
"alertingRuleSaved": "Reguła alarmowa zapisana",
"alertingRuleSavedCreatedDescription": "Nowa reguła alarmowa została utworzona. Możesz ją kontynuować edytować na tej stronie.",
"alertingRuleSavedUpdatedDescription": "Twoje zmiany w tej regule alarmowej zostały zapisane.",
"alertingEditRule": "Edytuj regułę alarmową",
"alertingCreateRule": "Utwórz regułę alarmową",
"alertingRuleCredenzaDescription": "Wybierz, co obserwować, kiedy uruchamiać i jak powiadamiać.",
"alertingRuleNamePlaceholder": "Strona produkcyjna w dół",
"alertingRuleEnabled": "Reguła włączona",
"alertingSectionSource": "Źródło",
"alertingSourceType": "Typ źródła",
"alertingSourceSite": "Witryna",
"alertingSourceHealthCheck": "Kontrola zdrowia",
"alertingPickSites": "Witryny",
"alertingPickHealthChecks": "Kontrole zdrowia",
"alertingPickResources": "Zasoby",
"alertingAllSites": "Wszystkie witryny",
"alertingAllSitesDescription": "Alarm uruchomiony dla dowolnej witryny",
"alertingSpecificSites": "Określone witryny",
"alertingSpecificSitesDescription": "Wybierz określone witryny do obserwacji",
"alertingAllHealthChecks": "Wszystkie Kontrole Zdrowia",
"alertingAllHealthChecksDescription": "Alarm uruchomiony dla dowolnej kontroli zdrowia",
"alertingSpecificHealthChecks": "Określone Kontrole Zdrowia",
"alertingSpecificHealthChecksDescription": "Wybierz określone kontrole zdrowia do obserwacji",
"alertingAllResources": "Wszystkie zasoby",
"alertingAllResourcesDescription": "Alarm uruchomiony dla dowolnego zasobu",
"alertingSpecificResources": "Określone Zasoby",
"alertingSpecificResourcesDescription": "Wybierz określone zasoby do obserwacji",
"alertingSelectResources": "Wybierz zasoby…",
"alertingResourcesSelected": "{count} zasobów wybrano",
"alertingResourcesEmpty": "Brak zasobów z celami w pierwszych 10 wynikach.",
"alertingSectionTrigger": "Ustawienie",
"alertingTrigger": "Kiedy alarmować",
"alertingTriggerSiteOnline": "Strona online",
"alertingTriggerSiteOffline": "Strona offline",
"alertingTriggerSiteToggle": "Status strony zmienia się",
"alertingTriggerHcHealthy": "Kontrola zdrowia zdrowa",
"alertingTriggerHcUnhealthy": "Kontrola zdrowia niezdrowa",
"alertingTriggerHcToggle": "Status kontroli zdrowia zmienia się",
"alertingTriggerResourceHealthy": "Zasób zdrowy",
"alertingTriggerResourceUnhealthy": "Zasób niezdrowy",
"alertingTriggerResourceDegraded": "Zasób pogorszony",
"alertingSearchHealthChecks": "Szukaj kontroli zdrowia…",
"alertingHealthChecksEmpty": "Brak dostępnych kontroli zdrowia.",
"alertingTriggerResourceToggle": "Zmiany statusu zasobu",
"alertingSourceResource": "Zasób",
"alertingSectionActions": "Akcje",
"alertingAddAction": "Dodaj Akcję",
"alertingActionNotify": "E-mail",
"alertingActionNotifyDescription": "Wyślij powiadomienia e-mail do użytkowników lub ról",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Wyślij żądanie HTTP do niestandardowego punktu końcowego",
"alertingExternalIntegration": "Integracja Zewnętrzna",
"alertingExternalPagerDutyDescription": "Przesyłaj alerty do PagerDuty do zarządzania incydentami",
"alertingExternalOpsgenieDescription": "Kieruj alerty do Opsgenie dla zarządzania dyżurem",
"alertingExternalServiceNowDescription": "Twórz incydenty ServiceNow z alertów",
"alertingExternalIncidentIoDescription": "Wyzwalaj przepływy Incident.io z alertów",
"alertingActionType": "Typ akcji",
"alertingNotifyUsers": "Użytkownicy",
"alertingNotifyRoles": "Role",
"alertingNotifyEmails": "Adres e-mail",
"alertingEmailPlaceholder": "Dodaj e-mail i naciśnij Enter",
"alertingWebhookMethod": "Metoda HTTP",
"alertingWebhookSecret": "Sekret podpisu (opcjonalny)",
"alertingWebhookSecretPlaceholder": "Sekret HMAC",
"alertingWebhookHeaders": "Nagłówki",
"alertingAddHeader": "Dodaj nagłówek",
"alertingSelectSites": "Wybierz witryny…",
"alertingSitesSelected": "{count} witryny wybrano",
"alertingSelectHealthChecks": "Wybierz wyniki zdrowia…",
"alertingHealthChecksSelected": "{count} wyniki zdrowia wybrane",
"alertingNoHealthChecks": "Brak celów z aktywowanymi kontrolami zdrowia",
"alertingHealthCheckStub": "Wybór źródła kontroli zdrowia jeszcze nie skonfigurowany - możesz nadal skonfigurować wyzwalacze i akcje.",
"alertingSelectUsers": "Wybierz użytkowników…",
"alertingUsersSelected": "{count} użytkowników wybrano",
"alertingSelectRoles": "Wybierz role…",
"alertingRolesSelected": "{count} ról wybrano",
"alertingSummarySites": "Witryny ({count})",
"alertingSummaryAllSites": "Wszystkie witryny",
"alertingSummaryHealthChecks": "Kontrole zdrowia ({count})",
"alertingSummaryAllHealthChecks": "Wszystkie kontrole zdrowia",
"alertingSummaryResources": "Zasoby ({count})",
"alertingSummaryAllResources": "Wszystkie zasoby",
"alertingErrorNameRequired": "Wprowadź nazwę",
"alertingErrorActionsMin": "Dodaj co najmniej jedną akcję",
"alertingErrorPickSites": "Wybierz co najmniej jedną witrynę",
"alertingErrorPickHealthChecks": "Wybierz co najmniej jedną kontrolę zdrowia",
"alertingErrorPickResources": "Wybierz co najmniej jeden zasób",
"alertingErrorTriggerSite": "Wybierz wyzwalacz witryny",
"alertingErrorTriggerHealth": "Wybierz wyzwalacz kontroli zdrowia",
"alertingErrorTriggerResource": "Wybierz wyzwalacz zasobu",
"alertingErrorNotifyRecipients": "Wybierz użytkowników, role lub co najmniej jeden e-mail",
"alertingConfigureSource": "Skonfiguruj źródło",
"alertingConfigureTrigger": "Skonfiguruj wyzwalacz",
"alertingConfigureActions": "Skonfiguruj akcje",
"alertingBackToRules": "Powrót do reguł",
"alertingRuleCooldown": "Czas ochłodzenia (sekundy)",
"alertingRuleCooldownDescription": "Minimalny czas między powtórzonymi alarmami dla tej samej reguły. Ustaw na 0, aby wyzwalać za każdym razem.",
"alertingDraftBadge": "Szkic - zapisz, aby zachować tę regułę",
"alertingSidebarHint": "Kliknij krok na kanwie, aby edytować go tutaj.",
"alertingGraphCanvasTitle": "Przepływ reguł",
"alertingGraphCanvasDescription": "Wizualny podgląd źródła, wyzwalacza i akcji. Wybierz węzeł, aby edytować go w panelu.",
"alertingNodeNotConfigured": "Nie skonfigurowano jeszcze",
"alertingNodeActionsCount": "{count, plural, one {# akcja} few {# akcje} many {# akcji} other {# akcji}}",
"alertingNodeRoleSource": "Źródło",
"alertingNodeRoleTrigger": "Wyzwalacz",
"alertingNodeRoleAction": "Akcja",
"alertingTabRules": "Reguły Alarmowe",
"alertingTabHealthChecks": "Kontrole Zdrowia",
"alertingRulesBannerTitle": "Otrzymaj Powiadomienie",
"alertingRulesBannerDescription": "Każda reguła wiąże ze sobą co obserwować (np. witryna, kontrola zdrowia czy zasób), kiedy uruchomić (np. offline lub niezdrowy), oraz jak powiadomić zespół przez e-mail, webhooks lub integracje. Użyj tej listy, aby utworzyć, włączyć i zarządzać tymi regułami.",
"alertingHealthChecksBannerTitle": "Monitor Zdrowia i Zasobów",
"alertingHealthChecksBannerDescription": "Kontrole zdrowia to monitory HTTP lub TCP, które definiujesz raz. Następnie możesz używać ich jako źródeł w regułach alarmowych, aby otrzymywać powiadomienia, kiedy cel stanie się zdrowy lub niezdrowy. Kontrole zdrowia w zasobach również pojawiają się tutaj.",
"standaloneHcTableTitle": "Kontrole Zdrowia",
"standaloneHcSearchPlaceholder": "Szukaj kontroli zdrowia…",
"standaloneHcAddButton": "Utwórz Kontrolę Zdrowia",
"standaloneHcCreateTitle": "Utwórz Kontrolę Zdrowia",
"standaloneHcEditTitle": "Edytuj Kontrolę Zdrowia",
"standaloneHcDescription": "Skonfiguruj kontrolę zdrowia HTTP lub TCP do wykorzystania w regułach alarmowych.",
"standaloneHcNameLabel": "Nazwa",
"standaloneHcNamePlaceholder": "Mój Monitor HTTP",
"standaloneHcDeleteTitle": "Usuń kontrolę zdrowia",
"standaloneHcDeleteQuestion": "Potwierdź, że chcesz usunąć tę kontrolę zdrowia.",
"standaloneHcDeleted": "Kontrola zdrowia usunięta",
"standaloneHcSaved": "Kontrola zdrowia zapisana",
"standaloneHcColumnHealth": "Zdrowie",
"standaloneHcColumnMode": "Tryb",
"standaloneHcColumnTarget": "Cel",
"standaloneHcHealthStateHealthy": "Zdrowy",
"standaloneHcHealthStateUnhealthy": "Niezdrowy",
"standaloneHcHealthStateUnknown": "Nieznany",
"standaloneHcFilterAnySite": "Wszystkie witryny",
"standaloneHcFilterAnyResource": "Wszystkie zasoby",
"standaloneHcFilterMode": "Tryb",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Zdrowie",
"standaloneHcFilterEnabled": "Włączone",
"standaloneHcFilterEnabledOn": "Włączone",
"standaloneHcFilterEnabledOff": "Wyłączone",
"standaloneHcFilterSiteIdFallback": "Witryna {id}",
"standaloneHcFilterResourceIdFallback": "Zasób {id}",
"blueprints": "Schematy", "blueprints": "Schematy",
"blueprintsDescription": "Zastosuj konfiguracje deklaracyjne i wyświetl poprzednie operacje", "blueprintsDescription": "Zastosuj konfiguracje deklaracyjne i wyświetl poprzednie operacje",
"blueprintAdd": "Dodaj schemat", "blueprintAdd": "Dodaj schemat",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Utwórz początkowe konto administratora serwera. Może istnieć tylko jeden administrator serwera. Zawsze można zmienić te dane uwierzytelniające.", "initialSetupDescription": "Utwórz początkowe konto administratora serwera. Może istnieć tylko jeden administrator serwera. Zawsze można zmienić te dane uwierzytelniające.",
"createAdminAccount": "Utwórz konto administratora", "createAdminAccount": "Utwórz konto administratora",
"setupErrorCreateAdmin": "Wystąpił błąd podczas tworzenia konta administratora serwera.", "setupErrorCreateAdmin": "Wystąpił błąd podczas tworzenia konta administratora serwera.",
"certificateStatus": "Status certyfikatu", "certificateStatus": "Certyfikat",
"certificateStatusAutoRefreshHint": "Status odświeża się automatycznie.",
"loading": "Ładowanie", "loading": "Ładowanie",
"loadingAnalytics": "Ładowanie Analityki", "loadingAnalytics": "Ładowanie Analityki",
"restart": "Uruchom ponownie", "restart": "Uruchom ponownie",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Zobacz informacje o wydaniu", "pangolinUpdateAvailableReleaseNotes": "Zobacz informacje o wydaniu",
"newtUpdateAvailable": "Dostępna aktualizacja", "newtUpdateAvailable": "Dostępna aktualizacja",
"newtUpdateAvailableInfo": "Nowa wersja Newt jest dostępna. Prosimy o aktualizację do najnowszej wersji dla najlepszej pracy.", "newtUpdateAvailableInfo": "Nowa wersja Newt jest dostępna. Prosimy o aktualizację do najnowszej wersji dla najlepszej pracy.",
"pangolinNodeUpdateAvailableInfo": "Nowa wersja Pangolin Node jest dostępna. Prosimy o aktualizację do najnowszej wersji dla najlepszej pracy.",
"domainPickerEnterDomain": "Domena", "domainPickerEnterDomain": "Domena",
"domainPickerPlaceholder": "mojapp.example.com", "domainPickerPlaceholder": "mojapp.example.com",
"domainPickerDescription": "Wpisz pełną domenę zasobu, aby zobaczyć dostępne opcje.", "domainPickerDescription": "Wpisz pełną domenę zasobu, aby zobaczyć dostępne opcje.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Skonfiguruj Kontrolę Zdrowia", "configureHealthCheck": "Skonfiguruj Kontrolę Zdrowia",
"configureHealthCheckDescription": "Skonfiguruj monitorowanie zdrowia dla {target}", "configureHealthCheckDescription": "Skonfiguruj monitorowanie zdrowia dla {target}",
"enableHealthChecks": "Włącz Kontrole Zdrowia", "enableHealthChecks": "Włącz Kontrole Zdrowia",
"healthCheckDisabledStateDescription": "Gdy wyłączone, strona nie będzie wykonywać kontroli zdrowia, a stan zostanie uznany za nieznany.",
"enableHealthChecksDescription": "Monitoruj zdrowie tego celu. Możesz monitorować inny punkt końcowy niż docelowy w razie potrzeby.", "enableHealthChecksDescription": "Monitoruj zdrowie tego celu. Możesz monitorować inny punkt końcowy niż docelowy w razie potrzeby.",
"healthScheme": "Metoda", "healthScheme": "Metoda",
"healthSelectScheme": "Wybierz metodę", "healthSelectScheme": "Wybierz metodę",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Interwał sprawdzania musi wynosić co najmniej 5 sekund", "healthCheckIntervalMin": "Interwał sprawdzania musi wynosić co najmniej 5 sekund",
"healthCheckTimeoutMin": "Limit czasu musi wynosić co najmniej 1 sekundę", "healthCheckTimeoutMin": "Limit czasu musi wynosić co najmniej 1 sekundę",
"healthCheckRetryMin": "Liczba prób ponowienia musi wynosić co najmniej 1", "healthCheckRetryMin": "Liczba prób ponowienia musi wynosić co najmniej 1",
"healthCheckMode": "Tryb kontroli",
"healthCheckStrategy": "Strategia",
"healthCheckModeDescription": "Tryb TCP weryfikuje tylko łączność. Tryb HTTP ocenia odpowiedź HTTP.",
"healthyThreshold": "Próg zdrowia",
"healthyThresholdDescription": "Wymagane sukcesy pod rząd, zanim oznaczy się jako zdrowe.",
"unhealthyThreshold": "Próg niezdrowia",
"unhealthyThresholdDescription": "Wymagane niepowodzenia z rzędu, zanim oznaczy się jako niezdrowe.",
"healthCheckHealthyThresholdMin": "Próg zdrowia musi wynosić co najmniej 1",
"healthCheckUnhealthyThresholdMin": "Próg niezdrowia musi wynosić co najmniej 1",
"httpMethod": "Metoda HTTP", "httpMethod": "Metoda HTTP",
"selectHttpMethod": "Wybierz metodę HTTP", "selectHttpMethod": "Wybierz metodę HTTP",
"domainPickerSubdomainLabel": "Poddomena", "domainPickerSubdomainLabel": "Poddomena",
"domainPickerWildcard": "Uniwersalny",
"domainPickerWildcardPaidOnly": "Uniwersalne subdomeny są płatną funkcją. Proszę dokonać aktualizacji, aby uzyskać dostęp do tej funkcji.",
"domainPickerBaseDomainLabel": "Domen bazowa", "domainPickerBaseDomainLabel": "Domen bazowa",
"domainPickerSearchDomains": "Szukaj domen...", "domainPickerSearchDomains": "Szukaj domen...",
"domainPickerNoDomainsFound": "Nie znaleziono domen", "domainPickerNoDomainsFound": "Nie znaleziono domen",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Ten adres jest częścią podsieci użyteczności organizacji. Jest używany do rozwiązywania rekordów aliasu przy użyciu wewnętrznej rozdzielczości DNS.", "resourcesTableAliasAddressInfo": "Ten adres jest częścią podsieci użyteczności organizacji. Jest używany do rozwiązywania rekordów aliasu przy użyciu wewnętrznej rozdzielczości DNS.",
"resourcesTableClients": "Klientami", "resourcesTableClients": "Klientami",
"resourcesTableAndOnlyAccessibleInternally": "i są dostępne tylko wewnętrznie po połączeniu z klientem.", "resourcesTableAndOnlyAccessibleInternally": "i są dostępne tylko wewnętrznie po połączeniu z klientem.",
"resourcesTableNoTargets": "Brak celów",
"resourcesTableHealthy": "Zdrowe", "resourcesTableHealthy": "Zdrowe",
"resourcesTableDegraded": "Degradacja", "resourcesTableDegraded": "Degradacja",
"resourcesTableOffline": "Offline", "resourcesTableUnhealthy": "Niezdrowy",
"resourcesTableUnknown": "Nieznane", "resourcesTableUnknown": "Nieznane",
"resourcesTableNotMonitored": "Nie monitorowano", "resourcesTableNotMonitored": "Nie monitorowano",
"resourcesTableNoTargets": "Brak celów",
"editInternalResourceDialogEditClientResource": "Edytuj Zasoby Prywatne", "editInternalResourceDialogEditClientResource": "Edytuj Zasoby Prywatne",
"editInternalResourceDialogUpdateResourceProperties": "Aktualizuj konfigurację zasobów i kontrolę dostępu dla {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Aktualizuj konfigurację zasobów i kontrolę dostępu dla {resourceName}",
"editInternalResourceDialogResourceProperties": "Właściwości zasobów", "editInternalResourceDialogResourceProperties": "Właściwości zasobów",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Port", "editInternalResourceDialogModePort": "Port",
"editInternalResourceDialogModeHost": "Host", "editInternalResourceDialogModeHost": "Host",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Schemat",
"editInternalResourceDialogEnableSsl": "Włącz SSL",
"editInternalResourceDialogEnableSslDescription": "Włącz szyfrowanie SSL/TLS dla bezpiecznych połączeń HTTPS z miejscem docelowym.",
"editInternalResourceDialogDestination": "Miejsce docelowe", "editInternalResourceDialogDestination": "Miejsce docelowe",
"editInternalResourceDialogDestinationHostDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.", "editInternalResourceDialogDestinationHostDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.",
"editInternalResourceDialogDestinationIPDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.", "editInternalResourceDialogDestinationIPDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Nazwa", "createInternalResourceDialogName": "Nazwa",
"createInternalResourceDialogSite": "Witryna", "createInternalResourceDialogSite": "Witryna",
"selectSite": "Wybierz stronę...", "selectSite": "Wybierz stronę...",
"multiSitesSelectorSitesCount": "{count, plural, one {# witryna} few {# witryny} many {# witryn} other {# witryn}}",
"noSitesFound": "Nie znaleziono stron.", "noSitesFound": "Nie znaleziono stron.",
"createInternalResourceDialogProtocol": "Protokół", "createInternalResourceDialogProtocol": "Protokół",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Port", "createInternalResourceDialogModePort": "Port",
"createInternalResourceDialogModeHost": "Host", "createInternalResourceDialogModeHost": "Host",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Schemat",
"createInternalResourceDialogScheme": "Schemat",
"createInternalResourceDialogEnableSsl": "Włącz SSL",
"createInternalResourceDialogEnableSslDescription": "Włącz szyfrowanie SSL/TLS dla bezpiecznych połączeń HTTPS z miejscem docelowym.",
"createInternalResourceDialogDestination": "Miejsce docelowe", "createInternalResourceDialogDestination": "Miejsce docelowe",
"createInternalResourceDialogDestinationHostDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.", "createInternalResourceDialogDestinationHostDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.",
"createInternalResourceDialogDestinationCidrDescription": "Zakres CIDR zasobu w sieci witryny.", "createInternalResourceDialogDestinationCidrDescription": "Zakres CIDR zasobu w sieci witryny.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Opcjonalny wewnętrzny alias DNS dla tego zasobu.", "createInternalResourceDialogAliasDescription": "Opcjonalny wewnętrzny alias DNS dla tego zasobu.",
"internalResourceDownstreamSchemeRequired": "Schemat jest wymagany dla zasobów HTTP",
"internalResourceHttpPortRequired": "Port docelowy jest wymagany dla zasobów HTTP",
"siteConfiguration": "Konfiguracja", "siteConfiguration": "Konfiguracja",
"siteAcceptClientConnections": "Akceptuj połączenia klienta", "siteAcceptClientConnections": "Akceptuj połączenia klienta",
"siteAcceptClientConnectionsDescription": "Zezwalaj urządzeniom i klientom na dostęp do zasobów na tej stronie. Może to zostać zmienione później.", "siteAcceptClientConnectionsDescription": "Zezwalaj urządzeniom i klientom na dostęp do zasobów na tej stronie. Może to zostać zmienione później.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Zweryfikowano", "domainPickerVerified": "Zweryfikowano",
"domainPickerUnverified": "Niezweryfikowane", "domainPickerUnverified": "Niezweryfikowane",
"domainPickerManual": "Podręcznik", "domainPickerManual": "Podręcznik",
"domainPickerInvalidSubdomainStructure": "Ta subdomena zawiera nieprawidłowe znaki lub strukturę. Zostanie ona automatycznie oczyszczona po zapisaniu.", "domainPickerInvalidSubdomainStructure": "Nieprawidłowe znaki zostaną zsanitowane, gdy zostaną zapisane.",
"domainPickerError": "Błąd", "domainPickerError": "Błąd",
"domainPickerErrorLoadDomains": "Nie udało się załadować domen organizacji", "domainPickerErrorLoadDomains": "Nie udało się załadować domen organizacji",
"domainPickerErrorCheckAvailability": "Nie udało się sprawdzić dostępności domeny", "domainPickerErrorCheckAvailability": "Nie udało się sprawdzić dostępności domeny",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Wybierz swojego dostawcę tożsamości, aby kontynuować", "orgAuthChooseIdpDescription": "Wybierz swojego dostawcę tożsamości, aby kontynuować",
"orgAuthNoIdpConfigured": "Ta organizacja nie ma skonfigurowanych żadnych dostawców tożsamości. Zamiast tego możesz zalogować się za pomocą swojej tożsamości Pangolin.", "orgAuthNoIdpConfigured": "Ta organizacja nie ma skonfigurowanych żadnych dostawców tożsamości. Zamiast tego możesz zalogować się za pomocą swojej tożsamości Pangolin.",
"orgAuthSignInWithPangolin": "Zaloguj się używając Pangolin", "orgAuthSignInWithPangolin": "Zaloguj się używając Pangolin",
"orgAuthSignInToOrg": "Zaloguj się do organizacji", "orgAuthSignInToOrg": "Dostawca tożsamości organizacji (SSO)",
"orgAuthSelectOrgTitle": "Logowanie do organizacji", "orgAuthSelectOrgTitle": "Logowanie do organizacji",
"orgAuthSelectOrgDescription": "Wprowadź identyfikator organizacji, aby kontynuować", "orgAuthSelectOrgDescription": "Wprowadź identyfikator organizacji, aby kontynuować",
"orgAuthOrgIdPlaceholder": "twoja-organizacja", "orgAuthOrgIdPlaceholder": "twoja-organizacja",
@@ -2429,6 +2648,7 @@
"validPassword": "Prawidłowe hasło", "validPassword": "Prawidłowe hasło",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Połączony Klient",
"resourceBlocked": "Zasób zablokowany", "resourceBlocked": "Zasób zablokowany",
"droppedByRule": "Upuszczone przez regułę", "droppedByRule": "Upuszczone przez regułę",
"noSessions": "Brak sesji", "noSessions": "Brak sesji",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Dodaj klientów", "editInternalResourceDialogAddClients": "Dodaj klientów",
"editInternalResourceDialogDestinationLabel": "Miejsce docelowe", "editInternalResourceDialogDestinationLabel": "Miejsce docelowe",
"editInternalResourceDialogDestinationDescription": "Określ adres docelowy dla wewnętrznego zasobu. Może to być nazwa hosta, adres IP lub zakres CIDR, w zależności od wybranego trybu. Opcjonalnie ustaw wewnętrzny alias DNS dla łatwiejszej identyfikacji.", "editInternalResourceDialogDestinationDescription": "Określ adres docelowy dla wewnętrznego zasobu. Może to być nazwa hosta, adres IP lub zakres CIDR, w zależności od wybranego trybu. Opcjonalnie ustaw wewnętrzny alias DNS dla łatwiejszej identyfikacji.",
"internalResourceFormMultiSiteRoutingHelp": "Wybór wielu stron umożliwia odporne trasowanie i awarię dla wysokiej dostępności.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Dowiedz się więcej",
"editInternalResourceDialogPortRestrictionsDescription": "Ogranicz dostęp do konkretnych portów TCP/UDP lub zezwól/zablokuj wszystkie porty.", "editInternalResourceDialogPortRestrictionsDescription": "Ogranicz dostęp do konkretnych portów TCP/UDP lub zezwól/zablokuj wszystkie porty.",
"createInternalResourceDialogHttpConfiguration": "Konfiguracja HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Wybierz domenę, której klienci będą używać, aby dotrzeć do tego zasobu przez HTTP lub HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Konfiguracja HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Wybierz domenę, której klienci będą używać, aby dotrzeć do tego zasobu przez HTTP lub HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Wrócimy wkrótce! Nasza strona przechodzi obecnie zaplanowaną konserwację.", "maintenancePageMessagePlaceholder": "Wrócimy wkrótce! Nasza strona przechodzi obecnie zaplanowaną konserwację.",
"maintenancePageMessageDescription": "Szczegółowy komunikat wyjaśniający konserwację", "maintenancePageMessageDescription": "Szczegółowy komunikat wyjaśniający konserwację",
"maintenancePageTimeTitle": "Szacowany czas zakończenia (opcjonalnie)", "maintenancePageTimeTitle": "Szacowany czas zakończenia (opcjonalnie)",
"privateMaintenanceScreenTitle": "Ekraan prywatnego utrzymania",
"privateMaintenanceScreenMessage": "Ta domena jest wykorzystywana na prywatnym zasobie. Połącz się za pomocą klienta Pangolin, aby uzyskać dostęp do tego zasobu.",
"privateMaintenanceScreenSteps": "Po połączeniu, jeśli nadal widzisz tę wiadomość, pamięć podręczna DNS przeglądarki może nadal wskazywać na stary adres. Aby to naprawić: zamknij i otwórz ponownie tę kartę lub przeglądarkę, a następnie przejdź z powrotem na tę stronę.",
"maintenanceTime": "np. 2 godziny, 1 listopad o 17:00", "maintenanceTime": "np. 2 godziny, 1 listopad o 17:00",
"maintenanceEstimatedTimeDescription": "Kiedy oczekujesz zakończenia konserwacji", "maintenanceEstimatedTimeDescription": "Kiedy oczekujesz zakończenia konserwacji",
"editDomain": "Edytuj domenę", "editDomain": "Edytuj domenę",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Dodaj cel HTTP", "httpDestAddTitle": "Dodaj cel HTTP",
"httpDestEditDescription": "Aktualizuj konfigurację dla tego celu przesyłania strumieniowego zdarzeń HTTP.", "httpDestEditDescription": "Aktualizuj konfigurację dla tego celu przesyłania strumieniowego zdarzeń HTTP.",
"httpDestAddDescription": "Skonfiguruj nowy punkt końcowy HTTP, aby otrzymywać wydarzenia organizacji.", "httpDestAddDescription": "Skonfiguruj nowy punkt końcowy HTTP, aby otrzymywać wydarzenia organizacji.",
"S3DestEditTitle": "Edytuj Miejsce Docelowe",
"S3DestAddTitle": "Dodaj Miejsce Docelowe S3",
"S3DestEditDescription": "Zaktualizuj konfigurację dla tego miejsca docelowego strumieniowego zdarzeń S3.",
"S3DestAddDescription": "Skonfiguruj nowy punkt końcowy S3, aby odbierać zdarzenia Twojej organizacji.",
"datadogDestEditTitle": "Edytuj Miejsce Docelowe",
"datadogDestAddTitle": "Dodaj Miejsce Docelowe Datadog",
"datadogDestEditDescription": "Zaktualizuj konfigurację dla tego miejsca docelowego strumieniowego zdarzeń Datadog.",
"datadogDestAddDescription": "Skonfiguruj nowy punkt końcowy Datadog, aby odbierać zdarzenia Twojej organizacji.",
"httpDestTabSettings": "Ustawienia", "httpDestTabSettings": "Ustawienia",
"httpDestTabHeaders": "Nagłówki", "httpDestTabHeaders": "Nagłówki",
"httpDestTabBody": "Ciało", "httpDestTabBody": "Ciało",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Cel został utworzony pomyślnie", "httpDestCreatedSuccess": "Cel został utworzony pomyślnie",
"httpDestUpdateFailed": "Nie udało się zaktualizować miejsca docelowego", "httpDestUpdateFailed": "Nie udało się zaktualizować miejsca docelowego",
"httpDestCreateFailed": "Nie udało się utworzyć miejsca docelowego", "httpDestCreateFailed": "Nie udało się utworzyć miejsca docelowego",
"followRedirects": "Podążaj za przekierowaniami",
"followRedirectsDescription": "Automatycznie podążaj za przekierowaniami HTTP dla żądań.",
"alertingErrorWebhookUrl": "Proszę wprowadzić poprawny URL dla web hooka.",
"healthCheckStrategyHttp": "Weryfikuje łączność i sprawdza status odpowiedzi HTTP.",
"healthCheckStrategyTcp": "Weryfikuje wyłącznie łączność TCP, bez sprawdzania odpowiedzi.",
"healthCheckStrategySnmp": "Wykonuje żądanie SNMP get w celu sprawdzenia stanu urządzeń sieciowych i infrastruktury.",
"healthCheckStrategyIcmp": "Używa żądań ICMP echo (pingów), aby sprawdzić, czy zasób jest dostępny i reagujący.",
"healthCheckTabStrategy": "Strategia",
"healthCheckTabConnection": "Łączenie",
"healthCheckTabAdvanced": "Zaawansowane",
"healthCheckStrategyNotAvailable": "Strategia ta nie jest dostępna. Skontaktuj się z działem sprzedaży, aby włączyć tę funkcję.",
"uptime30d": "Czas działania (30d)",
"idpAddActionCreateNew": "Utwórz nowego dostawcę tożsamości", "idpAddActionCreateNew": "Utwórz nowego dostawcę tożsamości",
"idpAddActionImportFromOrg": "Importuj z innej organizacji", "idpAddActionImportFromOrg": "Importuj z innej organizacji",
"idpImportDialogTitle": "Importuj dostawcę tożsamości", "idpImportDialogTitle": "Importuj dostawcę tożsamości",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Tego nie można cofnąć dla tej organizacji.", "idpUnassociateWarning": "Tego nie można cofnąć dla tej organizacji.",
"idpUnassociatedDescription": "Dostawca tożsamości pomyślnie odłączony od tej organizacji", "idpUnassociatedDescription": "Dostawca tożsamości pomyślnie odłączony od tej organizacji",
"idpUnassociateMenu": "Odłącz", "idpUnassociateMenu": "Odłącz",
"idpDeleteAllOrgsMenu": "Usuń" "idpDeleteAllOrgsMenu": "Usuń",
"publicIpEndpoint": "Koniec punktu pracy",
"lastTriggeredAt": "Ostatnie Wyzwolenie",
"reject": "Odrzuć",
"uptimeDaysAgo": "{count} dni temu",
"uptimeToday": "Dzisiaj",
"uptimeNoDataAvailable": "Brak danych dostępnych",
"uptimeSuffix": "czas pracy",
"uptimeDowntimeSuffix": "czas przestoju",
"uptimeTooltipUptimeLabel": "Czas pracy",
"uptimeTooltipDowntimeLabel": "Czas przestoju",
"uptimeOngoing": "w toku",
"uptimeNoMonitoringData": "Brak danych monitorowania",
"uptimeNoData": "Brak danych",
"uptimeMiniBarDown": "Nieaktywny",
"uptimeSectionTitle": "Czas pracy",
"uptimeSectionDescription": "Dostępność za ostatnie {days} dni",
"uptimeAddAlert": "Dodaj Alert",
"uptimeViewAlerts": "Zobacz Alerty",
"uptimeCreateEmailAlert": "Utwórz Alert Email",
"uptimeAlertDescriptionSite": "Otrzymuj powiadomienia e-mail, gdy ta strona jest offline lub wraca online.",
"uptimeAlertDescriptionResource": "Otrzymuj powiadomienia e-mail, gdy to zasób jest offline lub wraca online.",
"uptimeAlertNamePlaceholder": "Nazwa alertu",
"uptimeAdditionalEmails": "Dodatkowe adresy e-mail",
"uptimeCreateAlert": "Utwórz Alert",
"uptimeAlertNoRecipients": "Brak odbiorców",
"uptimeAlertNoRecipientsDescription": "Proszę dodać przynajmniej jednego użytkownika, rolę lub adres email do powiadomienia.",
"uptimeAlertCreated": "Alert utworzony",
"uptimeAlertCreatedDescription": "Zostaniesz powiadomiony, gdy status się zmieni.",
"uptimeAlertCreateFailed": "Nie udało się utworzyć alertu",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Klucz",
"webhookHeaderValuePlaceholder": "Wartość",
"alertLabel": "Alert",
"domainPickerWildcardSubdomainNotAllowed": "Uniwersalne subdomeny nie są dozwolone.",
"domainPickerWildcardCertWarning": "Uniwersalne zasoby mogą wymagać dodatkowej konfiguracji, aby działać poprawnie.",
"domainPickerWildcardCertWarningLink": "Dowiedz się więcej",
"health": "Zdrowie",
"domainPendingErrorTitle": "Problem z weryfikacją"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Contacte vendas para ativar esta funcionalidade.",
"contactSalesBookDemo": "Agende uma demonstração",
"contactSalesOr": "ou",
"contactSalesContactUs": "contacte-nos",
"setupCreate": "Criar a organização, o site e os recursos", "setupCreate": "Criar a organização, o site e os recursos",
"headerAuthCompatibilityInfo": "Habilite isso para forçar uma resposta 401 Unauthorized quando um token de autenticação estiver faltando. Isso é necessário para navegadores ou bibliotecas HTTP específicas que não enviam credenciais sem um desafio do servidor.", "headerAuthCompatibilityInfo": "Habilite isso para forçar uma resposta 401 Unauthorized quando um token de autenticação estiver faltando. Isso é necessário para navegadores ou bibliotecas HTTP específicas que não enviam credenciais sem um desafio do servidor.",
"headerAuthCompatibility": "Compatibilidade Estendida", "headerAuthCompatibility": "Compatibilidade Estendida",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.", "componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.",
"dismiss": "Rejeitar", "dismiss": "Rejeitar",
"subscriptionViolationMessage": "Você está além dos seus limites para o seu plano atual. Corrija o problema removendo sites, usuários, ou outros recursos para ficar em seu plano.", "subscriptionViolationMessage": "Você está além dos seus limites para o seu plano atual. Corrija o problema removendo sites, usuários, ou outros recursos para ficar em seu plano.",
"trialBannerMessage": "Sua avaliação termina em {countdown}. Faça o upgrade para manter o acesso.",
"trialBannerExpired": "Sua avaliação expirou. Faça o upgrade agora para restaurar o acesso.",
"trialActive": "Avaliação Gratuita Ativa",
"trialExpired": "Avaliação Expirada",
"trialHasEnded": "Sua avaliação terminou.",
"trialDaysRemaining": "{count, plural, one {# dia restante} other {# dias restantes}}",
"trialDaysLeftShort": "{days}d restante na avaliação",
"trialGoToBilling": "Ir para a página de faturamento",
"subscriptionViolationViewBilling": "Ver faturamento", "subscriptionViolationViewBilling": "Ver faturamento",
"componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.", "componentsLicenseViolation": "Violação de Licença: Este servidor está usando sites {usedSites} que excedem o limite licenciado de sites {maxSites} . Siga os termos da licença para continuar usando todos os recursos.",
"componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!", "componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Eu copiei a configuração", "siteConfirmCopy": "Eu copiei a configuração",
"searchSitesProgress": "Procurar sites...", "searchSitesProgress": "Procurar sites...",
"siteAdd": "Adicionar Site", "siteAdd": "Adicionar Site",
"sitesTableViewPublicResources": "Visualizar Recursos Públicos",
"sitesTableViewPrivateResources": "Visualizar Recursos Privados",
"siteInstallNewt": "Instalar Novo", "siteInstallNewt": "Instalar Novo",
"siteInstallNewtDescription": "Novo item em execução no seu sistema", "siteInstallNewtDescription": "Novo item em execução no seu sistema",
"WgConfiguration": "Configuração do WireGuard", "WgConfiguration": "Configuração do WireGuard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "O site foi atualizado.", "siteUpdatedDescription": "O site foi atualizado.",
"siteGeneralDescription": "Configurar as configurações gerais para este site", "siteGeneralDescription": "Configurar as configurações gerais para este site",
"siteSettingDescription": "Configurar as configurações no site", "siteSettingDescription": "Configurar as configurações no site",
"siteResourcesTab": "Recursos",
"siteResourcesNoneOnSite": "Este site ainda não possui recursos públicos ou privados.",
"siteResourcesSectionPublic": "Recursos Públicos",
"siteResourcesSectionPrivate": "Recursos Privados",
"siteResourcesSectionPublicDescription": "Recursos expostos externamente por meio de domínios ou portas.",
"siteResourcesSectionPrivateDescription": "Recursos disponíveis na sua rede privada por meio do site.",
"siteResourcesViewAllPublic": "Ver todos os recursos",
"siteResourcesViewAllPrivate": "Ver todos os recursos",
"siteResourcesDialogDescription": "Visão geral dos recursos públicos e privados associados a este site.",
"siteResourcesShowMore": "Mostrar Mais",
"siteResourcesPermissionDenied": "Você não tem permissão para listar estes recursos.",
"siteResourcesEmptyPublic": "Ainda não há recursos públicos direcionados para este site.",
"siteResourcesEmptyPrivate": "Ainda não há recursos privados associados a este site.",
"siteResourcesHowToAccess": "Como acessar",
"siteResourcesTargetsOnSite": "Alvos neste site",
"siteSetting": "Configurações do {siteName}", "siteSetting": "Configurações do {siteName}",
"siteNewtTunnel": "Novo Site (Recomendado)", "siteNewtTunnel": "Novo Site (Recomendado)",
"siteNewtTunnelDescription": "Maneira mais fácil de criar um ponto de entrada em qualquer rede. Nenhuma configuração extra.", "siteNewtTunnelDescription": "Maneira mais fácil de criar um ponto de entrada em qualquer rede. Nenhuma configuração extra.",
@@ -267,8 +296,11 @@
"orgMissing": "ID da Organização Ausente", "orgMissing": "ID da Organização Ausente",
"orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.", "orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.",
"accessUsersManage": "Gerir Utilizadores", "accessUsersManage": "Gerir Utilizadores",
"accessUserManage": "Gerir Utilizador",
"accessUsersDescription": "Convidar e gerenciar usuários com acesso a esta organização", "accessUsersDescription": "Convidar e gerenciar usuários com acesso a esta organização",
"accessUsersSearch": "Procurar utilizadores...", "accessUsersSearch": "Procurar utilizadores...",
"accessUsersRoleFilterCount": "{count, plural, one {# função} other {# funções}}",
"accessUsersRoleFilterClear": "Limpar filtros de funções",
"accessUserCreate": "Criar Usuário", "accessUserCreate": "Criar Usuário",
"accessUserRemove": "Remover utilizador", "accessUserRemove": "Remover utilizador",
"username": "Usuário:", "username": "Usuário:",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Chave Secreta", "newtSecretKey": "Chave Secreta",
"newtVersion": "Versão",
"architecture": "Arquitetura", "architecture": "Arquitetura",
"sites": "sites", "sites": "sites",
"siteWgAnyClients": "Use qualquer cliente do WireGuard para se conectar. Você terá que endereçar recursos internos usando o IP de pares.", "siteWgAnyClients": "Use qualquer cliente do WireGuard para se conectar. Você terá que endereçar recursos internos usando o IP de pares.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Visualizar registros", "actionViewLogs": "Visualizar registros",
"noneSelected": "Nenhum selecionado", "noneSelected": "Nenhum selecionado",
"orgNotFound2": "Nenhuma organização encontrada.", "orgNotFound2": "Nenhuma organização encontrada.",
"search": "Pesquisar…",
"searchPlaceholder": "Buscar...", "searchPlaceholder": "Buscar...",
"emptySearchOptions": "Nenhuma opção encontrada", "emptySearchOptions": "Nenhuma opção encontrada",
"create": "Criar", "create": "Criar",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Gerir", "sidebarGeneral": "Gerir",
"sidebarLogAndAnalytics": "Registo & Análise", "sidebarLogAndAnalytics": "Registo & Análise",
"sidebarBluePrints": "Diagramas", "sidebarBluePrints": "Diagramas",
"sidebarAlerting": "Alertas",
"sidebarHealthChecks": "Verificações de Saúde",
"sidebarOrganization": "Organização", "sidebarOrganization": "Organização",
"sidebarManagement": "Gestão", "sidebarManagement": "Gestão",
"sidebarBillingAndLicenses": "Faturamento e Licenças", "sidebarBillingAndLicenses": "Faturamento e Licenças",
"sidebarLogsAnalytics": "Análises", "sidebarLogsAnalytics": "Análises",
"alertingTitle": "Alertas",
"alertingDescription": "Defina fontes, gatilhos e ações para notificações",
"alertingRules": "Regras de alerta",
"alertingSearchRules": "Pesquisar regras…",
"alertingAddRule": "Criar Regra",
"alertingColumnSource": "Fonte",
"alertingColumnTrigger": "Gatilho",
"alertingColumnActions": "Ações",
"alertingColumnEnabled": "Ativado",
"alertingDeleteQuestion": "Por favor, confirme que deseja excluir esta regra de alerta.",
"alertingDeleteRule": "Excluir regra de alerta",
"alertingRuleDeleted": "Regra de alerta excluída",
"alertingRuleSaved": "Regra de alerta salva",
"alertingRuleSavedCreatedDescription": "Sua nova regra de alerta foi criada. Você pode continuar editando-a nesta página.",
"alertingRuleSavedUpdatedDescription": "As suas alterações para esta regra de alerta foram salvas.",
"alertingEditRule": "Editar Regra de Alerta",
"alertingCreateRule": "Criar Regra de Alerta",
"alertingRuleCredenzaDescription": "Escolha o que observar, quando disparar e como notificar",
"alertingRuleNamePlaceholder": "Site de produção fora do ar",
"alertingRuleEnabled": "Regra ativada",
"alertingSectionSource": "Fonte",
"alertingSourceType": "Tipo de Fonte",
"alertingSourceSite": "Site",
"alertingSourceHealthCheck": "Verificação de Saúde",
"alertingPickSites": "Sites",
"alertingPickHealthChecks": "Verificações de Saúde",
"alertingPickResources": "Recursos",
"alertingAllSites": "Todos os Sites",
"alertingAllSitesDescription": "Alerta disparado para qualquer site",
"alertingSpecificSites": "Sites Específicos",
"alertingSpecificSitesDescription": "Escolha sites específicos para observar",
"alertingAllHealthChecks": "Todas as Verificações de Saúde",
"alertingAllHealthChecksDescription": "Alerta disparado para qualquer verificação de saúde",
"alertingSpecificHealthChecks": "Verificações de Saúde Específicas",
"alertingSpecificHealthChecksDescription": "Escolha verificações de saúde específicas para observar",
"alertingAllResources": "Todos os Recursos",
"alertingAllResourcesDescription": "Alerta disparado para qualquer recurso",
"alertingSpecificResources": "Recursos Específicos",
"alertingSpecificResourcesDescription": "Escolha recursos específicos para observar",
"alertingSelectResources": "Selecionar recursos…",
"alertingResourcesSelected": "{count} recursos selecionados",
"alertingResourcesEmpty": "Nenhum recurso com alvos nos primeiros 10 resultados.",
"alertingSectionTrigger": "Gatilho",
"alertingTrigger": "Quando alertar",
"alertingTriggerSiteOnline": "Site online",
"alertingTriggerSiteOffline": "Site offline",
"alertingTriggerSiteToggle": "Status do site muda",
"alertingTriggerHcHealthy": "Verificação de saúde saudável",
"alertingTriggerHcUnhealthy": "Verificação de saúde não saudável",
"alertingTriggerHcToggle": "Status da verificação de saúde muda",
"alertingTriggerResourceHealthy": "Recurso saudável",
"alertingTriggerResourceUnhealthy": "Recurso não saudável",
"alertingTriggerResourceDegraded": "Recurso degradado",
"alertingSearchHealthChecks": "Pesquisar verificações de saúde…",
"alertingHealthChecksEmpty": "Nenhuma verificação de saúde disponível.",
"alertingTriggerResourceToggle": "Status do recurso muda",
"alertingSourceResource": "Recurso",
"alertingSectionActions": "Ações",
"alertingAddAction": "Adicionar Ação",
"alertingActionNotify": "E-mail",
"alertingActionNotifyDescription": "Enviar notificações por e-mail para usuários ou funções",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Envie uma solicitação HTTP para um endpoint personalizado",
"alertingExternalIntegration": "Integração Externa",
"alertingExternalPagerDutyDescription": "Envie alertas para PagerDuty para gerenciamento de incidentes",
"alertingExternalOpsgenieDescription": "Direcione alertas para Opsgenie para gestão de plantão",
"alertingExternalServiceNowDescription": "Crie incidentes do ServiceNow a partir de eventos de alerta",
"alertingExternalIncidentIoDescription": "Dispare fluxos de trabalho do Incident.io a partir de eventos de alerta",
"alertingActionType": "Tipo de Ação",
"alertingNotifyUsers": "Utilizadores",
"alertingNotifyRoles": "Papéis",
"alertingNotifyEmails": "Endereços de e-mail",
"alertingEmailPlaceholder": "Adicione o e-mail e pressione Enter",
"alertingWebhookMethod": "Método HTTP",
"alertingWebhookSecret": "Segredo de assinatura (opcional)",
"alertingWebhookSecretPlaceholder": "Segredo HMAC",
"alertingWebhookHeaders": "Cabeçalhos",
"alertingAddHeader": "Adicionar cabeçalho",
"alertingSelectSites": "Selecionar sites…",
"alertingSitesSelected": "{count} sites selecionados",
"alertingSelectHealthChecks": "Selecionar verificações de saúde…",
"alertingHealthChecksSelected": "{count} verificações de saúde selecionadas",
"alertingNoHealthChecks": "Nenhum alvo com verificações de saúde ativadas",
"alertingHealthCheckStub": "A seleção da fonte de verificação de saúde ainda não está configurada - você ainda pode configurar gatilhos e ações.",
"alertingSelectUsers": "Selecionar utilizadores…",
"alertingUsersSelected": "{count} utilizadores selecionados",
"alertingSelectRoles": "Selecionar funções…",
"alertingRolesSelected": "{count} funções selecionadas",
"alertingSummarySites": "Sites ({count})",
"alertingSummaryAllSites": "Todos os sites",
"alertingSummaryHealthChecks": "Verificações de saúde ({count})",
"alertingSummaryAllHealthChecks": "Todas as verificações de saúde",
"alertingSummaryResources": "Recursos ({count})",
"alertingSummaryAllResources": "Todos os recursos",
"alertingErrorNameRequired": "Digite um nome",
"alertingErrorActionsMin": "Adicione pelo menos uma ação",
"alertingErrorPickSites": "Selecione pelo menos um site",
"alertingErrorPickHealthChecks": "Selecione pelo menos uma verificação de saúde",
"alertingErrorPickResources": "Selecione pelo menos um recurso",
"alertingErrorTriggerSite": "Escolha um gatilho de site",
"alertingErrorTriggerHealth": "Escolha um gatilho de verificação de saúde",
"alertingErrorTriggerResource": "Escolha um gatilho de recurso",
"alertingErrorNotifyRecipients": "Escolha utilizadores, funções ou pelo menos um e-mail",
"alertingConfigureSource": "Configurar Fonte",
"alertingConfigureTrigger": "Configurar Gatilho",
"alertingConfigureActions": "Configurar Ações",
"alertingBackToRules": "Voltar às Regras",
"alertingRuleCooldown": "Tempo de Resfriamento (segundos)",
"alertingRuleCooldownDescription": "Tempo mínimo entre alertas repetidos para a mesma regra. Defina para 0 para disparar todas as vezes.",
"alertingDraftBadge": "Rascunho - salvar para armazenar esta regra",
"alertingSidebarHint": "Clique em um passo na tela para editá-lo aqui.",
"alertingGraphCanvasTitle": "Fluxo de Regras",
"alertingGraphCanvasDescription": "Visão geral visual de fonte, gatilho e ações. Selecione um nó para editá-lo no painel.",
"alertingNodeNotConfigured": "Ainda não configurado",
"alertingNodeActionsCount": "{count, plural, one {# ação} other {# ações}}",
"alertingNodeRoleSource": "Fonte",
"alertingNodeRoleTrigger": "Gatilho",
"alertingNodeRoleAction": "Ação",
"alertingTabRules": "Regras de Alerta",
"alertingTabHealthChecks": "Verificações de Saúde",
"alertingRulesBannerTitle": "Seja Notificado",
"alertingRulesBannerDescription": "Cada regra une o que observar (um site, verificação de saúde ou recurso), quando disparar (por exemplo, offline ou não saudável) e como notificar sua equipe por e-mail, webhooks ou integrações. Use esta lista para criar, ativar e gerenciar essas regras.",
"alertingHealthChecksBannerTitle": "Monitorar Saúde & Recursos",
"alertingHealthChecksBannerDescription": "As verificações de saúde são monitores HTTP ou TCP que você define uma vez. Você pode, então, usá-los como fontes em regras de alerta, para ser notificado quando um alvo se tornar saudável ou não saudável. As verificações de saúde em recursos também aparecem aqui.",
"standaloneHcTableTitle": "Verificações de Saúde",
"standaloneHcSearchPlaceholder": "Pesquisar verificações de saúde…",
"standaloneHcAddButton": "Criar Verificação de Saúde",
"standaloneHcCreateTitle": "Criar Verificação de Saúde",
"standaloneHcEditTitle": "Editar Verificação de Saúde",
"standaloneHcDescription": "Configure uma verificação de saúde HTTP ou TCP para uso em regras de alerta.",
"standaloneHcNameLabel": "Nome",
"standaloneHcNamePlaceholder": "Meu Monitor HTTP",
"standaloneHcDeleteTitle": "Excluir verificação de saúde",
"standaloneHcDeleteQuestion": "Por favor, confirme que deseja excluir esta verificação de saúde.",
"standaloneHcDeleted": "Verificação de saúde excluída",
"standaloneHcSaved": "Verificação de saúde salva",
"standaloneHcColumnHealth": "Saúde",
"standaloneHcColumnMode": "Modo",
"standaloneHcColumnTarget": "Alvo",
"standaloneHcHealthStateHealthy": "Saudável",
"standaloneHcHealthStateUnhealthy": "Não Saudável",
"standaloneHcHealthStateUnknown": "Desconhecido",
"standaloneHcFilterAnySite": "Todos os sites",
"standaloneHcFilterAnyResource": "Todos os recursos",
"standaloneHcFilterMode": "Modo",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Saúde",
"standaloneHcFilterEnabled": "Ativado",
"standaloneHcFilterEnabledOn": "Ativado",
"standaloneHcFilterEnabledOff": "Desativado",
"standaloneHcFilterSiteIdFallback": "Site {id}",
"standaloneHcFilterResourceIdFallback": "Recurso {id}",
"blueprints": "Diagramas", "blueprints": "Diagramas",
"blueprintsDescription": "Aplicar configurações declarativas e ver execuções anteriores", "blueprintsDescription": "Aplicar configurações declarativas e ver execuções anteriores",
"blueprintAdd": "Adicionar Diagrama", "blueprintAdd": "Adicionar Diagrama",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Crie a conta de administrador inicial do servidor. Apenas um administrador do servidor pode existir. Você sempre pode alterar essas credenciais posteriormente.", "initialSetupDescription": "Crie a conta de administrador inicial do servidor. Apenas um administrador do servidor pode existir. Você sempre pode alterar essas credenciais posteriormente.",
"createAdminAccount": "Criar Conta de Administrador", "createAdminAccount": "Criar Conta de Administrador",
"setupErrorCreateAdmin": "Ocorreu um erro ao criar a conta de administrador do servidor.", "setupErrorCreateAdmin": "Ocorreu um erro ao criar a conta de administrador do servidor.",
"certificateStatus": "Status do Certificado", "certificateStatus": "Certificado",
"certificateStatusAutoRefreshHint": "Status atualiza automaticamente.",
"loading": "Carregando", "loading": "Carregando",
"loadingAnalytics": "Carregando Analytics", "loadingAnalytics": "Carregando Analytics",
"restart": "Reiniciar", "restart": "Reiniciar",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Ver notas de versão", "pangolinUpdateAvailableReleaseNotes": "Ver notas de versão",
"newtUpdateAvailable": "Nova Atualização Disponível", "newtUpdateAvailable": "Nova Atualização Disponível",
"newtUpdateAvailableInfo": "Uma nova versão do Newt está disponível. Atualize para a versão mais recente para uma melhor experiência.", "newtUpdateAvailableInfo": "Uma nova versão do Newt está disponível. Atualize para a versão mais recente para uma melhor experiência.",
"pangolinNodeUpdateAvailableInfo": "Uma nova versão do Pangolin Node está disponível. Atualize para a versão mais recente para uma melhor experiência.",
"domainPickerEnterDomain": "Domínio", "domainPickerEnterDomain": "Domínio",
"domainPickerPlaceholder": "myapp.exemplo.com", "domainPickerPlaceholder": "myapp.exemplo.com",
"domainPickerDescription": "Insira o domínio completo do recurso para ver as opções disponíveis.", "domainPickerDescription": "Insira o domínio completo do recurso para ver as opções disponíveis.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Configurar Verificação de Saúde", "configureHealthCheck": "Configurar Verificação de Saúde",
"configureHealthCheckDescription": "Configure a monitorização de saúde para {target}", "configureHealthCheckDescription": "Configure a monitorização de saúde para {target}",
"enableHealthChecks": "Ativar Verificações de Saúde", "enableHealthChecks": "Ativar Verificações de Saúde",
"healthCheckDisabledStateDescription": "Quando desativado, o site não realizará verificações de saúde e o estado será considerado desconhecido.",
"enableHealthChecksDescription": "Monitore a saúde deste alvo. Você pode monitorar um ponto de extremidade diferente do alvo, se necessário.", "enableHealthChecksDescription": "Monitore a saúde deste alvo. Você pode monitorar um ponto de extremidade diferente do alvo, se necessário.",
"healthScheme": "Método", "healthScheme": "Método",
"healthSelectScheme": "Selecione o Método", "healthSelectScheme": "Selecione o Método",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "O intervalo de verificação deve ser de pelo menos 5 segundos", "healthCheckIntervalMin": "O intervalo de verificação deve ser de pelo menos 5 segundos",
"healthCheckTimeoutMin": "O tempo limite deve ser de pelo menos 1 segundo", "healthCheckTimeoutMin": "O tempo limite deve ser de pelo menos 1 segundo",
"healthCheckRetryMin": "As tentativas de repetição devem ser pelo menos 1", "healthCheckRetryMin": "As tentativas de repetição devem ser pelo menos 1",
"healthCheckMode": "Modo de Verificação",
"healthCheckStrategy": "Estratégia",
"healthCheckModeDescription": "Modo TCP verifica apenas a conectividade. Modo HTTP valida a resposta HTTP.",
"healthyThreshold": "Limite de Saúde",
"healthyThresholdDescription": "Sucessos consecutivos necessários antes de marcar como saudável.",
"unhealthyThreshold": "Limite de Não Saúde",
"unhealthyThresholdDescription": "Falhas consecutivas necessárias antes de marcar como não saudável.",
"healthCheckHealthyThresholdMin": "Limite de saúde deve ser pelo menos 1",
"healthCheckUnhealthyThresholdMin": "Limite de não saúde deve ser pelo menos 1",
"httpMethod": "Método HTTP", "httpMethod": "Método HTTP",
"selectHttpMethod": "Selecionar método HTTP", "selectHttpMethod": "Selecionar método HTTP",
"domainPickerSubdomainLabel": "Subdomínio", "domainPickerSubdomainLabel": "Subdomínio",
"domainPickerWildcard": "Coringa",
"domainPickerWildcardPaidOnly": "Subdomínios curinga são um recurso pago. Por favor, atualize para acessar este recurso.",
"domainPickerBaseDomainLabel": "Domínio Base", "domainPickerBaseDomainLabel": "Domínio Base",
"domainPickerSearchDomains": "Buscar domínios...", "domainPickerSearchDomains": "Buscar domínios...",
"domainPickerNoDomainsFound": "Nenhum domínio encontrado", "domainPickerNoDomainsFound": "Nenhum domínio encontrado",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Este endereço faz parte da sub-rede de utilitários da organização. É usado para resolver registros de alias usando resolução de DNS interno.", "resourcesTableAliasAddressInfo": "Este endereço faz parte da sub-rede de utilitários da organização. É usado para resolver registros de alias usando resolução de DNS interno.",
"resourcesTableClients": "Clientes", "resourcesTableClients": "Clientes",
"resourcesTableAndOnlyAccessibleInternally": "e são acessíveis apenas internamente quando conectados com um cliente.", "resourcesTableAndOnlyAccessibleInternally": "e são acessíveis apenas internamente quando conectados com um cliente.",
"resourcesTableNoTargets": "Nenhum alvo",
"resourcesTableHealthy": "Saudável", "resourcesTableHealthy": "Saudável",
"resourcesTableDegraded": "Degradado", "resourcesTableDegraded": "Degradado",
"resourcesTableOffline": "Desconectado", "resourcesTableUnhealthy": "Não Saudável",
"resourcesTableUnknown": "Desconhecido", "resourcesTableUnknown": "Desconhecido",
"resourcesTableNotMonitored": "Não monitorado", "resourcesTableNotMonitored": "Não monitorado",
"resourcesTableNoTargets": "Sem alvos",
"editInternalResourceDialogEditClientResource": "Editar Recurso Privado", "editInternalResourceDialogEditClientResource": "Editar Recurso Privado",
"editInternalResourceDialogUpdateResourceProperties": "Atualizar as configurações de recursos e controles de acesso para {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Atualizar as configurações de recursos e controles de acesso para {resourceName}",
"editInternalResourceDialogResourceProperties": "Propriedades do Recurso", "editInternalResourceDialogResourceProperties": "Propriedades do Recurso",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Porta", "editInternalResourceDialogModePort": "Porta",
"editInternalResourceDialogModeHost": "Servidor", "editInternalResourceDialogModeHost": "Servidor",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Esquema",
"editInternalResourceDialogEnableSsl": "Ativar SSL",
"editInternalResourceDialogEnableSslDescription": "Ativar criptografia SSL/TLS para conexões HTTPS seguras com o destino.",
"editInternalResourceDialogDestination": "Destino", "editInternalResourceDialogDestination": "Destino",
"editInternalResourceDialogDestinationHostDescription": "O endereço IP ou o nome do host do recurso na rede do site.", "editInternalResourceDialogDestinationHostDescription": "O endereço IP ou o nome do host do recurso na rede do site.",
"editInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.", "editInternalResourceDialogDestinationIPDescription": "O IP ou endereço do hostname do recurso na rede do site.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Nome", "createInternalResourceDialogName": "Nome",
"createInternalResourceDialogSite": "Site", "createInternalResourceDialogSite": "Site",
"selectSite": "Selecionar site...", "selectSite": "Selecionar site...",
"multiSitesSelectorSitesCount": "{count, plural, one {# site} other {# sites}}",
"noSitesFound": "Nenhum site encontrado.", "noSitesFound": "Nenhum site encontrado.",
"createInternalResourceDialogProtocol": "Protocolo", "createInternalResourceDialogProtocol": "Protocolo",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Porta", "createInternalResourceDialogModePort": "Porta",
"createInternalResourceDialogModeHost": "Servidor", "createInternalResourceDialogModeHost": "Servidor",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Esquema",
"createInternalResourceDialogScheme": "Esquema",
"createInternalResourceDialogEnableSsl": "Ativar SSL",
"createInternalResourceDialogEnableSslDescription": "Ativar criptografia SSL/TLS para conexões HTTPS seguras com o destino.",
"createInternalResourceDialogDestination": "Destino", "createInternalResourceDialogDestination": "Destino",
"createInternalResourceDialogDestinationHostDescription": "O endereço IP ou o nome do host do recurso na rede do site.", "createInternalResourceDialogDestinationHostDescription": "O endereço IP ou o nome do host do recurso na rede do site.",
"createInternalResourceDialogDestinationCidrDescription": "A faixa CIDR do recurso na rede do site.", "createInternalResourceDialogDestinationCidrDescription": "A faixa CIDR do recurso na rede do site.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Um alias de DNS interno opcional para este recurso.", "createInternalResourceDialogAliasDescription": "Um alias de DNS interno opcional para este recurso.",
"internalResourceDownstreamSchemeRequired": "Esquema é obrigatório para recursos HTTP",
"internalResourceHttpPortRequired": "Porta de destino é obrigatória para recursos HTTP",
"siteConfiguration": "Configuração", "siteConfiguration": "Configuração",
"siteAcceptClientConnections": "Aceitar Conexões de Clientes", "siteAcceptClientConnections": "Aceitar Conexões de Clientes",
"siteAcceptClientConnectionsDescription": "Permitir que dispositivos de usuário e clientes acessem recursos neste site. Isso pode ser alterado mais tarde.", "siteAcceptClientConnectionsDescription": "Permitir que dispositivos de usuário e clientes acessem recursos neste site. Isso pode ser alterado mais tarde.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Verificada", "domainPickerVerified": "Verificada",
"domainPickerUnverified": "Não verificado", "domainPickerUnverified": "Não verificado",
"domainPickerManual": "Manual", "domainPickerManual": "Manual",
"domainPickerInvalidSubdomainStructure": "Este subdomínio contém caracteres ou estrutura inválidos. Ele será eliminado automaticamente quando você salvar.", "domainPickerInvalidSubdomainStructure": "Caracteres inválidos serão sanitizados ao serem salvos.",
"domainPickerError": "ERRO", "domainPickerError": "ERRO",
"domainPickerErrorLoadDomains": "Falha ao carregar domínios da organização", "domainPickerErrorLoadDomains": "Falha ao carregar domínios da organização",
"domainPickerErrorCheckAvailability": "Não foi possível verificar a disponibilidade do domínio", "domainPickerErrorCheckAvailability": "Não foi possível verificar a disponibilidade do domínio",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Escolha o seu provedor de identidade para continuar", "orgAuthChooseIdpDescription": "Escolha o seu provedor de identidade para continuar",
"orgAuthNoIdpConfigured": "Esta organização não tem nenhum provedor de identidade configurado. Você pode entrar com a identidade do seu Pangolin.", "orgAuthNoIdpConfigured": "Esta organização não tem nenhum provedor de identidade configurado. Você pode entrar com a identidade do seu Pangolin.",
"orgAuthSignInWithPangolin": "Entrar com o Pangolin", "orgAuthSignInWithPangolin": "Entrar com o Pangolin",
"orgAuthSignInToOrg": "Fazer login em uma organização", "orgAuthSignInToOrg": "Provedor de Identidade da Organização (SSO)",
"orgAuthSelectOrgTitle": "Entrada da Organização", "orgAuthSelectOrgTitle": "Entrada da Organização",
"orgAuthSelectOrgDescription": "Digite seu ID da organização para continuar", "orgAuthSelectOrgDescription": "Digite seu ID da organização para continuar",
"orgAuthOrgIdPlaceholder": "sua-organização", "orgAuthOrgIdPlaceholder": "sua-organização",
@@ -2429,6 +2648,7 @@
"validPassword": "Senha válida", "validPassword": "Senha válida",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Cliente Conectado",
"resourceBlocked": "Recurso bloqueado", "resourceBlocked": "Recurso bloqueado",
"droppedByRule": "Derrubado pela regra", "droppedByRule": "Derrubado pela regra",
"noSessions": "Sem Sessões", "noSessions": "Sem Sessões",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Adicionar Clientes", "editInternalResourceDialogAddClients": "Adicionar Clientes",
"editInternalResourceDialogDestinationLabel": "Destino", "editInternalResourceDialogDestinationLabel": "Destino",
"editInternalResourceDialogDestinationDescription": "Especifique o endereço de destino para o recurso interno. Isso pode ser um nome de host, endereço IP ou intervalo CIDR, dependendo do modo selecionado. Opcionalmente, defina um alias interno de DNS para facilitar a identificação.", "editInternalResourceDialogDestinationDescription": "Especifique o endereço de destino para o recurso interno. Isso pode ser um nome de host, endereço IP ou intervalo CIDR, dependendo do modo selecionado. Opcionalmente, defina um alias interno de DNS para facilitar a identificação.",
"internalResourceFormMultiSiteRoutingHelp": "Selecionar múltiplos sites permite roteamento resiliente e failover para alta disponibilidade.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Saiba mais",
"editInternalResourceDialogPortRestrictionsDescription": "Restrinja o acesso a portas TCP/UDP específicas ou permita/bloqueie todas as portas.", "editInternalResourceDialogPortRestrictionsDescription": "Restrinja o acesso a portas TCP/UDP específicas ou permita/bloqueie todas as portas.",
"createInternalResourceDialogHttpConfiguration": "Configuração HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Escolha o domínio que os clientes usarão para acessar este recurso via HTTP ou HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Configuração HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Escolha o domínio que os clientes usarão para acessar este recurso via HTTP ou HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Voltaremos em breve! Nosso site está passando por manutenção programada.", "maintenancePageMessagePlaceholder": "Voltaremos em breve! Nosso site está passando por manutenção programada.",
"maintenancePageMessageDescription": "Mensagem detalhada explicando a manutenção", "maintenancePageMessageDescription": "Mensagem detalhada explicando a manutenção",
"maintenancePageTimeTitle": "Hora de Conclusão Estimada (Opcional)", "maintenancePageTimeTitle": "Hora de Conclusão Estimada (Opcional)",
"privateMaintenanceScreenTitle": "Tela de Placeholder Privada",
"privateMaintenanceScreenMessage": "Este domínio está sendo usado em um recurso privado. Por favor, conecte-se usando o cliente Pangolin para acessar este recurso.",
"privateMaintenanceScreenSteps": "Depois de conectado, se você ainda estiver vendo esta mensagem, o cache DNS do seu navegador pode ainda apontar para o antigo endereço. Para corrigir isso: feche completamente e reabra esta aba, ou o seu navegador, e então navegue de volta para esta página.",
"maintenanceTime": "por exemplo, 2 horas, 1 de Nov às 17h00", "maintenanceTime": "por exemplo, 2 horas, 1 de Nov às 17h00",
"maintenanceEstimatedTimeDescription": "Quando você espera que a manutenção seja concluída", "maintenanceEstimatedTimeDescription": "Quando você espera que a manutenção seja concluída",
"editDomain": "Editar Domínio", "editDomain": "Editar Domínio",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Adicionar Destino HTTP", "httpDestAddTitle": "Adicionar Destino HTTP",
"httpDestEditDescription": "Atualizar a configuração para este destino de transmissão de eventos 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.", "httpDestAddDescription": "Configure um novo ponto de extremidade HTTP para receber eventos da sua organização.",
"S3DestEditTitle": "Editar Destino",
"S3DestAddTitle": "Adicionar Destino S3",
"S3DestEditDescription": "Atualize a configuração para este destino de streaming de eventos S3.",
"S3DestAddDescription": "Configure um novo endpoint S3 para receber os eventos da sua organização.",
"datadogDestEditTitle": "Editar Destino",
"datadogDestAddTitle": "Adicionar Destino Datadog",
"datadogDestEditDescription": "Atualize a configuração para este destino de streaming de eventos Datadog.",
"datadogDestAddDescription": "Configure um novo endpoint Datadog para receber os eventos da sua organização.",
"httpDestTabSettings": "Confirgurações", "httpDestTabSettings": "Confirgurações",
"httpDestTabHeaders": "Cabeçalhos", "httpDestTabHeaders": "Cabeçalhos",
"httpDestTabBody": "Conteúdo", "httpDestTabBody": "Conteúdo",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Destino criado com sucesso", "httpDestCreatedSuccess": "Destino criado com sucesso",
"httpDestUpdateFailed": "Falha ao atualizar destino", "httpDestUpdateFailed": "Falha ao atualizar destino",
"httpDestCreateFailed": "Falha ao criar destino", "httpDestCreateFailed": "Falha ao criar destino",
"followRedirects": "Seguir Redirecionamentos",
"followRedirectsDescription": "Siga automaticamente os redirecionamentos HTTP para requisições.",
"alertingErrorWebhookUrl": "Por favor, insira um URL válido para o webhook.",
"healthCheckStrategyHttp": "Valida conectividade e verifica o status da resposta HTTP.",
"healthCheckStrategyTcp": "Verifica apenas conectividade TCP, sem inspecionar a resposta.",
"healthCheckStrategySnmp": "Faz uma solicitação SNMP para verificar a saúde dos dispositivos e infraestruturas de rede.",
"healthCheckStrategyIcmp": "Usa solicitações de eco ICMP (pings) para verificar se um recurso é acessível e responsivo.",
"healthCheckTabStrategy": "Estratégia",
"healthCheckTabConnection": "Conexão",
"healthCheckTabAdvanced": "Avançado",
"healthCheckStrategyNotAvailable": "Esta estratégia não está disponível. Por favor, contacte vendas para ativar esta funcionalidade.",
"uptime30d": "Uptime (30d)",
"idpAddActionCreateNew": "Criar novo provedor de identidade", "idpAddActionCreateNew": "Criar novo provedor de identidade",
"idpAddActionImportFromOrg": "Importar de outra organização", "idpAddActionImportFromOrg": "Importar de outra organização",
"idpImportDialogTitle": "Importar Provedor de Identidade", "idpImportDialogTitle": "Importar Provedor de Identidade",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Isso não pode ser desfeito para esta organização.", "idpUnassociateWarning": "Isso não pode ser desfeito para esta organização.",
"idpUnassociatedDescription": "Provedor de identidade desassociado desta organização com sucesso", "idpUnassociatedDescription": "Provedor de identidade desassociado desta organização com sucesso",
"idpUnassociateMenu": "Desassociar", "idpUnassociateMenu": "Desassociar",
"idpDeleteAllOrgsMenu": "Excluir" "idpDeleteAllOrgsMenu": "Excluir",
"publicIpEndpoint": "Endpoint",
"lastTriggeredAt": "Último Gatilho",
"reject": "Rejeitar",
"uptimeDaysAgo": "há {count} dias",
"uptimeToday": "Hoje",
"uptimeNoDataAvailable": "Sem dados disponíveis",
"uptimeSuffix": "tempo de atividade",
"uptimeDowntimeSuffix": "tempo de inatividade",
"uptimeTooltipUptimeLabel": "Tempo de Atividade",
"uptimeTooltipDowntimeLabel": "Tempo de Inatividade",
"uptimeOngoing": "em andamento",
"uptimeNoMonitoringData": "Sem dados de monitoramento",
"uptimeNoData": "Sem dados",
"uptimeMiniBarDown": "Inativo",
"uptimeSectionTitle": "Tempo de Atividade",
"uptimeSectionDescription": "Disponibilidade nos últimos {days} dias",
"uptimeAddAlert": "Adicionar Alerta",
"uptimeViewAlerts": "Visualizar Alertas",
"uptimeCreateEmailAlert": "Criar Alerta por Email",
"uptimeAlertDescriptionSite": "Seja notificado por email quando este site sair do ar ou voltar online.",
"uptimeAlertDescriptionResource": "Seja notificado por email quando este recurso sair do ar ou voltar online.",
"uptimeAlertNamePlaceholder": "Nome do alerta",
"uptimeAdditionalEmails": "Emails Adicionais",
"uptimeCreateAlert": "Criar Alerta",
"uptimeAlertNoRecipients": "Sem destinatários",
"uptimeAlertNoRecipientsDescription": "Por favor, adicione pelo menos um usuário, função ou email para notificar.",
"uptimeAlertCreated": "Alerta criado",
"uptimeAlertCreatedDescription": "Você será notificado quando isso mudar de status.",
"uptimeAlertCreateFailed": "Falha ao criar alerta",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Chave",
"webhookHeaderValuePlaceholder": "Valor",
"alertLabel": "Alerta",
"domainPickerWildcardSubdomainNotAllowed": "Subdomínios curinga não são permitidos.",
"domainPickerWildcardCertWarning": "Recursos curinga podem exigir configurações adicionais para funcionarem corretamente.",
"domainPickerWildcardCertWarningLink": "Saiba mais",
"health": "Saúde",
"domainPendingErrorTitle": "Problema de Verificação"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Свяжитесь с отделом продаж, чтобы включить эту функцию.",
"contactSalesBookDemo": "Записаться на демонстрацию",
"contactSalesOr": "или",
"contactSalesContactUs": "свяжитесь с нами",
"setupCreate": "Создать организацию, сайт и ресурсы", "setupCreate": "Создать организацию, сайт и ресурсы",
"headerAuthCompatibilityInfo": "Включите это, чтобы принудительно вернуть ответ 401 Unauthorized, если отсутствует токен аутентификации. Это требуется для браузеров или определенных библиотек HTTP, которые не отправляют учетные данные без запроса сервера.", "headerAuthCompatibilityInfo": "Включите это, чтобы принудительно вернуть ответ 401 Unauthorized, если отсутствует токен аутентификации. Это требуется для браузеров или определенных библиотек HTTP, которые не отправляют учетные данные без запроса сервера.",
"headerAuthCompatibility": "Дополнительная совместимость", "headerAuthCompatibility": "Дополнительная совместимость",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Обнаружены недействительные или просроченные лицензионные ключи. Соблюдайте условия лицензии для использования всех функций.", "componentsInvalidKey": "Обнаружены недействительные или просроченные лицензионные ключи. Соблюдайте условия лицензии для использования всех функций.",
"dismiss": "Отменить", "dismiss": "Отменить",
"subscriptionViolationMessage": "Вы превысили лимиты для вашего текущего плана. Исправьте проблему, удалив сайты, пользователей или другие ресурсы, чтобы остаться в пределах вашего плана.", "subscriptionViolationMessage": "Вы превысили лимиты для вашего текущего плана. Исправьте проблему, удалив сайты, пользователей или другие ресурсы, чтобы остаться в пределах вашего плана.",
"trialBannerMessage": "Ваш пробный период истекает через {countdown}. Обновите, чтобы сохранить доступ.",
"trialBannerExpired": "Ваш пробный период истек. Обновите сейчас, чтобы восстановить доступ.",
"trialActive": "Бесплатный пробный период активен",
"trialExpired": "Пробный период истек",
"trialHasEnded": "Ваш пробный период окончен.",
"trialDaysRemaining": "{count, plural, one {# день остался} few {# дня осталось} many {# дней осталось} other {# дней осталось}}",
"trialDaysLeftShort": "Осталось {days}д в пробном периоде",
"trialGoToBilling": "Перейти на страницу выставления счетов",
"subscriptionViolationViewBilling": "Просмотр биллинга", "subscriptionViolationViewBilling": "Просмотр биллинга",
"componentsLicenseViolation": "Нарушение лицензии: Сервер использует {usedSites} сайтов, что превышает лицензионный лимит в {maxSites} сайтов. Соблюдайте условия лицензии для использования всех функций.", "componentsLicenseViolation": "Нарушение лицензии: Сервер использует {usedSites} сайтов, что превышает лицензионный лимит в {maxSites} сайтов. Соблюдайте условия лицензии для использования всех функций.",
"componentsSupporterMessage": "Спасибо за поддержку Pangolin в качестве {tier}!", "componentsSupporterMessage": "Спасибо за поддержку Pangolin в качестве {tier}!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Я скопировал(а) конфигурацию", "siteConfirmCopy": "Я скопировал(а) конфигурацию",
"searchSitesProgress": "Поиск сайтов...", "searchSitesProgress": "Поиск сайтов...",
"siteAdd": "Добавить сайт", "siteAdd": "Добавить сайт",
"sitesTableViewPublicResources": "Просмотр публичных ресурсов",
"sitesTableViewPrivateResources": "Просмотр частных ресурсов",
"siteInstallNewt": "Установить Newt", "siteInstallNewt": "Установить Newt",
"siteInstallNewtDescription": "Запустите Newt в вашей системе", "siteInstallNewtDescription": "Запустите Newt в вашей системе",
"WgConfiguration": "Конфигурация WireGuard", "WgConfiguration": "Конфигурация WireGuard",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Сайт был успешно обновлён.", "siteUpdatedDescription": "Сайт был успешно обновлён.",
"siteGeneralDescription": "Настройте общие параметры для этого сайта", "siteGeneralDescription": "Настройте общие параметры для этого сайта",
"siteSettingDescription": "Настройка параметров на сайте", "siteSettingDescription": "Настройка параметров на сайте",
"siteResourcesTab": "Ресурсы",
"siteResourcesNoneOnSite": "На этом сайте пока нет публичных или частных ресурсов.",
"siteResourcesSectionPublic": "Публичные ресурсы",
"siteResourcesSectionPrivate": "Частные ресурсы",
"siteResourcesSectionPublicDescription": "Ресурсы, доступные извне через домены или порты.",
"siteResourcesSectionPrivateDescription": "Ресурсы доступны на вашем частном сетевом ресурсе через сайт.",
"siteResourcesViewAllPublic": "Просмотреть все ресурсы",
"siteResourcesViewAllPrivate": "Просмотреть все ресурсы",
"siteResourcesDialogDescription": "Обзор публичных и частных ресурсов, связанных с этим сайтом.",
"siteResourcesShowMore": "Показать еще",
"siteResourcesPermissionDenied": "У вас нет разрешения на просмотр этих ресурсов.",
"siteResourcesEmptyPublic": "Ни один публичный ресурс еще не нацелен на этот сайт.",
"siteResourcesEmptyPrivate": "С этим сайтом еще не связано ни одного частного ресурса.",
"siteResourcesHowToAccess": "Как получить доступ",
"siteResourcesTargetsOnSite": "Цели на этом сайте",
"siteSetting": "Настройки {siteName}", "siteSetting": "Настройки {siteName}",
"siteNewtTunnel": "Новый сайт (рекомендуется)", "siteNewtTunnel": "Новый сайт (рекомендуется)",
"siteNewtTunnelDescription": "Самый простой способ создать точку входа в любую сеть. Дополнительная настройка не требуется.", "siteNewtTunnelDescription": "Самый простой способ создать точку входа в любую сеть. Дополнительная настройка не требуется.",
@@ -267,8 +296,11 @@
"orgMissing": "Отсутствует ID организации", "orgMissing": "Отсутствует ID организации",
"orgMissingMessage": "Невозможно восстановить приглашение без ID организации.", "orgMissingMessage": "Невозможно восстановить приглашение без ID организации.",
"accessUsersManage": "Управление пользователями", "accessUsersManage": "Управление пользователями",
"accessUserManage": "Управление пользователем",
"accessUsersDescription": "Пригласить и управлять пользователями с доступом к этой организации", "accessUsersDescription": "Пригласить и управлять пользователями с доступом к этой организации",
"accessUsersSearch": "Поиск пользователей...", "accessUsersSearch": "Поиск пользователей...",
"accessUsersRoleFilterCount": "{count, plural, one {# роль} few {# роли} many {# ролей} other {# роли}}",
"accessUsersRoleFilterClear": "Очистить фильтры ролей",
"accessUserCreate": "Создать пользователя", "accessUserCreate": "Создать пользователя",
"accessUserRemove": "Удалить пользователя", "accessUserRemove": "Удалить пользователя",
"username": "Имя пользователя", "username": "Имя пользователя",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Секретный ключ", "newtSecretKey": "Секретный ключ",
"newtVersion": "Версия",
"architecture": "Архитектура", "architecture": "Архитектура",
"sites": "Сайты", "sites": "Сайты",
"siteWgAnyClients": "Для подключения используйте любой клиент WireGuard. Вы должны будете адресовать внутренние ресурсы, используя IP адрес пира.", "siteWgAnyClients": "Для подключения используйте любой клиент WireGuard. Вы должны будете адресовать внутренние ресурсы, используя IP адрес пира.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Просмотр журналов", "actionViewLogs": "Просмотр журналов",
"noneSelected": "Ничего не выбрано", "noneSelected": "Ничего не выбрано",
"orgNotFound2": "Организации не найдены.", "orgNotFound2": "Организации не найдены.",
"search": "Поиск…",
"searchPlaceholder": "Поиск...", "searchPlaceholder": "Поиск...",
"emptySearchOptions": "Опции не найдены", "emptySearchOptions": "Опции не найдены",
"create": "Создать", "create": "Создать",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Управление", "sidebarGeneral": "Управление",
"sidebarLogAndAnalytics": "Журнал и аналитика", "sidebarLogAndAnalytics": "Журнал и аналитика",
"sidebarBluePrints": "Чертежи", "sidebarBluePrints": "Чертежи",
"sidebarAlerting": "Оповещения",
"sidebarHealthChecks": "Проверки здоровья",
"sidebarOrganization": "Организация", "sidebarOrganization": "Организация",
"sidebarManagement": "Управление", "sidebarManagement": "Управление",
"sidebarBillingAndLicenses": "Биллинг и лицензии", "sidebarBillingAndLicenses": "Биллинг и лицензии",
"sidebarLogsAnalytics": "Статистика", "sidebarLogsAnalytics": "Статистика",
"alertingTitle": "Оповещения",
"alertingDescription": "Определите источники, триггеры и действия для уведомлений",
"alertingRules": "Правила оповещений",
"alertingSearchRules": "Поиск правил…",
"alertingAddRule": "Создать правило",
"alertingColumnSource": "Источник",
"alertingColumnTrigger": "Триггер",
"alertingColumnActions": "Действия",
"alertingColumnEnabled": "Включено",
"alertingDeleteQuestion": "Пожалуйста, подтвердите удаление этого правила оповещений.",
"alertingDeleteRule": "Удалить правило оповещений",
"alertingRuleDeleted": "Правило оповещений удалено",
"alertingRuleSaved": "Правило оповещений сохранено",
"alertingRuleSavedCreatedDescription": "Ваше новое правило оповещений создано. Вы можете продолжать редактировать его на этой странице.",
"alertingRuleSavedUpdatedDescription": "Ваши изменения в этом правиле оповещений были сохранены.",
"alertingEditRule": "Редактировать правило оповещений",
"alertingCreateRule": "Создать правило оповещений",
"alertingRuleCredenzaDescription": "Выберите, что отслеживать, когда срабатывать и как уведомлять",
"alertingRuleNamePlaceholder": "Рабочий сайт не доступен",
"alertingRuleEnabled": "Правило включено",
"alertingSectionSource": "Источник",
"alertingSourceType": "Тип источника",
"alertingSourceSite": "Сайт",
"alertingSourceHealthCheck": "Проверка здоровья",
"alertingPickSites": "Сайты",
"alertingPickHealthChecks": "Проверки здоровья",
"alertingPickResources": "Ресурсы",
"alertingAllSites": "Все сайты",
"alertingAllSitesDescription": "Оповещение срабатывает на любом сайте",
"alertingSpecificSites": "Конкретные сайты",
"alertingSpecificSitesDescription": "Выберите конкретные сайты для отслеживания",
"alertingAllHealthChecks": "Все проверки здоровья",
"alertingAllHealthChecksDescription": "Оповещение срабатывает на любой проверке здоровья",
"alertingSpecificHealthChecks": "Конкретные проверки здоровья",
"alertingSpecificHealthChecksDescription": "Выберите конкретные проверки здоровья для отслеживания",
"alertingAllResources": "Все ресурсы",
"alertingAllResourcesDescription": "Оповещение срабатывает на любом ресурсе",
"alertingSpecificResources": "Конкретные ресурсы",
"alertingSpecificResourcesDescription": "Выберите конкретные ресурсы для отслеживания",
"alertingSelectResources": "Выберите ресурсы…",
"alertingResourcesSelected": "Выбрано {count} ресурсов",
"alertingResourcesEmpty": "Нет ресурсов с целью в первых 10 результатах.",
"alertingSectionTrigger": "Триггер",
"alertingTrigger": "Когда оповестить",
"alertingTriggerSiteOnline": "Сайт онлайн",
"alertingTriggerSiteOffline": "Сайт офлайн",
"alertingTriggerSiteToggle": "Статус сайта изменяется",
"alertingTriggerHcHealthy": "Проверка здоровья успешна",
"alertingTriggerHcUnhealthy": "Проверка здоровья не успешна",
"alertingTriggerHcToggle": "Статус проверки здоровья изменяется",
"alertingTriggerResourceHealthy": "Ресурс в нормальном состоянии",
"alertingTriggerResourceUnhealthy": "Ресурс в ненормальном состоянии",
"alertingTriggerResourceDegraded": "Ресурс ухудшен",
"alertingSearchHealthChecks": "Поиск проверок здоровья…",
"alertingHealthChecksEmpty": "Нет доступных проверок здоровья.",
"alertingTriggerResourceToggle": "Статус ресурса изменяется",
"alertingSourceResource": "Ресурс",
"alertingSectionActions": "Действия",
"alertingAddAction": "Добавить действие",
"alertingActionNotify": "Электронная почта",
"alertingActionNotifyDescription": "Отправляйте email уведомления пользователям или ролям",
"alertingActionWebhook": "Веб-хук",
"alertingActionWebhookDescription": "Отправьте HTTP-запрос на пользовательскую конечную точку",
"alertingExternalIntegration": "Внешняя интеграция",
"alertingExternalPagerDutyDescription": "Отправляйте оповещения в PagerDuty для управления инцидентами",
"alertingExternalOpsgenieDescription": "Маршрутизируйте оповещения в Opsgenie для управления дежурной службой",
"alertingExternalServiceNowDescription": "Создавайте инциденты ServiceNow из событий оповещений",
"alertingExternalIncidentIoDescription": "Запускайте рабочие процессы Incident.io из событий оповещений",
"alertingActionType": "Тип действия",
"alertingNotifyUsers": "Пользователи",
"alertingNotifyRoles": "Роли",
"alertingNotifyEmails": "Email адреса",
"alertingEmailPlaceholder": "Добавьте email и нажмите Enter",
"alertingWebhookMethod": "HTTP метод",
"alertingWebhookSecret": "Секрет подписания (необязательно)",
"alertingWebhookSecretPlaceholder": "HMAC секрет",
"alertingWebhookHeaders": "Заголовки",
"alertingAddHeader": "Добавить заголовок",
"alertingSelectSites": "Выберите сайты…",
"alertingSitesSelected": "Выбрано {count} сайтов",
"alertingSelectHealthChecks": "Выберите проверки здоровья…",
"alertingHealthChecksSelected": "Выбрано {count} проверок здоровья",
"alertingNoHealthChecks": "Цели без включенных проверок здоровья отсутствуют",
"alertingHealthCheckStub": "Выбор источника проверки здоровья ещё не подключён - вы все ещё можете настроить триггеры и действия.",
"alertingSelectUsers": "Выберите пользователей…",
"alertingUsersSelected": "Выбрано {count} пользователей",
"alertingSelectRoles": "Выберите роли…",
"alertingRolesSelected": "Выбрано {count} ролей",
"alertingSummarySites": "Сайты ({count})",
"alertingSummaryAllSites": "Все сайты",
"alertingSummaryHealthChecks": "Проверки здоровья ({count})",
"alertingSummaryAllHealthChecks": "Все проверки здоровья",
"alertingSummaryResources": "Ресурсы ({count})",
"alertingSummaryAllResources": "Все ресурсы",
"alertingErrorNameRequired": "Введите название",
"alertingErrorActionsMin": "Добавьте как минимум одно действие",
"alertingErrorPickSites": "Выберите как минимум один сайт",
"alertingErrorPickHealthChecks": "Выберите как минимум одну проверку здоровья",
"alertingErrorPickResources": "Выберите как минимум один ресурс",
"alertingErrorTriggerSite": "Выберите триггер сайта",
"alertingErrorTriggerHealth": "Выберите триггер проверки здоровья",
"alertingErrorTriggerResource": "Выберите триггер ресурса",
"alertingErrorNotifyRecipients": "Выберите пользователей, роли или как минимум один email",
"alertingConfigureSource": "Настроить источник",
"alertingConfigureTrigger": "Настроить триггер",
"alertingConfigureActions": "Настроить действия",
"alertingBackToRules": "Назад к правилам",
"alertingRuleCooldown": "Охлаждение (секунды)",
"alertingRuleCooldownDescription": "Минимальное время между повторными оповещениями для одного и того же правила. Установите 0 для каждого вызова.",
"alertingDraftBadge": "Черновик - сохраните, чтобы сохранить это правило",
"alertingSidebarHint": "Кликните по шагу на холсте, чтобы редактировать его здесь.",
"alertingGraphCanvasTitle": "Поток правил",
"alertingGraphCanvasDescription": "Визуальный обзор источника, триггера и действий. Выберите узел, чтобы редактировать его в панели.",
"alertingNodeNotConfigured": "Ещё не настроено",
"alertingNodeActionsCount": "{count, plural, one {# действие} few {# действия} many {# действий} other {# действий}}",
"alertingNodeRoleSource": "Источник",
"alertingNodeRoleTrigger": "Триггер",
"alertingNodeRoleAction": "Действие",
"alertingTabRules": "Правила оповещений",
"alertingTabHealthChecks": "Проверки здоровья",
"alertingRulesBannerTitle": "Получить уведомление",
"alertingRulesBannerDescription": "Каждое правило объединяет, что отслеживать (сайт, проверка состояния или ресурс), когда срабатывать (например, оффлайн или нарушение), и как уведомлять вашу команду через email, вебхуки или интеграции. Используйте этот список для создания, включения и управления этими правилами.",
"alertingHealthChecksBannerTitle": "Мониторинг здоровья и ресурсов",
"alertingHealthChecksBannerDescription": "Проверки здоровья — это HTTP или TCP мониторы, которые вы определяете один раз. Затем вы можете использовать их в правилах оповещений, чтобы получать уведомления, когда цель становится здоровой или нездоровой. Проверки здоровья для ресурсов также появляются здесь.",
"standaloneHcTableTitle": "Проверки здоровья",
"standaloneHcSearchPlaceholder": "Поиск проверок здоровья…",
"standaloneHcAddButton": "Создать проверку здоровья",
"standaloneHcCreateTitle": "Создать проверку здоровья",
"standaloneHcEditTitle": "Редактировать проверку здоровья",
"standaloneHcDescription": "Настройте проверку здоровья HTTP или TCP для использования в правилах оповещений.",
"standaloneHcNameLabel": "Имя",
"standaloneHcNamePlaceholder": "Мой HTTP монитор",
"standaloneHcDeleteTitle": "Удалить проверку здоровья",
"standaloneHcDeleteQuestion": "Пожалуйста, подтвердите удаление этой проверки здоровья.",
"standaloneHcDeleted": "Проверка здоровья удалена",
"standaloneHcSaved": "Проверка здоровья сохранена",
"standaloneHcColumnHealth": "Здоровье",
"standaloneHcColumnMode": "Режим",
"standaloneHcColumnTarget": "Цель",
"standaloneHcHealthStateHealthy": "Здоровый",
"standaloneHcHealthStateUnhealthy": "Нездоровый",
"standaloneHcHealthStateUnknown": "Неизвестно",
"standaloneHcFilterAnySite": "Все сайты",
"standaloneHcFilterAnyResource": "Все ресурсы",
"standaloneHcFilterMode": "Режим",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Пинг",
"standaloneHcFilterHealth": "Здоровье",
"standaloneHcFilterEnabled": "Включено",
"standaloneHcFilterEnabledOn": "Включено",
"standaloneHcFilterEnabledOff": "Отключено",
"standaloneHcFilterSiteIdFallback": "Сайт {id}",
"standaloneHcFilterResourceIdFallback": "Ресурс {id}",
"blueprints": "Чертежи", "blueprints": "Чертежи",
"blueprintsDescription": "Применить декларирующие конфигурации и просмотреть предыдущие запуски", "blueprintsDescription": "Применить декларирующие конфигурации и просмотреть предыдущие запуски",
"blueprintAdd": "Добавить чертёж", "blueprintAdd": "Добавить чертёж",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "Создайте первоначальную учётную запись администратора сервера. Может существовать только один администратор сервера. Вы всегда можете изменить эти учётные данные позже.", "initialSetupDescription": "Создайте первоначальную учётную запись администратора сервера. Может существовать только один администратор сервера. Вы всегда можете изменить эти учётные данные позже.",
"createAdminAccount": "Создать учётную запись администратора", "createAdminAccount": "Создать учётную запись администратора",
"setupErrorCreateAdmin": "Произошла ошибка при создании учётной записи администратора сервера.", "setupErrorCreateAdmin": "Произошла ошибка при создании учётной записи администратора сервера.",
"certificateStatus": "Статус сертификата", "certificateStatus": "Сертификат",
"certificateStatusAutoRefreshHint": "Статус обновляется автоматически.",
"loading": "Загрузка", "loading": "Загрузка",
"loadingAnalytics": "Загрузка аналитики", "loadingAnalytics": "Загрузка аналитики",
"restart": "Перезагрузка", "restart": "Перезагрузка",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Просмотреть примечания к выпуску", "pangolinUpdateAvailableReleaseNotes": "Просмотреть примечания к выпуску",
"newtUpdateAvailable": "Доступно обновление", "newtUpdateAvailable": "Доступно обновление",
"newtUpdateAvailableInfo": "Доступна новая версия Newt. Пожалуйста, обновитесь до последней версии для лучшего опыта.", "newtUpdateAvailableInfo": "Доступна новая версия Newt. Пожалуйста, обновитесь до последней версии для лучшего опыта.",
"pangolinNodeUpdateAvailableInfo": "Доступна новая версия Pangolin Node. Пожалуйста, обновитесь до последней версии для лучшего опыта.",
"domainPickerEnterDomain": "Домен", "domainPickerEnterDomain": "Домен",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Введите полный домен ресурса, чтобы увидеть доступные опции.", "domainPickerDescription": "Введите полный домен ресурса, чтобы увидеть доступные опции.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Настроить проверку здоровья", "configureHealthCheck": "Настроить проверку здоровья",
"configureHealthCheckDescription": "Настройте мониторинг состояния для {target}", "configureHealthCheckDescription": "Настройте мониторинг состояния для {target}",
"enableHealthChecks": "Включить проверки здоровья", "enableHealthChecks": "Включить проверки здоровья",
"healthCheckDisabledStateDescription": "Когда отключен, сайт не будет выполнять проверки состояния и состояние будет считаться неизвестным.",
"enableHealthChecksDescription": "Мониторинг здоровья этой цели. При необходимости можно контролировать другую конечную точку.", "enableHealthChecksDescription": "Мониторинг здоровья этой цели. При необходимости можно контролировать другую конечную точку.",
"healthScheme": "Метод", "healthScheme": "Метод",
"healthSelectScheme": "Выберите метод", "healthSelectScheme": "Выберите метод",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Интервал проверки должен составлять не менее 5 секунд", "healthCheckIntervalMin": "Интервал проверки должен составлять не менее 5 секунд",
"healthCheckTimeoutMin": "Тайм-аут должен составлять не менее 1 секунды", "healthCheckTimeoutMin": "Тайм-аут должен составлять не менее 1 секунды",
"healthCheckRetryMin": "Количество попыток должно быть не менее 1", "healthCheckRetryMin": "Количество попыток должно быть не менее 1",
"healthCheckMode": "Режим проверки",
"healthCheckStrategy": "Стратегия",
"healthCheckModeDescription": "Режим TCP проверяет только возможность подключения. Режим HTTP проверяет ответ HTTP.",
"healthyThreshold": "Порог здорового состояния",
"healthyThresholdDescription": "Последовательные успехи, необходимые перед тем, как пометить как здоровый.",
"unhealthyThreshold": "Порог нездорового состояния",
"unhealthyThresholdDescription": "Последовательные неудачи, необходимые перед тем, как пометить как нездоровый.",
"healthCheckHealthyThresholdMin": "Порог здорового состояния должен быть не менее 1",
"healthCheckUnhealthyThresholdMin": "Порог нездорового состояния должен быть не менее 1",
"httpMethod": "HTTP метод", "httpMethod": "HTTP метод",
"selectHttpMethod": "Выберите HTTP метод", "selectHttpMethod": "Выберите HTTP метод",
"domainPickerSubdomainLabel": "Поддомен", "domainPickerSubdomainLabel": "Поддомен",
"domainPickerWildcard": "Подстановочный знак",
"domainPickerWildcardPaidOnly": "Wildcard поддомены являются платной функцией. Пожалуйста, обновите подписку, чтобы воспользоваться этой функцией.",
"domainPickerBaseDomainLabel": "Основной домен", "domainPickerBaseDomainLabel": "Основной домен",
"domainPickerSearchDomains": "Поиск доменов...", "domainPickerSearchDomains": "Поиск доменов...",
"domainPickerNoDomainsFound": "Доменов не найдено", "domainPickerNoDomainsFound": "Доменов не найдено",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Этот адрес является частью вспомогательной подсети организации. Он используется для разрешения псевдонимов с использованием внутреннего разрешения DNS.", "resourcesTableAliasAddressInfo": "Этот адрес является частью вспомогательной подсети организации. Он используется для разрешения псевдонимов с использованием внутреннего разрешения DNS.",
"resourcesTableClients": "Клиенты", "resourcesTableClients": "Клиенты",
"resourcesTableAndOnlyAccessibleInternally": "и доступны только внутренне при подключении с клиентом.", "resourcesTableAndOnlyAccessibleInternally": "и доступны только внутренне при подключении с клиентом.",
"resourcesTableNoTargets": "Нет ярлыков",
"resourcesTableHealthy": "Здоровые", "resourcesTableHealthy": "Здоровые",
"resourcesTableDegraded": "Ухудшение", "resourcesTableDegraded": "Ухудшение",
"resourcesTableOffline": "Оффлайн", "resourcesTableUnhealthy": "Проблемные",
"resourcesTableUnknown": "Неизвестен", "resourcesTableUnknown": "Неизвестен",
"resourcesTableNotMonitored": "Не отслеживается", "resourcesTableNotMonitored": "Не отслеживается",
"resourcesTableNoTargets": "Нет целей",
"editInternalResourceDialogEditClientResource": "Изменить приватный ресурс", "editInternalResourceDialogEditClientResource": "Изменить приватный ресурс",
"editInternalResourceDialogUpdateResourceProperties": "Обновить настройки ресурса и элементы управления доступом для {resourceName}", "editInternalResourceDialogUpdateResourceProperties": "Обновить настройки ресурса и элементы управления доступом для {resourceName}",
"editInternalResourceDialogResourceProperties": "Свойства ресурса", "editInternalResourceDialogResourceProperties": "Свойства ресурса",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Порт", "editInternalResourceDialogModePort": "Порт",
"editInternalResourceDialogModeHost": "Хост", "editInternalResourceDialogModeHost": "Хост",
"editInternalResourceDialogModeCidr": "СИДР", "editInternalResourceDialogModeCidr": "СИДР",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Схема",
"editInternalResourceDialogEnableSsl": "Включить SSL",
"editInternalResourceDialogEnableSslDescription": "Включите шифрование SSL/TLS для защищенных HTTPS соединений с конечной точкой.",
"editInternalResourceDialogDestination": "Пункт назначения", "editInternalResourceDialogDestination": "Пункт назначения",
"editInternalResourceDialogDestinationHostDescription": "IP адрес или имя хоста ресурса в сети сайта.", "editInternalResourceDialogDestinationHostDescription": "IP адрес или имя хоста ресурса в сети сайта.",
"editInternalResourceDialogDestinationIPDescription": "IP или адрес хоста ресурса в сети сайта.", "editInternalResourceDialogDestinationIPDescription": "IP или адрес хоста ресурса в сети сайта.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Имя", "createInternalResourceDialogName": "Имя",
"createInternalResourceDialogSite": "Сайт", "createInternalResourceDialogSite": "Сайт",
"selectSite": "Выберите сайт...", "selectSite": "Выберите сайт...",
"multiSitesSelectorSitesCount": "{count, plural, one {# сайт} few {# сайта} many {# сайтов} other {# сайтов}}",
"noSitesFound": "Сайты не найдены.", "noSitesFound": "Сайты не найдены.",
"createInternalResourceDialogProtocol": "Протокол", "createInternalResourceDialogProtocol": "Протокол",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Порт", "createInternalResourceDialogModePort": "Порт",
"createInternalResourceDialogModeHost": "Хост", "createInternalResourceDialogModeHost": "Хост",
"createInternalResourceDialogModeCidr": "СИДР", "createInternalResourceDialogModeCidr": "СИДР",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Схема",
"createInternalResourceDialogScheme": "Схема",
"createInternalResourceDialogEnableSsl": "Включить SSL",
"createInternalResourceDialogEnableSslDescription": "Включите SSL/TLS шифрование для защищенных HTTPS соединений с конечной точкой.",
"createInternalResourceDialogDestination": "Пункт назначения", "createInternalResourceDialogDestination": "Пункт назначения",
"createInternalResourceDialogDestinationHostDescription": "IP адрес или имя хоста ресурса в сети сайта.", "createInternalResourceDialogDestinationHostDescription": "IP адрес или имя хоста ресурса в сети сайта.",
"createInternalResourceDialogDestinationCidrDescription": "Диапазон CIDR ресурса в сети сайта.", "createInternalResourceDialogDestinationCidrDescription": "Диапазон CIDR ресурса в сети сайта.",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "Дополнительный внутренний DNS псевдоним для этого ресурса.", "createInternalResourceDialogAliasDescription": "Дополнительный внутренний DNS псевдоним для этого ресурса.",
"internalResourceDownstreamSchemeRequired": "Схема обязательна для HTTP ресурсов",
"internalResourceHttpPortRequired": "Порт назначения обязателен для HTTP ресурсов",
"siteConfiguration": "Конфигурация", "siteConfiguration": "Конфигурация",
"siteAcceptClientConnections": "Принимать подключения клиентов", "siteAcceptClientConnections": "Принимать подключения клиентов",
"siteAcceptClientConnectionsDescription": "Разрешить пользовательским устройствам и клиентам доступ к ресурсам на этом сайте. Это может быть изменено позже.", "siteAcceptClientConnectionsDescription": "Разрешить пользовательским устройствам и клиентам доступ к ресурсам на этом сайте. Это может быть изменено позже.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Подтверждено", "domainPickerVerified": "Подтверждено",
"domainPickerUnverified": "Не подтверждено", "domainPickerUnverified": "Не подтверждено",
"domainPickerManual": "Ручной", "domainPickerManual": "Ручной",
"domainPickerInvalidSubdomainStructure": "Этот поддомен содержит недопустимые символы или структуру. Он будет очищен автоматически при сохранении.", "domainPickerInvalidSubdomainStructure": "Недопустимые символы будут очищены при сохранении.",
"domainPickerError": "Ошибка", "domainPickerError": "Ошибка",
"domainPickerErrorLoadDomains": "Не удалось загрузить домены организации", "domainPickerErrorLoadDomains": "Не удалось загрузить домены организации",
"domainPickerErrorCheckAvailability": "Не удалось проверить доступность домена", "domainPickerErrorCheckAvailability": "Не удалось проверить доступность домена",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Выберите своего поставщика удостоверений личности для продолжения", "orgAuthChooseIdpDescription": "Выберите своего поставщика удостоверений личности для продолжения",
"orgAuthNoIdpConfigured": "Эта организация не имеет настроенных поставщиков идентификационных данных. Вместо этого вы можете войти в свой Pangolin.", "orgAuthNoIdpConfigured": "Эта организация не имеет настроенных поставщиков идентификационных данных. Вместо этого вы можете войти в свой Pangolin.",
"orgAuthSignInWithPangolin": "Войти через Pangolin", "orgAuthSignInWithPangolin": "Войти через Pangolin",
"orgAuthSignInToOrg": "Войти в организацию", "orgAuthSignInToOrg": "Поставщик удостоверений организации (SSO)",
"orgAuthSelectOrgTitle": "Вход в организацию", "orgAuthSelectOrgTitle": "Вход в организацию",
"orgAuthSelectOrgDescription": "Введите ID вашей организации, чтобы продолжить", "orgAuthSelectOrgDescription": "Введите ID вашей организации, чтобы продолжить",
"orgAuthOrgIdPlaceholder": "ваша-организация", "orgAuthOrgIdPlaceholder": "ваша-организация",
@@ -2429,6 +2648,7 @@
"validPassword": "Допустимый пароль", "validPassword": "Допустимый пароль",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "Подключенный клиент",
"resourceBlocked": "Ресурс заблокирован", "resourceBlocked": "Ресурс заблокирован",
"droppedByRule": "Отброшено по правилам", "droppedByRule": "Отброшено по правилам",
"noSessions": "Нет сессий", "noSessions": "Нет сессий",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Добавить клиентов", "editInternalResourceDialogAddClients": "Добавить клиентов",
"editInternalResourceDialogDestinationLabel": "Пункт назначения", "editInternalResourceDialogDestinationLabel": "Пункт назначения",
"editInternalResourceDialogDestinationDescription": "Укажите адрес назначения для внутреннего ресурса. Это может быть имя хоста, IP-адрес или диапазон CIDR в зависимости от выбранного режима. При необходимости установите внутренний DNS-алиас для облегчения идентификации.", "editInternalResourceDialogDestinationDescription": "Укажите адрес назначения для внутреннего ресурса. Это может быть имя хоста, IP-адрес или диапазон CIDR в зависимости от выбранного режима. При необходимости установите внутренний DNS-алиас для облегчения идентификации.",
"internalResourceFormMultiSiteRoutingHelp": "Выбор нескольких сайтов позволяет обеспечить отказоустойчивую маршрутизацию и фейловер для высокой доступности.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Узнать больше",
"editInternalResourceDialogPortRestrictionsDescription": "Ограничьте доступ к определенным TCP/UDP-портам или разрешите/заблокируйте все порты.", "editInternalResourceDialogPortRestrictionsDescription": "Ограничьте доступ к определенным TCP/UDP-портам или разрешите/заблокируйте все порты.",
"createInternalResourceDialogHttpConfiguration": "Конфигурация HTTP",
"createInternalResourceDialogHttpConfigurationDescription": "Выберите домен, который клиенты будут использовать для доступа к этому ресурсу через HTTP или HTTPS.",
"editInternalResourceDialogHttpConfiguration": "Конфигурация HTTP",
"editInternalResourceDialogHttpConfigurationDescription": "Выберите домен, который клиенты будут использовать для доступа к этому ресурсу через HTTP или HTTPS.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Мы скоро вернемся! Наш сайт в настоящее время проходит плановое техническое обслуживание.", "maintenancePageMessagePlaceholder": "Мы скоро вернемся! Наш сайт в настоящее время проходит плановое техническое обслуживание.",
"maintenancePageMessageDescription": "Подробное сообщение, объясняющее обслуживание", "maintenancePageMessageDescription": "Подробное сообщение, объясняющее обслуживание",
"maintenancePageTimeTitle": "Предполагаемое время завершения (необязательно)", "maintenancePageTimeTitle": "Предполагаемое время завершения (необязательно)",
"privateMaintenanceScreenTitle": "Экраны частной заглушки",
"privateMaintenanceScreenMessage": "Этот домен используется на частном ресурсе. Пожалуйста, подключитесь с помощью клиента Pangolin для доступа к этому ресурсу.",
"privateMaintenanceScreenSteps": "После подключения, если это сообщение по-прежнему отображается, кэш DNS вашего браузера может указывать на старый адрес. Чтобы исправить эту неисправность: полностью закройте и снова откройте эту вкладку или браузер, затем вернитесь на эту страницу.",
"maintenanceTime": "например, 2 часа, 1 ноября в 5:00 вечера", "maintenanceTime": "например, 2 часа, 1 ноября в 5:00 вечера",
"maintenanceEstimatedTimeDescription": "Когда вы ожидаете завершения обслуживания", "maintenanceEstimatedTimeDescription": "Когда вы ожидаете завершения обслуживания",
"editDomain": "Редактировать домен", "editDomain": "Редактировать домен",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "Добавить HTTP адрес", "httpDestAddTitle": "Добавить HTTP адрес",
"httpDestEditDescription": "Обновление конфигурации для этого HTTP события потокового назначения.", "httpDestEditDescription": "Обновление конфигурации для этого HTTP события потокового назначения.",
"httpDestAddDescription": "Настройте новую HTTP-конечную точку для получения событий вашей организации.", "httpDestAddDescription": "Настройте новую HTTP-конечную точку для получения событий вашей организации.",
"S3DestEditTitle": "Редактировать пункт назначения",
"S3DestAddTitle": "Добавить S3 пункт назначения",
"S3DestEditDescription": "Обновите конфигурацию для этого S3 пункта назначения потоковых событий.",
"S3DestAddDescription": "Настройте новую S3 конечную точку для получения событий вашей организации.",
"datadogDestEditTitle": "Редактировать пункт назначения",
"datadogDestAddTitle": "Добавить пункт назначения Datadog",
"datadogDestEditDescription": "Обновите конфигурацию для этого пункта назначения потоковых событий Datadog.",
"datadogDestAddDescription": "Настройте новую конечную точку Datadog для получения событий вашей организации.",
"httpDestTabSettings": "Настройки", "httpDestTabSettings": "Настройки",
"httpDestTabHeaders": "Заголовки", "httpDestTabHeaders": "Заголовки",
"httpDestTabBody": "Тело", "httpDestTabBody": "Тело",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Адрес назначения успешно создан", "httpDestCreatedSuccess": "Адрес назначения успешно создан",
"httpDestUpdateFailed": "Не удалось обновить место назначения", "httpDestUpdateFailed": "Не удалось обновить место назначения",
"httpDestCreateFailed": "Не удалось создать место назначения", "httpDestCreateFailed": "Не удалось создать место назначения",
"followRedirects": "Следовать за перенаправлениями",
"followRedirectsDescription": "Автоматически следуйте HTTP перенаправлениям для запросов.",
"alertingErrorWebhookUrl": "Пожалуйста, введите корректный URL для вебхука.",
"healthCheckStrategyHttp": "Проверяет возможность подключения и проверяет статус HTTP ответа.",
"healthCheckStrategyTcp": "Проверяет только подключение TCP, не инспектируя ответ.",
"healthCheckStrategySnmp": "Выполняет SNMP get-запрос, чтобы проверить состояние сетевых устройств и инфраструктуры.",
"healthCheckStrategyIcmp": "Использует эхо-запросы ICMP (ping), чтобы проверить, доступен ли и отзывчив ли ресурс.",
"healthCheckTabStrategy": "Стратегия",
"healthCheckTabConnection": "Подключение",
"healthCheckTabAdvanced": "Дополнительно",
"healthCheckStrategyNotAvailable": "Эта стратегия недоступна. Пожалуйста, свяжитесь с отделом продаж для включения этой функции.",
"uptime30d": "Время работы (30 дней)",
"idpAddActionCreateNew": "Создать нового поставщика удостоверений", "idpAddActionCreateNew": "Создать нового поставщика удостоверений",
"idpAddActionImportFromOrg": "Импортировать из другой организации", "idpAddActionImportFromOrg": "Импортировать из другой организации",
"idpImportDialogTitle": "Импортировать поставщика удостоверений", "idpImportDialogTitle": "Импортировать поставщика удостоверений",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Это не может быть отменено для этой организации.", "idpUnassociateWarning": "Это не может быть отменено для этой организации.",
"idpUnassociatedDescription": "Поставщик удостоверений успешно рассоединен с этой организацией", "idpUnassociatedDescription": "Поставщик удостоверений успешно рассоединен с этой организацией",
"idpUnassociateMenu": "Рассоединить", "idpUnassociateMenu": "Рассоединить",
"idpDeleteAllOrgsMenu": "Удалить" "idpDeleteAllOrgsMenu": "Удалить",
"publicIpEndpoint": "Конечная точка",
"lastTriggeredAt": "Последний триггер",
"reject": "Отклонить",
"uptimeDaysAgo": "{count} дней назад",
"uptimeToday": "Сегодня",
"uptimeNoDataAvailable": "Нет доступных данных",
"uptimeSuffix": "время работы",
"uptimeDowntimeSuffix": "время простоя",
"uptimeTooltipUptimeLabel": "Время работы",
"uptimeTooltipDowntimeLabel": "Время простоя",
"uptimeOngoing": "в процессе",
"uptimeNoMonitoringData": "Отсутствуют данные мониторинга",
"uptimeNoData": "Нет данных",
"uptimeMiniBarDown": "Не работает",
"uptimeSectionTitle": "Время работы",
"uptimeSectionDescription": "Доступность за последние {days} дней",
"uptimeAddAlert": "Добавить предупреждение",
"uptimeViewAlerts": "Просмотр предупреждений",
"uptimeCreateEmailAlert": "Создать оповещение по электронной почте",
"uptimeAlertDescriptionSite": "Получайте уведомления по электронной почте, когда этот сайт выходит из сети или снова подключается.",
"uptimeAlertDescriptionResource": "Получайте уведомления по электронной почте, когда этот ресурс выходит из сети или снова подключается.",
"uptimeAlertNamePlaceholder": "Название предупреждения",
"uptimeAdditionalEmails": "Дополнительные адреса электронной почты",
"uptimeCreateAlert": "Создать предупреждение",
"uptimeAlertNoRecipients": "Нет получателей",
"uptimeAlertNoRecipientsDescription": "Пожалуйста, добавьте хотя бы одного пользователя, роль или email для уведомления.",
"uptimeAlertCreated": "Предупреждение создано",
"uptimeAlertCreatedDescription": "Вы будете уведомлены, когда статус изменится.",
"uptimeAlertCreateFailed": "Не удалось создать предупреждение",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Ключ",
"webhookHeaderValuePlaceholder": "Значение",
"alertLabel": "Предупреждение",
"domainPickerWildcardSubdomainNotAllowed": "Wildcard поддомены не допускаются.",
"domainPickerWildcardCertWarning": "Wildcard ресурсы могут потребовать дополнительной настройки для правильной работы.",
"domainPickerWildcardCertWarningLink": "Узнать больше",
"health": "Состояние",
"domainPendingErrorTitle": "Проблема с подтверждением"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "Bu özelliği etkinleştirmek için satış ekibiyle iletişime geçin.",
"contactSalesBookDemo": "Demo ayırt",
"contactSalesOr": "veya",
"contactSalesContactUs": "bize ulaşın",
"setupCreate": "Organizasyonu, siteyi ve kaynakları oluşturun", "setupCreate": "Organizasyonu, siteyi ve kaynakları oluşturun",
"headerAuthCompatibilityInfo": "Kimlik doğrulama belirteci eksik olduğunda 401 Yetkisiz yanıtı zorlamak için bunu etkinleştirin. Bu, sunucu sorunu olmadan kimlik bilgilerini göndermeyen tarayıcılar veya belirli HTTP kütüphaneleri için gereklidir.", "headerAuthCompatibilityInfo": "Kimlik doğrulama belirteci eksik olduğunda 401 Yetkisiz yanıtı zorlamak için bunu etkinleştirin. Bu, sunucu sorunu olmadan kimlik bilgilerini göndermeyen tarayıcılar veya belirli HTTP kütüphaneleri için gereklidir.",
"headerAuthCompatibility": "Genişletilmiş Uyumluluk", "headerAuthCompatibility": "Genişletilmiş Uyumluluk",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "Geçersiz veya süresi dolmuş lisans anahtarları tespit edildi. Tüm özellikleri kullanmaya devam etmek için lisans koşullarına uyun.", "componentsInvalidKey": "Geçersiz veya süresi dolmuş lisans anahtarları tespit edildi. Tüm özellikleri kullanmaya devam etmek için lisans koşullarına uyun.",
"dismiss": "Kapat", "dismiss": "Kapat",
"subscriptionViolationMessage": "Geçerli planınız için limitlerinizi aştınız. Planınız dahilinde kalmak için siteleri, kullanıcıları veya diğer kaynakları kaldırarak sorunu düzeltin.", "subscriptionViolationMessage": "Geçerli planınız için limitlerinizi aştınız. Planınız dahilinde kalmak için siteleri, kullanıcıları veya diğer kaynakları kaldırarak sorunu düzeltin.",
"trialBannerMessage": "Deneme süreniz {countdown} içinde sona eriyor. Erişimi sürdürmek için yükseltin.",
"trialBannerExpired": "Deneme süreniz sona erdi. Erişimi geri yüklemek için şimdi yükseltin.",
"trialActive": "Ücretsiz Deneme Aktif",
"trialExpired": "Deneme Süresi Doldu",
"trialHasEnded": "Deneme süreniz sona erdi.",
"trialDaysRemaining": "{count, plural, one {# gün kaldı} other {# gün kaldı}}",
"trialDaysLeftShort": "Deneme süresi için {days}g kaldı",
"trialGoToBilling": "Fatura sayfasına git",
"subscriptionViolationViewBilling": "Faturalamayı görüntüle", "subscriptionViolationViewBilling": "Faturalamayı görüntüle",
"componentsLicenseViolation": "Lisans İhlali: Bu sunucu, lisanslı sınırı olan {maxSites} sitesini aşarak {usedSites} site kullanmaktadır. Tüm özellikleri kullanmaya devam etmek için lisans koşullarına uyun.", "componentsLicenseViolation": "Lisans İhlali: Bu sunucu, lisanslı sınırı olan {maxSites} sitesini aşarak {usedSites} site kullanmaktadır. Tüm özellikleri kullanmaya devam etmek için lisans koşullarına uyun.",
"componentsSupporterMessage": "Pangolin'e {tier} olarak destek olduğunuz için teşekkür ederiz!", "componentsSupporterMessage": "Pangolin'e {tier} olarak destek olduğunuz için teşekkür ederiz!",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "Yapılandırmayı kopyaladım", "siteConfirmCopy": "Yapılandırmayı kopyaladım",
"searchSitesProgress": "Siteleri ara...", "searchSitesProgress": "Siteleri ara...",
"siteAdd": "Site Ekle", "siteAdd": "Site Ekle",
"sitesTableViewPublicResources": "Genel Kaynakları Görüntüle",
"sitesTableViewPrivateResources": "Özel Kaynakları Görüntüle",
"siteInstallNewt": "Newt Yükle", "siteInstallNewt": "Newt Yükle",
"siteInstallNewtDescription": "Newt'i sisteminizde çalıştırma", "siteInstallNewtDescription": "Newt'i sisteminizde çalıştırma",
"WgConfiguration": "WireGuard Yapılandırması", "WgConfiguration": "WireGuard Yapılandırması",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "Site güncellendi.", "siteUpdatedDescription": "Site güncellendi.",
"siteGeneralDescription": "Bu site için genel ayarları yapılandırın", "siteGeneralDescription": "Bu site için genel ayarları yapılandırın",
"siteSettingDescription": "Sitenizdeki ayarları yapılandırın", "siteSettingDescription": "Sitenizdeki ayarları yapılandırın",
"siteResourcesTab": "Kaynaklar",
"siteResourcesNoneOnSite": "Bu sitede henüz genel veya özel kaynak yok.",
"siteResourcesSectionPublic": "Genel Kaynaklar",
"siteResourcesSectionPrivate": "Özel Kaynaklar",
"siteResourcesSectionPublicDescription": "Alanlar veya portlar üzerinden dışarıdan açığa çıkan kaynaklar.",
"siteResourcesSectionPrivateDescription": "Site aracılığıyla özel ağınızda mevcut olan kaynaklar.",
"siteResourcesViewAllPublic": "Tüm kaynakları görüntüle",
"siteResourcesViewAllPrivate": "Tüm kaynakları görüntüle",
"siteResourcesDialogDescription": "Bu siteyle ilişkili genel ve özel kaynakların genel bakışı.",
"siteResourcesShowMore": "Daha fazla göster",
"siteResourcesPermissionDenied": "Bu kaynakları listeleme izniniz yok.",
"siteResourcesEmptyPublic": "Bu siteyi hedefleyen herhangi bir genel kaynak yok.",
"siteResourcesEmptyPrivate": "Bu siteyle ilişkilendirilmiş özel kaynak yok.",
"siteResourcesHowToAccess": "Nasıl erişilir",
"siteResourcesTargetsOnSite": "Bu sitedeki hedefler",
"siteSetting": "{siteName} Ayarları", "siteSetting": "{siteName} Ayarları",
"siteNewtTunnel": "Newt Site (Önerilen)", "siteNewtTunnel": "Newt Site (Önerilen)",
"siteNewtTunnelDescription": "Ağınıza giriş noktası oluşturmanın en kolay yolu. Ekstra kurulum gerekmez.", "siteNewtTunnelDescription": "Ağınıza giriş noktası oluşturmanın en kolay yolu. Ekstra kurulum gerekmez.",
@@ -267,8 +296,11 @@
"orgMissing": "Organizasyon Kimliği Eksik", "orgMissing": "Organizasyon Kimliği Eksik",
"orgMissingMessage": "Organizasyon kimliği olmadan daveti yeniden oluşturmanız mümkün değildir.", "orgMissingMessage": "Organizasyon kimliği olmadan daveti yeniden oluşturmanız mümkün değildir.",
"accessUsersManage": "Kullanıcıları Yönet", "accessUsersManage": "Kullanıcıları Yönet",
"accessUserManage": "Kullanıcıyı Yönet",
"accessUsersDescription": "Bu organizasyona erişimi olan kullanıcıları davet edin ve yönetin", "accessUsersDescription": "Bu organizasyona erişimi olan kullanıcıları davet edin ve yönetin",
"accessUsersSearch": "Kullanıcıları ara...", "accessUsersSearch": "Kullanıcıları ara...",
"accessUsersRoleFilterCount": "{count, plural, one {# rol} other {# roller}}",
"accessUsersRoleFilterClear": "Rol filtrelerini temizle",
"accessUserCreate": "Kullanıcı Oluştur", "accessUserCreate": "Kullanıcı Oluştur",
"accessUserRemove": "Kullanıcıyı Kaldır", "accessUserRemove": "Kullanıcıyı Kaldır",
"username": "Kullanıcı Adı", "username": "Kullanıcı Adı",
@@ -731,6 +763,7 @@
"newtEndpoint": "Uç Nokta", "newtEndpoint": "Uç Nokta",
"newtId": "Kimlik", "newtId": "Kimlik",
"newtSecretKey": "Gizli", "newtSecretKey": "Gizli",
"newtVersion": "Sürüm",
"architecture": "Mimari", "architecture": "Mimari",
"sites": "Siteler", "sites": "Siteler",
"siteWgAnyClients": "Herhangi bir WireGuard istemcisi kullanarak bağlanın. Dahili kaynaklara eş IP adresini kullanarak erişmeniz gerekecek.", "siteWgAnyClients": "Herhangi bir WireGuard istemcisi kullanarak bağlanın. Dahili kaynaklara eş IP adresini kullanarak erişmeniz gerekecek.",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "Kayıtları Görüntüle", "actionViewLogs": "Kayıtları Görüntüle",
"noneSelected": "Hiçbiri seçili değil", "noneSelected": "Hiçbiri seçili değil",
"orgNotFound2": "Hiçbir organizasyon bulunamadı.", "orgNotFound2": "Hiçbir organizasyon bulunamadı.",
"search": "Ara…",
"searchPlaceholder": "Ara...", "searchPlaceholder": "Ara...",
"emptySearchOptions": "Seçenek bulunamadı", "emptySearchOptions": "Seçenek bulunamadı",
"create": "Oluştur", "create": "Oluştur",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "Yönet", "sidebarGeneral": "Yönet",
"sidebarLogAndAnalytics": "Kayıt & Analiz", "sidebarLogAndAnalytics": "Kayıt & Analiz",
"sidebarBluePrints": "Planlar", "sidebarBluePrints": "Planlar",
"sidebarAlerting": "Uyarı",
"sidebarHealthChecks": "Sağlık kontrolleri",
"sidebarOrganization": "Organizasyon", "sidebarOrganization": "Organizasyon",
"sidebarManagement": "Yönetim", "sidebarManagement": "Yönetim",
"sidebarBillingAndLicenses": "Faturalandırma & Lisanslar", "sidebarBillingAndLicenses": "Faturalandırma & Lisanslar",
"sidebarLogsAnalytics": "Analitik", "sidebarLogsAnalytics": "Analitik",
"alertingTitle": "Uyarı",
"alertingDescription": "Bildirimler için kaynakları, tetikleyicileri ve eylemleri tanımlayın",
"alertingRules": "Uyarı kuralları",
"alertingSearchRules": "Kuralları ara…",
"alertingAddRule": "Kural Oluştur",
"alertingColumnSource": "Kaynak",
"alertingColumnTrigger": "Tetikle",
"alertingColumnActions": "İşlemler",
"alertingColumnEnabled": "Etkin",
"alertingDeleteQuestion": "Bu uyarı kuralını silmek istediğinizi onaylayın lütfen.",
"alertingDeleteRule": "Uyarı kuralını sil",
"alertingRuleDeleted": "Uyarı kuralı silindi",
"alertingRuleSaved": "Uyarı kuralı kaydedildi",
"alertingRuleSavedCreatedDescription": "Yeni uyarı kuralınız oluşturuldu. Bu sayfada düzenlemeye devam edebilirsiniz.",
"alertingRuleSavedUpdatedDescription": "Bu uyarı kuralındaki değişiklikleriniz kaydedildi.",
"alertingEditRule": "Uyarı Kuralını Düzenle",
"alertingCreateRule": "Uyarı Kuralı Oluştur",
"alertingRuleCredenzaDescription": "Ne izlenecek, ne zaman tetiklenecek ve nasıl bildirilecek, bunları seçin",
"alertingRuleNamePlaceholder": "Üretim sitesi kapalı",
"alertingRuleEnabled": "Kural etkinleştirildi",
"alertingSectionSource": "Kaynak",
"alertingSourceType": "Kaynak türü",
"alertingSourceSite": "Site",
"alertingSourceHealthCheck": "Sağlık kontrolü",
"alertingPickSites": "Siteler",
"alertingPickHealthChecks": "Sağlık kontrolleri",
"alertingPickResources": "Kaynaklar",
"alertingAllSites": "Tüm Siteler",
"alertingAllSitesDescription": "Herhangi bir site için uyarı tetiklenir",
"alertingSpecificSites": "Belirli Siteler",
"alertingSpecificSitesDescription": "İzlemek için belirli siteleri seçin",
"alertingAllHealthChecks": "Tüm Sağlık Kontrolleri",
"alertingAllHealthChecksDescription": "Herhangi bir sağlık kontrolü için uyarı tetiklenir",
"alertingSpecificHealthChecks": "Belirli Sağlık Kontrolleri",
"alertingSpecificHealthChecksDescription": "İzlemek için belirli sağlık kontrollerini seçin",
"alertingAllResources": "Tüm Kaynaklar",
"alertingAllResourcesDescription": "Herhangi bir kaynak için uyarı tetiklenir",
"alertingSpecificResources": "Belirli Kaynaklar",
"alertingSpecificResourcesDescription": "İzlemek için belirli kaynakları seçin",
"alertingSelectResources": "Kaynakları seçin…",
"alertingResourcesSelected": "{count} kaynak seçildi",
"alertingResourcesEmpty": "İlk 10 sonuçta hedefleri olan kaynak yok.",
"alertingSectionTrigger": "Tetikle",
"alertingTrigger": "Uyarı zamanı",
"alertingTriggerSiteOnline": "Site çevrimiçi",
"alertingTriggerSiteOffline": "Site çevrimdışı",
"alertingTriggerSiteToggle": "Site durumu değişiyor",
"alertingTriggerHcHealthy": "Sağlık kontrolü sağlıklı",
"alertingTriggerHcUnhealthy": "Sağlık kontrolü sağlıksız",
"alertingTriggerHcToggle": "Sağlık kontrolü durumu değişiyor",
"alertingTriggerResourceHealthy": "Kaynak sağlıklı",
"alertingTriggerResourceUnhealthy": "Kaynak sağlıksız",
"alertingTriggerResourceDegraded": "Kaynak bozuk",
"alertingSearchHealthChecks": "Sağlık kontrollerini ara…",
"alertingHealthChecksEmpty": "Mevcut sağlık kontrolü yok.",
"alertingTriggerResourceToggle": "Kaynak durumu değişiyor",
"alertingSourceResource": "Kaynak",
"alertingSectionActions": "İşlemler",
"alertingAddAction": "Eylem Ekle",
"alertingActionNotify": "E-posta",
"alertingActionNotifyDescription": "Kullanıcılara veya role e-posta bildirimleri gönder",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "Özel bir uç noktaya HTTP isteği gönderin",
"alertingExternalIntegration": "Harici Entegrasyon",
"alertingExternalPagerDutyDescription": "Olay yönetimi için uyarıları PagerDuty'ye gönderin",
"alertingExternalOpsgenieDescription": "Nöbetçi yönetimi için uyarıları Opsgenie'ye yönlendirin",
"alertingExternalServiceNowDescription": "Uyarı olaylarından ServiceNow olayları oluşturun",
"alertingExternalIncidentIoDescription": "Uyarı olaylarından Incident.io iş akışlarını tetikleyin",
"alertingActionType": "Eylem türü",
"alertingNotifyUsers": "Kullanıcılar",
"alertingNotifyRoles": "Roller",
"alertingNotifyEmails": "E-posta adresleri",
"alertingEmailPlaceholder": "E-posta ekleyin ve Enter tuşuna basın",
"alertingWebhookMethod": "HTTP yöntemi",
"alertingWebhookSecret": "İmza sırrı (isteğe bağlı)",
"alertingWebhookSecretPlaceholder": "HMAC sırrı",
"alertingWebhookHeaders": "Başlıklar",
"alertingAddHeader": "Başlık ekle",
"alertingSelectSites": "Siteleri seçin…",
"alertingSitesSelected": "{count} site seçildi",
"alertingSelectHealthChecks": "Sağlık kontrolleri seçin…",
"alertingHealthChecksSelected": "{count} sağlık kontrolü seçildi",
"alertingNoHealthChecks": "Hedefleri etkinleştirilmiş sağlık kontrolleri yok",
"alertingHealthCheckStub": "Sağlık kontrolü kaynak seçimi henüz bağlanmadı - yine de tetikleyicileri ve eylemleri yapılandırabilirsiniz.",
"alertingSelectUsers": "Kullanıcıları seçin…",
"alertingUsersSelected": "{count} kullanıcı seçildi",
"alertingSelectRoles": "Rolleri seçin…",
"alertingRolesSelected": "{count} rol seçildi",
"alertingSummarySites": "Siteler ({count})",
"alertingSummaryAllSites": "Tüm siteler",
"alertingSummaryHealthChecks": "Sağlık kontrolleri ({count})",
"alertingSummaryAllHealthChecks": "Tüm sağlık kontrolleri",
"alertingSummaryResources": "Kaynaklar ({count})",
"alertingSummaryAllResources": "Tüm kaynaklar",
"alertingErrorNameRequired": "Bir ad girin",
"alertingErrorActionsMin": "En az bir eylem ekleyin",
"alertingErrorPickSites": "En az bir site seçin",
"alertingErrorPickHealthChecks": "En az bir sağlık kontrolü seçin",
"alertingErrorPickResources": "En az bir kaynak seçin",
"alertingErrorTriggerSite": "Bir site tetikleyicisi seçin",
"alertingErrorTriggerHealth": "Bir sağlık kontrolü tetikleyicisi seçin",
"alertingErrorTriggerResource": "Bir kaynak tetikleyicisi seçin",
"alertingErrorNotifyRecipients": "Kullanıcıları, rolleri veya en az bir e-posta seçin",
"alertingConfigureSource": "Kaynağı Yapılandır",
"alertingConfigureTrigger": "Tetikleyiciyi Yapılandır",
"alertingConfigureActions": "Eylemleri Yapılandır",
"alertingBackToRules": "Kurallara Geri Dön",
"alertingRuleCooldown": "Serinleme süresi (saniye)",
"alertingRuleCooldownDescription": "Aynı kural için tekrarlanan uyarılar arasında minimum süre. Her seferinde tetiklenmesi için 0 olarak ayarlayın.",
"alertingDraftBadge": "Taslak - bu kuralı kaydetmek için kaydedin",
"alertingSidebarHint": "Düzenlemek için kanvas üzerindeki bir adıma tıklayın.",
"alertingGraphCanvasTitle": "Kural Akışı",
"alertingGraphCanvasDescription": "Kaynak, tetikleyici ve eylemlerin görsel genel bakışı. Düzenlemek için bir düğümü seçin.",
"alertingNodeNotConfigured": "Henüz yapılandırılmadı",
"alertingNodeActionsCount": "{count, plural, one {# eylem} other {# eylemler}}",
"alertingNodeRoleSource": "Kaynak",
"alertingNodeRoleTrigger": "Tetikle",
"alertingNodeRoleAction": "Aksiyon",
"alertingTabRules": "Uyarı Kuralları",
"alertingTabHealthChecks": "Sağlık Kontrolleri",
"alertingRulesBannerTitle": "Bildirim Alın",
"alertingRulesBannerDescription": "Her kural neyin izleneceğini (bir site, sağlık kontrolü veya kaynak), ne zaman tetikleneceğini (örneğin çevrimdışı veya sağlıksız) ve ekibinize e-posta, web kancaları veya entegrasyonlar aracılığıyla nasıl bildirileceğini bağlar. Bu listeyi kullanarak kuralları oluşturun, etkinleştirin ve yönetin.",
"alertingHealthChecksBannerTitle": "Sağlık ve Kaynakları İzleyin",
"alertingHealthChecksBannerDescription": "Sağlık kontrolleri bir kez tanımladığınız HTTP veya TCP monitörleridir. Ardından hedef sağlıklı veya sağlıksız olduğunda bildirilmeniz için onları uyarı kurallarında kaynak olarak kullanabilirsiniz. Kaynaklar üzerindeki sağlık kontrolleri de burada görünür.",
"standaloneHcTableTitle": "Sağlık Kontrolleri",
"standaloneHcSearchPlaceholder": "Sağlık kontrollerini ara…",
"standaloneHcAddButton": "Sağlık Kontrolü Oluştur",
"standaloneHcCreateTitle": "Sağlık Kontrolü Oluştur",
"standaloneHcEditTitle": "Sağlık Kontrolünü Düzenle",
"standaloneHcDescription": "Uyarı kurallarında kullanılmak üzere bir HTTP veya TCP sağlık kontrolü yapılandırın.",
"standaloneHcNameLabel": "Ad",
"standaloneHcNamePlaceholder": "HTTP Monitörüm",
"standaloneHcDeleteTitle": "Sağlık kontrolünü sil",
"standaloneHcDeleteQuestion": "Bu sağlık kontrolünü silmek istediğinizi onaylayın lütfen.",
"standaloneHcDeleted": "Sağlık kontrolü silindi",
"standaloneHcSaved": "Sağlık kontrolü kaydedildi",
"standaloneHcColumnHealth": "Sağlık",
"standaloneHcColumnMode": "Mod",
"standaloneHcColumnTarget": "Hedef",
"standaloneHcHealthStateHealthy": "Sağlıklı",
"standaloneHcHealthStateUnhealthy": "Sağlıksız",
"standaloneHcHealthStateUnknown": "Bilinmiyor",
"standaloneHcFilterAnySite": "Tüm siteler",
"standaloneHcFilterAnyResource": "Tüm kaynaklar",
"standaloneHcFilterMode": "Mod",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "Sağlık",
"standaloneHcFilterEnabled": "Etkin",
"standaloneHcFilterEnabledOn": "Etkin",
"standaloneHcFilterEnabledOff": "Devre Dışı",
"standaloneHcFilterSiteIdFallback": "Site {id}",
"standaloneHcFilterResourceIdFallback": "Kaynak {id}",
"blueprints": "Planlar", "blueprints": "Planlar",
"blueprintsDescription": "Deklaratif yapılandırmaları uygulayın ve önceki çalışmaları görüntüleyin", "blueprintsDescription": "Deklaratif yapılandırmaları uygulayın ve önceki çalışmaları görüntüleyin",
"blueprintAdd": "Plan Ekle", "blueprintAdd": "Plan Ekle",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "İlk sunucu yönetici hesabını oluşturun. Yalnızca bir sunucu yöneticisi olabilir. Bu kimlik bilgilerini daha sonra her zaman değiştirebilirsiniz.", "initialSetupDescription": "İlk sunucu yönetici hesabını oluşturun. Yalnızca bir sunucu yöneticisi olabilir. Bu kimlik bilgilerini daha sonra her zaman değiştirebilirsiniz.",
"createAdminAccount": "Yönetici Hesabı Oluştur", "createAdminAccount": "Yönetici Hesabı Oluştur",
"setupErrorCreateAdmin": "Sunucu yönetici hesabı oluşturulurken bir hata oluştu.", "setupErrorCreateAdmin": "Sunucu yönetici hesabı oluşturulurken bir hata oluştu.",
"certificateStatus": "Sertifika Durumu", "certificateStatus": "Sertifika",
"certificateStatusAutoRefreshHint": "Durum otomatik olarak yenilenir.",
"loading": "Yükleniyor", "loading": "Yükleniyor",
"loadingAnalytics": "Analiz Yükleniyor", "loadingAnalytics": "Analiz Yükleniyor",
"restart": "Yeniden Başlat", "restart": "Yeniden Başlat",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "Yayın Notlarını Görüntüle", "pangolinUpdateAvailableReleaseNotes": "Yayın Notlarını Görüntüle",
"newtUpdateAvailable": "Güncelleme Mevcut", "newtUpdateAvailable": "Güncelleme Mevcut",
"newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.", "newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.",
"pangolinNodeUpdateAvailableInfo": "Pangolin Node'un yeni bir sürümü mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.",
"domainPickerEnterDomain": "Alan Adı", "domainPickerEnterDomain": "Alan Adı",
"domainPickerPlaceholder": "myapp.example.com", "domainPickerPlaceholder": "myapp.example.com",
"domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.", "domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "Sağlık Kontrolünü Yapılandır", "configureHealthCheck": "Sağlık Kontrolünü Yapılandır",
"configureHealthCheckDescription": "{hedef} için sağlık izleme kurun", "configureHealthCheckDescription": "{hedef} için sağlık izleme kurun",
"enableHealthChecks": "Sağlık Kontrollerini Etkinleştir", "enableHealthChecks": "Sağlık Kontrollerini Etkinleştir",
"healthCheckDisabledStateDescription": "Devre dışı bırakıldığında, site sağlık kontrolleri yapmaz ve durum bilinmeyen olarak kabul edilecektir.",
"enableHealthChecksDescription": "Bu hedefin sağlığını izleyin. Gerekirse hedef dışındaki bir son noktayı izleyebilirsiniz.", "enableHealthChecksDescription": "Bu hedefin sağlığını izleyin. Gerekirse hedef dışındaki bir son noktayı izleyebilirsiniz.",
"healthScheme": "Yöntem", "healthScheme": "Yöntem",
"healthSelectScheme": "Yöntem Seç", "healthSelectScheme": "Yöntem Seç",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "Kontrol aralığı en az 5 saniye olmalıdır", "healthCheckIntervalMin": "Kontrol aralığı en az 5 saniye olmalıdır",
"healthCheckTimeoutMin": "Zaman aşımı en az 1 saniye olmalıdır", "healthCheckTimeoutMin": "Zaman aşımı en az 1 saniye olmalıdır",
"healthCheckRetryMin": "Tekrar deneme girişimleri en az 1 olmalıdır", "healthCheckRetryMin": "Tekrar deneme girişimleri en az 1 olmalıdır",
"healthCheckMode": "Modu Kontrol Et",
"healthCheckStrategy": "Strateji",
"healthCheckModeDescription": "TCP modu yalnızca bağlantıyı doğrular. HTTP modu HTTP yanıtını doğrular.",
"healthyThreshold": "Sağlıklı Eşik",
"healthyThresholdDescription": "Sağlıklı olarak işaretlenmeden önce gereken ardışık başarılar.",
"unhealthyThreshold": "Sağlıksız Eşik",
"unhealthyThresholdDescription": "Sağlıksız olarak işaretlenmeden önce gereken ardışık başarısızlıklar.",
"healthCheckHealthyThresholdMin": "Sağlıklı eşik en az 1 olmalıdır",
"healthCheckUnhealthyThresholdMin": "Sağlıksız eşik en az 1 olmalıdır",
"httpMethod": "HTTP Yöntemi", "httpMethod": "HTTP Yöntemi",
"selectHttpMethod": "HTTP yöntemini seçin", "selectHttpMethod": "HTTP yöntemini seçin",
"domainPickerSubdomainLabel": "Alt Alan Adı", "domainPickerSubdomainLabel": "Alt Alan Adı",
"domainPickerWildcard": "Genel karakter",
"domainPickerWildcardPaidOnly": "Genel alt alanlar ücretli bir özelliktir. Bu özelliğe erişmek için lütfen yükseltin.",
"domainPickerBaseDomainLabel": "Temel Alan Adı", "domainPickerBaseDomainLabel": "Temel Alan Adı",
"domainPickerSearchDomains": "Alan adlarını ara...", "domainPickerSearchDomains": "Alan adlarını ara...",
"domainPickerNoDomainsFound": "Hiçbir alan adı bulunamadı", "domainPickerNoDomainsFound": "Hiçbir alan adı bulunamadı",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "Bu adres, kuruluşun yardımcı ağ alt bantının bir parçasıdır. Alias kayıtlarını çözümlemek için dahili DNS çözümlemesi kullanılır.", "resourcesTableAliasAddressInfo": "Bu adres, kuruluşun yardımcı ağ alt bantının bir parçasıdır. Alias kayıtlarını çözümlemek için dahili DNS çözümlemesi kullanılır.",
"resourcesTableClients": "İstemciler", "resourcesTableClients": "İstemciler",
"resourcesTableAndOnlyAccessibleInternally": "veyalnızca bir istemci ile bağlandığında dahili olarak erişilebilir.", "resourcesTableAndOnlyAccessibleInternally": "veyalnızca bir istemci ile bağlandığında dahili olarak erişilebilir.",
"resourcesTableNoTargets": "Hedef yok",
"resourcesTableHealthy": "Sağlıklı", "resourcesTableHealthy": "Sağlıklı",
"resourcesTableDegraded": "Düşük Performanslı", "resourcesTableDegraded": "Düşük Performanslı",
"resourcesTableOffline": "Çevrimdışı", "resourcesTableUnhealthy": "Sağlıksız",
"resourcesTableUnknown": "Bilinmiyor", "resourcesTableUnknown": "Bilinmiyor",
"resourcesTableNotMonitored": "İzlenmiyor", "resourcesTableNotMonitored": "İzlenmiyor",
"resourcesTableNoTargets": "Hedef yok",
"editInternalResourceDialogEditClientResource": "Özel Kaynak Düzenleyin", "editInternalResourceDialogEditClientResource": "Özel Kaynak Düzenleyin",
"editInternalResourceDialogUpdateResourceProperties": "{resourceName} için kaynak ayarlarını ve erişim kontrollerini güncelleyin", "editInternalResourceDialogUpdateResourceProperties": "{resourceName} için kaynak ayarlarını ve erişim kontrollerini güncelleyin",
"editInternalResourceDialogResourceProperties": "Kaynak Özellikleri", "editInternalResourceDialogResourceProperties": "Kaynak Özellikleri",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "Bağlantı Noktası", "editInternalResourceDialogModePort": "Bağlantı Noktası",
"editInternalResourceDialogModeHost": "Ev Sahibi", "editInternalResourceDialogModeHost": "Ev Sahibi",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "Şema",
"editInternalResourceDialogEnableSsl": "SSL'i Etkinleştir",
"editInternalResourceDialogEnableSslDescription": "Hedefe güvenli HTTPS bağlantıları için SSL/TLS şifrelemeyi etkinleştirin.",
"editInternalResourceDialogDestination": "Hedef", "editInternalResourceDialogDestination": "Hedef",
"editInternalResourceDialogDestinationHostDescription": "Site ağındaki kaynağın IP adresi veya ana bilgisayar adı.", "editInternalResourceDialogDestinationHostDescription": "Site ağındaki kaynağın IP adresi veya ana bilgisayar adı.",
"editInternalResourceDialogDestinationIPDescription": "Kaynağın site ağındaki IP veya ana bilgisayar adresi.", "editInternalResourceDialogDestinationIPDescription": "Kaynağın site ağındaki IP veya ana bilgisayar adresi.",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "Ad", "createInternalResourceDialogName": "Ad",
"createInternalResourceDialogSite": "Site", "createInternalResourceDialogSite": "Site",
"selectSite": "Site seç...", "selectSite": "Site seç...",
"multiSitesSelectorSitesCount": "{count, plural, one {# site} other {# siteler}}",
"noSitesFound": "Site bulunamadı.", "noSitesFound": "Site bulunamadı.",
"createInternalResourceDialogProtocol": "Protokol", "createInternalResourceDialogProtocol": "Protokol",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "Bağlantı Noktası", "createInternalResourceDialogModePort": "Bağlantı Noktası",
"createInternalResourceDialogModeHost": "Ev Sahibi", "createInternalResourceDialogModeHost": "Ev Sahibi",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "Şema",
"createInternalResourceDialogScheme": "Şema",
"createInternalResourceDialogEnableSsl": "SSL'i Etkinleştir",
"createInternalResourceDialogEnableSslDescription": "Hedefe güvenli HTTPS bağlantıları için SSL/TLS şifrelemeyi etkinleştirin.",
"createInternalResourceDialogDestination": "Hedef", "createInternalResourceDialogDestination": "Hedef",
"createInternalResourceDialogDestinationHostDescription": "Site ağındaki kaynağın IP adresi veya ana bilgisayar adı.", "createInternalResourceDialogDestinationHostDescription": "Site ağındaki kaynağın IP adresi veya ana bilgisayar adı.",
"createInternalResourceDialogDestinationCidrDescription": "Site ağındaki kaynağın CIDR aralığı.", "createInternalResourceDialogDestinationCidrDescription": "Site ağındaki kaynağın CIDR aralığı.",
"createInternalResourceDialogAlias": "Takma Ad", "createInternalResourceDialogAlias": "Takma Ad",
"createInternalResourceDialogAliasDescription": "Bu kaynak için isteğe bağlı dahili DNS takma adı.", "createInternalResourceDialogAliasDescription": "Bu kaynak için isteğe bağlı dahili DNS takma adı.",
"internalResourceDownstreamSchemeRequired": "HTTP kaynakları için şema gereklidir",
"internalResourceHttpPortRequired": "HTTP kaynakları için hedef bağlantı noktası gereklidir",
"siteConfiguration": "Yapılandırma", "siteConfiguration": "Yapılandırma",
"siteAcceptClientConnections": "İstemci Bağlantılarını Kabul Et", "siteAcceptClientConnections": "İstemci Bağlantılarını Kabul Et",
"siteAcceptClientConnectionsDescription": "Kullanıcı cihazları ve istemcilerin bu sitedeki kaynaklara erişmesine izin verin. Bu daha sonra değiştirilebilir.", "siteAcceptClientConnectionsDescription": "Kullanıcı cihazları ve istemcilerin bu sitedeki kaynaklara erişmesine izin verin. Bu daha sonra değiştirilebilir.",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "Doğrulandı", "domainPickerVerified": "Doğrulandı",
"domainPickerUnverified": "Doğrulanmadı", "domainPickerUnverified": "Doğrulanmadı",
"domainPickerManual": "Manuel", "domainPickerManual": "Manuel",
"domainPickerInvalidSubdomainStructure": "Bu alt alan adı geçersiz karakterler veya yapı içeriyor. Kaydettiğinizde otomatik olarak temizlenecektir.", "domainPickerInvalidSubdomainStructure": "Geçersiz karakterler kaydedildiğinde temizlenecektir.",
"domainPickerError": "Hata", "domainPickerError": "Hata",
"domainPickerErrorLoadDomains": "Organizasyon alan adları yüklenemedi", "domainPickerErrorLoadDomains": "Organizasyon alan adları yüklenemedi",
"domainPickerErrorCheckAvailability": "Alan adı kullanılabilirliği kontrol edilemedi", "domainPickerErrorCheckAvailability": "Alan adı kullanılabilirliği kontrol edilemedi",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "Devam etmek için kimlik sağlayıcınızı seçin", "orgAuthChooseIdpDescription": "Devam etmek için kimlik sağlayıcınızı seçin",
"orgAuthNoIdpConfigured": "Bu kuruluşta yapılandırılmış kimlik sağlayıcı yok. Bunun yerine Pangolin kimliğinizle giriş yapabilirsiniz.", "orgAuthNoIdpConfigured": "Bu kuruluşta yapılandırılmış kimlik sağlayıcı yok. Bunun yerine Pangolin kimliğinizle giriş yapabilirsiniz.",
"orgAuthSignInWithPangolin": "Pangolin ile Giriş Yap", "orgAuthSignInWithPangolin": "Pangolin ile Giriş Yap",
"orgAuthSignInToOrg": "Bir kuruluşa giriş yapın", "orgAuthSignInToOrg": "Kuruluş Kimlik Sağlayıcısı (SSO)",
"orgAuthSelectOrgTitle": "Kuruluş Giriş", "orgAuthSelectOrgTitle": "Kuruluş Giriş",
"orgAuthSelectOrgDescription": "Devam etmek için kuruluş kimliğinizi girin", "orgAuthSelectOrgDescription": "Devam etmek için kuruluş kimliğinizi girin",
"orgAuthOrgIdPlaceholder": "kuruluşunuz", "orgAuthOrgIdPlaceholder": "kuruluşunuz",
@@ -2429,6 +2648,7 @@
"validPassword": "Geçerli Şifre", "validPassword": "Geçerli Şifre",
"validEmail": "Geçerli E-posta", "validEmail": "Geçerli E-posta",
"validSSO": "Geçerli SSO", "validSSO": "Geçerli SSO",
"connectedClient": "Bağlı İstemci",
"resourceBlocked": "Kaynak Engellendi", "resourceBlocked": "Kaynak Engellendi",
"droppedByRule": "Kurallara Göre Çıkartıldı", "droppedByRule": "Kurallara Göre Çıkartıldı",
"noSessions": "Oturum Yok", "noSessions": "Oturum Yok",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "Müşteriler Ekle", "editInternalResourceDialogAddClients": "Müşteriler Ekle",
"editInternalResourceDialogDestinationLabel": "Hedef", "editInternalResourceDialogDestinationLabel": "Hedef",
"editInternalResourceDialogDestinationDescription": "Dahili kaynak için hedef adresi belirtin. Seçilen moda bağlı olarak bu bir ana bilgisayar adı, IP adresi veya CIDR aralığı olabilir. Daha kolay tanımlama için isteğe bağlı olarak dahili bir DNS takma adı ayarlayın.", "editInternalResourceDialogDestinationDescription": "Dahili kaynak için hedef adresi belirtin. Seçilen moda bağlı olarak bu bir ana bilgisayar adı, IP adresi veya CIDR aralığı olabilir. Daha kolay tanımlama için isteğe bağlı olarak dahili bir DNS takma adı ayarlayın.",
"internalResourceFormMultiSiteRoutingHelp": "Birden fazla site seçmek, yüksek kullanılabilirlik için dirençli yönlendirme ve yedeklik sağlar.",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Daha fazla bilgi",
"editInternalResourceDialogPortRestrictionsDescription": "Belirtilen TCP/UDP portlarına erişimi kısıtlayın veya tüm portlara izin/engelleme verin.", "editInternalResourceDialogPortRestrictionsDescription": "Belirtilen TCP/UDP portlarına erişimi kısıtlayın veya tüm portlara izin/engelleme verin.",
"createInternalResourceDialogHttpConfiguration": "HTTP yapılandırması",
"createInternalResourceDialogHttpConfigurationDescription": "HTTP veya HTTPS üzerinden bu kaynağa ulaşmak için istemcilerin kullanacağı alan adını seçin.",
"editInternalResourceDialogHttpConfiguration": "HTTP yapılandırması",
"editInternalResourceDialogHttpConfigurationDescription": "HTTP veya HTTPS üzerinden bu kaynağa ulaşmak için istemcilerin kullanacağı alan adını seçin.",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "Yakında geri döneceğiz! Sitemiz şu anda planlı bakım altındadır.", "maintenancePageMessagePlaceholder": "Yakında geri döneceğiz! Sitemiz şu anda planlı bakım altındadır.",
"maintenancePageMessageDescription": "Bakımın detaylarınııklayan mesaj", "maintenancePageMessageDescription": "Bakımın detaylarınııklayan mesaj",
"maintenancePageTimeTitle": "Tahmini Tamamlanma Süresi (İsteğe Bağlı)", "maintenancePageTimeTitle": "Tahmini Tamamlanma Süresi (İsteğe Bağlı)",
"privateMaintenanceScreenTitle": "Özel Yer Tutucu Ekran",
"privateMaintenanceScreenMessage": "Bu alan adı özel bir kaynak üzerinde kullanılmaktadır. Bu kaynağa erişmek için Pangolin istemcisini kullanarak bağlanın.",
"privateMaintenanceScreenSteps": "Bağlanıldıktan sonra, hâlâ bu mesajı görüyorsanız tarayıcınızın DNS önbelleği eski adrese işaret ediyor olabilir. Bunu düzeltmek için: bu sekmeyi veya tarayıcınızı tamamen kapatıp tekrar açın, ardından bu sayfaya geri dönün.",
"maintenanceTime": "ör. 2 saat, 1 Kasım saat 17:00", "maintenanceTime": "ör. 2 saat, 1 Kasım saat 17:00",
"maintenanceEstimatedTimeDescription": "Bakımın ne zaman tamamlanmasını bekliyorsunuz", "maintenanceEstimatedTimeDescription": "Bakımın ne zaman tamamlanmasını bekliyorsunuz",
"editDomain": "Alan Adını Düzenle", "editDomain": "Alan Adını Düzenle",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "HTTP Hedefi Ekle", "httpDestAddTitle": "HTTP Hedefi Ekle",
"httpDestEditDescription": "Bu HTTP olay akışı hedefine yapılandırmayı güncelleyin.", "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.", "httpDestAddDescription": "Organizasyonunuzun olaylarını almak için yeni bir HTTP uç noktası yapılandırın.",
"S3DestEditTitle": "Hedefi Düzenle",
"S3DestAddTitle": "S3 Hedefi Ekle",
"S3DestEditDescription": "Bu S3 olay akışı hedefi için yapılandırmayı güncelleyin.",
"S3DestAddDescription": "Kuruluşunuzun olaylarını almak için yeni bir S3 uç noktası yapılandırın.",
"datadogDestEditTitle": "Hedefi Düzenle",
"datadogDestAddTitle": "Datadog Hedefi Ekle",
"datadogDestEditDescription": "Bu Datadog olay akışı hedefi için yapılandırmayı güncelleyin.",
"datadogDestAddDescription": "Kuruluşunuzun olaylarını almak için yeni bir Datadog uç noktası yapılandırın.",
"httpDestTabSettings": "Ayarlar", "httpDestTabSettings": "Ayarlar",
"httpDestTabHeaders": "Başlıklar", "httpDestTabHeaders": "Başlıklar",
"httpDestTabBody": "Gövde", "httpDestTabBody": "Gövde",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "Hedef başarıyla oluşturuldu", "httpDestCreatedSuccess": "Hedef başarıyla oluşturuldu",
"httpDestUpdateFailed": "Hedef güncellenemedi", "httpDestUpdateFailed": "Hedef güncellenemedi",
"httpDestCreateFailed": "Hedef oluşturulamadı", "httpDestCreateFailed": "Hedef oluşturulamadı",
"followRedirects": "Yönlendirmeleri Takip Et",
"followRedirectsDescription": "İstekler için HTTP yönlendirmelerini otomatik olarak takip edin.",
"alertingErrorWebhookUrl": "Webhook için geçerli bir URL girin lütfen.",
"healthCheckStrategyHttp": "Bağlantıyı doğrular ve HTTP yanıt durumunu kontrol eder.",
"healthCheckStrategyTcp": "Yanıtı denetlemeden sadece TCP bağlantısını doğrular.",
"healthCheckStrategySnmp": "Ağ aygıtlarının ve altyapısının sağlığını kontrol etmek için bir SNMP alma isteği yapar.",
"healthCheckStrategyIcmp": "Bir kaynağın erişilebilir ve yanıt verebilir olup olmadığını kontrol etmek için ICMP yankı isteklerini (ping) kullanır.",
"healthCheckTabStrategy": "Strateji",
"healthCheckTabConnection": "Bağlantı",
"healthCheckTabAdvanced": "Gelişmiş",
"healthCheckStrategyNotAvailable": "Bu strateji kullanılamıyor. Bu özelliği etkinleştirmek için lütfen satış ekibiyle iletişime geçin.",
"uptime30d": "Çalışma Süresi (30g)",
"idpAddActionCreateNew": "Yeni kimlik sağlayıcı oluştur", "idpAddActionCreateNew": "Yeni kimlik sağlayıcı oluştur",
"idpAddActionImportFromOrg": "Başka bir kuruluştan içe aktar", "idpAddActionImportFromOrg": "Başka bir kuruluştan içe aktar",
"idpImportDialogTitle": "Kimlik Sağlayıcı İçe Aktar", "idpImportDialogTitle": "Kimlik Sağlayıcı İçe Aktar",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "Bu işlem bu kuruluş için geri alınamaz.", "idpUnassociateWarning": "Bu işlem bu kuruluş için geri alınamaz.",
"idpUnassociatedDescription": "Kimlik sağlayıcı bu kuruluştan başarıyla ayrıldı", "idpUnassociatedDescription": "Kimlik sağlayıcı bu kuruluştan başarıyla ayrıldı",
"idpUnassociateMenu": "İlişkiyi Kes", "idpUnassociateMenu": "İlişkiyi Kes",
"idpDeleteAllOrgsMenu": "Sil" "idpDeleteAllOrgsMenu": "Sil",
"publicIpEndpoint": "Uç Nokta",
"lastTriggeredAt": "Son Tetikleyici",
"reject": "Reddet",
"uptimeDaysAgo": "{count} gün önce",
"uptimeToday": "Bugün",
"uptimeNoDataAvailable": "Veri yok",
"uptimeSuffix": "çalışma süresi",
"uptimeDowntimeSuffix": "çalışma dışı",
"uptimeTooltipUptimeLabel": "Çalışma süresi",
"uptimeTooltipDowntimeLabel": "Çalışma dışı",
"uptimeOngoing": "devam eden",
"uptimeNoMonitoringData": "İzleme verisi yok",
"uptimeNoData": "Veri yok",
"uptimeMiniBarDown": "Kapalı",
"uptimeSectionTitle": "Çalışma Süresi",
"uptimeSectionDescription": "Son {days} gün boyunca kullanılabilirlik",
"uptimeAddAlert": "Uyarı Ekle",
"uptimeViewAlerts": "Uyarıları Görüntüle",
"uptimeCreateEmailAlert": "E-posta Uyarısı Oluştur",
"uptimeAlertDescriptionSite": "Bu site çevrimdışıyken veya yeniden çevrimiçi olduğunda e-posta ile bildirim alın.",
"uptimeAlertDescriptionResource": "Bu kaynak çevrimdışıyken veya yeniden çevrimiçi olduğunda e-posta ile bildirim alın.",
"uptimeAlertNamePlaceholder": "Uyarı adı",
"uptimeAdditionalEmails": "Ek E-postalar",
"uptimeCreateAlert": "Uyarı Oluştur",
"uptimeAlertNoRecipients": "Alıcı yok",
"uptimeAlertNoRecipientsDescription": "Lütfen en az bir kullanıcı, rol veya e-posta ekleyin.",
"uptimeAlertCreated": "Uyarı oluşturuldu",
"uptimeAlertCreatedDescription": "Durum değiştiğinde haberdar edileceksiniz.",
"uptimeAlertCreateFailed": "Uyarı oluşturulamadı",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "Anahtar",
"webhookHeaderValuePlaceholder": "Değer",
"alertLabel": "Uyarı",
"domainPickerWildcardSubdomainNotAllowed": "Genel alt alanlara izin verilmiyor.",
"domainPickerWildcardCertWarning": "Genel kaynaklar düzgün çalışmak için ek yapılandırma gerektirebilir.",
"domainPickerWildcardCertWarningLink": "Daha fazla bilgi",
"health": "Sağlık",
"domainPendingErrorTitle": "Doğrulama Sorunu"
} }

View File

@@ -1,4 +1,8 @@
{ {
"contactSalesEnable": "联系销售以启用此功能。",
"contactSalesBookDemo": "预订演示",
"contactSalesOr": "或",
"contactSalesContactUs": "联系我们",
"setupCreate": "创建组织、站点和资源", "setupCreate": "创建组织、站点和资源",
"headerAuthCompatibilityInfo": "启用此功能以在身份验证令牌缺失时强制返回401未授权响应。对于不在没有服务器挑战的情况下不发送凭证的浏览器或特定HTTP库这是必需的。", "headerAuthCompatibilityInfo": "启用此功能以在身份验证令牌缺失时强制返回401未授权响应。对于不在没有服务器挑战的情况下不发送凭证的浏览器或特定HTTP库这是必需的。",
"headerAuthCompatibility": "扩展兼容性", "headerAuthCompatibility": "扩展兼容性",
@@ -19,6 +23,14 @@
"componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。", "componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。",
"dismiss": "忽略", "dismiss": "忽略",
"subscriptionViolationMessage": "您的当前计划超出了您的限制。通过移除站点、用户或其他资源以保持在您的计划范围内来纠正问题。", "subscriptionViolationMessage": "您的当前计划超出了您的限制。通过移除站点、用户或其他资源以保持在您的计划范围内来纠正问题。",
"trialBannerMessage": "您的试用将在 {countdown} 到期。升级以保持访问。",
"trialBannerExpired": "您的试用已到期。立即升级以恢复访问。",
"trialActive": "免费试用中",
"trialExpired": "试用到期",
"trialHasEnded": "您的试用已结束。",
"trialDaysRemaining": "{count, plural, one {# day remaining} other {# days remaining}}",
"trialDaysLeftShort": "试用期剩余 {days} 天",
"trialGoToBilling": "转到账单页面",
"subscriptionViolationViewBilling": "查看计费", "subscriptionViolationViewBilling": "查看计费",
"componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。", "componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。",
"componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。", "componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。",
@@ -81,6 +93,8 @@
"siteConfirmCopy": "我已经复制了配置信息", "siteConfirmCopy": "我已经复制了配置信息",
"searchSitesProgress": "搜索站点...", "searchSitesProgress": "搜索站点...",
"siteAdd": "添加站点", "siteAdd": "添加站点",
"sitesTableViewPublicResources": "查看公共资源",
"sitesTableViewPrivateResources": "查看私有资源",
"siteInstallNewt": "安装 Newt", "siteInstallNewt": "安装 Newt",
"siteInstallNewtDescription": "在您的系统中运行 Newt", "siteInstallNewtDescription": "在您的系统中运行 Newt",
"WgConfiguration": "WireGuard 配置", "WgConfiguration": "WireGuard 配置",
@@ -98,6 +112,21 @@
"siteUpdatedDescription": "网站已更新。", "siteUpdatedDescription": "网站已更新。",
"siteGeneralDescription": "配置此站点的常规设置", "siteGeneralDescription": "配置此站点的常规设置",
"siteSettingDescription": "配置站点设置", "siteSettingDescription": "配置站点设置",
"siteResourcesTab": "资源",
"siteResourcesNoneOnSite": "此站点尚无公开或私人资源。",
"siteResourcesSectionPublic": "公共资源",
"siteResourcesSectionPrivate": "私有资源",
"siteResourcesSectionPublicDescription": "通过域名或端口公开的资源。",
"siteResourcesSectionPrivateDescription": "通过站点可在您的私有网络上访问的资源。",
"siteResourcesViewAllPublic": "查看所有资源",
"siteResourcesViewAllPrivate": "查看所有资源",
"siteResourcesDialogDescription": "此站点的公开和私有资源概览。",
"siteResourcesShowMore": "显示更多",
"siteResourcesPermissionDenied": "您无权列出这些资源。",
"siteResourcesEmptyPublic": "尚无针对该站点的公共资源。",
"siteResourcesEmptyPrivate": "尚无与此站点关联的私有资源。",
"siteResourcesHowToAccess": "如何访问",
"siteResourcesTargetsOnSite": "此站点上的目标",
"siteSetting": "{siteName} 设置", "siteSetting": "{siteName} 设置",
"siteNewtTunnel": "新站点 (推荐)", "siteNewtTunnel": "新站点 (推荐)",
"siteNewtTunnelDescription": "最简单的方式来创建任何网络的入口。没有额外的设置。", "siteNewtTunnelDescription": "最简单的方式来创建任何网络的入口。没有额外的设置。",
@@ -267,8 +296,11 @@
"orgMissing": "缺少组织 ID", "orgMissing": "缺少组织 ID",
"orgMissingMessage": "没有组织ID无法重新生成邀请。", "orgMissingMessage": "没有组织ID无法重新生成邀请。",
"accessUsersManage": "管理用户", "accessUsersManage": "管理用户",
"accessUserManage": "管理用户",
"accessUsersDescription": "邀请和管理访问此组织的用户", "accessUsersDescription": "邀请和管理访问此组织的用户",
"accessUsersSearch": "搜索用户...", "accessUsersSearch": "搜索用户...",
"accessUsersRoleFilterCount": "{count, plural, one {# role} other {# roles}}",
"accessUsersRoleFilterClear": "清除角色过滤器",
"accessUserCreate": "创建用户", "accessUserCreate": "创建用户",
"accessUserRemove": "删除用户", "accessUserRemove": "删除用户",
"username": "用户名", "username": "用户名",
@@ -731,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "密钥", "newtSecretKey": "密钥",
"newtVersion": "版本",
"architecture": "架构", "architecture": "架构",
"sites": "站点", "sites": "站点",
"siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决内部资源问题。", "siteWgAnyClients": "使用任何 WireGuard 客户端连接。您必须使用对等IP解决内部资源问题。",
@@ -1257,6 +1290,7 @@
"actionViewLogs": "查看日志", "actionViewLogs": "查看日志",
"noneSelected": "未选择", "noneSelected": "未选择",
"orgNotFound2": "未找到组织。", "orgNotFound2": "未找到组织。",
"search": "搜索…",
"searchPlaceholder": "搜索...", "searchPlaceholder": "搜索...",
"emptySearchOptions": "未找到选项", "emptySearchOptions": "未找到选项",
"create": "创建", "create": "创建",
@@ -1341,10 +1375,167 @@
"sidebarGeneral": "管理", "sidebarGeneral": "管理",
"sidebarLogAndAnalytics": "日志与分析", "sidebarLogAndAnalytics": "日志与分析",
"sidebarBluePrints": "蓝图", "sidebarBluePrints": "蓝图",
"sidebarAlerting": "告警",
"sidebarHealthChecks": "健康检查",
"sidebarOrganization": "组织", "sidebarOrganization": "组织",
"sidebarManagement": "管理", "sidebarManagement": "管理",
"sidebarBillingAndLicenses": "帐单和许可证", "sidebarBillingAndLicenses": "帐单和许可证",
"sidebarLogsAnalytics": "分析", "sidebarLogsAnalytics": "分析",
"alertingTitle": "告警",
"alertingDescription": "定义通知的来源、触发器和操作",
"alertingRules": "告警规则",
"alertingSearchRules": "搜索规则…",
"alertingAddRule": "创建规则",
"alertingColumnSource": "来源",
"alertingColumnTrigger": "触发",
"alertingColumnActions": "操作",
"alertingColumnEnabled": "已启用",
"alertingDeleteQuestion": "请确认您要删除此告警规则。",
"alertingDeleteRule": "删除告警规则",
"alertingRuleDeleted": "告警规则已删除",
"alertingRuleSaved": "告警规则已保存",
"alertingRuleSavedCreatedDescription": "您的新告警规则已创建。您可以在此页面继续编辑它。",
"alertingRuleSavedUpdatedDescription": "对此告警规则的更改已保存。",
"alertingEditRule": "编辑告警规则",
"alertingCreateRule": "创建告警规则",
"alertingRuleCredenzaDescription": "选择要监视的内容、何时触发以及如何通知",
"alertingRuleNamePlaceholder": "生产站点故障",
"alertingRuleEnabled": "规则已启用",
"alertingSectionSource": "来源",
"alertingSourceType": "来源类型",
"alertingSourceSite": "站点",
"alertingSourceHealthCheck": "健康检查",
"alertingPickSites": "站点",
"alertingPickHealthChecks": "健康检查",
"alertingPickResources": "资源",
"alertingAllSites": "所有站点",
"alertingAllSitesDescription": "任何站点的告警触发",
"alertingSpecificSites": "特定站点",
"alertingSpecificSitesDescription": "选择要监视的特定站点",
"alertingAllHealthChecks": "所有健康检查",
"alertingAllHealthChecksDescription": "任何健康检查的告警触发",
"alertingSpecificHealthChecks": "特定健康检查",
"alertingSpecificHealthChecksDescription": "选择要监视的特定健康检查",
"alertingAllResources": "所有资源",
"alertingAllResourcesDescription": "任何资源的告警触发",
"alertingSpecificResources": "特定资源",
"alertingSpecificResourcesDescription": "选择要监视的特定资源",
"alertingSelectResources": "选择资源…",
"alertingResourcesSelected": "{count} 个资源已选择",
"alertingResourcesEmpty": "在前 10 个结果中没有带目标的资源。",
"alertingSectionTrigger": "触发",
"alertingTrigger": "何时告警",
"alertingTriggerSiteOnline": "站点在线",
"alertingTriggerSiteOffline": "站点离线",
"alertingTriggerSiteToggle": "站点状态变更",
"alertingTriggerHcHealthy": "健康检查正常",
"alertingTriggerHcUnhealthy": "健康检查不正常",
"alertingTriggerHcToggle": "健康检查状态变更",
"alertingTriggerResourceHealthy": "资源正常",
"alertingTriggerResourceUnhealthy": "资源不正常",
"alertingTriggerResourceDegraded": "资源降级",
"alertingSearchHealthChecks": "搜索健康检查…",
"alertingHealthChecksEmpty": "无可用健康检查。",
"alertingTriggerResourceToggle": "资源状态变更",
"alertingSourceResource": "资源",
"alertingSectionActions": "操作",
"alertingAddAction": "新增操作",
"alertingActionNotify": "电子邮件",
"alertingActionNotifyDescription": "向用户或角色发送电子邮件通知",
"alertingActionWebhook": "Webhook",
"alertingActionWebhookDescription": "发送 HTTP 请求到自定义终端",
"alertingExternalIntegration": "外部集成",
"alertingExternalPagerDutyDescription": "将告警发送给 PagerDuty 以进行事件管理",
"alertingExternalOpsgenieDescription": "将告警路由到 Opsgenie 进行电话值班管理",
"alertingExternalServiceNowDescription": "从告警事件创建 ServiceNow 事件",
"alertingExternalIncidentIoDescription": "从告警事件触发 Incident.io 工作流程",
"alertingActionType": "操作类型",
"alertingNotifyUsers": "用户",
"alertingNotifyRoles": "角色",
"alertingNotifyEmails": "电子邮件地址",
"alertingEmailPlaceholder": "添加电子邮件并按回车键",
"alertingWebhookMethod": "HTTP 方法",
"alertingWebhookSecret": "签名密钥(可选)",
"alertingWebhookSecretPlaceholder": "HMAC 密钥",
"alertingWebhookHeaders": "标头",
"alertingAddHeader": "添加标头",
"alertingSelectSites": "选择站点…",
"alertingSitesSelected": "{count} 个站点已选择",
"alertingSelectHealthChecks": "选择健康检查…",
"alertingHealthChecksSelected": "{count} 个健康检查已选择",
"alertingNoHealthChecks": "没有启用健康检查的目标",
"alertingHealthCheckStub": "健康检查来源选择尚未配置 - 你仍然可以配置触发器和操作。",
"alertingSelectUsers": "选择用户…",
"alertingUsersSelected": "{count} 个用户已选择",
"alertingSelectRoles": "选择角色…",
"alertingRolesSelected": "{count} 个角色已选择",
"alertingSummarySites": "站点 ({count})",
"alertingSummaryAllSites": "所有站点",
"alertingSummaryHealthChecks": "健康检查 ({count})",
"alertingSummaryAllHealthChecks": "所有健康检查",
"alertingSummaryResources": "资源 ({count})",
"alertingSummaryAllResources": "所有资源",
"alertingErrorNameRequired": "输入名称",
"alertingErrorActionsMin": "添加至少一个操作",
"alertingErrorPickSites": "至少选择一个站点",
"alertingErrorPickHealthChecks": "至少选择一个健康检查",
"alertingErrorPickResources": "至少选择一个资源",
"alertingErrorTriggerSite": "选择站点触发器",
"alertingErrorTriggerHealth": "选择健康检查触发器",
"alertingErrorTriggerResource": "选择资源触发器",
"alertingErrorNotifyRecipients": "选择用户、角色或至少一个电子邮件",
"alertingConfigureSource": "配置来源",
"alertingConfigureTrigger": "配置触发器",
"alertingConfigureActions": "配置操作",
"alertingBackToRules": "返回规则",
"alertingRuleCooldown": "冷却时间(秒)",
"alertingRuleCooldownDescription": "相同规则间隔重复告警的最小时间。设置为 0 固定触发。",
"alertingDraftBadge": "草稿 - 保存以存储此规则",
"alertingSidebarHint": "点击画布上的步骤在此处编辑。",
"alertingGraphCanvasTitle": "规则流程",
"alertingGraphCanvasDescription": "源、触发器和操作的视觉概况。选择一个节点,在面板上进行编辑。",
"alertingNodeNotConfigured": "尚未配置",
"alertingNodeActionsCount": "{count, plural, one {# action} other {# actions}}",
"alertingNodeRoleSource": "来源",
"alertingNodeRoleTrigger": "触发",
"alertingNodeRoleAction": "行为",
"alertingTabRules": "告警规则",
"alertingTabHealthChecks": "健康检查",
"alertingRulesBannerTitle": "获取通知",
"alertingRulesBannerDescription": "每条规则都连接要监视的对象站点、健康检查或资源触发时间例如离线或不健康以及如何通过电子邮件、Webhooks 或集成将通知发送给团队。使用此列表创建、启用和管理这些规则。",
"alertingHealthChecksBannerTitle": "监视健康和资源",
"alertingHealthChecksBannerDescription": "健康检查是您一次定义的 HTTP 或 TCP 监控。然后可以将它们用作告警规则中的来源,以便目标变得正常或不正常时得到通知。资源上的健康检查也会出现在此处。",
"standaloneHcTableTitle": "健康检查",
"standaloneHcSearchPlaceholder": "搜索健康检查…",
"standaloneHcAddButton": "创建健康检查",
"standaloneHcCreateTitle": "创建健康检查",
"standaloneHcEditTitle": "编辑健康检查",
"standaloneHcDescription": "配置 HTTP 或 TCP 健康检查以用于告警规则。",
"standaloneHcNameLabel": "名称",
"standaloneHcNamePlaceholder": "我的 HTTP 监视器",
"standaloneHcDeleteTitle": "删除健康检查",
"standaloneHcDeleteQuestion": "请确认您要删除此健康检查。",
"standaloneHcDeleted": "健康检查已删除",
"standaloneHcSaved": "健康检查已保存",
"standaloneHcColumnHealth": "健康",
"standaloneHcColumnMode": "模式",
"standaloneHcColumnTarget": "目标",
"standaloneHcHealthStateHealthy": "健康",
"standaloneHcHealthStateUnhealthy": "不健康",
"standaloneHcHealthStateUnknown": "未知",
"standaloneHcFilterAnySite": "所有站点",
"standaloneHcFilterAnyResource": "所有资源",
"standaloneHcFilterMode": "模式",
"standaloneHcFilterModeHttp": "HTTP",
"standaloneHcFilterModeTcp": "TCP",
"standaloneHcFilterModeSnmp": "SNMP",
"standaloneHcFilterModePing": "Ping",
"standaloneHcFilterHealth": "健康",
"standaloneHcFilterEnabled": "已启用",
"standaloneHcFilterEnabledOn": "已启用",
"standaloneHcFilterEnabledOff": "已禁用",
"standaloneHcFilterSiteIdFallback": "站点 {id}",
"standaloneHcFilterResourceIdFallback": "资源 {id}",
"blueprints": "蓝图", "blueprints": "蓝图",
"blueprintsDescription": "应用声明配置并查看先前运行的", "blueprintsDescription": "应用声明配置并查看先前运行的",
"blueprintAdd": "添加蓝图", "blueprintAdd": "添加蓝图",
@@ -1406,7 +1597,8 @@
"initialSetupDescription": "创建初始服务器管理员帐户。 只能存在一个服务器管理员。 您可以随时更改这些凭据。", "initialSetupDescription": "创建初始服务器管理员帐户。 只能存在一个服务器管理员。 您可以随时更改这些凭据。",
"createAdminAccount": "创建管理员帐户", "createAdminAccount": "创建管理员帐户",
"setupErrorCreateAdmin": "创建服务器管理员账户时发生错误。", "setupErrorCreateAdmin": "创建服务器管理员账户时发生错误。",
"certificateStatus": "证书状态", "certificateStatus": "证书",
"certificateStatusAutoRefreshHint": "状态自动刷新。",
"loading": "加载中", "loading": "加载中",
"loadingAnalytics": "加载分析", "loadingAnalytics": "加载分析",
"restart": "重启", "restart": "重启",
@@ -1475,6 +1667,7 @@
"pangolinUpdateAvailableReleaseNotes": "查看发布说明", "pangolinUpdateAvailableReleaseNotes": "查看发布说明",
"newtUpdateAvailable": "更新可用", "newtUpdateAvailable": "更新可用",
"newtUpdateAvailableInfo": "新版本的 Newt 已可用。请更新到最新版本以获得最佳体验。", "newtUpdateAvailableInfo": "新版本的 Newt 已可用。请更新到最新版本以获得最佳体验。",
"pangolinNodeUpdateAvailableInfo": "新版本的 Pangolin Node 已可用。请更新到最新版本以获得最佳体验。",
"domainPickerEnterDomain": "域名", "domainPickerEnterDomain": "域名",
"domainPickerPlaceholder": "example.com", "domainPickerPlaceholder": "example.com",
"domainPickerDescription": "输入资源的完整域名以查看可用选项。", "domainPickerDescription": "输入资源的完整域名以查看可用选项。",
@@ -1714,6 +1907,7 @@
"configureHealthCheck": "配置健康检查", "configureHealthCheck": "配置健康检查",
"configureHealthCheckDescription": "为 {target} 设置健康监控", "configureHealthCheckDescription": "为 {target} 设置健康监控",
"enableHealthChecks": "启用健康检查", "enableHealthChecks": "启用健康检查",
"healthCheckDisabledStateDescription": "禁用后,站点不会进行健康检查,状态将被视为未知。",
"enableHealthChecksDescription": "监视此目标的健康状况。如果需要,您可以监视一个不同的终点。", "enableHealthChecksDescription": "监视此目标的健康状况。如果需要,您可以监视一个不同的终点。",
"healthScheme": "方法", "healthScheme": "方法",
"healthSelectScheme": "选择方法", "healthSelectScheme": "选择方法",
@@ -1763,9 +1957,20 @@
"healthCheckIntervalMin": "检查间隔必须至少为 5 秒", "healthCheckIntervalMin": "检查间隔必须至少为 5 秒",
"healthCheckTimeoutMin": "超时必须至少为 1 秒", "healthCheckTimeoutMin": "超时必须至少为 1 秒",
"healthCheckRetryMin": "重试次数必须至少为 1 次", "healthCheckRetryMin": "重试次数必须至少为 1 次",
"healthCheckMode": "检查模式",
"healthCheckStrategy": "策略",
"healthCheckModeDescription": "TCP 模式仅验证连接性。HTTP 模式验证 HTTP 响应。",
"healthyThreshold": "正常阈值",
"healthyThresholdDescription": "标记为正常之前所需的连续成功次数。",
"unhealthyThreshold": "不正常阈值",
"unhealthyThresholdDescription": "标记为不正常之前所需的连续失败次数。",
"healthCheckHealthyThresholdMin": "健康阈值至少为 1",
"healthCheckUnhealthyThresholdMin": "不健康阈值至少为 1",
"httpMethod": "HTTP 方法", "httpMethod": "HTTP 方法",
"selectHttpMethod": "选择 HTTP 方法", "selectHttpMethod": "选择 HTTP 方法",
"domainPickerSubdomainLabel": "子域名", "domainPickerSubdomainLabel": "子域名",
"domainPickerWildcard": "通配符",
"domainPickerWildcardPaidOnly": "通配符子域是付费功能。请升级以使用此功能。",
"domainPickerBaseDomainLabel": "根域名", "domainPickerBaseDomainLabel": "根域名",
"domainPickerSearchDomains": "搜索域名...", "domainPickerSearchDomains": "搜索域名...",
"domainPickerNoDomainsFound": "未找到域名", "domainPickerNoDomainsFound": "未找到域名",
@@ -1791,12 +1996,12 @@
"resourcesTableAliasAddressInfo": "此地址是组织实用子网的一部分。它用来使用内部DNS解析来解析别名记录。", "resourcesTableAliasAddressInfo": "此地址是组织实用子网的一部分。它用来使用内部DNS解析来解析别名记录。",
"resourcesTableClients": "客户端", "resourcesTableClients": "客户端",
"resourcesTableAndOnlyAccessibleInternally": "且仅在与客户端连接时可内部访问。", "resourcesTableAndOnlyAccessibleInternally": "且仅在与客户端连接时可内部访问。",
"resourcesTableNoTargets": "没有目标",
"resourcesTableHealthy": "健康的", "resourcesTableHealthy": "健康的",
"resourcesTableDegraded": "降级", "resourcesTableDegraded": "降级",
"resourcesTableOffline": "离线的", "resourcesTableUnhealthy": "不健康",
"resourcesTableUnknown": "未知的", "resourcesTableUnknown": "未知的",
"resourcesTableNotMonitored": "未监视的", "resourcesTableNotMonitored": "未监视的",
"resourcesTableNoTargets": "无目标",
"editInternalResourceDialogEditClientResource": "编辑私有资源", "editInternalResourceDialogEditClientResource": "编辑私有资源",
"editInternalResourceDialogUpdateResourceProperties": "更新{resourceName}的资源配置和访问控制。", "editInternalResourceDialogUpdateResourceProperties": "更新{resourceName}的资源配置和访问控制。",
"editInternalResourceDialogResourceProperties": "资源属性", "editInternalResourceDialogResourceProperties": "资源属性",
@@ -1822,6 +2027,11 @@
"editInternalResourceDialogModePort": "端口", "editInternalResourceDialogModePort": "端口",
"editInternalResourceDialogModeHost": "主机", "editInternalResourceDialogModeHost": "主机",
"editInternalResourceDialogModeCidr": "CIDR", "editInternalResourceDialogModeCidr": "CIDR",
"editInternalResourceDialogModeHttp": "HTTP",
"editInternalResourceDialogModeHttps": "HTTPS",
"editInternalResourceDialogScheme": "方案",
"editInternalResourceDialogEnableSsl": "启用 SSL",
"editInternalResourceDialogEnableSslDescription": "为目标的安全 HTTPS 连接启用 SSL/TLS 加密。",
"editInternalResourceDialogDestination": "目标", "editInternalResourceDialogDestination": "目标",
"editInternalResourceDialogDestinationHostDescription": "站点网络上资源的 IP 地址或主机名。", "editInternalResourceDialogDestinationHostDescription": "站点网络上资源的 IP 地址或主机名。",
"editInternalResourceDialogDestinationIPDescription": "站点网络上资源的IP或主机名地址。", "editInternalResourceDialogDestinationIPDescription": "站点网络上资源的IP或主机名地址。",
@@ -1837,6 +2047,7 @@
"createInternalResourceDialogName": "名称", "createInternalResourceDialogName": "名称",
"createInternalResourceDialogSite": "站点", "createInternalResourceDialogSite": "站点",
"selectSite": "选择站点...", "selectSite": "选择站点...",
"multiSitesSelectorSitesCount": "{count, plural, one {# site} other {# sites}}",
"noSitesFound": "未找到站点。", "noSitesFound": "未找到站点。",
"createInternalResourceDialogProtocol": "协议", "createInternalResourceDialogProtocol": "协议",
"createInternalResourceDialogTcp": "TCP", "createInternalResourceDialogTcp": "TCP",
@@ -1865,11 +2076,19 @@
"createInternalResourceDialogModePort": "端口", "createInternalResourceDialogModePort": "端口",
"createInternalResourceDialogModeHost": "主机", "createInternalResourceDialogModeHost": "主机",
"createInternalResourceDialogModeCidr": "CIDR", "createInternalResourceDialogModeCidr": "CIDR",
"createInternalResourceDialogModeHttp": "HTTP",
"createInternalResourceDialogModeHttps": "HTTPS",
"scheme": "方案",
"createInternalResourceDialogScheme": "方案",
"createInternalResourceDialogEnableSsl": "启用 SSL",
"createInternalResourceDialogEnableSslDescription": "为目标的安全 HTTPS 连接启用 SSL/TLS 加密。",
"createInternalResourceDialogDestination": "目标", "createInternalResourceDialogDestination": "目标",
"createInternalResourceDialogDestinationHostDescription": "站点网络上资源的 IP 地址或主机名。", "createInternalResourceDialogDestinationHostDescription": "站点网络上资源的 IP 地址或主机名。",
"createInternalResourceDialogDestinationCidrDescription": "站点网络上资源的 CIDR 范围。", "createInternalResourceDialogDestinationCidrDescription": "站点网络上资源的 CIDR 范围。",
"createInternalResourceDialogAlias": "Alias", "createInternalResourceDialogAlias": "Alias",
"createInternalResourceDialogAliasDescription": "此资源可选的内部DNS别名。", "createInternalResourceDialogAliasDescription": "此资源可选的内部DNS别名。",
"internalResourceDownstreamSchemeRequired": "HTTP 资源需要方案",
"internalResourceHttpPortRequired": "HTTP 资源需要目的端口",
"siteConfiguration": "配置", "siteConfiguration": "配置",
"siteAcceptClientConnections": "接受客户端连接", "siteAcceptClientConnections": "接受客户端连接",
"siteAcceptClientConnectionsDescription": "允许用户设备和客户端访问此站点上的资源。这可以稍后更改。", "siteAcceptClientConnectionsDescription": "允许用户设备和客户端访问此站点上的资源。这可以稍后更改。",
@@ -2123,7 +2342,7 @@
"domainPickerVerified": "已验证", "domainPickerVerified": "已验证",
"domainPickerUnverified": "未验证", "domainPickerUnverified": "未验证",
"domainPickerManual": "手动", "domainPickerManual": "手动",
"domainPickerInvalidSubdomainStructure": "此子域包含无效的字符或结构。当您保存时,它将被自动清除。", "domainPickerInvalidSubdomainStructure": "保存时将清除无效字符。",
"domainPickerError": "错误", "domainPickerError": "错误",
"domainPickerErrorLoadDomains": "加载组织域名失败", "domainPickerErrorLoadDomains": "加载组织域名失败",
"domainPickerErrorCheckAvailability": "检查域可用性失败", "domainPickerErrorCheckAvailability": "检查域可用性失败",
@@ -2136,7 +2355,7 @@
"orgAuthChooseIdpDescription": "选择您的身份提供商以继续", "orgAuthChooseIdpDescription": "选择您的身份提供商以继续",
"orgAuthNoIdpConfigured": "此机构没有配置任何身份提供者。您可以使用您的 Pangolin 身份登录。", "orgAuthNoIdpConfigured": "此机构没有配置任何身份提供者。您可以使用您的 Pangolin 身份登录。",
"orgAuthSignInWithPangolin": "使用 Pangolin 登录", "orgAuthSignInWithPangolin": "使用 Pangolin 登录",
"orgAuthSignInToOrg": "登录到组织", "orgAuthSignInToOrg": "组织身份提供商 (SSO)",
"orgAuthSelectOrgTitle": "组织登录", "orgAuthSelectOrgTitle": "组织登录",
"orgAuthSelectOrgDescription": "输入您的组织ID以继续", "orgAuthSelectOrgDescription": "输入您的组织ID以继续",
"orgAuthOrgIdPlaceholder": "您的组织", "orgAuthOrgIdPlaceholder": "您的组织",
@@ -2429,6 +2648,7 @@
"validPassword": "有效密码", "validPassword": "有效密码",
"validEmail": "Valid email", "validEmail": "Valid email",
"validSSO": "Valid SSO", "validSSO": "Valid SSO",
"connectedClient": "已连接客户端",
"resourceBlocked": "资源被阻止", "resourceBlocked": "资源被阻止",
"droppedByRule": "被规则删除", "droppedByRule": "被规则删除",
"noSessions": "无会话", "noSessions": "无会话",
@@ -2667,7 +2887,13 @@
"editInternalResourceDialogAddClients": "添加客户端", "editInternalResourceDialogAddClients": "添加客户端",
"editInternalResourceDialogDestinationLabel": "目标", "editInternalResourceDialogDestinationLabel": "目标",
"editInternalResourceDialogDestinationDescription": "指定内部资源的目标地址。根据选择的模式这可以是主机名、IP地址或CIDR范围。可选的设置一个内部DNS别名以便于识别。", "editInternalResourceDialogDestinationDescription": "指定内部资源的目标地址。根据选择的模式这可以是主机名、IP地址或CIDR范围。可选的设置一个内部DNS别名以便于识别。",
"internalResourceFormMultiSiteRoutingHelp": "选择多个站点可以实现高可用性的弹性路由和故障转移。",
"internalResourceFormMultiSiteRoutingHelpLearnMore": "了解更多",
"editInternalResourceDialogPortRestrictionsDescription": "限制访问特定的TCP/UDP端口或允许/阻止所有端口。", "editInternalResourceDialogPortRestrictionsDescription": "限制访问特定的TCP/UDP端口或允许/阻止所有端口。",
"createInternalResourceDialogHttpConfiguration": "HTTP 配置",
"createInternalResourceDialogHttpConfigurationDescription": "选择客户将使用的域名通过 HTTP 或 HTTPS 访问此资源。",
"editInternalResourceDialogHttpConfiguration": "HTTP 配置",
"editInternalResourceDialogHttpConfigurationDescription": "选择客户将使用的域名通过 HTTP 或 HTTPS 访问此资源。",
"editInternalResourceDialogTcp": "TCP", "editInternalResourceDialogTcp": "TCP",
"editInternalResourceDialogUdp": "UDP", "editInternalResourceDialogUdp": "UDP",
"editInternalResourceDialogIcmp": "ICMP", "editInternalResourceDialogIcmp": "ICMP",
@@ -2706,6 +2932,9 @@
"maintenancePageMessagePlaceholder": "我们很快回来! 我们的网站目前正在进行计划中的维护。", "maintenancePageMessagePlaceholder": "我们很快回来! 我们的网站目前正在进行计划中的维护。",
"maintenancePageMessageDescription": "详细说明维护的消息", "maintenancePageMessageDescription": "详细说明维护的消息",
"maintenancePageTimeTitle": "预计完成时间(可选)", "maintenancePageTimeTitle": "预计完成时间(可选)",
"privateMaintenanceScreenTitle": "私有占位符界面",
"privateMaintenanceScreenMessage": "此域名正在私有资源上使用。请连接 Pangolin 客户端以访问此资源。",
"privateMaintenanceScreenSteps": "连接后如果您仍然看到此消息说明您的浏览器的DNS缓存可能仍指向旧地址。解决方法完全关闭并重新打开此标签页或浏览器然后返回此页面。",
"maintenanceTime": "例如2小时11月1日下午5:00", "maintenanceTime": "例如2小时11月1日下午5:00",
"maintenanceEstimatedTimeDescription": "您期望维护完成的时间", "maintenanceEstimatedTimeDescription": "您期望维护完成的时间",
"editDomain": "编辑域名", "editDomain": "编辑域名",
@@ -2843,6 +3072,14 @@
"httpDestAddTitle": "添加 HTTP 目标", "httpDestAddTitle": "添加 HTTP 目标",
"httpDestEditDescription": "更新此 HTTP 事件流媒体目的地的配置。", "httpDestEditDescription": "更新此 HTTP 事件流媒体目的地的配置。",
"httpDestAddDescription": "配置新的 HTTP 端点来接收您的组织事件。", "httpDestAddDescription": "配置新的 HTTP 端点来接收您的组织事件。",
"S3DestEditTitle": "编辑目的地",
"S3DestAddTitle": "添加 S3 目的地",
"S3DestEditDescription": "更新此 S3 事件流目的地的配置。",
"S3DestAddDescription": "配置新的 S3 终端以接收您的组织事件。",
"datadogDestEditTitle": "编辑目的地",
"datadogDestAddTitle": "添加 Datadog 目的地",
"datadogDestEditDescription": "更新此 Datadog 事件流目的地的配置。",
"datadogDestAddDescription": "配置新的 Datadog 终端以接收您的组织事件。",
"httpDestTabSettings": "设置", "httpDestTabSettings": "设置",
"httpDestTabHeaders": "信头", "httpDestTabHeaders": "信头",
"httpDestTabBody": "正文内容", "httpDestTabBody": "正文内容",
@@ -2901,6 +3138,18 @@
"httpDestCreatedSuccess": "目标创建成功", "httpDestCreatedSuccess": "目标创建成功",
"httpDestUpdateFailed": "更新目标失败", "httpDestUpdateFailed": "更新目标失败",
"httpDestCreateFailed": "创建目标失败", "httpDestCreateFailed": "创建目标失败",
"followRedirects": "遵循重定向",
"followRedirectsDescription": "自动跟踪请求的 HTTP 重定向。",
"alertingErrorWebhookUrl": "请输入有效的 Webhook URL。",
"healthCheckStrategyHttp": "验证连接并检查 HTTP 响应状态。",
"healthCheckStrategyTcp": "只验证 TCP 连接性,不检查响应。",
"healthCheckStrategySnmp": "进行 SNMP get 请求以检查网络设备和基础架构的健康状况。",
"healthCheckStrategyIcmp": "使用 ICMP 回显请求ping检查资源是否可达并响应。",
"healthCheckTabStrategy": "策略",
"healthCheckTabConnection": "连接",
"healthCheckTabAdvanced": "高级",
"healthCheckStrategyNotAvailable": "此策略不可用。请联系销售以启用此功能。",
"uptime30d": "正常运行时间30天",
"idpAddActionCreateNew": "创建新的身份提供者", "idpAddActionCreateNew": "创建新的身份提供者",
"idpAddActionImportFromOrg": "从另一个组织导入", "idpAddActionImportFromOrg": "从另一个组织导入",
"idpImportDialogTitle": "导入身份提供者", "idpImportDialogTitle": "导入身份提供者",
@@ -2917,5 +3166,43 @@
"idpUnassociateWarning": "此操作无法对该组织撤销。", "idpUnassociateWarning": "此操作无法对该组织撤销。",
"idpUnassociatedDescription": "身份提供者已成功从该组织中取消关联", "idpUnassociatedDescription": "身份提供者已成功从该组织中取消关联",
"idpUnassociateMenu": "取消关联", "idpUnassociateMenu": "取消关联",
"idpDeleteAllOrgsMenu": "删除" "idpDeleteAllOrgsMenu": "删除",
"publicIpEndpoint": "终端",
"lastTriggeredAt": "最后触发",
"reject": "拒绝",
"uptimeDaysAgo": "{count} 天前",
"uptimeToday": "今天",
"uptimeNoDataAvailable": "暂无数据",
"uptimeSuffix": "正常运行时间",
"uptimeDowntimeSuffix": "停机时间",
"uptimeTooltipUptimeLabel": "正常运行",
"uptimeTooltipDowntimeLabel": "停机",
"uptimeOngoing": "正在进行",
"uptimeNoMonitoringData": "无监控数据",
"uptimeNoData": "无数据",
"uptimeMiniBarDown": "停机",
"uptimeSectionTitle": "正常运行时间",
"uptimeSectionDescription": "过去 {days} 天的可用性",
"uptimeAddAlert": "添加警报",
"uptimeViewAlerts": "查看警报",
"uptimeCreateEmailAlert": "创建电子邮件警报",
"uptimeAlertDescriptionSite": "当此站点下线或恢复上线时,将通过电子邮件通知您。",
"uptimeAlertDescriptionResource": "当此资源下线或恢复上线时,将通过电子邮件通知您。",
"uptimeAlertNamePlaceholder": "警报名称",
"uptimeAdditionalEmails": "附加电子邮件",
"uptimeCreateAlert": "创建警报",
"uptimeAlertNoRecipients": "无收件人",
"uptimeAlertNoRecipientsDescription": "请至少添加一个用户、角色或电子邮件进行通知。",
"uptimeAlertCreated": "警报已创建",
"uptimeAlertCreatedDescription": "状态变化时将通知您。",
"uptimeAlertCreateFailed": "创建警报失败",
"webhookUrlLabel": "URL",
"webhookHeaderKeyPlaceholder": "关键字",
"webhookHeaderValuePlaceholder": "值",
"alertLabel": "警报",
"domainPickerWildcardSubdomainNotAllowed": "不允许使用通配符子域。",
"domainPickerWildcardCertWarning": "通配符资源可能需要额外配置才能正常工作。",
"domainPickerWildcardCertWarningLink": "了解更多",
"health": "健康",
"domainPendingErrorTitle": "验证问题"
} }

201
package-lock.json generated
View File

@@ -44,9 +44,8 @@
"@tailwindcss/forms": "0.5.11", "@tailwindcss/forms": "0.5.11",
"@tanstack/react-query": "5.90.21", "@tanstack/react-query": "5.90.21",
"@tanstack/react-table": "8.21.3", "@tanstack/react-table": "8.21.3",
"@xyflow/react": "^12.8.4",
"arctic": "3.7.0", "arctic": "3.7.0",
"axios": "1.13.5", "axios": "1.15.0",
"better-sqlite3": "11.9.1", "better-sqlite3": "11.9.1",
"canvas-confetti": "1.9.4", "canvas-confetti": "1.9.4",
"class-variance-authority": "0.7.1", "class-variance-authority": "0.7.1",
@@ -56,7 +55,7 @@
"cors": "2.8.6", "cors": "2.8.6",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"d3": "7.9.0", "d3": "7.9.0",
"drizzle-orm": "0.45.1", "drizzle-orm": "0.45.2",
"express": "5.2.1", "express": "5.2.1",
"express-rate-limit": "8.3.0", "express-rate-limit": "8.3.0",
"glob": "13.0.6", "glob": "13.0.6",
@@ -70,12 +69,12 @@
"lucide-react": "0.577.0", "lucide-react": "0.577.0",
"maxmind": "5.0.5", "maxmind": "5.0.5",
"moment": "2.30.1", "moment": "2.30.1",
"next": "15.5.14", "next": "15.5.15",
"next-intl": "4.8.3", "next-intl": "4.8.3",
"next-themes": "0.4.6", "next-themes": "0.4.6",
"nextjs-toploader": "3.9.17", "nextjs-toploader": "3.9.17",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "8.0.4", "nodemailer": "8.0.5",
"oslo": "1.2.1", "oslo": "1.2.1",
"pg": "8.20.0", "pg": "8.20.0",
"posthog-node": "5.28.0", "posthog-node": "5.28.0",
@@ -2886,9 +2885,9 @@
} }
}, },
"node_modules/@next/env": { "node_modules/@next/env": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.15.tgz",
"integrity": "sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA==", "integrity": "sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@next/eslint-plugin-next": { "node_modules/@next/eslint-plugin-next": {
@@ -2902,9 +2901,9 @@
} }
}, },
"node_modules/@next/swc-darwin-arm64": { "node_modules/@next/swc-darwin-arm64": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.15.tgz",
"integrity": "sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg==", "integrity": "sha512-6PvFO2Tzt10GFK2Ro9tAVEtacMqRmTarYMFKAnV2vYMdwWc73xzmDQyAV7SwEdMhzmiRoo7+m88DuiXlJlGeaw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2918,9 +2917,9 @@
} }
}, },
"node_modules/@next/swc-darwin-x64": { "node_modules/@next/swc-darwin-x64": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.15.tgz",
"integrity": "sha512-aNnkSMjSFRTOmkd7qoNI2/rETQm/vKD6c/Ac9BZGa9CtoOzy3c2njgz7LvebQJ8iPxdeTuGnAjagyis8a9ifBw==", "integrity": "sha512-G+YNV+z6FDZTp/+IdGyIMFqalBTaQSnvAA+X/hrt+eaTRFSznRMz9K7rTmzvM6tDmKegNtyzgufZW0HwVzEqaQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2934,9 +2933,9 @@
} }
}, },
"node_modules/@next/swc-linux-arm64-gnu": { "node_modules/@next/swc-linux-arm64-gnu": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.15.tgz",
"integrity": "sha512-tjlpia+yStPRS//6sdmlVwuO1Rioern4u2onafa5n+h2hCS9MAvMXqpVbSrjgiEOoCs0nJy7oPOmWgtRRNSM5Q==", "integrity": "sha512-eVkrMcVIBqGfXB+QUC7jjZ94Z6uX/dNStbQFabewAnk13Uy18Igd1YZ/GtPRzdhtm7QwC0e6o7zOQecul4iC1w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2950,9 +2949,9 @@
} }
}, },
"node_modules/@next/swc-linux-arm64-musl": { "node_modules/@next/swc-linux-arm64-musl": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.15.tgz",
"integrity": "sha512-8B8cngBaLadl5lbDRdxGCP1Lef8ipD6KlxS3v0ElDAGil6lafrAM3B258p1KJOglInCVFUjk751IXMr2ixeQOQ==", "integrity": "sha512-RwSHKMQ7InLy5GfkY2/n5PcFycKA08qI1VST78n09nN36nUPqCvGSMiLXlfUmzmpQpF6XeBYP2KRWHi0UW3uNg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2966,9 +2965,9 @@
} }
}, },
"node_modules/@next/swc-linux-x64-gnu": { "node_modules/@next/swc-linux-x64-gnu": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.15.tgz",
"integrity": "sha512-bAS6tIAg8u4Gn3Nz7fCPpSoKAexEt2d5vn1mzokcqdqyov6ZJ6gu6GdF9l8ORFrBuRHgv3go/RfzYz5BkZ6YSQ==", "integrity": "sha512-nplqvY86LakS+eeiuWsNWvfmK8pFcOEW7ZtVRt4QH70lL+0x6LG/m1OpJ/tvrbwjmR8HH9/fH2jzW1GlL03TIg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2982,9 +2981,9 @@
} }
}, },
"node_modules/@next/swc-linux-x64-musl": { "node_modules/@next/swc-linux-x64-musl": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.15.tgz",
"integrity": "sha512-mMxv/FcrT7Gfaq4tsR22l17oKWXZmH/lVqcvjX0kfp5I0lKodHYLICKPoX1KRnnE+ci6oIUdriUhuA3rBCDiSw==", "integrity": "sha512-eAgl9NKQ84/sww0v81DQINl/vL2IBxD7sMybd0cWRw6wqgouVI53brVRBrggqBRP/NWeIAE1dm5cbKYoiMlqDQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2998,9 +2997,9 @@
} }
}, },
"node_modules/@next/swc-win32-arm64-msvc": { "node_modules/@next/swc-win32-arm64-msvc": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.15.tgz",
"integrity": "sha512-OTmiBlYThppnvnsqx0rBqjDRemlmIeZ8/o4zI7veaXoeO1PVHoyj2lfTfXTiiGjCyRDhA10y4h6ZvZvBiynr2g==", "integrity": "sha512-GJVZC86lzSquh0MtvZT+L7G8+jMnJcldloOjA8Kf3wXvBrvb6OGe2MzPuALxFshSm/IpwUtD2mIoof39ymf52A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -3014,9 +3013,9 @@
} }
}, },
"node_modules/@next/swc-win32-x64-msvc": { "node_modules/@next/swc-win32-x64-msvc": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.14.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.15.tgz",
"integrity": "sha512-+W7eFf3RS7m4G6tppVTOSyP9Y6FsJXfOuKzav1qKniiFm3KFByQfPEcouHdjlZmysl4zJGuGLQ/M9XyVeyeNEg==", "integrity": "sha512-nFucjVdwlFqxh/JG3hWSJ4p8+YJV7Ii8aPDuBQULB6DzUF4UNZETXLfEUk+oI2zEznWWULPt7MeuTE6xtK1HSA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -8719,6 +8718,7 @@
"version": "3.0.7", "version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/d3-selection": "*" "@types/d3-selection": "*"
@@ -8834,6 +8834,7 @@
"version": "3.0.11", "version": "3.0.11",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/d3-shape": { "node_modules/@types/d3-shape": {
@@ -8868,6 +8869,7 @@
"version": "3.0.9", "version": "3.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/d3-selection": "*" "@types/d3-selection": "*"
@@ -8877,6 +8879,7 @@
"version": "3.0.8", "version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/d3-interpolate": "*", "@types/d3-interpolate": "*",
@@ -9680,38 +9683,6 @@
"win32" "win32"
] ]
}, },
"node_modules/@xyflow/react": {
"version": "12.8.4",
"resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.8.4.tgz",
"integrity": "sha512-bqUu4T5QSHiCFPkoH+b+LROKwQJdLvcjhGbNW9c1dLafCBRjmH1IYz0zPE+lRDXCtQ9kRyFxz3tG19+8VORJ1w==",
"license": "MIT",
"dependencies": {
"@xyflow/system": "0.0.68",
"classcat": "^5.0.3",
"zustand": "^4.4.0"
},
"peerDependencies": {
"react": ">=17",
"react-dom": ">=17"
}
},
"node_modules/@xyflow/system": {
"version": "0.0.68",
"resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.68.tgz",
"integrity": "sha512-QDG2wxIG4qX+uF8yzm1ULVZrcXX3MxPBoxv7O52FWsX87qIImOqifUhfa/TwsvLdzn7ic2DDBH1uI8TKbdNTYA==",
"license": "MIT",
"dependencies": {
"@types/d3-drag": "^3.0.7",
"@types/d3-interpolate": "^3.0.4",
"@types/d3-selection": "^3.0.10",
"@types/d3-transition": "^3.0.8",
"@types/d3-zoom": "^3.0.8",
"d3-drag": "^3.0.0",
"d3-interpolate": "^3.0.1",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
@@ -10155,13 +10126,14 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.13.5", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.11", "follow-redirects": "^1.15.11",
"form-data": "^4.0.5", "form-data": "^4.0.5",
"proxy-from-env": "^1.1.0" "proxy-from-env": "^2.1.0"
} }
}, },
"node_modules/axobject-query": { "node_modules/axobject-query": {
@@ -10564,12 +10536,6 @@
"url": "https://polar.sh/cva" "url": "https://polar.sh/cva"
} }
}, },
"node_modules/classcat": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
"license": "MIT"
},
"node_modules/cli-spinners": { "node_modules/cli-spinners": {
"version": "2.9.2", "version": "2.9.2",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
@@ -11802,9 +11768,9 @@
} }
}, },
"node_modules/drizzle-orm": { "node_modules/drizzle-orm": {
"version": "0.45.1", "version": "0.45.2",
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.1.tgz", "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.2.tgz",
"integrity": "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==", "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peerDependencies": { "peerDependencies": {
"@aws-sdk/client-rds-data": ">=3", "@aws-sdk/client-rds-data": ">=3",
@@ -13326,9 +13292,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.15.11", "version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@@ -15034,9 +15000,9 @@
} }
}, },
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.23", "version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.defaults": { "node_modules/lodash.defaults": {
@@ -15498,13 +15464,13 @@
} }
}, },
"node_modules/next": { "node_modules/next": {
"version": "15.5.14", "version": "15.5.15",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz", "resolved": "https://registry.npmjs.org/next/-/next-15.5.15.tgz",
"integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==", "integrity": "sha512-VSqCrJwtLVGwAVE0Sb/yikrQfkwkZW9p+lL/J4+xe+G3ZA+QnWPqgcfH1tDUEuk9y+pthzzVFp4L/U8JerMfMQ==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"@next/env": "15.5.14", "@next/env": "15.5.15",
"@swc/helpers": "0.5.15", "@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579", "caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31", "postcss": "8.4.31",
@@ -15517,14 +15483,14 @@
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0" "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@next/swc-darwin-arm64": "15.5.14", "@next/swc-darwin-arm64": "15.5.15",
"@next/swc-darwin-x64": "15.5.14", "@next/swc-darwin-x64": "15.5.15",
"@next/swc-linux-arm64-gnu": "15.5.14", "@next/swc-linux-arm64-gnu": "15.5.15",
"@next/swc-linux-arm64-musl": "15.5.14", "@next/swc-linux-arm64-musl": "15.5.15",
"@next/swc-linux-x64-gnu": "15.5.14", "@next/swc-linux-x64-gnu": "15.5.15",
"@next/swc-linux-x64-musl": "15.5.14", "@next/swc-linux-x64-musl": "15.5.15",
"@next/swc-win32-arm64-msvc": "15.5.14", "@next/swc-win32-arm64-msvc": "15.5.15",
"@next/swc-win32-x64-msvc": "15.5.14", "@next/swc-win32-x64-msvc": "15.5.15",
"sharp": "^0.34.3" "sharp": "^0.34.3"
}, },
"peerDependencies": { "peerDependencies": {
@@ -15720,9 +15686,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "8.0.4", "version": "8.0.5",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.4.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.5.tgz",
"integrity": "sha512-k+jf6N8PfQJ0Fe8ZhJlgqU5qJU44Lpvp2yvidH3vp1lPnVQMgi4yEEMPXg5eJS1gFIJTVq1NHBk7Ia9ARdSBdQ==", "integrity": "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==",
"license": "MIT-0", "license": "MIT-0",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
@@ -16800,10 +16766,13 @@
} }
}, },
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT" "license": "MIT",
"engines": {
"node": ">=10"
}
}, },
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.3", "version": "3.0.3",
@@ -19881,34 +19850,6 @@
"peerDependencies": { "peerDependencies": {
"zod": "^3.25.0 || ^4.0.0" "zod": "^3.25.0 || ^4.0.0"
} }
},
"node_modules/zustand": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
"license": "MIT",
"dependencies": {
"use-sync-external-store": "^1.2.2"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
} }
} }
} }

View File

@@ -67,9 +67,8 @@
"@tailwindcss/forms": "0.5.11", "@tailwindcss/forms": "0.5.11",
"@tanstack/react-query": "5.90.21", "@tanstack/react-query": "5.90.21",
"@tanstack/react-table": "8.21.3", "@tanstack/react-table": "8.21.3",
"@xyflow/react": "^12.8.4",
"arctic": "3.7.0", "arctic": "3.7.0",
"axios": "1.13.5", "axios": "1.15.0",
"better-sqlite3": "11.9.1", "better-sqlite3": "11.9.1",
"canvas-confetti": "1.9.4", "canvas-confetti": "1.9.4",
"class-variance-authority": "0.7.1", "class-variance-authority": "0.7.1",
@@ -79,7 +78,7 @@
"cors": "2.8.6", "cors": "2.8.6",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"d3": "7.9.0", "d3": "7.9.0",
"drizzle-orm": "0.45.1", "drizzle-orm": "0.45.2",
"express": "5.2.1", "express": "5.2.1",
"express-rate-limit": "8.3.0", "express-rate-limit": "8.3.0",
"glob": "13.0.6", "glob": "13.0.6",
@@ -93,12 +92,12 @@
"lucide-react": "0.577.0", "lucide-react": "0.577.0",
"maxmind": "5.0.5", "maxmind": "5.0.5",
"moment": "2.30.1", "moment": "2.30.1",
"next": "15.5.14", "next": "15.5.15",
"next-intl": "4.8.3", "next-intl": "4.8.3",
"next-themes": "0.4.6", "next-themes": "0.4.6",
"nextjs-toploader": "3.9.17", "nextjs-toploader": "3.9.17",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "8.0.4", "nodemailer": "8.0.5",
"oslo": "1.2.1", "oslo": "1.2.1",
"pg": "8.20.0", "pg": "8.20.0",
"posthog-node": "5.28.0", "posthog-node": "5.28.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 KiB

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 KiB

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 KiB

After

Width:  |  Height:  |  Size: 621 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 516 KiB

View File

@@ -121,7 +121,7 @@ export function createApiServer() {
const httpServer = apiServer.listen(externalPort, (err?: any) => { const httpServer = apiServer.listen(externalPort, (err?: any) => {
if (err) throw err; if (err) throw err;
logger.info( logger.info(
`API server is running on http://localhost:${externalPort}` `Dashboard API server is running on http://localhost:${externalPort}`
); );
}); });

View File

@@ -122,7 +122,6 @@ export enum ActionsEnum {
createOrgDomain = "createOrgDomain", createOrgDomain = "createOrgDomain",
deleteOrgDomain = "deleteOrgDomain", deleteOrgDomain = "deleteOrgDomain",
restartOrgDomain = "restartOrgDomain", restartOrgDomain = "restartOrgDomain",
sendUsageNotification = "sendUsageNotification",
createRemoteExitNode = "createRemoteExitNode", createRemoteExitNode = "createRemoteExitNode",
updateRemoteExitNode = "updateRemoteExitNode", updateRemoteExitNode = "updateRemoteExitNode",
getRemoteExitNode = "getRemoteExitNode", getRemoteExitNode = "getRemoteExitNode",
@@ -153,10 +152,7 @@ export enum ActionsEnum {
createHealthCheck = "createHealthCheck", createHealthCheck = "createHealthCheck",
updateHealthCheck = "updateHealthCheck", updateHealthCheck = "updateHealthCheck",
deleteHealthCheck = "deleteHealthCheck", deleteHealthCheck = "deleteHealthCheck",
listHealthChecks = "listHealthChecks", listHealthChecks = "listHealthChecks"
triggerSiteAlert = "triggerSiteAlert",
triggerResourceAlert = "triggerResourceAlert",
triggerHealthCheckAlert = "triggerHealthCheckAlert"
} }
export async function checkUserActionPermission( export async function checkUserActionPermission(

View File

@@ -1,94 +1,53 @@
{ {
"PowerMac4,4": "eMac",
"PowerMac6,4": "eMac",
"PowerBook2,1": "iBook",
"PowerBook2,2": "iBook",
"PowerBook4,1": "iBook",
"PowerBook4,2": "iBook",
"PowerBook4,3": "iBook",
"PowerBook6,3": "iBook",
"PowerBook6,5": "iBook",
"PowerBook6,7": "iBook",
"iMac,1": "iMac",
"PowerMac2,1": "iMac",
"PowerMac2,2": "iMac",
"PowerMac4,1": "iMac",
"PowerMac4,2": "iMac",
"PowerMac4,5": "iMac",
"PowerMac6,1": "iMac",
"PowerMac6,3*": "iMac",
"PowerMac6,3": "iMac",
"PowerMac8,1": "iMac",
"PowerMac8,2": "iMac",
"PowerMac12,1": "iMac",
"iMac4,1": "iMac",
"iMac4,2": "iMac",
"iMac5,2": "iMac",
"iMac5,1": "iMac",
"iMac6,1": "iMac",
"iMac7,1": "iMac",
"iMac8,1": "iMac",
"iMac9,1": "iMac",
"iMac10,1": "iMac",
"iMac11,1": "iMac",
"iMac11,2": "iMac",
"iMac11,3": "iMac",
"iMac12,1": "iMac",
"iMac12,2": "iMac",
"iMac13,1": "iMac",
"iMac13,2": "iMac",
"iMac14,1": "iMac",
"iMac14,3": "iMac",
"iMac14,2": "iMac",
"iMac14,4": "iMac",
"iMac15,1": "iMac",
"iMac16,1": "iMac",
"iMac16,2": "iMac",
"iMac17,1": "iMac",
"iMac18,1": "iMac",
"iMac18,2": "iMac",
"iMac18,3": "iMac",
"iMac19,2": "iMac",
"iMac19,1": "iMac",
"iMac20,1": "iMac",
"iMac20,2": "iMac",
"iMac21,2": "iMac",
"iMac21,1": "iMac",
"iMacPro1,1": "iMac Pro",
"PowerMac10,1": "Mac mini",
"PowerMac10,2": "Mac mini",
"Macmini1,1": "Mac mini",
"Macmini2,1": "Mac mini",
"Macmini3,1": "Mac mini",
"Macmini4,1": "Mac mini",
"Macmini5,1": "Mac mini",
"Macmini5,2": "Mac mini",
"Macmini5,3": "Mac mini",
"Macmini6,1": "Mac mini",
"Macmini6,2": "Mac mini",
"Macmini7,1": "Mac mini",
"Macmini8,1": "Mac mini",
"ADP3,2": "Mac mini", "ADP3,2": "Mac mini",
"Macmini9,1": "Mac mini",
"Mac14,3": "Mac mini",
"Mac14,12": "Mac mini",
"MacPro1,1*": "Mac Pro",
"MacPro2,1": "Mac Pro",
"MacPro3,1": "Mac Pro",
"MacPro4,1": "Mac Pro",
"MacPro5,1": "Mac Pro",
"MacPro6,1": "Mac Pro",
"MacPro7,1": "Mac Pro",
"N/A*": "Power Macintosh",
"PowerMac1,1": "Power Macintosh",
"PowerMac3,1": "Power Macintosh",
"PowerMac3,3": "Power Macintosh",
"PowerMac3,4": "Power Macintosh",
"PowerMac3,5": "Power Macintosh",
"PowerMac3,6": "Power Macintosh",
"Mac13,1": "Mac Studio", "Mac13,1": "Mac Studio",
"Mac13,2": "Mac Studio", "Mac13,2": "Mac Studio",
"Mac14,10": "MacBook Pro",
"Mac14,12": "Mac mini",
"Mac14,13": "Mac Studio",
"Mac14,14": "Mac Studio",
"Mac14,15": "MacBook Air",
"Mac14,2": "MacBook Air",
"Mac14,3": "Mac mini",
"Mac14,5": "MacBook Pro",
"Mac14,6": "MacBook Pro",
"Mac14,7": "MacBook Pro",
"Mac14,8": "Mac Pro",
"Mac14,9": "MacBook Pro",
"Mac15,10": "MacBook Pro",
"Mac15,11": "MacBook Pro",
"Mac15,12": "MacBook Air",
"Mac15,13": "MacBook Air",
"Mac15,14": "Mac Studio",
"Mac15,3": "MacBook Pro",
"Mac15,4": "iMac",
"Mac15,5": "iMac",
"Mac15,6": "MacBook Pro",
"Mac15,7": "MacBook Pro",
"Mac15,8": "MacBook Pro",
"Mac15,9": "MacBook Pro",
"Mac16,1": "MacBook Pro",
"Mac16,10": "Mac mini",
"Mac16,11": "Mac mini",
"Mac16,12": "MacBook Air",
"Mac16,13": "MacBook Air",
"Mac16,2": "iMac",
"Mac16,3": "iMac",
"Mac16,5": "MacBook Pro",
"Mac16,6": "MacBook Pro",
"Mac16,7": "MacBook Pro",
"Mac16,8": "MacBook Pro",
"Mac16,9": "Mac Studio",
"Mac17,2": "MacBook Pro",
"Mac17,3": "MacBook Air",
"Mac17,4": "MacBook Air",
"Mac17,5": "MacBook Neo",
"Mac17,6": "MacBook Pro",
"Mac17,7": "MacBook Pro",
"Mac17,8": "MacBook Pro",
"Mac17,9": "MacBook Pro",
"MacBook1,1": "MacBook", "MacBook1,1": "MacBook",
"MacBook10,1": "MacBook",
"MacBook2,1": "MacBook", "MacBook2,1": "MacBook",
"MacBook3,1": "MacBook", "MacBook3,1": "MacBook",
"MacBook4,1": "MacBook", "MacBook4,1": "MacBook",
@@ -98,8 +57,8 @@
"MacBook7,1": "MacBook", "MacBook7,1": "MacBook",
"MacBook8,1": "MacBook", "MacBook8,1": "MacBook",
"MacBook9,1": "MacBook", "MacBook9,1": "MacBook",
"MacBook10,1": "MacBook",
"MacBookAir1,1": "MacBook Air", "MacBookAir1,1": "MacBook Air",
"MacBookAir10,1": "MacBook Air",
"MacBookAir2,1": "MacBook Air", "MacBookAir2,1": "MacBook Air",
"MacBookAir3,1": "MacBook Air", "MacBookAir3,1": "MacBook Air",
"MacBookAir3,2": "MacBook Air", "MacBookAir3,2": "MacBook Air",
@@ -114,88 +73,163 @@
"MacBookAir8,1": "MacBook Air", "MacBookAir8,1": "MacBook Air",
"MacBookAir8,2": "MacBook Air", "MacBookAir8,2": "MacBook Air",
"MacBookAir9,1": "MacBook Air", "MacBookAir9,1": "MacBook Air",
"MacBookAir10,1": "MacBook Air",
"Mac14,2": "MacBook Air",
"MacBookPro1,1": "MacBook Pro", "MacBookPro1,1": "MacBook Pro",
"MacBookPro1,2": "MacBook Pro", "MacBookPro1,2": "MacBook Pro",
"MacBookPro2,2": "MacBook Pro",
"MacBookPro2,1": "MacBook Pro",
"MacBookPro3,1": "MacBook Pro",
"MacBookPro4,1": "MacBook Pro",
"MacBookPro5,1": "MacBook Pro",
"MacBookPro5,2": "MacBook Pro",
"MacBookPro5,5": "MacBook Pro",
"MacBookPro5,4": "MacBook Pro",
"MacBookPro5,3": "MacBook Pro",
"MacBookPro7,1": "MacBook Pro",
"MacBookPro6,2": "MacBook Pro",
"MacBookPro6,1": "MacBook Pro",
"MacBookPro8,1": "MacBook Pro",
"MacBookPro8,2": "MacBook Pro",
"MacBookPro8,3": "MacBook Pro",
"MacBookPro9,2": "MacBook Pro",
"MacBookPro9,1": "MacBook Pro",
"MacBookPro10,1": "MacBook Pro", "MacBookPro10,1": "MacBook Pro",
"MacBookPro10,2": "MacBook Pro", "MacBookPro10,2": "MacBook Pro",
"MacBookPro11,1": "MacBook Pro", "MacBookPro11,1": "MacBook Pro",
"MacBookPro11,2": "MacBook Pro", "MacBookPro11,2": "MacBook Pro",
"MacBookPro11,3": "MacBook Pro", "MacBookPro11,3": "MacBook Pro",
"MacBookPro12,1": "MacBook Pro",
"MacBookPro11,4": "MacBook Pro", "MacBookPro11,4": "MacBook Pro",
"MacBookPro11,5": "MacBook Pro", "MacBookPro11,5": "MacBook Pro",
"MacBookPro12,1": "MacBook Pro",
"MacBookPro13,1": "MacBook Pro", "MacBookPro13,1": "MacBook Pro",
"MacBookPro13,2": "MacBook Pro", "MacBookPro13,2": "MacBook Pro",
"MacBookPro13,3": "MacBook Pro", "MacBookPro13,3": "MacBook Pro",
"MacBookPro14,1": "MacBook Pro", "MacBookPro14,1": "MacBook Pro",
"MacBookPro14,2": "MacBook Pro", "MacBookPro14,2": "MacBook Pro",
"MacBookPro14,3": "MacBook Pro", "MacBookPro14,3": "MacBook Pro",
"MacBookPro15,2": "MacBook Pro",
"MacBookPro15,1": "MacBook Pro", "MacBookPro15,1": "MacBook Pro",
"MacBookPro15,2": "MacBook Pro",
"MacBookPro15,3": "MacBook Pro", "MacBookPro15,3": "MacBook Pro",
"MacBookPro15,4": "MacBook Pro", "MacBookPro15,4": "MacBook Pro",
"MacBookPro16,1": "MacBook Pro", "MacBookPro16,1": "MacBook Pro",
"MacBookPro16,3": "MacBook Pro",
"MacBookPro16,2": "MacBook Pro", "MacBookPro16,2": "MacBook Pro",
"MacBookPro16,3": "MacBook Pro",
"MacBookPro16,4": "MacBook Pro", "MacBookPro16,4": "MacBook Pro",
"MacBookPro17,1": "MacBook Pro", "MacBookPro17,1": "MacBook Pro",
"MacBookPro18,3": "MacBook Pro",
"MacBookPro18,4": "MacBook Pro",
"MacBookPro18,1": "MacBook Pro", "MacBookPro18,1": "MacBook Pro",
"MacBookPro18,2": "MacBook Pro", "MacBookPro18,2": "MacBook Pro",
"Mac14,7": "MacBook Pro", "MacBookPro18,3": "MacBook Pro",
"Mac14,9": "MacBook Pro", "MacBookPro18,4": "MacBook Pro",
"Mac14,5": "MacBook Pro", "MacBookPro2,1": "MacBook Pro",
"Mac14,10": "MacBook Pro", "MacBookPro2,2": "MacBook Pro",
"Mac14,6": "MacBook Pro", "MacBookPro3,1": "MacBook Pro",
"PowerMac1,2": "Power Macintosh", "MacBookPro4,1": "MacBook Pro",
"PowerMac5,1": "Power Macintosh", "MacBookPro5,1": "MacBook Pro",
"PowerMac7,2": "Power Macintosh", "MacBookPro5,2": "MacBook Pro",
"PowerMac7,3": "Power Macintosh", "MacBookPro5,3": "MacBook Pro",
"PowerMac9,1": "Power Macintosh", "MacBookPro5,4": "MacBook Pro",
"PowerMac11,2": "Power Macintosh", "MacBookPro5,5": "MacBook Pro",
"MacBookPro6,1": "MacBook Pro",
"MacBookPro6,2": "MacBook Pro",
"MacBookPro7,1": "MacBook Pro",
"MacBookPro8,1": "MacBook Pro",
"MacBookPro8,2": "MacBook Pro",
"MacBookPro8,3": "MacBook Pro",
"MacBookPro9,1": "MacBook Pro",
"MacBookPro9,2": "MacBook Pro",
"MacPro1,1": "Mac Pro",
"MacPro2,1": "Mac Pro",
"MacPro3,1": "Mac Pro",
"MacPro4,1": "Mac Pro",
"MacPro5,1": "Mac Pro",
"MacPro6,1": "Mac Pro",
"MacPro7,1": "Mac Pro",
"Macmini1,1": "Mac mini",
"Macmini2,1": "Mac mini",
"Macmini3,1": "Mac mini",
"Macmini4,1": "Mac mini",
"Macmini5,1": "Mac mini",
"Macmini5,2": "Mac mini",
"Macmini5,3": "Mac mini",
"Macmini6,1": "Mac mini",
"Macmini6,2": "Mac mini",
"Macmini7,1": "Mac mini",
"Macmini8,1": "Mac mini",
"Macmini9,1": "Mac mini",
"PowerBook1,1": "PowerBook", "PowerBook1,1": "PowerBook",
"PowerBook2,1": "iBook",
"PowerBook2,2": "iBook",
"PowerBook3,1": "PowerBook", "PowerBook3,1": "PowerBook",
"PowerBook3,2": "PowerBook", "PowerBook3,2": "PowerBook",
"PowerBook3,3": "PowerBook", "PowerBook3,3": "PowerBook",
"PowerBook3,4": "PowerBook", "PowerBook3,4": "PowerBook",
"PowerBook3,5": "PowerBook", "PowerBook3,5": "PowerBook",
"PowerBook6,1": "PowerBook", "PowerBook4,1": "iBook",
"PowerBook4,2": "iBook",
"PowerBook4,3": "iBook",
"PowerBook5,1": "PowerBook", "PowerBook5,1": "PowerBook",
"PowerBook6,2": "PowerBook",
"PowerBook5,2": "PowerBook", "PowerBook5,2": "PowerBook",
"PowerBook5,3": "PowerBook", "PowerBook5,3": "PowerBook",
"PowerBook6,4": "PowerBook",
"PowerBook5,4": "PowerBook", "PowerBook5,4": "PowerBook",
"PowerBook5,5": "PowerBook", "PowerBook5,5": "PowerBook",
"PowerBook6,8": "PowerBook",
"PowerBook5,6": "PowerBook", "PowerBook5,6": "PowerBook",
"PowerBook5,7": "PowerBook", "PowerBook5,7": "PowerBook",
"PowerBook5,8": "PowerBook", "PowerBook5,8": "PowerBook",
"PowerBook5,9": "PowerBook", "PowerBook5,9": "PowerBook",
"PowerBook6,1": "PowerBook",
"PowerBook6,2": "PowerBook",
"PowerBook6,3": "iBook",
"PowerBook6,4": "PowerBook",
"PowerBook6,5": "iBook",
"PowerBook6,7": "iBook",
"PowerBook6,8": "PowerBook",
"PowerMac1,1": "Power Macintosh",
"PowerMac1,2": "Power Macintosh",
"PowerMac10,1": "Mac mini",
"PowerMac10,2": "Mac mini",
"PowerMac11,2": "Power Macintosh",
"PowerMac12,1": "iMac",
"PowerMac2,1": "iMac",
"PowerMac2,2": "iMac",
"PowerMac3,1": "Mac Server",
"PowerMac3,3": "Power Macintosh",
"PowerMac3,4": "Power Macintosh",
"PowerMac3,5": "Power Macintosh",
"PowerMac3,6": "Power Macintosh",
"PowerMac4,1": "iMac",
"PowerMac4,2": "iMac",
"PowerMac4,4": "eMac",
"PowerMac4,5": "iMac",
"PowerMac5,1": "Power Macintosh",
"PowerMac6,1": "iMac",
"PowerMac6,3": "iMac",
"PowerMac6,4": "eMac",
"PowerMac7,2": "Power Macintosh",
"PowerMac7,3": "Power Macintosh",
"PowerMac8,1": "iMac",
"PowerMac8,2": "iMac",
"PowerMac9,1": "Power Macintosh",
"RackMac1,1": "Xserve", "RackMac1,1": "Xserve",
"RackMac1,2": "Xserve", "RackMac1,2": "Xserve",
"RackMac3,1": "Xserve", "RackMac3,1": "Xserve",
"Xserve1,1": "Xserve", "Xserve1,1": "Xserve",
"Xserve2,1": "Xserve", "Xserve2,1": "Xserve",
"Xserve3,1": "Xserve" "Xserve3,1": "Xserve",
} "iMac,1": "iMac",
"iMac10,1": "iMac",
"iMac11,1": "iMac",
"iMac11,2": "iMac",
"iMac11,3": "iMac",
"iMac12,1": "iMac",
"iMac12,2": "iMac",
"iMac13,1": "iMac",
"iMac13,2": "iMac",
"iMac14,1": "iMac",
"iMac14,2": "iMac",
"iMac14,3": "iMac",
"iMac14,4": "iMac",
"iMac15,1": "iMac",
"iMac16,1": "iMac",
"iMac16,2": "iMac",
"iMac17,1": "iMac",
"iMac18,1": "iMac",
"iMac18,2": "iMac",
"iMac18,3": "iMac",
"iMac19,1": "iMac",
"iMac19,2": "iMac",
"iMac20,1": "iMac",
"iMac20,2": "iMac",
"iMac21,1": "iMac",
"iMac21,2": "iMac",
"iMac4,1": "iMac",
"iMac4,2": "iMac",
"iMac5,1": "iMac",
"iMac5,2": "iMac",
"iMac6,1": "iMac",
"iMac7,1": "iMac",
"iMac8,1": "iMac",
"iMac9,1": "iMac",
"iMacPro1,1": "iMac Pro"
}

View File

@@ -91,6 +91,8 @@ export const subscriptions = pgTable("subscriptions", {
updatedAt: bigint("updatedAt", { mode: "number" }), updatedAt: bigint("updatedAt", { mode: "number" }),
version: integer("version"), version: integer("version"),
billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" }), billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" }),
expiresAt: bigint("expiresAt", { mode: "number" }),
trial: boolean("trial").default(false),
type: varchar("type", { length: 50 }) // tier1, tier2, tier3, or license type: varchar("type", { length: 50 }) // tier1, tier2, tier3, or license
}); });
@@ -482,6 +484,7 @@ export const alertRules = pgTable("alertRules", {
| "health_check_toggle" | "health_check_toggle"
| "resource_healthy" | "resource_healthy"
| "resource_unhealthy" | "resource_unhealthy"
| "resource_degraded"
| "resource_toggle" | "resource_toggle"
>() >()
.notNull(), .notNull(),
@@ -563,6 +566,17 @@ export const alertWebhookActions = pgTable("alertWebhookActions", {
lastSentAt: bigint("lastSentAt", { mode: "number" }) // nullable lastSentAt: bigint("lastSentAt", { mode: "number" }) // nullable
}); });
export const trialNotifications = pgTable("trialNotifications", {
notificationId: serial("notificationId").primaryKey(),
subscriptionId: varchar("subscriptionId", { length: 255 })
.notNull()
.references(() => subscriptions.subscriptionId, {
onDelete: "cascade"
}),
notificationType: varchar("notificationType", { length: 50 }).notNull(), // trial_ending_5d, trial_ending_24h, trial_ended
sentAt: bigint("sentAt", { mode: "number" }).notNull()
});
export type Approval = InferSelectModel<typeof approvals>; export type Approval = InferSelectModel<typeof approvals>;
export type Limit = InferSelectModel<typeof limits>; export type Limit = InferSelectModel<typeof limits>;
export type Account = InferSelectModel<typeof account>; export type Account = InferSelectModel<typeof account>;
@@ -601,3 +615,12 @@ export type EventStreamingCursor = InferSelectModel<
typeof eventStreamingCursors typeof eventStreamingCursors
>; >;
export type AlertResources = InferSelectModel<typeof alertResources>; export type AlertResources = InferSelectModel<typeof alertResources>;
export type AlertHealthChecks = InferSelectModel<typeof alertHealthChecks>;
export type AlertSites = InferSelectModel<typeof alertSites>;
export type AlertRules = InferSelectModel<typeof alertRules>;
export type AlertEmailActions = InferSelectModel<typeof alertEmailActions>;
export type AlertEmailRecipients = InferSelectModel<
typeof alertEmailRecipients
>;
export type AlertWebhookActions = InferSelectModel<typeof alertWebhookActions>;
export type TrialNotification = InferSelectModel<typeof trialNotifications>;

View File

@@ -157,7 +157,9 @@ export const resources = pgTable("resources", {
maintenanceTitle: text("maintenanceTitle"), maintenanceTitle: text("maintenanceTitle"),
maintenanceMessage: text("maintenanceMessage"), maintenanceMessage: text("maintenanceMessage"),
maintenanceEstimatedTime: text("maintenanceEstimatedTime"), maintenanceEstimatedTime: text("maintenanceEstimatedTime"),
postAuthPath: text("postAuthPath") postAuthPath: text("postAuthPath"),
health: varchar("health").default("unknown"), // "healthy", "unhealthy", "unknown"
wildcard: boolean("wildcard").notNull().default(false)
}); });
export const targets = pgTable("targets", { export const targets = pgTable("targets", {
@@ -194,6 +196,9 @@ export const targetHealthCheck = pgTable("targetHealthCheck", {
onDelete: "cascade" onDelete: "cascade"
}) })
.notNull(), .notNull(),
siteId: integer("siteId").references(() => sites.siteId, {
onDelete: "cascade"
}).notNull(),
name: varchar("name"), name: varchar("name"),
hcEnabled: boolean("hcEnabled").notNull().default(false), hcEnabled: boolean("hcEnabled").notNull().default(false),
hcPath: varchar("hcPath"), hcPath: varchar("hcPath"),

View File

@@ -25,7 +25,7 @@ import {
ResourceHeaderAuthExtendedCompatibility, ResourceHeaderAuthExtendedCompatibility,
resourceHeaderAuthExtendedCompatibility resourceHeaderAuthExtendedCompatibility
} from "@server/db"; } from "@server/db";
import { and, eq, inArray } from "drizzle-orm"; import { and, eq, inArray, or, sql } from "drizzle-orm";
export type ResourceWithAuth = { export type ResourceWithAuth = {
resource: Resource | null; resource: Resource | null;
@@ -47,7 +47,17 @@ export type UserSessionWithUser = {
export async function getResourceByDomain( export async function getResourceByDomain(
domain: string domain: string
): Promise<ResourceWithAuth | null> { ): Promise<ResourceWithAuth | null> {
const [result] = await db // Build wildcard domain variants to match against.
// For a domain like "me.example.test.com", we want to match:
// - "*.example.test.com" (subdomain wildcard)
// - "*.test.com" (parent wildcard, i.e. just "*" subdomain on parent)
const parts = domain.split(".");
const wildcardCandidates: string[] = [];
for (let i = 1; i < parts.length; i++) {
wildcardCandidates.push(`*.${parts.slice(i).join(".")}`);
}
const potentialResults = await db
.select() .select()
.from(resources) .from(resources)
.leftJoin( .leftJoin(
@@ -70,8 +80,29 @@ export async function getResourceByDomain(
) )
) )
.innerJoin(orgs, eq(orgs.orgId, resources.orgId)) .innerJoin(orgs, eq(orgs.orgId, resources.orgId))
.where(eq(resources.fullDomain, domain)) .where(
.limit(1); or(
// Exact match
eq(resources.fullDomain, domain),
// Wildcard match: resource fullDomain is one of the wildcard candidates
wildcardCandidates.length > 0
? and(
eq(resources.wildcard, true),
inArray(resources.fullDomain, wildcardCandidates)
)
: sql`false`
)
);
if (!potentialResults.length) {
return null;
}
// Prefer exact match over wildcard match
const exactMatch = potentialResults.find(
(r) => r.resources?.fullDomain === domain
);
const result = exactMatch ?? potentialResults[0];
if (!result) { if (!result) {
return null; return null;

View File

@@ -21,6 +21,9 @@ import {
targetHealthCheck, targetHealthCheck,
users users
} from "./schema"; } from "./schema";
import { serial, varchar } from "drizzle-orm/mysql-core";
import { pgTable } from "drizzle-orm/pg-core";
import { bigint } from "zod";
export const certificates = sqliteTable("certificates", { export const certificates = sqliteTable("certificates", {
certId: integer("certId").primaryKey({ autoIncrement: true }), certId: integer("certId").primaryKey({ autoIncrement: true }),
@@ -85,6 +88,8 @@ export const subscriptions = sqliteTable("subscriptions", {
createdAt: integer("createdAt").notNull(), createdAt: integer("createdAt").notNull(),
updatedAt: integer("updatedAt"), updatedAt: integer("updatedAt"),
version: integer("version"), version: integer("version"),
expiresAt: integer("expiresAt"),
trial: integer("trial", { mode: "boolean" }).default(false),
billingCycleAnchor: integer("billingCycleAnchor"), billingCycleAnchor: integer("billingCycleAnchor"),
type: text("type") // tier1, tier2, tier3, or license type: text("type") // tier1, tier2, tier3, or license
}); });
@@ -423,10 +428,18 @@ export const eventStreamingDestinations = sqliteTable(
orgId: text("orgId") orgId: text("orgId")
.notNull() .notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }), .references(() => orgs.orgId, { onDelete: "cascade" }),
sendConnectionLogs: integer("sendConnectionLogs", { mode: "boolean" }).notNull().default(false), sendConnectionLogs: integer("sendConnectionLogs", { mode: "boolean" })
sendRequestLogs: integer("sendRequestLogs", { mode: "boolean" }).notNull().default(false), .notNull()
sendActionLogs: integer("sendActionLogs", { mode: "boolean" }).notNull().default(false), .default(false),
sendAccessLogs: integer("sendAccessLogs", { 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. type: text("type").notNull(), // e.g. "http", "kafka", etc.
config: text("config").notNull(), // JSON string with the configuration for the destination config: text("config").notNull(), // JSON string with the configuration for the destination
enabled: integer("enabled", { mode: "boolean" }) enabled: integer("enabled", { mode: "boolean" })
@@ -474,14 +487,19 @@ export const alertRules = sqliteTable("alertRules", {
| "health_check_toggle" | "health_check_toggle"
| "resource_healthy" | "resource_healthy"
| "resource_unhealthy" | "resource_unhealthy"
| "resource_degraded"
| "resource_toggle" | "resource_toggle"
>() >()
.notNull(), .notNull(),
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true), enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
cooldownSeconds: integer("cooldownSeconds").notNull().default(300), cooldownSeconds: integer("cooldownSeconds").notNull().default(300),
allSites: integer("allSites", { mode: "boolean" }).notNull().default(false), allSites: integer("allSites", { mode: "boolean" }).notNull().default(false),
allHealthChecks: integer("allHealthChecks", { mode: "boolean" }).notNull().default(false), allHealthChecks: integer("allHealthChecks", { mode: "boolean" })
allResources: integer("allResources", { mode: "boolean" }).notNull().default(false), .notNull()
.default(false),
allResources: integer("allResources", { mode: "boolean" })
.notNull()
.default(false),
lastTriggeredAt: integer("lastTriggeredAt"), lastTriggeredAt: integer("lastTriggeredAt"),
createdAt: integer("createdAt").notNull(), createdAt: integer("createdAt").notNull(),
updatedAt: integer("updatedAt").notNull() updatedAt: integer("updatedAt").notNull()
@@ -529,23 +547,44 @@ export const alertEmailRecipients = sqliteTable("alertEmailRecipients", {
recipientId: integer("recipientId").primaryKey({ autoIncrement: true }), recipientId: integer("recipientId").primaryKey({ autoIncrement: true }),
emailActionId: integer("emailActionId") emailActionId: integer("emailActionId")
.notNull() .notNull()
.references(() => alertEmailActions.emailActionId, { onDelete: "cascade" }), .references(() => alertEmailActions.emailActionId, {
userId: text("userId").references(() => users.userId, { onDelete: "cascade" }), onDelete: "cascade"
roleId: integer("roleId").references(() => roles.roleId, { onDelete: "cascade" }), }),
userId: text("userId").references(() => users.userId, {
onDelete: "cascade"
}),
roleId: integer("roleId").references(() => roles.roleId, {
onDelete: "cascade"
}),
email: text("email") email: text("email")
}); });
export const alertWebhookActions = sqliteTable("alertWebhookActions", { export const alertWebhookActions = sqliteTable("alertWebhookActions", {
webhookActionId: integer("webhookActionId").primaryKey({ autoIncrement: true }), webhookActionId: integer("webhookActionId").primaryKey({
autoIncrement: true
}),
alertRuleId: integer("alertRuleId") alertRuleId: integer("alertRuleId")
.notNull() .notNull()
.references(() => alertRules.alertRuleId, { onDelete: "cascade" }), .references(() => alertRules.alertRuleId, { onDelete: "cascade" }),
webhookUrl: text("webhookUrl").notNull(), webhookUrl: text("webhookUrl").notNull(),
config: text("config"), // encrypted JSON with auth config (authType, credentials) config: text("config"), // encrypted JSON with auth config (authType, credentials)
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true), enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
lastSentAt: integer("lastSentAt") lastSentAt: integer("lastSentAt")
}); });
export const trialNotifications = sqliteTable("trialNotifications", {
notificationId: integer("notificationId").primaryKey({
autoIncrement: true
}),
subscriptionId: text("subscriptionId")
.notNull()
.references(() => subscriptions.subscriptionId, {
onDelete: "cascade"
}),
notificationType: text("notificationType").notNull(), // trial_ending_5d, trial_ending_24h, trial_ended
sentAt: integer("sentAt").notNull()
});
export type Approval = InferSelectModel<typeof approvals>; export type Approval = InferSelectModel<typeof approvals>;
export type Limit = InferSelectModel<typeof limits>; export type Limit = InferSelectModel<typeof limits>;
export type Account = InferSelectModel<typeof account>; export type Account = InferSelectModel<typeof account>;
@@ -578,3 +617,10 @@ export type EventStreamingCursor = InferSelectModel<
typeof eventStreamingCursors typeof eventStreamingCursors
>; >;
export type AlertResources = InferSelectModel<typeof alertResources>; export type AlertResources = InferSelectModel<typeof alertResources>;
export type AlertHealthChecks = InferSelectModel<typeof alertHealthChecks>;
export type AlertSites = InferSelectModel<typeof alertSites>;
export type AlertRule = InferSelectModel<typeof alertRules>;
export type AlertEmailAction = InferSelectModel<typeof alertEmailActions>;
export type AlertEmailRecipient = InferSelectModel<typeof alertEmailRecipients>;
export type AlertWebhookAction = InferSelectModel<typeof alertWebhookActions>;
export type TrialNotification = InferSelectModel<typeof trialNotifications>;

View File

@@ -178,7 +178,9 @@ export const resources = sqliteTable("resources", {
maintenanceTitle: text("maintenanceTitle"), maintenanceTitle: text("maintenanceTitle"),
maintenanceMessage: text("maintenanceMessage"), maintenanceMessage: text("maintenanceMessage"),
maintenanceEstimatedTime: text("maintenanceEstimatedTime"), maintenanceEstimatedTime: text("maintenanceEstimatedTime"),
postAuthPath: text("postAuthPath") postAuthPath: text("postAuthPath"),
health: text("health").default("unknown"), // "healthy", "unhealthy", "unknown"
wildcard: integer("wildcard", { mode: "boolean" }).notNull().default(false)
}); });
export const targets = sqliteTable("targets", { export const targets = sqliteTable("targets", {
@@ -217,6 +219,9 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", {
onDelete: "cascade" onDelete: "cascade"
}) })
.notNull(), .notNull(),
siteId: integer("siteId").references(() => sites.siteId, {
onDelete: "cascade"
}).notNull(),
name: text("name"), name: text("name"),
hcEnabled: integer("hcEnabled", { mode: "boolean" }) hcEnabled: integer("hcEnabled", { mode: "boolean" })
.notNull() .notNull()

View File

@@ -8,9 +8,11 @@ import {
EmailHeading, EmailHeading,
EmailInfoSection, EmailInfoSection,
EmailLetterHead, EmailLetterHead,
EmailSection,
EmailSignature, EmailSignature,
EmailText EmailText
} from "./components/Email"; } from "./components/Email";
import ButtonLink from "./components/ButtonLink";
export type AlertEventType = export type AlertEventType =
| "site_online" | "site_online"
@@ -21,20 +23,22 @@ export type AlertEventType =
| "health_check_toggle" | "health_check_toggle"
| "resource_healthy" | "resource_healthy"
| "resource_unhealthy" | "resource_unhealthy"
| "resource_degraded"
| "resource_toggle"; | "resource_toggle";
interface Props { export type AlertNotificationProps = {
eventType: AlertEventType; eventType: AlertEventType;
orgId: string; orgId: string;
data: Record<string, unknown>; data: Record<string, unknown>;
} dashboardLink: string;
};
function getEventMeta(eventType: AlertEventType): { function getEventMeta(eventType: AlertEventType): {
heading: string; heading: string;
previewText: string; previewText: string;
summary: string; summary: string;
statusLabel: string; statusLabel: string | null;
statusColor: string; statusColor: string | null;
} { } {
switch (eventType) { switch (eventType) {
case "site_online": case "site_online":
@@ -51,7 +55,7 @@ function getEventMeta(eventType: AlertEventType): {
heading: "Site Offline", heading: "Site Offline",
previewText: "A site in your organization has gone offline.", previewText: "A site in your organization has gone offline.",
summary: summary:
"A site in your organization has gone offline and is no longer reachable. Please investigate as soon as possible.", "A site in your organization has gone offline and is no longer reachable.",
statusLabel: "Offline", statusLabel: "Offline",
statusColor: "#dc2626" statusColor: "#dc2626"
}; };
@@ -59,10 +63,9 @@ function getEventMeta(eventType: AlertEventType): {
return { return {
heading: "Site Status Changed", heading: "Site Status Changed",
previewText: "A site in your organization has changed status.", previewText: "A site in your organization has changed status.",
summary: summary: "A site in your organization has changed status.",
"A site in your organization has changed status. Please review the details below and take action if needed.", statusLabel: null,
statusLabel: "Status Changed", statusColor: null
statusColor: "#f59e0b"
}; };
case "health_check_healthy": case "health_check_healthy":
return { return {
@@ -80,7 +83,7 @@ function getEventMeta(eventType: AlertEventType): {
previewText: previewText:
"A health check in your organization is not healthy.", "A health check in your organization is not healthy.",
summary: summary:
"A health check in your organization is currently failing. Please review the details below and take action if needed.", "A health check in your organization is currently failing.",
statusLabel: "Not Healthy", statusLabel: "Not Healthy",
statusColor: "#dc2626" statusColor: "#dc2626"
}; };
@@ -90,9 +93,9 @@ function getEventMeta(eventType: AlertEventType): {
previewText: previewText:
"A health check in your organization has changed status.", "A health check in your organization has changed status.",
summary: summary:
"A health check in your organization has changed status. Please review the details below and take action if needed.", "A health check in your organization has changed status.",
statusLabel: "Status Changed", statusLabel: null,
statusColor: "#f59e0b" statusColor: null
}; };
case "resource_healthy": case "resource_healthy":
return { return {
@@ -108,37 +111,65 @@ function getEventMeta(eventType: AlertEventType): {
heading: "Resource Unhealthy", heading: "Resource Unhealthy",
previewText: "A resource in your organization is not healthy.", previewText: "A resource in your organization is not healthy.",
summary: summary:
"A resource in your organization is currently unhealthy. Please review the details below and take action if needed.", "A resource in your organization is currently unhealthy.",
statusLabel: "Unhealthy", statusLabel: "Unhealthy",
statusColor: "#dc2626" statusColor: "#dc2626"
}; };
case "resource_degraded":
return {
heading: "Resource Degraded",
previewText: "A resource in your organization is degraded.",
summary:
"A resource in your organization is currently degraded.",
statusLabel: "Degraded",
statusColor: "#dc2626"
};
case "resource_toggle": case "resource_toggle":
return { return {
heading: "Resource Status Changed", heading: "Resource Status Changed",
previewText: previewText:
"A resource in your organization has changed status.", "A resource in your organization has changed status.",
summary: summary: "A resource in your organization has changed status.",
"A resource in your organization has changed status. Please review the details below and take action if needed.", statusLabel: null,
statusLabel: "Status Changed", statusColor: null
statusColor: "#f59e0b"
}; };
default: default:
return { return {
heading: "Alert Notification", heading: "Alert Notification",
previewText: "An alert event has occurred in your organization.", previewText:
summary: "An alert event has occurred in your organization.",
"An alert event has occurred in your organization. Please review the details below and take action if needed.", summary: "An alert event has occurred in your organization.",
statusLabel: "Alert", statusLabel: "Alert",
statusColor: "#f59e0b" statusColor: "#f59e0b"
}; };
} }
} }
function resolveToggleStatus(status: unknown): {
label: string;
color: string;
} {
switch (String(status).toLowerCase()) {
case "online":
return { label: "Online", color: "#16a34a" };
case "offline":
return { label: "Offline", color: "#dc2626" };
case "healthy":
return { label: "Healthy", color: "#16a34a" };
case "unhealthy":
return { label: "Unhealthy", color: "#dc2626" };
case "degraded":
return { label: "Degraded", color: "#dc2626" };
default:
return { label: String(status ?? "Unknown"), color: "#f59e0b" };
}
}
function formatDataItems( function formatDataItems(
data: Record<string, unknown> data: Record<string, unknown>
): { label: string; value: React.ReactNode }[] { ): { label: string; value: React.ReactNode }[] {
return Object.entries(data) return Object.entries(data)
.filter(([key]) => key !== "orgId") .filter(([key]) => key !== "orgId" && key !== "status")
.map(([key, value]) => ({ .map(([key, value]) => ({
label: key label: key
.replace(/([A-Z])/g, " $1") .replace(/([A-Z])/g, " $1")
@@ -148,17 +179,41 @@ function formatDataItems(
})); }));
} }
export const AlertNotification = ({ eventType, orgId, data }: Props) => { export const AlertNotification = (props: AlertNotificationProps) => {
const { eventType, orgId, data, dashboardLink } = props;
const meta = getEventMeta(eventType); const meta = getEventMeta(eventType);
const dataItems = formatDataItems(data); const dataItems = formatDataItems(data);
const isToggle =
eventType === "site_toggle" ||
eventType === "health_check_toggle" ||
eventType === "resource_toggle";
const resolvedStatus = isToggle
? resolveToggleStatus(data.status)
: meta.statusLabel != null
? { label: meta.statusLabel, color: meta.statusColor! }
: null;
const allItems: { label: string; value: React.ReactNode }[] = [ const allItems: { label: string; value: React.ReactNode }[] = [
{ label: "Organization", value: orgId }, { label: "Organization", value: orgId },
{ label: "Status", value: ( ...(resolvedStatus != null
<span style={{ color: meta.statusColor, fontWeight: 600 }}> ? [
{meta.statusLabel} {
</span> label: "Status",
)}, value: (
<span
style={{
color: resolvedStatus.color,
fontWeight: 600
}}
>
{resolvedStatus.label}
</span>
)
}
]
: []),
{ label: "Time", value: new Date().toUTCString() }, { label: "Time", value: new Date().toUTCString() },
...dataItems ...dataItems
]; ];
@@ -184,10 +239,16 @@ export const AlertNotification = ({ eventType, orgId, data }: Props) => {
/> />
<EmailText> <EmailText>
Log in to your dashboard to view more details and Open your dashboard to view more details and manage
manage your alert rules. your alert rules.
</EmailText> </EmailText>
<EmailSection>
<ButtonLink href={dashboardLink}>
Open Dashboard
</ButtonLink>
</EmailSection>
<EmailFooter> <EmailFooter>
<EmailSignature /> <EmailSignature />
</EmailFooter> </EmailFooter>

View File

@@ -0,0 +1,126 @@
import React from "react";
import { Body, Head, Html, Preview, Tailwind } from "@react-email/components";
import { themeColors } from "./lib/theme";
import {
EmailContainer,
EmailFooter,
EmailGreeting,
EmailHeading,
EmailLetterHead,
EmailSignature,
EmailText
} from "./components/Email";
interface Props {
email: string;
orgName: string;
trialEndsAt: string;
daysRemaining: number | null;
billingLink: string;
}
export const NotifyTrialExpiring = ({
email,
orgName,
trialEndsAt,
daysRemaining,
billingLink
}: Props) => {
const hasEnded = daysRemaining === null || daysRemaining === 0;
const isLastDay = daysRemaining === 1;
const previewText = hasEnded
? `Your trial for ${orgName} has ended.`
: isLastDay
? `Your trial for ${orgName} ends tomorrow.`
: `Your trial for ${orgName} ends in ${daysRemaining} days.`;
const heading = hasEnded
? "Your Trial Ended"
: "Your Trial is Ending Soon";
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind config={themeColors}>
<Body className="font-sans bg-gray-50">
<EmailContainer>
<EmailLetterHead />
<EmailHeading>{heading}</EmailHeading>
<EmailGreeting>Hi there,</EmailGreeting>
{hasEnded ? (
<>
<EmailText>
Your free trial for{" "}
<strong>{orgName}</strong> ended on{" "}
<strong>{trialEndsAt}</strong>. Your account
has been moved to the free plan, which
includes limited functionality.
</EmailText>
<EmailText>
Some features and resources may now be
restricted. To restore full
access and continue using all the features
you had during your trial, please upgrade to
a paid plan.
</EmailText>
<EmailText>
You can{" "}
<a href={billingLink}>
upgrade your plan here
</a>{" "}
to get back up and running right away.
</EmailText>
</>
) : (
<>
<EmailText>
Just a reminder that your free trial for{" "}
<strong>{orgName}</strong> will end on{" "}
<strong>{trialEndsAt}</strong>
{isLastDay
? " - that's tomorrow!"
: `, in ${daysRemaining} days`}
.
</EmailText>
<EmailText>
After your trial ends, your account will be
moved to the free plan and some
functionality may be restricted.
</EmailText>
<EmailText>
To avoid any interruption to your service,
we encourage you to upgrade before your
trial expires. You can{" "}
<a href={billingLink}>
upgrade your plan here
</a>
.
</EmailText>
</>
)}
<EmailText>
If you have any questions or need assistance, please
don't hesitate to reach out to our support team.
</EmailText>
<EmailFooter>
<EmailSignature />
</EmailFooter>
</EmailContainer>
</Body>
</Tailwind>
</Html>
);
};
export default NotifyTrialExpiring;

View File

@@ -5,7 +5,7 @@ import { build } from "@server/build";
// EmailContainer: Wraps the entire email layout // EmailContainer: Wraps the entire email layout
export function EmailContainer({ children }: { children: React.ReactNode }) { export function EmailContainer({ children }: { children: React.ReactNode }) {
return ( return (
<Container className="bg-white border border-solid border-gray-200 max-w-lg mx-auto my-8 rounded-lg overflow-hidden shadow-sm"> <Container className="bg-white border border-solid border-gray-200 max-w-lg mx-auto my-8 rounded-xl overflow-hidden">
{children} {children}
</Container> </Container>
); );
@@ -18,7 +18,7 @@ export function EmailLetterHead() {
<Img <Img
src="https://fossorial-public-assets.s3.us-east-1.amazonaws.com/word_mark_black.png" src="https://fossorial-public-assets.s3.us-east-1.amazonaws.com/word_mark_black.png"
alt="Pangolin Logo" alt="Pangolin Logo"
width="120" width="180"
height="auto" height="auto"
className="mx-auto" className="mx-auto"
/> />

View File

@@ -36,7 +36,7 @@ export function createInternalServer() {
internalServer.listen(internalPort, (err?: any) => { internalServer.listen(internalPort, (err?: any) => {
if (err) throw err; if (err) throw err;
logger.info( logger.info(
`Internal server is running on http://localhost:${internalPort}` `Internal API server is running on http://localhost:${internalPort}`
); );
}); });

View File

@@ -1,19 +1,293 @@
// stub import logger from "@server/logger";
import { processAlerts } from "#dynamic/lib/alerts";
import {
db,
statusHistory,
targetHealthCheck,
targets,
resources,
Transaction,
logsDb
} from "@server/db";
import { eq } from "drizzle-orm";
import { invalidateStatusHistoryCache } from "@server/lib/statusHistory";
import {
fireResourceDegradedAlert,
fireResourceHealthyAlert,
fireResourceUnhealthyAlert,
fireResourceUnknownAlert
} from "./resourceEvents";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Fire a `health_check_healthy` alert for the given health check.
*
* Call this after a previously-failing health check has recovered so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the health check.
* @param healthCheckId - Numeric primary key of the health check.
* @param healthCheckName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireHealthCheckHealthyAlert( export async function fireHealthCheckHealthyAlert(
orgId: string, orgId: string,
healthCheckId: number, healthCheckId: number,
healthCheckName?: string, healthCheckName?: string | null,
extra?: Record<string, unknown> healthCheckTargetId?: number | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> { ): Promise<void> {
return; try {
await logsDb.insert(statusHistory).values({
entityType: "health_check",
entityId: healthCheckId,
orgId: orgId,
status: "healthy",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("health_check", healthCheckId);
await handleResource(orgId, healthCheckTargetId, send, trx);
if (!send) {
return;
}
await processAlerts({
eventType: "health_check_healthy",
orgId,
healthCheckId,
data: {
...(healthCheckName != null ? { healthCheckName } : {}),
...extra
}
});
await processAlerts({
eventType: "health_check_toggle",
orgId,
healthCheckId,
data: {
healthCheckId,
status: "healthy",
...(healthCheckName != null ? { healthCheckName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireHealthCheckHealthyAlert: unexpected error for healthCheckId ${healthCheckId}`,
err
);
}
} }
export async function fireHealthCheckNotHealthyAlert( /**
* Fire a `health_check_unhealthy` alert for the given health check.
*
* Call this after a health check has been detected as failing so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the health check.
* @param healthCheckId - Numeric primary key of the health check.
* @param healthCheckName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireHealthCheckUnhealthyAlert(
orgId: string, orgId: string,
healthCheckId: number, healthCheckId: number,
healthCheckName?: string, healthCheckName?: string | null,
extra?: Record<string, unknown> healthCheckTargetId?: number | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> { ): Promise<void> {
return; try {
await logsDb.insert(statusHistory).values({
entityType: "health_check",
entityId: healthCheckId,
orgId: orgId,
status: "unhealthy",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("health_check", healthCheckId);
await handleResource(orgId, healthCheckTargetId, send, trx);
if (!send) {
return;
}
await processAlerts({
eventType: "health_check_unhealthy",
orgId,
healthCheckId,
data: {
...(healthCheckName != null ? { healthCheckName } : {}),
...extra
}
});
await processAlerts({
eventType: "health_check_toggle",
orgId,
healthCheckId,
data: {
healthCheckId,
status: "unhealthy",
...(healthCheckName != null ? { healthCheckName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireHealthCheckUnhealthyAlert: unexpected error for healthCheckId ${healthCheckId}`,
err
);
}
}
export async function fireHealthCheckUnknownAlert(
orgId: string,
healthCheckId: number,
healthCheckName?: string | null,
healthCheckTargetId?: number | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> {
try {
await logsDb.insert(statusHistory).values({
entityType: "health_check",
entityId: healthCheckId,
orgId: orgId,
status: "unknown",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("health_check", healthCheckId);
await handleResource(orgId, healthCheckTargetId, send, trx);
if (!send) {
return;
}
} catch (err) {
logger.error(
`fireHealthCheckUnknownAlert: unexpected error for healthCheckId ${healthCheckId}`,
err
);
}
}
async function handleResource(
orgId: string,
healthCheckTargetId?: number | null,
send: boolean = true,
trx: Transaction | typeof db = db
) {
if (!healthCheckTargetId) {
return;
}
// we have targets lets get them
const [target] = await trx
.select()
.from(targets)
.where(eq(targets.targetId, healthCheckTargetId))
.limit(1);
if (!target) {
return;
}
const [resource] = await trx
.select()
.from(resources)
.where(eq(resources.resourceId, target.resourceId))
.limit(1);
if (!resource) {
return;
}
const otherTargets = await trx
.select({ hcHealth: targetHealthCheck.hcHealth })
.from(targets)
.innerJoin(
targetHealthCheck,
eq(targetHealthCheck.targetId, targets.targetId)
)
.where(eq(targets.resourceId, resource.resourceId));
let health = "healthy";
const allUnknown = otherTargets.every((t) => t.hcHealth === "unknown");
const allHealthy = otherTargets.every((t) => t.hcHealth === "healthy");
const allUnhealthy = otherTargets.every((t) => t.hcHealth === "unhealthy");
if (allUnknown) {
logger.debug(
`Marking resource ${resource.resourceId} as unknown because all health checks are disabled`
);
health = "unknown";
} else if (allHealthy) {
health = "healthy";
} else if (allUnhealthy) {
logger.debug(
`Marking resource ${resource.resourceId} as unhealthy because all targets are unhealthy`
);
health = "unhealthy";
} else {
logger.debug(
`Marking resource ${resource.resourceId} as degraded because some targets are unhealthy`
);
health = "degraded";
}
if (health != resource.health) {
// it changed
await trx
.update(resources)
.set({ health })
.where(eq(resources.resourceId, resource.resourceId));
if (health === "unknown") {
await fireResourceUnknownAlert(
orgId,
resource.resourceId,
resource.name,
undefined,
send,
trx
);
} else if (health === "unhealthy") {
await fireResourceUnhealthyAlert(
orgId,
resource.resourceId,
resource.name,
undefined,
send,
trx
);
} else if (health === "healthy") {
await fireResourceHealthyAlert(
orgId,
resource.resourceId,
resource.name,
undefined,
send,
trx
);
} else if (health === "degraded") {
await fireResourceDegradedAlert(
orgId,
resource.resourceId,
resource.name,
undefined,
send,
trx
);
}
}
} }

View File

@@ -0,0 +1,243 @@
import logger from "@server/logger";
import { processAlerts } from "#dynamic/lib/alerts";
import { db, logsDb, statusHistory, Transaction } from "@server/db";
import { invalidateStatusHistoryCache } from "@server/lib/statusHistory";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Fire a `resource_healthy` alert for the given resource.
*
* Call this after a previously-unhealthy resource has recovered so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceHealthyAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> {
try {
await logsDb.insert(statusHistory).values({
entityType: "resource",
entityId: resourceId,
orgId: orgId,
status: "healthy",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("resource", resourceId);
if (!send) {
return;
}
await processAlerts({
eventType: "resource_healthy",
orgId,
resourceId,
data: {
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
await processAlerts({
eventType: "resource_toggle",
orgId,
resourceId,
data: {
resourceId,
status: "healthy",
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceHealthyAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}
/**
* Fire a `resource_unhealthy` alert for the given resource.
*
* Call this after a resource has been detected as unhealthy so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceUnhealthyAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> {
try {
await logsDb.insert(statusHistory).values({
entityType: "resource",
entityId: resourceId,
orgId: orgId,
status: "unhealthy",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("resource", resourceId);
if (!send) {
return;
}
await processAlerts({
eventType: "resource_unhealthy",
orgId,
resourceId,
data: {
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
await processAlerts({
eventType: "resource_toggle",
orgId,
resourceId,
data: {
resourceId,
status: "unhealthy",
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceUnhealthyAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}
/**
* Fire a `resource_degraded` alert for the given resource.
*
* Call this after a resource has been detected as degraded so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceDegradedAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> {
try {
await logsDb.insert(statusHistory).values({
entityType: "resource",
entityId: resourceId,
orgId: orgId,
status: "degraded",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("resource", resourceId);
if (!send) {
return;
}
await processAlerts({
eventType: "resource_degraded",
orgId,
resourceId,
data: {
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
await processAlerts({
eventType: "resource_toggle",
orgId,
resourceId,
data: {
resourceId,
status: "degraded",
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceDegradedAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}
/**
* Fire a `resource_unknown` alert for the given resource.
*
* Call this when all health checks on a resource are disabled so that the
* resource status transitions to unknown.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceUnknownAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>,
send: boolean = true,
trx: Transaction | typeof db = db
): Promise<void> {
try {
await logsDb.insert(statusHistory).values({
entityType: "resource",
entityId: resourceId,
orgId: orgId,
status: "unknown",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("resource", resourceId);
if (!send) {
return;
}
await processAlerts({
eventType: "resource_toggle",
orgId,
resourceId,
data: {
resourceId,
status: "unknown",
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceUnknownAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}

View File

@@ -1,19 +1,156 @@
// stub import logger from "@server/logger";
import { processAlerts } from "#dynamic/lib/alerts";
import {
db,
logsDb,
statusHistory,
targetHealthCheck,
Transaction
} from "@server/db";
import { invalidateStatusHistoryCache } from "@server/lib/statusHistory";
import { and, eq, inArray } from "drizzle-orm";
import { fireHealthCheckUnhealthyAlert } from "./healthCheckEvents";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Fire a `site_online` alert for the given site.
*
* Call this after the site has been confirmed reachable / connected so that
* any matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the site.
* @param siteId - Numeric primary key of the site.
* @param siteName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireSiteOnlineAlert( export async function fireSiteOnlineAlert(
orgId: string, orgId: string,
siteId: number, siteId: number,
siteName?: string, siteName?: string,
extra?: Record<string, unknown> extra?: Record<string, unknown>,
trx: Transaction | typeof db = db
): Promise<void> { ): Promise<void> {
return; try {
await logsDb.insert(statusHistory).values({
entityType: "site",
entityId: siteId,
orgId: orgId,
status: "online",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("site", siteId);
await processAlerts({
eventType: "site_online",
orgId,
siteId,
data: {
...(siteName != null ? { siteName } : {}),
...extra
}
});
await processAlerts({
eventType: "site_toggle",
orgId,
siteId,
data: {
siteId,
status: "online",
...(siteName != null ? { siteName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireSiteOnlineAlert: unexpected error for siteId ${siteId}`,
err
);
}
} }
/**
* Fire a `site_offline` alert for the given site.
*
* Call this after the site has been detected as unreachable / disconnected so
* that any matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the site.
* @param siteId - Numeric primary key of the site.
* @param siteName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireSiteOfflineAlert( export async function fireSiteOfflineAlert(
orgId: string, orgId: string,
siteId: number, siteId: number,
siteName?: string, siteName?: string,
extra?: Record<string, unknown> extra?: Record<string, unknown>,
trx: Transaction | typeof db = db
): Promise<void> { ): Promise<void> {
return; try {
await logsDb.insert(statusHistory).values({
entityType: "site",
entityId: siteId,
orgId: orgId,
status: "offline",
timestamp: Math.floor(Date.now() / 1000)
});
await invalidateStatusHistoryCache("site", siteId);
const unhealthyHealthChecks = await trx
.update(targetHealthCheck)
.set({ hcHealth: "unhealthy" })
.where(
and(
eq(targetHealthCheck.orgId, orgId),
eq(targetHealthCheck.siteId, siteId),
eq(targetHealthCheck.hcEnabled, true) // only effect the ones that are enabled
)
)
.returning();
for (const healthCheck of unhealthyHealthChecks) {
logger.info(
`Marking health check ${healthCheck.targetHealthCheckId} unhealthy due to site ${siteId} being marked offline`
);
await fireHealthCheckUnhealthyAlert(
healthCheck.orgId,
healthCheck.targetHealthCheckId,
healthCheck.name,
healthCheck.targetId, // for the resource if we have one
undefined,
true,
trx
);
}
await processAlerts({
eventType: "site_offline",
orgId,
siteId,
data: {
...(siteName != null ? { siteName } : {}),
...extra
}
});
await processAlerts({
eventType: "site_toggle",
orgId,
siteId,
data: {
siteId,
status: "offline",
...(siteName != null ? { siteName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireSiteOfflineAlert: unexpected error for siteId ${siteId}`,
err
);
}
} }

View File

@@ -1,2 +1,4 @@
export * from "./events/siteEvents"; export * from "./events/siteEvents";
export * from "./events/healthCheckEvents"; export * from "./events/healthCheckEvents";
export * from "./events/resourceEvents";
export * from "./processAlerts";

View File

@@ -0,0 +1,5 @@
import { AlertContext } from "@server/routers/alertRule/types";
export async function processAlerts(context: AlertContext): Promise<void> {
return;
}

View File

@@ -1,8 +1,9 @@
export async function getOrgTierData( export async function getOrgTierData(
orgId: string orgId: string
): Promise<{ tier: string | null; active: boolean }> { ): Promise<{ tier: string | null; active: boolean; isTrial: boolean }> {
const tier = null; const tier = null;
const active = false; const active = false;
const isTrial = false;
return { tier, active }; return { tier, active, isTrial };
} }

View File

@@ -25,7 +25,7 @@ export const tier1LimitSet: LimitSet = {
export const tier2LimitSet: LimitSet = { export const tier2LimitSet: LimitSet = {
[FeatureId.USERS]: { [FeatureId.USERS]: {
value: 100, value: 50,
description: "Team limit" description: "Team limit"
}, },
[FeatureId.SITES]: { [FeatureId.SITES]: {
@@ -48,7 +48,7 @@ export const tier2LimitSet: LimitSet = {
export const tier3LimitSet: LimitSet = { export const tier3LimitSet: LimitSet = {
[FeatureId.USERS]: { [FeatureId.USERS]: {
value: 500, value: 250,
description: "Business limit" description: "Business limit"
}, },
[FeatureId.SITES]: { [FeatureId.SITES]: {

View File

@@ -23,7 +23,8 @@ export enum TierFeature {
HTTPPrivateResources = "httpPrivateResources", // handle downgrade by disabling HTTP private resources HTTPPrivateResources = "httpPrivateResources", // handle downgrade by disabling HTTP private resources
DomainNamespaces = "domainNamespaces", // handle downgrade by removing custom domain namespaces DomainNamespaces = "domainNamespaces", // handle downgrade by removing custom domain namespaces
StandaloneHealthChecks = "standaloneHealthChecks", StandaloneHealthChecks = "standaloneHealthChecks",
AlertingRules = "alertingRules" AlertingRules = "alertingRules",
WildcardSubdomain = "wildcardSubdomain"
} }
export const tierMatrix: Record<TierFeature, Tier[]> = { export const tierMatrix: Record<TierFeature, Tier[]> = {
@@ -63,6 +64,7 @@ export const tierMatrix: Record<TierFeature, Tier[]> = {
[TierFeature.SIEM]: ["enterprise"], [TierFeature.SIEM]: ["enterprise"],
[TierFeature.HTTPPrivateResources]: ["tier3", "enterprise"], [TierFeature.HTTPPrivateResources]: ["tier3", "enterprise"],
[TierFeature.DomainNamespaces]: ["tier1", "tier2", "tier3", "enterprise"], [TierFeature.DomainNamespaces]: ["tier1", "tier2", "tier3", "enterprise"],
[TierFeature.StandaloneHealthChecks]: ["tier2", "tier3", "enterprise"], [TierFeature.StandaloneHealthChecks]: ["tier3", "enterprise"],
[TierFeature.AlertingRules]: ["tier2", "tier3", "enterprise"] [TierFeature.AlertingRules]: ["tier3", "enterprise"],
[TierFeature.WildcardSubdomain]: ["tier1", "tier2", "tier3", "enterprise"]
}; };

View File

@@ -293,7 +293,7 @@ export async function applyBlueprint({
orgId, orgId,
name: name:
name ?? name ??
`${faker.word.adjective()} ${faker.word.adjective()} ${faker.word.noun()}`, `${faker.word.adjective()}-${faker.word.adjective()}-${faker.word.noun()}`,
contents: stringifyYaml(configData), contents: stringifyYaml(configData),
createdAt: Math.floor(Date.now() / 1000), createdAt: Math.floor(Date.now() / 1000),
succeeded: blueprintSucceeded, succeeded: blueprintSucceeded,

View File

@@ -125,47 +125,28 @@ export async function updateClientResources(
const existingSiteIds = existingResource?.networkId const existingSiteIds = existingResource?.networkId
? await trx ? await trx
.select({ siteId: sites.siteId }) .select({ siteId: siteNetworks.siteId })
.from(siteNetworks) .from(siteNetworks)
.where(eq(siteNetworks.networkId, existingResource.networkId)) .where(eq(siteNetworks.networkId, existingResource.networkId))
: []; : [];
let allSites: { siteId: number }[] = []; const allSites: { siteId: number }[] = [];
if (resourceData.site) { if (resourceData.site) {
let siteSingle; // Look up site by niceId
const resourceSiteId = resourceData.site; const [siteSingle] = await trx
.select({ siteId: sites.siteId })
if (resourceSiteId) { .from(sites)
// Look up site by niceId .where(
[siteSingle] = await trx and(
.select({ siteId: sites.siteId }) eq(sites.niceId, resourceData.site),
.from(sites) eq(sites.orgId, orgId)
.where(
and(
eq(sites.niceId, resourceSiteId),
eq(sites.orgId, orgId)
)
) )
.limit(1); )
} else if (siteId) { .limit(1);
// Use the provided siteId directly, but verify it belongs to the org if (siteSingle) {
[siteSingle] = await trx allSites.push(siteSingle);
.select({ siteId: sites.siteId })
.from(sites)
.where(
and(eq(sites.siteId, siteId), eq(sites.orgId, orgId))
)
.limit(1);
} else {
throw new Error(`Target site is required`);
} }
if (!siteSingle) {
throw new Error(
`Site not found: ${resourceSiteId} in org ${orgId}`
);
}
allSites.push(siteSingle);
} }
if (resourceData.sites) { if (resourceData.sites) {
@@ -180,15 +161,31 @@ export async function updateClientResources(
) )
) )
.limit(1); .limit(1);
if (!site) { if (site) {
throw new Error( allSites.push(site);
`Site not found: ${siteId} in org ${orgId}`
);
} }
allSites.push(site);
} }
} }
if (siteId && allSites.length === 0) {
// only add if there are not provided sites
// Use the provided siteId directly, but verify it belongs to the org
const [siteSingle] = await trx
.select({ siteId: sites.siteId })
.from(sites)
.where(and(eq(sites.siteId, siteId), eq(sites.orgId, orgId)))
.limit(1);
if (siteSingle) {
allSites.push(siteSingle);
}
}
if (allSites.length === 0) {
throw new Error(
`No valid sites found for private private resource ${resourceNiceId} in org ${orgId}`
);
}
if (existingResource) { if (existingResource) {
let domainInfo: let domainInfo:
| { subdomain: string | null; domainId: string } | { subdomain: string | null; domainId: string }
@@ -215,9 +212,17 @@ export async function updateClientResources(
enabled: true, // hardcoded for now enabled: true, // hardcoded for now
// enabled: resourceData.enabled ?? true, // enabled: resourceData.enabled ?? true,
alias: resourceData.alias || null, alias: resourceData.alias || null,
disableIcmp: resourceData["disable-icmp"], disableIcmp:
tcpPortRangeString: resourceData["tcp-ports"], resourceData["disable-icmp"] ||
udpPortRangeString: resourceData["udp-ports"], (resourceData.mode == "http" ? true : false), // default to true for http resources, otherwise false
tcpPortRangeString:
resourceData.mode == "http"
? "443,80"
: resourceData["tcp-ports"],
udpPortRangeString:
resourceData.mode == "http"
? ""
: resourceData["udp-ports"],
fullDomain: resourceData["full-domain"] || null, fullDomain: resourceData["full-domain"] || null,
subdomain: domainInfo ? domainInfo.subdomain : null, subdomain: domainInfo ? domainInfo.subdomain : null,
domainId: domainInfo ? domainInfo.domainId : null domainId: domainInfo ? domainInfo.domainId : null
@@ -397,9 +402,17 @@ export async function updateClientResources(
// enabled: resourceData.enabled ?? true, // enabled: resourceData.enabled ?? true,
alias: resourceData.alias || null, alias: resourceData.alias || null,
aliasAddress: aliasAddress, aliasAddress: aliasAddress,
disableIcmp: resourceData["disable-icmp"], disableIcmp:
tcpPortRangeString: resourceData["tcp-ports"], resourceData["disable-icmp"] ||
udpPortRangeString: resourceData["udp-ports"], (resourceData.mode == "http" ? true : false), // default to true for http resources, otherwise false
tcpPortRangeString:
resourceData.mode == "http"
? "443,80"
: resourceData["tcp-ports"],
udpPortRangeString:
resourceData.mode == "http"
? ""
: resourceData["udp-ports"],
fullDomain: resourceData["full-domain"] || null, fullDomain: resourceData["full-domain"] || null,
subdomain: domainInfo ? domainInfo.subdomain : null, subdomain: domainInfo ? domainInfo.subdomain : null,
domainId: domainInfo ? domainInfo.domainId : null domainId: domainInfo ? domainInfo.domainId : null

View File

@@ -1,5 +1,6 @@
import { import {
domains, domains,
domainNamespaces,
orgDomains, orgDomains,
Resource, Resource,
resourceHeaderAuth, resourceHeaderAuth,
@@ -33,6 +34,7 @@ import { hashPassword } from "@server/auth/password";
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators"; import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
import { isValidRegionId } from "@server/db/regions"; import { isValidRegionId } from "@server/db/regions";
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
import { fireHealthCheckUnknownAlert } from "@server/lib/alerts";
import { tierMatrix } from "../billing/tierMatrix"; import { tierMatrix } from "../billing/tierMatrix";
export type ProxyResourcesResults = { export type ProxyResourcesResults = {
@@ -141,6 +143,7 @@ export async function updateProxyResources(
.insert(targetHealthCheck) .insert(targetHealthCheck)
.values({ .values({
name: `${targetData.hostname}:${targetData.port}`, name: `${targetData.hostname}:${targetData.port}`,
siteId: site.siteId,
targetId: newTarget.targetId, targetId: newTarget.targetId,
orgId: orgId, orgId: orgId,
hcEnabled: healthcheckData?.enabled || false, hcEnabled: healthcheckData?.enabled || false,
@@ -162,11 +165,25 @@ export async function updateProxyResources(
hcStatus: healthcheckData?.status, hcStatus: healthcheckData?.status,
hcHealth: "unknown", hcHealth: "unknown",
hcHealthyThreshold: healthcheckData?.["healthy-threshold"], hcHealthyThreshold: healthcheckData?.["healthy-threshold"],
hcUnhealthyThreshold: healthcheckData?.["unhealthy-threshold"] hcUnhealthyThreshold:
healthcheckData?.["unhealthy-threshold"]
}) })
.returning(); .returning();
healthchecksToUpdate.push(newHealthcheck); healthchecksToUpdate.push(newHealthcheck);
// Insert unknown status history when HC is created in disabled state
if (!healthcheckData?.enabled) {
await fireHealthCheckUnknownAlert(
orgId,
newHealthcheck.targetHealthCheckId,
newHealthcheck.name,
newHealthcheck.targetId,
undefined,
true,
trx
);
}
} }
// Find existing resource by niceId and orgId // Find existing resource by niceId and orgId
@@ -235,6 +252,7 @@ export async function updateProxyResources(
fullDomain: http ? resourceData["full-domain"] : null, fullDomain: http ? resourceData["full-domain"] : null,
subdomain: domain ? domain.subdomain : null, subdomain: domain ? domain.subdomain : null,
domainId: domain ? domain.domainId : null, domainId: domain ? domain.domainId : null,
wildcard: domain ? domain.wildcard : false,
enabled: resourceEnabled, enabled: resourceEnabled,
sso: resourceData.auth?.["sso-enabled"] || false, sso: resourceData.auth?.["sso-enabled"] || false,
skipToIdpId: skipToIdpId:
@@ -527,8 +545,10 @@ export async function updateProxyResources(
healthcheckData?.["follow-redirects"], healthcheckData?.["follow-redirects"],
hcMethod: healthcheckData?.method, hcMethod: healthcheckData?.method,
hcStatus: healthcheckData?.status, hcStatus: healthcheckData?.status,
hcHealthyThreshold: healthcheckData?.["healthy-threshold"], hcHealthyThreshold:
hcUnhealthyThreshold: healthcheckData?.["unhealthy-threshold"] healthcheckData?.["healthy-threshold"],
hcUnhealthyThreshold:
healthcheckData?.["unhealthy-threshold"]
}) })
.where( .where(
eq( eq(
@@ -554,6 +574,21 @@ export async function updateProxyResources(
targetsToUpdate.push(updatedTarget); targetsToUpdate.push(updatedTarget);
} }
} }
// Insert unknown status history when HC is disabled
const isDisablingHc =
!healthcheckData?.enabled && oldHealthcheck?.hcEnabled;
if (isDisablingHc) {
await fireHealthCheckUnknownAlert(
orgId,
newHealthcheck.targetHealthCheckId,
newHealthcheck.name,
newHealthcheck.targetId,
undefined,
true,
trx
);
}
} else { } else {
await createTarget(existingResource.resourceId, targetData); await createTarget(existingResource.resourceId, targetData);
} }
@@ -682,6 +717,7 @@ export async function updateProxyResources(
fullDomain: http ? resourceData["full-domain"] : null, fullDomain: http ? resourceData["full-domain"] : null,
subdomain: domain ? domain.subdomain : null, subdomain: domain ? domain.subdomain : null,
domainId: domain ? domain.domainId : null, domainId: domain ? domain.domainId : null,
wildcard: domain ? domain.wildcard : false,
enabled: resourceEnabled, enabled: resourceEnabled,
sso: resourceData.auth?.["sso-enabled"] || false, sso: resourceData.auth?.["sso-enabled"] || false,
skipToIdpId: resourceData.auth?.["auto-login-idp"] || null, skipToIdpId: resourceData.auth?.["auto-login-idp"] || null,
@@ -1087,8 +1123,10 @@ function checkIfHealthcheckChanged(
JSON.stringify(incoming.hcHeaders) JSON.stringify(incoming.hcHeaders)
) )
return true; return true;
if (existing.hcHealthyThreshold !== incoming.hcHealthyThreshold) return true; if (existing.hcHealthyThreshold !== incoming.hcHealthyThreshold)
if (existing.hcUnhealthyThreshold !== incoming.hcUnhealthyThreshold) return true; return true;
if (existing.hcUnhealthyThreshold !== incoming.hcUnhealthyThreshold)
return true;
return false; return false;
} }
@@ -1151,7 +1189,13 @@ async function getDomainId(
orgId: string, orgId: string,
fullDomain: string, fullDomain: string,
trx: Transaction trx: Transaction
): Promise<{ subdomain: string | null; domainId: string } | null> { ): Promise<{
subdomain: string | null;
domainId: string;
wildcard: boolean;
} | null> {
const isWildcardFullDomain = fullDomain.startsWith("*.");
const possibleDomains = await trx const possibleDomains = await trx
.select() .select()
.from(domains) .from(domains)
@@ -1164,6 +1208,11 @@ async function getDomainId(
} }
const validDomains = possibleDomains.filter((domain) => { const validDomains = possibleDomains.filter((domain) => {
// Wildcard full-domains are not allowed on CNAME domains
if (isWildcardFullDomain && domain.domains.type === "cname") {
return false;
}
if (domain.domains.type == "ns" || domain.domains.type == "wildcard") { if (domain.domains.type == "ns" || domain.domains.type == "wildcard") {
return ( return (
fullDomain === domain.domains.baseDomain || fullDomain === domain.domains.baseDomain ||
@@ -1181,6 +1230,21 @@ async function getDomainId(
const domainSelection = validDomains[0].domains; const domainSelection = validDomains[0].domains;
const baseDomain = domainSelection.baseDomain; const baseDomain = domainSelection.baseDomain;
// Wildcard full-domains are not allowed on namespace (provided/free) domains
if (isWildcardFullDomain) {
const [namespaceDomain] = await trx
.select()
.from(domainNamespaces)
.where(eq(domainNamespaces.domainId, domainSelection.domainId))
.limit(1);
if (namespaceDomain) {
throw new Error(
`Wildcard full-domains are not supported for provided or free domains: ${fullDomain}`
);
}
}
// remove the base domain of the domain // remove the base domain of the domain
let subdomain = null; let subdomain = null;
if (fullDomain != baseDomain) { if (fullDomain != baseDomain) {
@@ -1190,6 +1254,7 @@ async function getDomainId(
// Return the first valid domain // Return the first valid domain
return { return {
subdomain: subdomain, subdomain: subdomain,
domainId: domainSelection.domainId domainId: domainSelection.domainId,
wildcard: isWildcardFullDomain
}; };
} }

View File

@@ -2,6 +2,7 @@ import { z } from "zod";
import { portRangeStringSchema } from "@server/lib/ip"; import { portRangeStringSchema } from "@server/lib/ip";
import { MaintenanceSchema } from "#dynamic/lib/blueprints/MaintenanceSchema"; import { MaintenanceSchema } from "#dynamic/lib/blueprints/MaintenanceSchema";
import { isValidRegionId } from "@server/db/regions"; import { isValidRegionId } from "@server/db/regions";
import { wildcardSubdomainSchema } from "@server/lib/schemas";
export const SiteSchema = z.object({ export const SiteSchema = z.object({
name: z.string().min(1).max(100), name: z.string().min(1).max(100),
@@ -319,6 +320,34 @@ export const ResourceSchema = z
message: message:
"Rules have conflicting or invalid priorities (must be unique, including auto-assigned ones)" "Rules have conflicting or invalid priorities (must be unique, including auto-assigned ones)"
} }
)
.refine(
(resource) => {
const fullDomain = resource["full-domain"];
if (!fullDomain || !fullDomain.includes("*")) return true;
// A wildcard full-domain must be of the form *.labels.basedomain
// Extract the leftmost label(s) before the first non-wildcard segment.
// e.g. "*.level1.example.com" → subdomain candidate is "*.level1"
// We do this by finding the base domain: everything after the first
// real (non-wildcard) dot-separated segment pair.
//
// Simple rule: split on ".", first token must be "*", rest must be
// valid hostname labels, and there must be at least 2 remaining labels
// (so the full domain has a real base domain).
const parts = fullDomain.split(".");
if (parts[0] !== "*") return false; // * must be the very first label
if (parts.includes("*", 1)) return false; // no further wildcards
if (parts.length < 3) return false; // need at least *.label.tld
const labelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/;
return parts.slice(1).every((label) => labelRegex.test(label));
},
{
path: ["full-domain"],
message:
'Wildcard full-domain must have "*" as the leftmost label only, followed by at least two valid hostname labels (e.g. "*.example.com" or "*.level1.example.com"). Patterns like "*example.com" or "level2.*.example.com" are not supported.'
}
); );
export function isTargetsOnlyResource(resource: any): boolean { export function isTargetsOnlyResource(resource: any): boolean {
@@ -329,7 +358,7 @@ export const ClientResourceSchema = z
.object({ .object({
name: z.string().min(1).max(255), name: z.string().min(1).max(255),
mode: z.enum(["host", "cidr", "http"]), mode: z.enum(["host", "cidr", "http"]),
site: z.string(), // DEPRECATED IN FAVOR OF sites site: z.string().optional(), // DEPRECATED IN FAVOR OF sites
sites: z.array(z.string()).optional().default([]), sites: z.array(z.string()).optional().default([]),
// protocol: z.enum(["tcp", "udp"]).optional(), // protocol: z.enum(["tcp", "udp"]).optional(),
// proxyPort: z.int().positive().optional(), // proxyPort: z.int().positive().optional(),

View File

@@ -1,5 +1,6 @@
export async function getValidCertificatesForDomains( export async function getValidCertificatesForDomains(
domains: Set<string> domains: Set<string>,
useCache: boolean = true
): Promise< ): Promise<
Array<{ Array<{
id: number; id: number;

View File

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

View File

@@ -1,14 +1,16 @@
import { db } from "@server/db"; import { db } from "@server/db";
import { domains, orgDomains } from "@server/db"; import { domains, orgDomains, domainNamespaces, resources } from "@server/db";
import { eq, and } from "drizzle-orm"; import { eq, and, like, not } from "drizzle-orm";
import { subdomainSchema } from "@server/lib/schemas"; import { subdomainSchema, wildcardSubdomainSchema } from "@server/lib/schemas";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import config from "./config";
export type DomainValidationResult = export type DomainValidationResult =
| { | {
success: true; success: true;
fullDomain: string; fullDomain: string;
subdomain: string | null; subdomain: string | null;
wildcard: boolean;
} }
| { | {
success: false; success: false;
@@ -66,6 +68,62 @@ export async function validateAndConstructDomain(
}; };
} }
// Detect wildcard subdomain request
const isWildcard =
subdomain !== undefined &&
subdomain !== null &&
subdomain.includes("*") &&
domainRes.domains.type !== "cname";
// Wildcard subdomains are not allowed on CNAME domains
if (isWildcard && domainRes.domains.type === "cname") {
return {
success: false,
error: "Wildcard subdomains are not supported for CNAME domains. CNAME domains must use a specific hostname."
};
}
// Wildcard subdomains are not allowed on namespace (provided/free) domains
if (isWildcard) {
const [namespaceDomain] = await db
.select()
.from(domainNamespaces)
.where(eq(domainNamespaces.domainId, domainId))
.limit(1);
if (namespaceDomain) {
return {
success: false,
error: "Wildcard subdomains are not supported for provided or free domains. Use a specific subdomain instead."
};
}
}
if (
isWildcard &&
domainRes.domains.type == "wildcard" &&
!(
domainRes.domains.preferWildcardCert ||
config.getRawConfig().traefik.prefer_wildcard_cert
)
) {
return {
success: false,
error: "Wildcard domains are not supported without configuring certificate resolver for wildcard certs and marking it as prefered."
};
}
// Validate wildcard subdomain format
if (isWildcard) {
const parsedWildcard = wildcardSubdomainSchema.safeParse(subdomain);
if (!parsedWildcard.success) {
return {
success: false,
error: fromError(parsedWildcard.error).toString()
};
}
}
// Construct full domain based on domain type // Construct full domain based on domain type
let fullDomain = ""; let fullDomain = "";
let finalSubdomain = subdomain; let finalSubdomain = subdomain;
@@ -81,13 +139,16 @@ export async function validateAndConstructDomain(
finalSubdomain = null; // CNAME domains don't use subdomains finalSubdomain = null; // CNAME domains don't use subdomains
} else if (domainRes.domains.type === "wildcard") { } else if (domainRes.domains.type === "wildcard") {
if (subdomain !== undefined && subdomain !== null) { if (subdomain !== undefined && subdomain !== null) {
// Validate subdomain format for wildcard domains if (!isWildcard) {
const parsedSubdomain = subdomainSchema.safeParse(subdomain); // Validate regular subdomain format for wildcard domains
if (!parsedSubdomain.success) { const parsedSubdomain =
return { subdomainSchema.safeParse(subdomain);
success: false, if (!parsedSubdomain.success) {
error: fromError(parsedSubdomain.error).toString() return {
}; success: false,
error: fromError(parsedSubdomain.error).toString()
};
}
} }
fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`; fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`;
} else { } else {
@@ -100,13 +161,14 @@ export async function validateAndConstructDomain(
finalSubdomain = null; finalSubdomain = null;
} }
// Convert to lowercase // Convert to lowercase (preserve * as-is)
fullDomain = fullDomain.toLowerCase(); fullDomain = fullDomain.toLowerCase();
return { return {
success: true, success: true,
fullDomain, fullDomain,
subdomain: finalSubdomain ?? null subdomain: finalSubdomain ?? null,
wildcard: isWildcard
}; };
} catch (error) { } catch (error) {
return { return {
@@ -115,3 +177,81 @@ export async function validateAndConstructDomain(
}; };
} }
} }
/**
* Checks whether a given fullDomain conflicts with any existing wildcard resources,
* or (if the fullDomain is itself a wildcard) whether any existing resources would
* be matched by it.
*
* @param fullDomain - The fully-constructed domain to check (may contain a leading `*`)
* @param excludeResourceId - Optional resource ID to exclude from the check (for updates)
* @returns An object with `conflict: true` and a human-readable `message`, or `conflict: false`
*/
export async function checkWildcardDomainConflict(
fullDomain: string,
excludeResourceId?: number
): Promise<{ conflict: false } | { conflict: true; message: string }> {
const isWildcard = fullDomain.startsWith("*.");
if (isWildcard) {
// e.g. fullDomain = "*.example.com" → suffix = ".example.com"
const suffix = fullDomain.slice(1); // ".example.com"
// Find any existing non-wildcard resource whose fullDomain ends with this suffix
// e.g. "test.example.com" or "foo.example.com"
const conflicting = await db
.select({
resourceId: resources.resourceId,
fullDomain: resources.fullDomain
})
.from(resources)
.where(like(resources.fullDomain, `%${suffix}`));
const matches = conflicting.filter(
(r) =>
!r.fullDomain!.startsWith("*.") &&
r.fullDomain!.endsWith(suffix) &&
(excludeResourceId === undefined ||
r.resourceId !== excludeResourceId)
);
if (matches.length > 0) {
return {
conflict: true,
message: `Wildcard domain ${fullDomain} conflicts with existing resource(s): ${matches.map((r) => r.fullDomain).join(", ")}`
};
}
} else {
// Specific domain — check if any existing wildcard would match it.
// e.g. fullDomain = "test.example.com"
// We look for a wildcard "*.example.com" which means fullDomain ends with ".example.com"
const dotIndex = fullDomain.indexOf(".");
if (dotIndex !== -1) {
const suffix = fullDomain.slice(dotIndex); // ".example.com"
const wildcardPattern = `*.${fullDomain.slice(dotIndex + 1)}`; // "*.example.com"
const conflicting = await db
.select({
resourceId: resources.resourceId,
fullDomain: resources.fullDomain
})
.from(resources)
.where(eq(resources.fullDomain, wildcardPattern));
const matches = conflicting.filter(
(r) =>
excludeResourceId === undefined ||
r.resourceId !== excludeResourceId
);
if (matches.length > 0) {
return {
conflict: true,
message: `Domain ${fullDomain} conflicts with existing wildcard resource(s): ${matches.map((r) => r.fullDomain).join(", ")}`
};
}
}
}
return { conflict: false };
}

View File

@@ -700,7 +700,6 @@ export async function generateSubnetProxyTargetV2(
targets.push({ targets.push({
sourcePrefixes: [], sourcePrefixes: [],
destPrefix: `${siteResource.aliasAddress}/32`, destPrefix: `${siteResource.aliasAddress}/32`,
rewriteTo: destination,
portRange, portRange,
disableIcmp, disableIcmp,
resourceId: siteResource.siteResourceId, resourceId: siteResource.siteResourceId,

View File

@@ -180,36 +180,41 @@ export async function rebuildClientAssociationsFromSiteResource(
/////////// process the client-siteResource associations /////////// /////////// process the client-siteResource associations ///////////
// get all of the clients associated with other resources in the same network, // get all of the clients associated with other site resources that share
// joined through siteNetworks so we know which siteId each client belongs to // any of the same sites as this site resource (via siteNetworks). We can't
const allUpdatedClientsFromOtherResourcesOnThisSite = siteResource.networkId // simply filter by networkId since each site resource has its own network;
? await trx // two site resources serving the same site typically belong to different
.select({ // networks that both happen to include the site through siteNetworks.
clientId: clientSiteResourcesAssociationsCache.clientId, const sitesListSiteIds = sitesList.map((s) => s.siteId);
siteId: siteNetworks.siteId const allUpdatedClientsFromOtherResourcesOnThisSite =
}) sitesListSiteIds.length > 0
.from(clientSiteResourcesAssociationsCache) ? await trx
.innerJoin( .select({
siteResources, clientId: clientSiteResourcesAssociationsCache.clientId,
eq( siteId: siteNetworks.siteId
clientSiteResourcesAssociationsCache.siteResourceId, })
siteResources.siteResourceId .from(clientSiteResourcesAssociationsCache)
) .innerJoin(
) siteResources,
.innerJoin( eq(
siteNetworks, clientSiteResourcesAssociationsCache.siteResourceId,
eq(siteNetworks.networkId, siteResources.networkId) siteResources.siteResourceId
)
.where(
and(
eq(siteResources.networkId, siteResource.networkId),
ne(
siteResources.siteResourceId,
siteResource.siteResourceId
) )
) )
) .innerJoin(
: []; siteNetworks,
eq(siteNetworks.networkId, siteResources.networkId)
)
.where(
and(
inArray(siteNetworks.siteId, sitesListSiteIds),
ne(
siteResources.siteResourceId,
siteResource.siteResourceId
)
)
)
: [];
// Build a per-site map so the loop below can check by siteId rather than // Build a per-site map so the loop below can check by siteId rather than
// across the entire network. // across the entire network.

View File

@@ -1,5 +1,41 @@
import { z } from "zod"; import { z } from "zod";
/**
* Validates a wildcard subdomain passed as the leftmost component of a full domain.
*
* The value represents everything to the left of the base domain, so when combined
* with e.g. "example.com" it must produce a valid SSL-style wildcard hostname.
*
* Valid:
* "*" → *.example.com
* "*.level1" → *.level1.example.com
*
* Invalid:
* "*example" → *example.com (no dot after *)
* "level2.*.level1" → wildcard not in leftmost position
* "*.level1.*" → multiple wildcards
*/
export const wildcardSubdomainSchema = z
.string()
.refine(
(val) => {
// Must start with "*."; the remainder (if any) must be valid hostname labels.
// A bare "*" is also valid (becomes *.baseDomain directly).
if (val === "*") return true;
if (!val.startsWith("*.")) return false;
const rest = val.slice(2); // everything after "*."
// rest must not be empty, must not contain another "*",
// and every label must be a valid hostname label.
if (!rest || rest.includes("*")) return false;
const labelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
return rest.split(".").every((label) => labelRegex.test(label));
},
{
message:
'Invalid wildcard subdomain. The wildcard "*" must be the leftmost label followed by a dot and valid hostname labels (e.g. "*" or "*.level1"). Patterns like "*example", "level2.*.level1", or multiple wildcards are not supported.'
}
);
export const subdomainSchema = z export const subdomainSchema = z
.string() .string()
.regex( .regex(

View File

@@ -1,15 +1,84 @@
import { z } from "zod"; import { z } from "zod";
import { db, logsDb, statusHistory } from "@server/db";
import { and, eq, gte, asc } from "drizzle-orm";
import cache from "@server/lib/cache";
const STATUS_HISTORY_CACHE_TTL = 60; // seconds
function statusHistoryCacheKey(
entityType: string,
entityId: number,
days: number
): string {
return `statusHistory:${entityType}:${entityId}:${days}`;
}
export async function getCachedStatusHistory(
entityType: string,
entityId: number,
days: number
): Promise<StatusHistoryResponse> {
const cacheKey = statusHistoryCacheKey(entityType, entityId, days);
const cached = await cache.get<StatusHistoryResponse>(cacheKey);
if (cached !== undefined) {
return cached;
}
const nowSec = Math.floor(Date.now() / 1000);
const startSec = nowSec - days * 86400;
const events = await logsDb
.select()
.from(statusHistory)
.where(
and(
eq(statusHistory.entityType, entityType),
eq(statusHistory.entityId, entityId),
gte(statusHistory.timestamp, startSec)
)
)
.orderBy(asc(statusHistory.timestamp));
const { buckets, totalDowntime } = computeBuckets(events, days);
const totalWindow = days * 86400;
const overallUptime =
totalWindow > 0
? Math.max(0, ((totalWindow - totalDowntime) / totalWindow) * 100)
: 100;
const result: StatusHistoryResponse = {
entityType,
entityId,
days: buckets,
overallUptimePercent: Math.round(overallUptime * 100) / 100,
totalDowntimeSeconds: totalDowntime
};
await cache.set(cacheKey, result, STATUS_HISTORY_CACHE_TTL);
return result;
}
export async function invalidateStatusHistoryCache(
entityType: string,
entityId: number
): Promise<void> {
const prefix = `statusHistory:${entityType}:${entityId}:`;
const keys = cache.keys().filter((k) => k.startsWith(prefix));
if (keys.length > 0) {
await cache.del(keys);
}
}
export const statusHistoryQuerySchema = z export const statusHistoryQuerySchema = z
.object({ .object({
days: z days: z
.string() .string()
.optional() .optional()
.transform((v) => (v ? parseInt(v, 10) : 90)), .transform((v) => (v ? parseInt(v, 10) : 90))
}) })
.pipe( .pipe(
z.object({ z.object({
days: z.number().int().min(1).max(365), days: z.number().int().min(1).max(365)
}) })
); );
@@ -18,7 +87,7 @@ export interface StatusHistoryDayBucket {
uptimePercent: number; // 0-100 uptimePercent: number; // 0-100
totalDowntimeSeconds: number; totalDowntimeSeconds: number;
downtimeWindows: { start: number; end: number | null; status: string }[]; downtimeWindows: { start: number; end: number | null; status: string }[];
status: "good" | "degraded" | "bad" | "no_data"; status: "good" | "degraded" | "bad" | "no_data" | "unknown";
} }
export interface StatusHistoryResponse { export interface StatusHistoryResponse {
@@ -30,7 +99,14 @@ export interface StatusHistoryResponse {
} }
export function computeBuckets( export function computeBuckets(
events: { entityType: string; entityId: number; orgId: string; status: string; timestamp: number; id: number }[], events: {
entityType: string;
entityId: number;
orgId: string;
status: string;
timestamp: number;
id: number;
}[],
days: number days: number
): { buckets: StatusHistoryDayBucket[]; totalDowntime: number } { ): { buckets: StatusHistoryDayBucket[]; totalDowntime: number } {
const nowSec = Math.floor(Date.now() / 1000); const nowSec = Math.floor(Date.now() / 1000);
@@ -52,8 +128,10 @@ export function computeBuckets(
const currentStatus = lastBeforeDay?.status ?? null; const currentStatus = lastBeforeDay?.status ?? null;
const windows: { start: number; end: number | null; status: string }[] = []; const windows: { start: number; end: number | null; status: string }[] =
[];
let dayDowntime = 0; let dayDowntime = 0;
let dayDegradedTime = 0;
let windowStart = dayStartSec; let windowStart = dayStartSec;
let windowStatus = currentStatus; let windowStatus = currentStatus;
@@ -62,15 +140,21 @@ export function computeBuckets(
if (windowStatus !== null && windowStatus !== evt.status) { if (windowStatus !== null && windowStatus !== evt.status) {
const windowEnd = evt.timestamp; const windowEnd = evt.timestamp;
const isDown = const isDown =
windowStatus === "offline" || windowStatus === "offline" || windowStatus === "unhealthy";
windowStatus === "unhealthy" || const isDegraded = windowStatus === "degraded";
windowStatus === "unknown";
if (isDown) { if (isDown) {
dayDowntime += windowEnd - windowStart; dayDowntime += windowEnd - windowStart;
windows.push({ windows.push({
start: windowStart, start: windowStart,
end: windowEnd, end: windowEnd,
status: windowStatus, status: windowStatus
});
} else if (isDegraded) {
dayDegradedTime += windowEnd - windowStart;
windows.push({
start: windowStart,
end: windowEnd,
status: windowStatus
}); });
} }
} }
@@ -82,15 +166,21 @@ export function computeBuckets(
if (windowStatus !== null) { if (windowStatus !== null) {
const finalEnd = Math.min(dayEndSec, nowSec); const finalEnd = Math.min(dayEndSec, nowSec);
const isDown = const isDown =
windowStatus === "offline" || windowStatus === "offline" || windowStatus === "unhealthy";
windowStatus === "unhealthy" || const isDegraded = windowStatus === "degraded";
windowStatus === "unknown";
if (isDown && finalEnd > windowStart) { if (isDown && finalEnd > windowStart) {
dayDowntime += finalEnd - windowStart; dayDowntime += finalEnd - windowStart;
windows.push({ windows.push({
start: windowStart, start: windowStart,
end: finalEnd, end: finalEnd,
status: windowStatus, status: windowStatus
});
} else if (isDegraded && finalEnd > windowStart) {
dayDegradedTime += finalEnd - windowStart;
windows.push({
start: windowStart,
end: finalEnd,
status: windowStatus
}); });
} }
} }
@@ -105,7 +195,7 @@ export function computeBuckets(
effectiveDayLength > 0 effectiveDayLength > 0
? Math.max( ? Math.max(
0, 0,
((effectiveDayLength - dayDowntime) / ((effectiveDayLength - dayDowntime - dayDegradedTime) /
effectiveDayLength) * effectiveDayLength) *
100 100
) )
@@ -113,11 +203,27 @@ export function computeBuckets(
const dateStr = new Date(dayStartSec * 1000).toISOString().slice(0, 10); const dateStr = new Date(dayStartSec * 1000).toISOString().slice(0, 10);
const hasAnyData = currentStatus !== null || dayEvents.length > 0;
// The whole observable window is "unknown" if every status we have seen is unknown
const allStatuses = [
...(currentStatus !== null ? [currentStatus] : []),
...dayEvents.map((e) => e.status)
];
const onlyUnknownData =
hasAnyData && allStatuses.every((s) => s === "unknown");
let status: StatusHistoryDayBucket["status"] = "no_data"; let status: StatusHistoryDayBucket["status"] = "no_data";
if (currentStatus !== null || dayEvents.length > 0) { if (hasAnyData) {
if (uptimePct >= 99) status = "good"; if (onlyUnknownData) {
else if (uptimePct >= 50) status = "degraded"; status = "unknown";
else status = "bad"; } else if (dayDowntime > 0 && uptimePct < 50) {
status = "bad";
} else if (dayDowntime > 0 || dayDegradedTime > 0) {
status = "degraded";
} else {
status = "good";
}
} }
buckets.push({ buckets.push({
@@ -125,7 +231,7 @@ export function computeBuckets(
uptimePercent: Math.round(uptimePct * 100) / 100, uptimePercent: Math.round(uptimePct * 100) / 100,
totalDowntimeSeconds: dayDowntime, totalDowntimeSeconds: dayDowntime,
downtimeWindows: windows, downtimeWindows: windows,
status, status
}); });
} }

View File

@@ -2,7 +2,7 @@ import { PostHog } from "posthog-node";
import config from "./config"; import config from "./config";
import { getHostMeta } from "./hostMeta"; import { getHostMeta } from "./hostMeta";
import logger from "@server/logger"; import logger from "@server/logger";
import { apiKeys, db, roles, siteResources } from "@server/db"; import { alertRules, apiKeys, blueprints, db, roles, siteResources } from "@server/db";
import { sites, users, orgs, resources, clients, idp } from "@server/db"; import { sites, users, orgs, resources, clients, idp } from "@server/db";
import { eq, count, notInArray, and, isNotNull, isNull } from "drizzle-orm"; import { eq, count, notInArray, and, isNotNull, isNull } from "drizzle-orm";
import { APP_VERSION } from "./consts"; import { APP_VERSION } from "./consts";
@@ -15,6 +15,7 @@ class TelemetryClient {
private client: PostHog | null = null; private client: PostHog | null = null;
private enabled: boolean; private enabled: boolean;
private intervalId: NodeJS.Timeout | null = null; private intervalId: NodeJS.Timeout | null = null;
private collectionIntervalDays = 14;
constructor() { constructor() {
const enabled = config.getRawConfig().app.telemetry.anonymous_usage; const enabled = config.getRawConfig().app.telemetry.anonymous_usage;
@@ -33,7 +34,7 @@ class TelemetryClient {
this.client = new PostHog( this.client = new PostHog(
"phc_QYuATSSZt6onzssWcYJbXLzQwnunIpdGGDTYhzK3VjX", "phc_QYuATSSZt6onzssWcYJbXLzQwnunIpdGGDTYhzK3VjX",
{ {
host: "https://pangolin.net/relay-O7yI" host: "https://telemetry.fossorial.io/relay-O7yI"
} }
); );
@@ -72,7 +73,7 @@ class TelemetryClient {
logger.debug("Successfully sent analytics data"); logger.debug("Successfully sent analytics data");
}); });
}, },
48 * 60 * 60 * 1000 this.collectionIntervalDays * 24 * 60 * 60 * 1000 // Convert days to milliseconds
); );
this.collectAndSendAnalytics().catch((err) => { this.collectAndSendAnalytics().catch((err) => {
@@ -157,6 +158,14 @@ class TelemetryClient {
}) })
.from(sites); .from(sites);
const [numAlertRules] = await db
.select({ count: count() })
.from(alertRules);
const [blueprintsCount] = await db
.select({ count: count() })
.from(blueprints);
const supporterKey = config.getSupporterData(); const supporterKey = config.getSupporterData();
const allPrivateResources = await db.select().from(siteResources); const allPrivateResources = await db.select().from(siteResources);
@@ -165,11 +174,14 @@ class TelemetryClient {
let numPrivResourceAliases = 0; let numPrivResourceAliases = 0;
let numPrivResourceHosts = 0; let numPrivResourceHosts = 0;
let numPrivResourceCidr = 0; let numPrivResourceCidr = 0;
let numPrivResourceHttp = 0;
for (const res of allPrivateResources) { for (const res of allPrivateResources) {
if (res.mode === "host") { if (res.mode === "host") {
numPrivResourceHosts += 1; numPrivResourceHosts += 1;
} else if (res.mode === "cidr") { } else if (res.mode === "cidr") {
numPrivResourceCidr += 1; numPrivResourceCidr += 1;
} else if (res.mode === "http") {
numPrivResourceHttp += 1;
} }
if (res.alias) { if (res.alias) {
@@ -187,6 +199,9 @@ class TelemetryClient {
numPrivateResources: numPrivResources, numPrivateResources: numPrivResources,
numPrivateResourceAliases: numPrivResourceAliases, numPrivateResourceAliases: numPrivResourceAliases,
numPrivateResourceHosts: numPrivResourceHosts, numPrivateResourceHosts: numPrivResourceHosts,
numPrivateResourceCidr: numPrivResourceCidr,
numPrivateResourceHttp: numPrivResourceHttp,
numAlertRules: numAlertRules.count,
numUserDevices: userDevicesCount.count, numUserDevices: userDevicesCount.count,
numMachineClients: machineClients.count, numMachineClients: machineClients.count,
numIdentityProviders: idpCount.count, numIdentityProviders: idpCount.count,
@@ -197,6 +212,7 @@ class TelemetryClient {
appVersion: APP_VERSION, appVersion: APP_VERSION,
numApiKeys: numApiKeys.count, numApiKeys: numApiKeys.count,
numCustomRoles: customRoles.count, numCustomRoles: customRoles.count,
numBlueprints: blueprintsCount.count,
supporterStatus: { supporterStatus: {
valid: supporterKey?.valid || false, valid: supporterKey?.valid || false,
tier: supporterKey?.tier || "None", tier: supporterKey?.tier || "None",
@@ -285,10 +301,12 @@ class TelemetryClient {
num_private_resource_aliases: num_private_resource_aliases:
stats.numPrivateResourceAliases, stats.numPrivateResourceAliases,
num_private_resource_hosts: stats.numPrivateResourceHosts, num_private_resource_hosts: stats.numPrivateResourceHosts,
num_private_resource_cidr: stats.numPrivateResourceCidr,
num_user_devices: stats.numUserDevices, num_user_devices: stats.numUserDevices,
num_machine_clients: stats.numMachineClients, num_machine_clients: stats.numMachineClients,
num_identity_providers: stats.numIdentityProviders, num_identity_providers: stats.numIdentityProviders,
num_sites_online: stats.numSitesOnline, num_sites_online: stats.numSitesOnline,
num_blueprint_runs: stats.numBlueprints,
num_resources_sso_enabled: stats.resources.filter( num_resources_sso_enabled: stats.resources.filter(
(r) => r.sso (r) => r.sso
).length, ).length,

View File

@@ -416,7 +416,8 @@ export class TraefikConfigManager {
// Get valid certificates for domains not covered by wildcards // Get valid certificates for domains not covered by wildcards
validCertificates = validCertificates =
await getValidCertificatesForDomains( await getValidCertificatesForDomains(
domainsToFetch domainsToFetch,
true
); );
this.lastCertificateFetch = new Date(); this.lastCertificateFetch = new Date();
this.lastKnownDomains = new Set(domains); this.lastKnownDomains = new Set(domains);
@@ -534,6 +535,24 @@ export class TraefikConfigManager {
if (match && match[1]) { if (match && match[1]) {
domains.add(match[1]); domains.add(match[1]);
} }
// Match HostRegexp(`^[^.]+\.parent.domain$`) generated for wildcard resources
const hostRegexpMatch = router.rule.match(
/HostRegexp\(`([^`]+)`\)/
);
if (hostRegexpMatch && hostRegexpMatch[1]) {
const innerRegex = hostRegexpMatch[1];
// Pattern is always ^[^.]+\.PARENT_DOMAIN$ where dots are escaped as \.
const domainMatch = innerRegex.match(
/^\^\[\^\.\]\+\\\.(.+)\$$/
);
if (domainMatch && domainMatch[1]) {
const parentDomain = domainMatch[1].replace(
/\\\./g,
"."
);
domains.add(`*.${parentDomain}`);
}
}
} }
} }
} }

View File

@@ -11,7 +11,7 @@ export async function createNextServer() {
// const app = next({ dev }); // const app = next({ dev });
const app = next({ const app = next({
dev: process.env.ENVIRONMENT !== "prod", dev: process.env.ENVIRONMENT !== "prod",
turbopack: true turbopack: false
}); });
const handle = app.getRequestHandler(); const handle = app.getRequestHandler();
@@ -29,7 +29,7 @@ export async function createNextServer() {
nextServer.listen(nextPort, (err?: any) => { nextServer.listen(nextPort, (err?: any) => {
if (err) throw err; if (err) throw err;
logger.info( logger.info(
`Next.js server is running on http://localhost:${nextPort}` `Dashboard Web UI server is running on http://localhost:${nextPort}`
); );
}); });

View File

@@ -12,6 +12,7 @@
*/ */
import fs from "fs"; import fs from "fs";
import path from "path";
import crypto from "crypto"; import crypto from "crypto";
import { import {
certificates, certificates,
@@ -50,7 +51,7 @@ interface AcmeJson {
}; };
} }
async function pushCertUpdateToAffectedNewts( export async function pushCertUpdateToAffectedNewts(
domain: string, domain: string,
domainId: string | null, domainId: string | null,
oldCertPem: string | null, oldCertPem: string | null,
@@ -89,7 +90,7 @@ async function pushCertUpdateToAffectedNewts(
return; return;
} }
logger.info( logger.debug(
`acmeCertSync: pushing cert update to ${affectedResources.length} affected site resource(s) for domain "${domain}"` `acmeCertSync: pushing cert update to ${affectedResources.length} affected site resource(s) for domain "${domain}"`
); );
@@ -187,7 +188,7 @@ async function pushCertUpdateToAffectedNewts(
newt.version newt.version
); );
logger.info( logger.debug(
`acmeCertSync: pushed cert update to newt for site ${siteId}, resource ${resource.siteResourceId}` `acmeCertSync: pushed cert update to newt for site ${siteId}, resource ${resource.siteResourceId}`
); );
} }
@@ -250,62 +251,125 @@ function extractFirstCert(pemBundle: string): string | null {
return match ? match[0] : null; return match ? match[0] : null;
} }
async function syncAcmeCerts( /**
acmeJsonPath: string, * Determine whether an ACME cert entry represents a wildcard cert by checking
resolver: string * both the primary domain (`main`) and the SANs. Some ACME clients (notably
): Promise<void> { * Traefik) store the bare apex in `main` and only put the wildcard form in
let raw: string; * `sans` (e.g. main="access.example.com", sans=["*.access.example.com"]).
try { */
raw = fs.readFileSync(acmeJsonPath, "utf8"); function detectWildcard(
} catch (err) { main: string,
logger.debug(`acmeCertSync: could not read ${acmeJsonPath}: ${err}`); sans: string[] | undefined
return; ): { wildcard: boolean; wildcardSan: string | null } {
if (main.startsWith("*.")) {
return { wildcard: true, wildcardSan: null };
} }
if (Array.isArray(sans)) {
let acmeJson: AcmeJson; for (const san of sans) {
try { if (typeof san !== "string") continue;
acmeJson = JSON.parse(raw); if (san === `*.${main}` || san.startsWith("*.")) {
} catch (err) { return { wildcard: true, wildcardSan: san };
logger.debug(`acmeCertSync: could not parse acme.json: ${err}`); }
return; }
} }
return { wildcard: false, wildcardSan: null };
}
const resolverData = acmeJson[resolver]; interface HttpCert {
if (!resolverData || !Array.isArray(resolverData.Certificates)) { wildcard: boolean;
altName: string;
certName: string;
commonName: string;
certFile: string;
keyFile: string;
}
async function syncAcmeCertsFromHttp(endpoint: string): Promise<void> {
let response: Response;
try {
response = await fetch(endpoint);
} catch (err) {
logger.debug( logger.debug(
`acmeCertSync: no certificates found for resolver "${resolver}"` `acmeCertSync: could not reach HTTP endpoint ${endpoint}: ${err}`
); );
return; return;
} }
for (const cert of resolverData.Certificates) { if (!response.ok) {
const domain = cert.domain?.main; logger.debug(
`acmeCertSync: HTTP endpoint returned status ${response.status}`
if (!domain) {
logger.debug(`acmeCertSync: skipping cert with missing domain`);
continue;
}
if (!cert.certificate || !cert.key) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - empty certificate or key field`
);
continue;
}
const certPem = Buffer.from(cert.certificate, "base64").toString(
"utf8"
); );
const keyPem = Buffer.from(cert.key, "base64").toString("utf8"); return;
}
if (!certPem.trim() || !keyPem.trim()) { let httpCerts: HttpCert[];
try {
httpCerts = await response.json();
} catch (err) {
logger.debug(
`acmeCertSync: could not parse JSON from HTTP endpoint: ${err}`
);
return;
}
if (!Array.isArray(httpCerts) || httpCerts.length === 0) {
logger.debug(
`acmeCertSync: no certificates returned from HTTP endpoint`
);
return;
}
for (const cert of httpCerts) {
const domain = cert?.certName;
if (!domain || typeof domain !== "string") {
logger.debug( logger.debug(
`acmeCertSync: skipping cert for ${domain} - blank PEM after base64 decode` `acmeCertSync: skipping HTTP cert with missing certName`
); );
continue; continue;
} }
// Check if cert already exists in DB const certPem = cert.certFile;
const keyPem = cert.keyFile;
if (!certPem?.trim() || !keyPem?.trim()) {
logger.debug(
`acmeCertSync: skipping HTTP cert for ${domain} - empty certFile or keyFile`
);
continue;
}
const firstCertPemForValidation = extractFirstCert(certPem);
if (!firstCertPemForValidation) {
logger.debug(
`acmeCertSync: skipping HTTP cert for ${domain} - no PEM certificate block found`
);
continue;
}
let validatedX509: crypto.X509Certificate;
try {
validatedX509 = new crypto.X509Certificate(
firstCertPemForValidation
);
} catch (err) {
logger.debug(
`acmeCertSync: skipping HTTP cert for ${domain} - invalid X.509 certificate: ${err}`
);
continue;
}
try {
crypto.createPrivateKey(keyPem);
} catch (err) {
logger.debug(
`acmeCertSync: skipping HTTP cert for ${domain} - invalid private key: ${err}`
);
continue;
}
const wildcard = cert.wildcard ?? false;
const existing = await db const existing = await db
.select() .select()
.from(certificates) .from(certificates)
@@ -321,10 +385,262 @@ async function syncAcmeCerts(
existing[0].certFile, existing[0].certFile,
config.getRawConfig().server.secret! config.getRawConfig().server.secret!
); );
if (storedCertPem === certPem) { const wildcardUnchanged = existing[0].wildcard === wildcard;
logger.debug( if (storedCertPem === certPem && wildcardUnchanged) {
`acmeCertSync: cert for ${domain} is unchanged, skipping` continue;
); }
oldCertPem = storedCertPem;
if (existing[0].keyFile) {
try {
oldKeyPem = decrypt(
existing[0].keyFile,
config.getRawConfig().server.secret!
);
} catch (keyErr) {
logger.debug(
`acmeCertSync: could not decrypt stored key for ${domain}: ${keyErr}`
);
}
}
} catch (err) {
logger.debug(
`acmeCertSync: could not decrypt stored cert for ${domain}, will update: ${err}`
);
}
}
let expiresAt: number | null = null;
try {
expiresAt = Math.floor(
new Date(validatedX509.validTo).getTime() / 1000
);
} catch (err) {
logger.debug(
`acmeCertSync: could not parse cert expiry for ${domain}: ${err}`
);
}
const encryptedCert = encrypt(
certPem,
config.getRawConfig().server.secret!
);
const encryptedKey = encrypt(
keyPem,
config.getRawConfig().server.secret!
);
const now = Math.floor(Date.now() / 1000);
const domainId = await findDomainId(domain);
if (domainId) {
logger.debug(
`acmeCertSync: resolved domainId "${domainId}" for HTTP cert domain "${domain}"`
);
} else {
logger.debug(
`acmeCertSync: no matching domain record found for HTTP cert domain "${domain}"`
);
}
if (existing.length > 0) {
logger.debug(
`acmeCertSync: updating existing certificate (HTTP) for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
);
await db
.update(certificates)
.set({
certFile: encryptedCert,
keyFile: encryptedKey,
status: "valid",
expiresAt,
updatedAt: now,
wildcard,
...(domainId !== null && { domainId })
})
.where(eq(certificates.domain, domain));
await pushCertUpdateToAffectedNewts(
domain,
domainId,
oldCertPem,
oldKeyPem
);
} else {
logger.debug(
`acmeCertSync: inserting new certificate (HTTP) for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
);
await db.insert(certificates).values({
domain,
domainId,
certFile: encryptedCert,
keyFile: encryptedKey,
status: "valid",
expiresAt,
createdAt: now,
updatedAt: now,
wildcard
});
await pushCertUpdateToAffectedNewts(domain, domainId, null, null);
}
}
}
function findAcmeJsonFiles(dirPath: string): string[] {
const results: string[] = [];
let entries: fs.Dirent[];
try {
entries = fs.readdirSync(dirPath, { withFileTypes: true });
} catch (err) {
logger.warn(
`acmeCertSync: could not read directory "${dirPath}": ${err}`
);
return results;
}
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
results.push(...findAcmeJsonFiles(fullPath));
} else if (entry.isFile() && entry.name === "acme.json") {
results.push(fullPath);
}
}
return results;
}
async function syncAcmeCerts(acmeJsonPath: string): Promise<void> {
let raw: string;
try {
raw = fs.readFileSync(acmeJsonPath, "utf8");
} catch (err) {
logger.warn(`acmeCertSync: could not read "${acmeJsonPath}": ${err}`);
return;
}
let acmeJson: AcmeJson;
try {
acmeJson = JSON.parse(raw);
} catch (err) {
logger.warn(
`acmeCertSync: could not parse "${acmeJsonPath}" as JSON: ${err}`
);
return;
}
const resolvers = Object.keys(acmeJson || {});
if (resolvers.length === 0) {
logger.debug(`acmeCertSync: no resolvers found in acme.json`);
return;
}
// Collect certificates from every resolver. If the same domain appears in
// multiple resolvers, the last one wins (resolvers iterated in object order).
const allCerts: AcmeCert[] = [];
for (const resolver of resolvers) {
const resolverData = acmeJson[resolver];
if (!resolverData || !Array.isArray(resolverData.Certificates)) {
logger.debug(
`acmeCertSync: no certificates found for resolver "${resolver}"`
);
continue;
}
logger.debug(
`acmeCertSync: found ${resolverData.Certificates.length} certificate(s) for resolver "${resolver}"`
);
for (const cert of resolverData.Certificates) {
allCerts.push(cert);
}
}
for (const cert of allCerts) {
const domain = cert?.domain?.main;
if (!domain || typeof domain !== "string") {
logger.debug(`acmeCertSync: skipping cert with missing domain`);
continue;
}
const { wildcard } = detectWildcard(domain, cert.domain?.sans);
if (!cert.certificate || !cert.key) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - empty certificate or key field`
);
continue;
}
let certPem: string;
let keyPem: string;
try {
certPem = Buffer.from(cert.certificate, "base64").toString("utf8");
keyPem = Buffer.from(cert.key, "base64").toString("utf8");
} catch (err) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - failed to base64-decode cert/key: ${err}`
);
continue;
}
if (!certPem.trim() || !keyPem.trim()) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - blank PEM after base64 decode`
);
continue;
}
// Validate that the decoded data actually parses as a real X.509 cert
// before we touch the database. This prevents importing partially-written
// or corrupted entries from acme.json.
const firstCertPemForValidation = extractFirstCert(certPem);
if (!firstCertPemForValidation) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - no PEM certificate block found`
);
continue;
}
let validatedX509: crypto.X509Certificate;
try {
validatedX509 = new crypto.X509Certificate(
firstCertPemForValidation
);
} catch (err) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - invalid X.509 certificate: ${err}`
);
continue;
}
// Sanity-check the private key parses too
try {
crypto.createPrivateKey(keyPem);
} catch (err) {
logger.debug(
`acmeCertSync: skipping cert for ${domain} - invalid private key: ${err}`
);
continue;
}
// Check if cert already exists in DB
const existing = await db
.select()
.from(certificates)
.where(and(eq(certificates.domain, domain)))
.limit(1);
let oldCertPem: string | null = null;
let oldKeyPem: string | null = null;
if (existing.length > 0 && existing[0].certFile) {
try {
const storedCertPem = decrypt(
existing[0].certFile,
config.getRawConfig().server.secret!
);
const wildcardUnchanged = existing[0].wildcard === wildcard;
if (storedCertPem === certPem && wildcardUnchanged) {
// logger.debug(
// `acmeCertSync: cert for ${domain} is unchanged, skipping`
// );
continue; continue;
} }
// Cert has changed; capture old values so we can send a correct // Cert has changed; capture old values so we can send a correct
@@ -350,21 +666,18 @@ async function syncAcmeCerts(
} }
} }
// Parse cert expiry from the first cert in the PEM bundle // Parse cert expiry from the validated X.509 certificate
let expiresAt: number | null = null; let expiresAt: number | null = null;
const firstCertPem = extractFirstCert(certPem); try {
if (firstCertPem) { expiresAt = Math.floor(
try { new Date(validatedX509.validTo).getTime() / 1000
const x509 = new crypto.X509Certificate(firstCertPem); );
expiresAt = Math.floor(new Date(x509.validTo).getTime() / 1000); } catch (err) {
} catch (err) { logger.debug(
logger.debug( `acmeCertSync: could not parse cert expiry for ${domain}: ${err}`
`acmeCertSync: could not parse cert expiry for ${domain}: ${err}` );
);
}
} }
const wildcard = domain.startsWith("*.");
const encryptedCert = encrypt( const encryptedCert = encrypt(
certPem, certPem,
config.getRawConfig().server.secret! config.getRawConfig().server.secret!
@@ -387,6 +700,9 @@ async function syncAcmeCerts(
} }
if (existing.length > 0) { if (existing.length > 0) {
logger.debug(
`acmeCertSync: updating existing certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
);
await db await db
.update(certificates) .update(certificates)
.set({ .set({
@@ -400,7 +716,7 @@ async function syncAcmeCerts(
}) })
.where(eq(certificates.domain, domain)); .where(eq(certificates.domain, domain));
logger.info( logger.debug(
`acmeCertSync: updated certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})` `acmeCertSync: updated certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
); );
@@ -411,6 +727,9 @@ async function syncAcmeCerts(
oldKeyPem oldKeyPem
); );
} else { } else {
logger.debug(
`acmeCertSync: inserting new certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
);
await db.insert(certificates).values({ await db.insert(certificates).values({
domain, domain,
domainId, domainId,
@@ -423,7 +742,7 @@ async function syncAcmeCerts(
wildcard wildcard
}); });
logger.info( logger.debug(
`acmeCertSync: inserted new certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})` `acmeCertSync: inserted new certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
); );
@@ -458,21 +777,63 @@ export function initAcmeCertSync(): void {
const acmeJsonPath = const acmeJsonPath =
privateConfigData.acme?.acme_json_path ?? privateConfigData.acme?.acme_json_path ??
"config/letsencrypt/acme.json"; "config/letsencrypt/acme.json";
const resolver = privateConfigData.acme?.resolver ?? "letsencrypt";
const intervalMs = privateConfigData.acme?.sync_interval_ms ?? 5000; const intervalMs = privateConfigData.acme?.sync_interval_ms ?? 5000;
const httpEndpoint = privateConfigData.acme?.acme_http_endpoint;
logger.info( logger.debug(
`acmeCertSync: starting ACME cert sync from "${acmeJsonPath}" using resolver "${resolver}" every ${intervalMs}ms` `acmeCertSync: starting ACME cert sync from "${acmeJsonPath}" across all resolvers every ${intervalMs}ms`
); );
if (httpEndpoint) {
logger.debug(
`acmeCertSync: also syncing from HTTP endpoint "${httpEndpoint}" every ${intervalMs}ms`
);
}
const runSync = () => {
if (httpEndpoint) {
syncAcmeCertsFromHttp(httpEndpoint).catch((err) => {
logger.error(`acmeCertSync: error during HTTP sync: ${err}`);
});
} else {
// only run the file-based sync if the HTTP endpoint is not configured, to avoid doubling up
let stat: fs.Stats | null = null;
try {
stat = fs.statSync(acmeJsonPath);
} catch (err) {
logger.warn(
`acmeCertSync: cannot stat path "${acmeJsonPath}": ${err}`
);
return;
}
if (stat.isDirectory()) {
const files = findAcmeJsonFiles(acmeJsonPath);
if (files.length === 0) {
logger.debug(
`acmeCertSync: no acme.json files found in directory "${acmeJsonPath}"`
);
return;
}
logger.debug(
`acmeCertSync: found ${files.length} acme.json file(s) in directory "${acmeJsonPath}"`
);
for (const file of files) {
syncAcmeCerts(file).catch((err) => {
logger.error(
`acmeCertSync: error during sync of "${file}": ${err}`
);
});
}
} else {
syncAcmeCerts(acmeJsonPath).catch((err) => {
logger.error(`acmeCertSync: error during sync: ${err}`);
});
}
}
};
// Run immediately on init, then on the configured interval // Run immediately on init, then on the configured interval
syncAcmeCerts(acmeJsonPath, resolver).catch((err) => { runSync();
logger.error(`acmeCertSync: error during initial sync: ${err}`);
});
setInterval(() => { setInterval(runSync, intervalMs);
syncAcmeCerts(acmeJsonPath, resolver).catch((err) => {
logger.error(`acmeCertSync: error during sync: ${err}`);
});
}, intervalMs);
} }

View File

@@ -1,91 +0,0 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025-2026 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 logger from "@server/logger";
import { processAlerts } from "../processAlerts";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Fire a `health_check_healthy` alert for the given health check.
*
* Call this after a previously-failing health check has recovered so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the health check.
* @param healthCheckId - Numeric primary key of the health check.
* @param healthCheckName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireHealthCheckHealthyAlert(
orgId: string,
healthCheckId: number,
healthCheckName?: string | null,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "health_check_healthy",
orgId,
healthCheckId,
data: {
healthCheckId,
...(healthCheckName != null ? { healthCheckName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireHealthCheckHealthyAlert: unexpected error for healthCheckId ${healthCheckId}`,
err
);
}
}
/**
* Fire a `health_check_unhealthy` alert for the given health check.
*
* Call this after a health check has been detected as failing so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the health check.
* @param healthCheckId - Numeric primary key of the health check.
* @param healthCheckName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireHealthCheckNotHealthyAlert(
orgId: string,
healthCheckId: number,
healthCheckName?: string | null,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "health_check_unhealthy",
orgId,
healthCheckId,
data: {
healthCheckId,
...(healthCheckName != null ? { healthCheckName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireHealthCheckNotHealthyAlert: unexpected error for healthCheckId ${healthCheckId}`,
err
);
}
}

View File

@@ -1,127 +0,0 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025-2026 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 logger from "@server/logger";
import { processAlerts } from "../processAlerts";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Fire a `resource_healthy` alert for the given resource.
*
* Call this after a previously-unhealthy resource has recovered so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceHealthyAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "resource_healthy",
orgId,
resourceId,
data: {
resourceId,
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceHealthyAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}
/**
* Fire a `resource_unhealthy` alert for the given resource.
*
* Call this after a resource has been detected as unhealthy so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceUnhealthyAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "resource_unhealthy",
orgId,
resourceId,
data: {
resourceId,
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceUnhealthyAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}
/**
* Fire a `resource_toggle` alert for the given resource.
*
* Call this when a resource's enabled/disabled status is toggled so that any
* matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the resource.
* @param resourceId - Numeric primary key of the resource.
* @param resourceName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireResourceToggleAlert(
orgId: string,
resourceId: number,
resourceName?: string | null,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "resource_toggle",
orgId,
resourceId,
data: {
resourceId,
...(resourceName != null ? { resourceName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireResourceToggleAlert: unexpected error for resourceId ${resourceId}`,
err
);
}
}

View File

@@ -1,91 +0,0 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025-2026 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 logger from "@server/logger";
import { processAlerts } from "../processAlerts";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Fire a `site_online` alert for the given site.
*
* Call this after the site has been confirmed reachable / connected so that
* any matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the site.
* @param siteId - Numeric primary key of the site.
* @param siteName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireSiteOnlineAlert(
orgId: string,
siteId: number,
siteName?: string,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "site_online",
orgId,
siteId,
data: {
siteId,
...(siteName != null ? { siteName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireSiteOnlineAlert: unexpected error for siteId ${siteId}`,
err
);
}
}
/**
* Fire a `site_offline` alert for the given site.
*
* Call this after the site has been detected as unreachable / disconnected so
* that any matching `alertRules` can dispatch their email and webhook actions.
*
* @param orgId - Organisation that owns the site.
* @param siteId - Numeric primary key of the site.
* @param siteName - Human-readable name shown in notifications (optional).
* @param extra - Any additional key/value pairs to include in the payload.
*/
export async function fireSiteOfflineAlert(
orgId: string,
siteId: number,
siteName?: string,
extra?: Record<string, unknown>
): Promise<void> {
try {
await processAlerts({
eventType: "site_offline",
orgId,
siteId,
data: {
siteId,
...(siteName != null ? { siteName } : {}),
...extra
}
});
} catch (err) {
logger.error(
`fireSiteOfflineAlert: unexpected error for siteId ${siteId}`,
err
);
}
}

View File

@@ -11,9 +11,6 @@
* This file is not licensed under the AGPLv3. * This file is not licensed under the AGPLv3.
*/ */
export * from "./types";
export * from "./processAlerts"; export * from "./processAlerts";
export * from "./sendAlertWebhook"; export * from "./sendAlertWebhook";
export * from "./sendAlertEmail"; export * from "./sendAlertEmail";
export * from "./events/siteEvents";
export * from "./events/healthCheckEvents";

View File

@@ -27,9 +27,9 @@ import {
import config from "@server/lib/config"; import config from "@server/lib/config";
import { decrypt } from "@server/lib/crypto"; import { decrypt } from "@server/lib/crypto";
import logger from "@server/logger"; import logger from "@server/logger";
import { AlertContext, WebhookAlertConfig } from "./types";
import { sendAlertWebhook } from "./sendAlertWebhook"; import { sendAlertWebhook } from "./sendAlertWebhook";
import { sendAlertEmail } from "./sendAlertEmail"; import { sendAlertEmail } from "./sendAlertEmail";
import { AlertContext, WebhookAlertConfig } from "@server/routers/alertRule/types";
/** /**
* Core alert processing pipeline. * Core alert processing pipeline.
@@ -330,4 +330,4 @@ async function resolveEmailRecipients(emailActionId: number): Promise<string[]>
} }
return Array.from(emailSet); return Array.from(emailSet);
} }

View File

@@ -15,7 +15,7 @@ import { sendEmail } from "@server/emails";
import AlertNotification from "@server/emails/templates/AlertNotification"; import AlertNotification from "@server/emails/templates/AlertNotification";
import config from "@server/lib/config"; import config from "@server/lib/config";
import logger from "@server/logger"; import logger from "@server/logger";
import { AlertContext } from "./types"; import { AlertContext } from "@server/routers/alertRule/types";
/** /**
* Sends an alert notification email to every address in `recipients`. * Sends an alert notification email to every address in `recipients`.
@@ -36,13 +36,17 @@ export async function sendAlertEmail(
const from = config.getNoReplyEmail(); const from = config.getNoReplyEmail();
const subject = buildSubject(context); const subject = buildSubject(context);
const baseUrl = config.getRawConfig().app.dashboard_url!.replace(/\/$/, "");
const dashboardLink = `${baseUrl}/${context.orgId}/settings`;
for (const to of recipients) { for (const to of recipients) {
try { try {
await sendEmail( await sendEmail(
AlertNotification({ AlertNotification({
eventType: context.eventType, eventType: context.eventType,
orgId: context.orgId, orgId: context.orgId,
data: context.data data: context.data,
dashboardLink
}), }),
{ {
from, from,
@@ -84,6 +88,8 @@ function buildSubject(context: AlertContext): string {
return "[Alert] Resource Healthy"; return "[Alert] Resource Healthy";
case "resource_unhealthy": case "resource_unhealthy":
return "[Alert] Resource Unhealthy"; return "[Alert] Resource Unhealthy";
case "resource_degraded":
return "[Alert] Resource Degraded";
case "resource_toggle": case "resource_toggle":
return "[Alert] Resource Status Changed"; return "[Alert] Resource Status Changed";
default: { default: {

View File

@@ -12,9 +12,14 @@
*/ */
import logger from "@server/logger"; import logger from "@server/logger";
import { AlertContext, WebhookAlertConfig } from "./types"; import {
AlertContext,
WebhookAlertConfig
} from "@server/routers/alertRule/types";
const REQUEST_TIMEOUT_MS = 15_000; const REQUEST_TIMEOUT_MS = 15_000;
const MAX_RETRIES = 3;
const RETRY_BASE_DELAY_MS = 500;
/** /**
* Sends a single webhook POST for an alert event. * Sends a single webhook POST for an alert event.
@@ -37,64 +42,144 @@ export async function sendAlertWebhook(
webhookConfig: WebhookAlertConfig, webhookConfig: WebhookAlertConfig,
context: AlertContext context: AlertContext
): Promise<void> { ): Promise<void> {
const payload = { const eventType = context.eventType;
event: context.eventType, const timestamp = new Date().toISOString();
timestamp: new Date().toISOString(), const status = deriveStatus(eventType, context.data);
data: { const data = { orgId: context.orgId, ...context.data };
orgId: context.orgId,
...context.data let body: string;
} if (webhookConfig.useBodyTemplate && webhookConfig.bodyTemplate?.trim()) {
}; body = renderTemplate(webhookConfig.bodyTemplate, {
event: eventType,
timestamp,
status,
data
});
} else {
body = JSON.stringify({ event: eventType, timestamp, status, data });
}
const body = JSON.stringify(payload);
const headers = buildHeaders(webhookConfig); const headers = buildHeaders(webhookConfig);
const controller = new AbortController(); let lastError: Error | undefined;
const timeoutHandle = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
let response: Response; for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try { const controller = new AbortController();
response = await fetch(url, { const timeoutHandle = setTimeout(
method: webhookConfig.method ?? "POST", () => controller.abort(),
headers, REQUEST_TIMEOUT_MS
body,
signal: controller.signal
});
} catch (err: unknown) {
const isAbort = err instanceof Error && err.name === "AbortError";
if (isAbort) {
throw new Error(
`Alert webhook: request to "${url}" timed out after ${REQUEST_TIMEOUT_MS} ms`
);
}
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`Alert webhook: request to "${url}" failed ${msg}`);
} finally {
clearTimeout(timeoutHandle);
}
if (!response.ok) {
let snippet = "";
try {
const text = await response.text();
snippet = text.slice(0, 300);
} catch {
// best-effort
}
throw new Error(
`Alert webhook: server at "${url}" returned HTTP ${response.status} ${response.statusText}` +
(snippet ? ` ${snippet}` : "")
); );
let response: Response;
try {
response = await fetch(url, {
method: webhookConfig.method ?? "POST",
headers,
body,
signal: controller.signal
});
} catch (err: unknown) {
clearTimeout(timeoutHandle);
const isAbort = err instanceof Error && err.name === "AbortError";
if (isAbort) {
lastError = new Error(
`Alert webhook: request to "${url}" timed out after ${REQUEST_TIMEOUT_MS} ms`
);
} else {
const msg = err instanceof Error ? err.message : String(err);
lastError = new Error(
`Alert webhook: request to "${url}" failed ${msg}`
);
}
if (attempt < MAX_RETRIES) {
const delay = RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
logger.warn(
`Alert webhook: attempt ${attempt}/${MAX_RETRIES} failed retrying in ${delay} ms. ${lastError.message}`
);
await new Promise((resolve) => setTimeout(resolve, delay));
}
continue;
} finally {
clearTimeout(timeoutHandle);
}
if (!response.ok) {
let snippet = "";
try {
const text = await response.text();
snippet = text.slice(0, 300);
} catch {
// best-effort
}
lastError = new Error(
`Alert webhook: server at "${url}" returned HTTP ${response.status} ${response.statusText}` +
(snippet ? ` ${snippet}` : "")
);
if (attempt < MAX_RETRIES) {
const delay = RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
logger.warn(
`Alert webhook: attempt ${attempt}/${MAX_RETRIES} failed retrying in ${delay} ms. ${lastError.message}`
);
await new Promise((resolve) => setTimeout(resolve, delay));
}
continue;
}
logger.debug(
`Alert webhook sent successfully to "${url}" for event "${context.eventType}" (attempt ${attempt}/${MAX_RETRIES})`
);
return;
} }
logger.debug(`Alert webhook sent successfully to "${url}" for event "${context.eventType}"`); throw (
lastError ??
new Error(
`Alert webhook: all ${MAX_RETRIES} attempts failed for "${url}"`
)
);
}
// ---------------------------------------------------------------------------
// Status derivation
// ---------------------------------------------------------------------------
function deriveStatus(
eventType: AlertContext["eventType"],
data: Record<string, unknown>
): string {
switch (eventType) {
case "site_online":
return "online";
case "site_offline":
return "offline";
case "site_toggle":
return String(data.status ?? "unknown");
case "health_check_healthy":
case "resource_healthy":
return "healthy";
case "health_check_unhealthy":
case "resource_unhealthy":
return "unhealthy";
case "resource_degraded":
return "degraded";
case "health_check_toggle":
case "resource_toggle":
return String(data.status ?? "unknown");
default: {
const _exhaustive: never = eventType;
void _exhaustive;
return "unknown";
}
}
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Header construction (mirrors HttpLogDestination.buildHeaders) // Header construction (mirrors HttpLogDestination.buildHeaders)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function buildHeaders(webhookConfig: WebhookAlertConfig): Record<string, string> { function buildHeaders(
webhookConfig: WebhookAlertConfig
): Record<string, string> {
const headers: Record<string, string> = { const headers: Record<string, string> = {
"Content-Type": "application/json" "Content-Type": "application/json"
}; };
@@ -137,4 +222,53 @@ function buildHeaders(webhookConfig: WebhookAlertConfig): Record<string, string>
} }
return headers; return headers;
} }
// ---------------------------------------------------------------------------
// Body template rendering
// ---------------------------------------------------------------------------
interface TemplateContext {
event: string;
timestamp: string;
status: string;
data: Record<string, unknown>;
}
/**
* Render a body template with {{event}}, {{timestamp}}, {{status}}, and
* {{data}} placeholders, mirroring the logic in HttpLogDestination.
*
* {{data}} is replaced first (as raw JSON) so that any literal "{{…}}"
* strings inside data values are not re-expanded.
*/
function renderTemplate(template: string, ctx: TemplateContext): string {
const rendered = template
.replace(/\{\{data\}\}/g, JSON.stringify(ctx.data))
.replace(/\{\{event\}\}/g, escapeJsonString(ctx.event))
.replace(/\{\{timestamp\}\}/g, escapeJsonString(ctx.timestamp))
.replace(/\{\{status\}\}/g, escapeJsonString(ctx.status));
// Validate the rendered result is valid JSON; if not, log a warning and
// fall back to the default payload so the webhook still fires.
try {
JSON.parse(rendered);
return rendered;
} catch {
logger.warn(
`sendAlertWebhook: body template produced invalid JSON for event ` +
`"${ctx.event}" destined for a webhook. Falling back to default ` +
`payload. Check that {{data}} is NOT wrapped in quotes in your template.`
);
return JSON.stringify({
event: ctx.event,
timestamp: ctx.timestamp,
status: ctx.status,
data: ctx.data
});
}
}
function escapeJsonString(value: string): string {
return JSON.stringify(value).slice(1, -1);
}

View File

@@ -18,13 +18,8 @@
export type AlertEventType = export type AlertEventType =
| "site_online" | "site_online"
| "site_offline" | "site_offline"
| "site_toggle"
| "health_check_healthy" | "health_check_healthy"
| "health_check_unhealthy" | "health_check_not_healthy";
| "health_check_toggle"
| "resource_healthy"
| "resource_unhealthy"
| "resource_toggle";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Webhook authentication config (stored as encrypted JSON in the DB) // Webhook authentication config (stored as encrypted JSON in the DB)
@@ -50,6 +45,10 @@ export interface WebhookAlertConfig {
headers?: Array<{ key: string; value: string }>; headers?: Array<{ key: string; value: string }>;
/** HTTP method (default POST) */ /** HTTP method (default POST) */
method?: string; method?: string;
/** Whether to use a custom body template */
useBodyTemplate?: boolean;
/** Mustache-style body template with {{event}}, {{timestamp}}, {{status}}, {{data}} placeholders */
bodyTemplate?: string;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -63,8 +62,6 @@ export interface AlertContext {
siteId?: number; siteId?: number;
/** Set for health_check_* events */ /** Set for health_check_* events */
healthCheckId?: number; healthCheckId?: number;
/** Set for resource_* events */
resourceId?: number;
/** Human-readable context data included in emails and webhook payloads */ /** Human-readable context data included in emails and webhook payloads */
data: Record<string, unknown>; data: Record<string, unknown>;
} }

View File

@@ -19,12 +19,13 @@ import { eq, and, ne } from "drizzle-orm";
export async function getOrgTierData( export async function getOrgTierData(
orgId: string orgId: string
): Promise<{ tier: Tier | null; active: boolean }> { ): Promise<{ tier: Tier | null; active: boolean; isTrial: boolean }> {
let tier: Tier | null = null; let tier: Tier | null = null;
let active = false; let active = false;
let isTrial = false;
if (build !== "saas") { if (build !== "saas") {
return { tier, active }; return { tier, active, isTrial };
} }
try { try {
@@ -35,7 +36,7 @@ export async function getOrgTierData(
.limit(1); .limit(1);
if (!org) { if (!org) {
return { tier, active }; return { tier, active, isTrial };
} }
let orgIdToUse = org.orgId; let orgIdToUse = org.orgId;
@@ -44,7 +45,7 @@ export async function getOrgTierData(
logger.warn( logger.warn(
`Org ${orgId} is not a billing org and does not have a billingOrgId` `Org ${orgId} is not a billing org and does not have a billingOrgId`
); );
return { tier, active }; return { tier, active, isTrial };
} }
orgIdToUse = org.billingOrgId; orgIdToUse = org.billingOrgId;
} }
@@ -57,7 +58,7 @@ export async function getOrgTierData(
.limit(1); .limit(1);
if (!customer) { if (!customer) {
return { tier, active }; return { tier, active, isTrial };
} }
// Query for active subscriptions that are not license type // Query for active subscriptions that are not license type
@@ -84,11 +85,13 @@ export async function getOrgTierData(
tier = subscription.type; tier = subscription.type;
active = true; active = true;
} }
isTrial = subscription.trial ?? false;
} }
} catch (error) { } catch (error) {
// If org not found or error occurs, return null tier and inactive // If org not found or error occurs, return null tier and inactive
// This is acceptable behavior as per the function signature // This is acceptable behavior as per the function signature
} }
return { tier, active }; return { tier, active, isTrial };
} }

View File

@@ -18,8 +18,7 @@ import { and, eq, isNotNull, or, inArray, sql } from "drizzle-orm";
import { decrypt } from "@server/lib/crypto"; import { decrypt } from "@server/lib/crypto";
import logger from "@server/logger"; import logger from "@server/logger";
import cache from "#private/lib/cache"; import cache from "#private/lib/cache";
import { build } from "@server/build";
// Define the return type for clarity and type safety // Define the return type for clarity and type safety
export type CertificateResult = { export type CertificateResult = {
@@ -78,6 +77,9 @@ export async function getValidCertificatesForDomains(
const parentDomainsArray = Array.from(parentDomainsToQuery); const parentDomainsArray = Array.from(parentDomainsToQuery);
// Build wildcard variants: for each parent domain "example.com", also query "*.example.com"
const wildcardPrefixedArray = build != "saas" ? parentDomainsArray.map((d) => `*.${d}`) : [];
// 4. Build and execute a single, efficient Drizzle query // 4. Build and execute a single, efficient Drizzle query
// This query fetches all potential exact and wildcard matches in one database round-trip. // This query fetches all potential exact and wildcard matches in one database round-trip.
const potentialCerts = await db const potentialCerts = await db
@@ -91,10 +93,13 @@ export async function getValidCertificatesForDomains(
or( or(
// Condition for exact matches on the requested domains // Condition for exact matches on the requested domains
inArray(certificates.domain, domainsToQueryArray), inArray(certificates.domain, domainsToQueryArray),
// Condition for wildcard matches on the parent domains // Condition for wildcard matches on the parent domains (stored as "example.com" or "*.example.com")
parentDomainsArray.length > 0 parentDomainsArray.length > 0
? and( ? and(
inArray(certificates.domain, parentDomainsArray), inArray(certificates.domain, [
...parentDomainsArray,
...wildcardPrefixedArray
]),
eq(certificates.wildcard, true) eq(certificates.wildcard, true)
) )
: // If there are no possible parent domains, this condition is false : // If there are no possible parent domains, this condition is false
@@ -103,13 +108,18 @@ export async function getValidCertificatesForDomains(
) )
); );
// Helper to normalize a wildcard cert's domain to its bare parent domain (strips leading "*.")
const normalizeWildcardDomain = (domain: string): string =>
domain.startsWith("*.") ? domain.slice(2) : domain;
// 5. Process the database results, prioritizing exact matches over wildcards // 5. Process the database results, prioritizing exact matches over wildcards
const exactMatches = new Map<string, (typeof potentialCerts)[0]>(); const exactMatches = new Map<string, (typeof potentialCerts)[0]>();
const wildcardMatches = new Map<string, (typeof potentialCerts)[0]>(); const wildcardMatches = new Map<string, (typeof potentialCerts)[0]>();
for (const cert of potentialCerts) { for (const cert of potentialCerts) {
if (cert.wildcard) { if (cert.wildcard) {
wildcardMatches.set(cert.domain, cert); // Normalize to bare parent domain so lookups are consistent regardless of storage format
wildcardMatches.set(normalizeWildcardDomain(cert.domain), cert);
} else { } else {
exactMatches.set(cert.domain, cert); exactMatches.set(cert.domain, cert);
} }
@@ -122,14 +132,15 @@ export async function getValidCertificatesForDomains(
if (exactMatches.has(domain)) { if (exactMatches.has(domain)) {
foundCert = exactMatches.get(domain); foundCert = exactMatches.get(domain);
} }
// Priority 2: Check for a wildcard certificate that matches the exact domain // Priority 2: Check for a wildcard certificate whose normalized domain equals the queried domain
else { else {
if (wildcardMatches.has(domain)) { const normalizedDomain = normalizeWildcardDomain(domain);
foundCert = wildcardMatches.get(domain); if (wildcardMatches.has(normalizedDomain)) {
foundCert = wildcardMatches.get(normalizedDomain);
} }
// Priority 3: Check for a wildcard match on the parent domain // Priority 3: Check for a wildcard match on the parent domain
else { else {
const parts = domain.split("."); const parts = normalizedDomain.split(".");
if (parts.length > 1) { if (parts.length > 1) {
const parentDomain = parts.slice(1).join("."); const parentDomain = parts.slice(1).join(".");
if (wildcardMatches.has(parentDomain)) { if (wildcardMatches.has(parentDomain)) {

View File

@@ -21,174 +21,172 @@ import { getEnvOrYaml } from "@server/lib/getEnvOrYaml";
const portSchema = z.number().positive().gt(0).lte(65535); const portSchema = z.number().positive().gt(0).lte(65535);
export const privateConfigSchema = z.object({ export const privateConfigSchema = z
app: z .object({
.object({ app: z
region: z.string().optional().default("default"), .object({
base_domain: z.string().optional(), region: z.string().optional().default("default"),
identity_provider_mode: z.enum(["global", "org"]).optional() base_domain: z.string().optional(),
}) identity_provider_mode: z.enum(["global", "org"]).optional()
.optional() })
.default({ .optional()
region: "default" .default({
}), region: "default"
server: z }),
.object({ server: z
reo_client_id: z .object({
.string() reo_client_id: z
.optional() .string()
.transform(getEnvOrYaml("REO_CLIENT_ID")), .optional()
fossorial_api: z .transform(getEnvOrYaml("REO_CLIENT_ID")),
.string() fossorial_api: z
.optional() .string()
.default("https://api.fossorial.io"), .optional()
fossorial_api_key: z .default("https://api.fossorial.io"),
.string() fossorial_api_key: z
.optional() .string()
.transform(getEnvOrYaml("FOSSORIAL_API_KEY")) .optional()
}) .transform(getEnvOrYaml("FOSSORIAL_API_KEY"))
.optional() })
.prefault({}), .optional()
redis: z .prefault({}),
.object({ redis: z
host: z.string(), .object({
port: portSchema, host: z.string(),
password: z port: portSchema,
.string() password: z
.optional() .string()
.transform(getEnvOrYaml("REDIS_PASSWORD")), .optional()
db: z.int().nonnegative().optional().default(0), .transform(getEnvOrYaml("REDIS_PASSWORD")),
replicas: z db: z.int().nonnegative().optional().default(0),
.array( replicas: z
z.object({ .array(
host: z.string(), z.object({
port: portSchema, host: z.string(),
password: z.string().optional(), port: portSchema,
db: z.int().nonnegative().optional().default(0) password: z.string().optional(),
db: z.int().nonnegative().optional().default(0)
})
)
.optional(),
tls: z
.object({
rejectUnauthorized: z.boolean().optional().default(true)
}) })
) .optional()
.optional(), })
tls: z .optional(),
.object({ gerbil: z
rejectUnauthorized: z .object({
.boolean() local_exit_node_reachable_at: z
.optional() .string()
.default(true) .optional()
}) .default("http://gerbil:3004")
.optional() })
}) .optional()
.optional(), .prefault({}),
gerbil: z flags: z
.object({ .object({
local_exit_node_reachable_at: z enable_redis: z.boolean().optional().default(false),
.string() use_pangolin_dns: z.boolean().optional().default(false),
.optional() use_org_only_idp: z.boolean().optional(),
.default("http://gerbil:3004") enable_acme_cert_sync: z.boolean().optional().default(true)
}) })
.optional() .optional()
.prefault({}), .prefault({}),
flags: z acme: z
.object({ .object({
enable_redis: z.boolean().optional().default(false), acme_json_path: z
use_pangolin_dns: z.boolean().optional().default(false), .string()
use_org_only_idp: z.boolean().optional(), .optional()
enable_acme_cert_sync: z.boolean().optional().default(true) .default("config/letsencrypt/acme.json"),
}) acme_http_endpoint: z.string().optional(),
.optional() sync_interval_ms: z.number().optional().default(5000)
.prefault({}), })
acme: z .optional(),
.object({ branding: z
acme_json_path: z .object({
.string() app_name: z.string().optional(),
.optional() background_image_path: z.string().optional(),
.default("config/letsencrypt/acme.json"), colors: z
resolver: z.string().optional().default("letsencrypt"), .object({
sync_interval_ms: z.number().optional().default(5000) light: colorsSchema.optional(),
}) dark: colorsSchema.optional()
.optional(),
branding: z
.object({
app_name: z.string().optional(),
background_image_path: z.string().optional(),
colors: z
.object({
light: colorsSchema.optional(),
dark: colorsSchema.optional()
})
.optional(),
logo: z
.object({
light_path: z.string().optional(),
dark_path: z.string().optional(),
auth_page: z
.object({
width: z.number().optional(),
height: z.number().optional()
})
.optional(),
navbar: z
.object({
width: z.number().optional(),
height: z.number().optional()
})
.optional()
})
.optional(),
footer: z
.array(
z.object({
text: z.string(),
href: z.string().optional()
}) })
) .optional(),
.optional(), logo: z
hide_auth_layout_footer: z.boolean().optional().default(false), .object({
login_page: z light_path: z.string().optional(),
.object({ dark_path: z.string().optional(),
subtitle_text: z.string().optional() auth_page: z
}) .object({
.optional(), width: z.number().optional(),
signup_page: z height: z.number().optional()
.object({ })
subtitle_text: z.string().optional() .optional(),
}) navbar: z
.optional(), .object({
resource_auth_page: z width: z.number().optional(),
.object({ height: z.number().optional()
show_logo: z.boolean().optional(), })
hide_powered_by: z.boolean().optional(), .optional()
title_text: z.string().optional(), })
subtitle_text: z.string().optional() .optional(),
}) footer: z
.optional(), .array(
emails: z z.object({
.object({ text: z.string(),
signature: z.string().optional(), href: z.string().optional()
colors: z
.object({
primary: z.string().optional()
}) })
.optional() )
}) .optional(),
.optional() hide_auth_layout_footer: z.boolean().optional().default(false),
}) login_page: z
.optional(), .object({
stripe: z subtitle_text: z.string().optional()
.object({ })
secret_key: z .optional(),
.string() signup_page: z
.optional() .object({
.transform(getEnvOrYaml("STRIPE_SECRET_KEY")), subtitle_text: z.string().optional()
webhook_secret: z })
.string() .optional(),
.optional() resource_auth_page: z
.transform(getEnvOrYaml("STRIPE_WEBHOOK_SECRET")), .object({
// s3Bucket: z.string(), show_logo: z.boolean().optional(),
// s3Region: z.string().default("us-east-1"), hide_powered_by: z.boolean().optional(),
// localFilePath: z.string().optional() title_text: z.string().optional(),
}) subtitle_text: z.string().optional()
.optional() })
}) .optional(),
emails: z
.object({
signature: z.string().optional(),
colors: z
.object({
primary: z.string().optional()
})
.optional()
})
.optional()
})
.optional(),
stripe: z
.object({
secret_key: z
.string()
.optional()
.transform(getEnvOrYaml("STRIPE_SECRET_KEY")),
webhook_secret: z
.string()
.optional()
.transform(getEnvOrYaml("STRIPE_WEBHOOK_SECRET"))
// s3Bucket: z.string(),
// s3Region: z.string().default("us-east-1"),
// localFilePath: z.string().optional()
})
.optional()
})
.transform((data) => { .transform((data) => {
// this to maintain backwards compatibility with the old config file // this to maintain backwards compatibility with the old config file
const identityProviderMode = data.app?.identity_provider_mode; const identityProviderMode = data.app?.identity_provider_mode;

View File

@@ -33,7 +33,15 @@ import {
} from "drizzle-orm"; } from "drizzle-orm";
import logger from "@server/logger"; import logger from "@server/logger";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { orgs, resources, sites, siteNetworks, siteResources, Target, targets } from "@server/db"; import {
orgs,
resources,
sites,
siteNetworks,
siteResources,
Target,
targets
} from "@server/db";
import { import {
sanitize, sanitize,
encodePath, encodePath,
@@ -100,6 +108,7 @@ export async function getTraefikConfig(
headers: resources.headers, headers: resources.headers,
proxyProtocol: resources.proxyProtocol, proxyProtocol: resources.proxyProtocol,
proxyProtocolVersion: resources.proxyProtocolVersion, proxyProtocolVersion: resources.proxyProtocolVersion,
wildcard: resources.wildcard,
maintenanceModeEnabled: resources.maintenanceModeEnabled, maintenanceModeEnabled: resources.maintenanceModeEnabled,
maintenanceModeType: resources.maintenanceModeType, maintenanceModeType: resources.maintenanceModeType,
@@ -238,6 +247,7 @@ export async function getTraefikConfig(
priority: priority, // may be null, we fallback later priority: priority, // may be null, we fallback later
domainCertResolver: row.domainCertResolver, domainCertResolver: row.domainCertResolver,
preferWildcardCert: row.preferWildcardCert, preferWildcardCert: row.preferWildcardCert,
wildcard: row.wildcard,
maintenanceModeEnabled: row.maintenanceModeEnabled, maintenanceModeEnabled: row.maintenanceModeEnabled,
maintenanceModeType: row.maintenanceModeType, maintenanceModeType: row.maintenanceModeType,
@@ -267,34 +277,37 @@ export async function getTraefikConfig(
}); });
}); });
// Query siteResources in HTTP mode with SSL enabled and aliases - cert generation / HTTPS edge let siteResourcesWithFullDomain: {
const siteResourcesWithFullDomain = await db siteResourceId: number;
.select({ fullDomain: string | null;
siteResourceId: siteResources.siteResourceId, mode: "http" | "host" | "cidr";
fullDomain: siteResources.fullDomain, }[] = [];
mode: siteResources.mode if (build == "enterprise") {
}) // we dont want to do this on the cloud
.from(siteResources) // Query siteResources in HTTP mode with SSL enabled and aliases - cert generation / HTTPS edge
.innerJoin(siteNetworks, eq(siteResources.networkId, siteNetworks.networkId)) siteResourcesWithFullDomain = await db
.innerJoin(sites, eq(siteNetworks.siteId, sites.siteId)) .select({
.where( siteResourceId: siteResources.siteResourceId,
and( fullDomain: siteResources.fullDomain,
eq(siteResources.enabled, true), mode: siteResources.mode
isNotNull(siteResources.fullDomain), })
eq(siteResources.mode, "http"), .from(siteResources)
eq(siteResources.ssl, true), .innerJoin(
or( siteNetworks,
eq(sites.exitNodeId, exitNodeId), eq(siteResources.networkId, siteNetworks.networkId)
and(
isNull(sites.exitNodeId),
sql`(${siteTypes.includes("local") ? 1 : 0} = 1)`,
eq(sites.type, "local"),
sql`(${build != "saas" ? 1 : 0} = 1)`
)
),
inArray(sites.type, siteTypes)
) )
); .innerJoin(sites, eq(siteNetworks.siteId, sites.siteId))
.where(
and(
eq(siteResources.enabled, true),
isNotNull(siteResources.fullDomain),
eq(siteResources.mode, "http"),
eq(siteResources.ssl, true),
eq(sites.exitNodeId, exitNodeId),
inArray(sites.type, siteTypes)
)
);
}
let validCerts: CertificateResult[] = []; let validCerts: CertificateResult[] = [];
if (privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) { if (privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) {
@@ -376,7 +389,16 @@ export async function getTraefikConfig(
...additionalMiddlewares ...additionalMiddlewares
]; ];
let rule = `Host(\`${fullDomain}\`)`; let rule: string;
if (resource.wildcard && fullDomain.startsWith("*.")) {
// Convert *.foo.bar.com -> HostRegexp(`^[^.]+\.foo\.bar\.com$`)
const escaped = fullDomain
.slice(2) // remove leading "*."
.replace(/\./g, "\\.");
rule = `HostRegexp(\`^[^.]+\\.${escaped}$\`)`;
} else {
rule = `Host(\`${fullDomain}\`)`;
}
// priority logic // priority logic
let priority: number; let priority: number;
@@ -419,7 +441,8 @@ export async function getTraefikConfig(
config.getRawConfig().traefik.prefer_wildcard_cert; config.getRawConfig().traefik.prefer_wildcard_cert;
const domainCertResolver = resource.domainCertResolver; const domainCertResolver = resource.domainCertResolver;
const preferWildcardCert = resource.preferWildcardCert; const preferWildcardCert =
resource.preferWildcardCert || resource.wildcard;
let resolverName: string | undefined; let resolverName: string | undefined;
let preferWildcard: boolean | undefined; let preferWildcard: boolean | undefined;
@@ -566,7 +589,7 @@ export async function getTraefikConfig(
resource.ssl ? entrypointHttps : entrypointHttp resource.ssl ? entrypointHttps : entrypointHttp
], ],
service: maintenanceServiceName, service: maintenanceServiceName,
rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`, rule: `${rule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`,
priority: 2001, priority: 2001,
...(resource.ssl ? { tls } : {}) ...(resource.ssl ? { tls } : {})
}; };
@@ -953,22 +976,17 @@ export async function getTraefikConfig(
}; };
// Middleware that rewrites any path to /maintenance-screen // Middleware that rewrites any path to /maintenance-screen
config_output.http.middlewares[ config_output.http.middlewares[siteResourceRewriteMiddlewareName] =
siteResourceRewriteMiddlewareName {
] = { replacePathRegex: {
replacePathRegex: { regex: "^/(.*)",
regex: "^/(.*)", replacement: "/private-maintenance-screen"
replacement: "/private-maintenance-screen" }
} };
};
// HTTP -> HTTPS redirect so the ACME challenge can be served // HTTP -> HTTPS redirect so the ACME challenge can be served
config_output.http.routers[ config_output.http.routers[`${siteResourceRouterName}-redirect`] = {
`${siteResourceRouterName}-redirect` entryPoints: [config.getRawConfig().traefik.http_entrypoint],
] = {
entryPoints: [
config.getRawConfig().traefik.http_entrypoint
],
middlewares: [redirectHttpsMiddlewareName], middlewares: [redirectHttpsMiddlewareName],
service: siteResourceServiceName, service: siteResourceServiceName,
rule: `Host(\`${fullDomain}\`)`, rule: `Host(\`${fullDomain}\`)`,
@@ -977,9 +995,7 @@ export async function getTraefikConfig(
// Determine TLS / cert-resolver configuration // Determine TLS / cert-resolver configuration
let tls: any = {}; let tls: any = {};
if ( if (!privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) {
!privateConfig.getRawPrivateConfig().flags.use_pangolin_dns
) {
const domainParts = fullDomain.split("."); const domainParts = fullDomain.split(".");
const wildCard = const wildCard =
domainParts.length <= 2 domainParts.length <= 2
@@ -1012,9 +1028,7 @@ export async function getTraefikConfig(
// HTTPS router - presence of this entry triggers cert generation // HTTPS router - presence of this entry triggers cert generation
config_output.http.routers[siteResourceRouterName] = { config_output.http.routers[siteResourceRouterName] = {
entryPoints: [ entryPoints: [config.getRawConfig().traefik.https_entrypoint],
config.getRawConfig().traefik.https_entrypoint
],
service: siteResourceServiceName, service: siteResourceServiceName,
middlewares: [siteResourceRewriteMiddlewareName], middlewares: [siteResourceRewriteMiddlewareName],
rule: `Host(\`${fullDomain}\`)`, rule: `Host(\`${fullDomain}\`)`,
@@ -1024,9 +1038,7 @@ export async function getTraefikConfig(
// Assets bypass router - lets Next.js static files load without rewrite // Assets bypass router - lets Next.js static files load without rewrite
config_output.http.routers[`${siteResourceRouterName}-assets`] = { config_output.http.routers[`${siteResourceRouterName}-assets`] = {
entryPoints: [ entryPoints: [config.getRawConfig().traefik.https_entrypoint],
config.getRawConfig().traefik.https_entrypoint
],
service: siteResourceServiceName, service: siteResourceServiceName,
rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`, rule: `Host(\`${fullDomain}\`) && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`))`,
priority: 101, priority: 101,

View File

@@ -14,7 +14,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { targetHealthCheck, statusHistory } from "@server/db"; import { targetHealthCheck } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -23,8 +23,8 @@ import { fromError } from "zod-validation-error";
import { eq, and } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
import { import {
fireHealthCheckHealthyAlert, fireHealthCheckHealthyAlert,
fireHealthCheckNotHealthyAlert fireHealthCheckUnhealthyAlert
} from "#private/lib/alerts/events/healthCheckEvents"; } from "@server/lib/alerts";
const paramsSchema = z.strictObject({ const paramsSchema = z.strictObject({
orgId: z.string().nonempty(), orgId: z.string().nonempty(),
@@ -73,10 +73,7 @@ export async function triggerHealthCheckAlert(
.from(targetHealthCheck) .from(targetHealthCheck)
.where( .where(
and( and(
eq( eq(targetHealthCheck.targetHealthCheckId, healthCheckId),
targetHealthCheck.targetHealthCheckId,
healthCheckId
),
eq(targetHealthCheck.orgId, orgId) eq(targetHealthCheck.orgId, orgId)
) )
) )
@@ -91,14 +88,6 @@ export async function triggerHealthCheckAlert(
); );
} }
await db.insert(statusHistory).values({
entityType: "healthCheck",
entityId: healthCheckId,
orgId,
status: eventType === "health_check_healthy" ? "healthy" : "unhealthy",
timestamp: Math.floor(Date.now() / 1000)
});
if (eventType === "health_check_healthy") { if (eventType === "health_check_healthy") {
await fireHealthCheckHealthyAlert( await fireHealthCheckHealthyAlert(
orgId, orgId,
@@ -106,7 +95,7 @@ export async function triggerHealthCheckAlert(
healthCheck.name ?? undefined healthCheck.name ?? undefined
); );
} else { } else {
await fireHealthCheckNotHealthyAlert( await fireHealthCheckUnhealthyAlert(
orgId, orgId,
healthCheckId, healthCheckId,
healthCheck.name ?? undefined healthCheck.name ?? undefined
@@ -126,4 +115,4 @@ export async function triggerHealthCheckAlert(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
); );
} }
} }

View File

@@ -14,7 +14,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resources, statusHistory } from "@server/db"; import { resources } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -24,8 +24,8 @@ import { eq, and } from "drizzle-orm";
import { import {
fireResourceHealthyAlert, fireResourceHealthyAlert,
fireResourceUnhealthyAlert, fireResourceUnhealthyAlert,
fireResourceToggleAlert fireResourceDegradedAlert
} from "#private/lib/alerts/events/resourceEvents"; } from "@server/lib/alerts";
const paramsSchema = z.strictObject({ const paramsSchema = z.strictObject({
orgId: z.string().nonempty(), orgId: z.string().nonempty(),
@@ -33,7 +33,12 @@ const paramsSchema = z.strictObject({
}); });
const bodySchema = z.strictObject({ const bodySchema = z.strictObject({
eventType: z.enum(["resource_healthy", "resource_unhealthy", "resource_toggle"]) eventType: z.enum([
"resource_healthy",
"resource_unhealthy",
"resource_degraded",
"resource_toggle"
])
}); });
export type TriggerResourceAlertResponse = { export type TriggerResourceAlertResponse = {
@@ -89,16 +94,6 @@ export async function triggerResourceAlert(
); );
} }
if (eventType === "resource_healthy" || eventType === "resource_unhealthy") {
await db.insert(statusHistory).values({
entityType: "resource",
entityId: resourceId,
orgId,
status: eventType === "resource_healthy" ? "healthy" : "unhealthy",
timestamp: Math.floor(Date.now() / 1000)
});
}
if (eventType === "resource_healthy") { if (eventType === "resource_healthy") {
await fireResourceHealthyAlert( await fireResourceHealthyAlert(
orgId, orgId,
@@ -111,8 +106,8 @@ export async function triggerResourceAlert(
resourceId, resourceId,
resource.name ?? undefined resource.name ?? undefined
); );
} else { } else if (eventType === "resource_degraded") {
await fireResourceToggleAlert( await fireResourceDegradedAlert(
orgId, orgId,
resourceId, resourceId,
resource.name ?? undefined resource.name ?? undefined
@@ -132,4 +127,4 @@ export async function triggerResourceAlert(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
); );
} }
} }

View File

@@ -14,17 +14,14 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { sites, statusHistory } from "@server/db"; import { sites } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { eq, and } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
import { import { fireSiteOnlineAlert, fireSiteOfflineAlert } from "@server/lib/alerts";
fireSiteOnlineAlert,
fireSiteOfflineAlert
} from "#private/lib/alerts/events/siteEvents";
const paramsSchema = z.strictObject({ const paramsSchema = z.strictObject({
orgId: z.string().nonempty(), orgId: z.string().nonempty(),
@@ -83,14 +80,6 @@ export async function triggerSiteAlert(
); );
} }
await db.insert(statusHistory).values({
entityType: "site",
entityId: siteId,
orgId,
status: eventType === "site_online" ? "online" : "offline",
timestamp: Math.floor(Date.now() / 1000)
});
if (eventType === "site_online") { if (eventType === "site_online") {
await fireSiteOnlineAlert(orgId, siteId, site.name ?? undefined); await fireSiteOnlineAlert(orgId, siteId, site.name ?? undefined);
} else { } else {
@@ -110,4 +99,4 @@ export async function triggerSiteAlert(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
); );
} }
} }

View File

@@ -31,8 +31,13 @@ import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { encrypt } from "@server/lib/crypto"; import { encrypt } from "@server/lib/crypto";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { CreateAlertRuleResponse } from "@server/routers/alertRule/types";
export const SITE_EVENT_TYPES = ["site_online", "site_offline", "site_toggle"] as const; export const SITE_EVENT_TYPES = [
"site_online",
"site_offline",
"site_toggle"
] as const;
export const HC_EVENT_TYPES = [ export const HC_EVENT_TYPES = [
"health_check_healthy", "health_check_healthy",
"health_check_unhealthy", "health_check_unhealthy",
@@ -41,6 +46,7 @@ export const HC_EVENT_TYPES = [
export const RESOURCE_EVENT_TYPES = [ export const RESOURCE_EVENT_TYPES = [
"resource_healthy", "resource_healthy",
"resource_unhealthy", "resource_unhealthy",
"resource_degraded",
"resource_toggle" "resource_toggle"
] as const; ] as const;
@@ -63,7 +69,7 @@ const bodySchema = z
...RESOURCE_EVENT_TYPES ...RESOURCE_EVENT_TYPES
]), ]),
enabled: z.boolean().optional().default(true), enabled: z.boolean().optional().default(true),
cooldownSeconds: z.number().int().nonnegative().optional().default(300), cooldownSeconds: z.number().int().nonnegative().optional().default(0),
// Source join tables - which is required depends on eventType // Source join tables - which is required depends on eventType
siteIds: z.array(z.number().int().positive()).optional().default([]), siteIds: z.array(z.number().int().positive()).optional().default([]),
allSites: z.boolean().optional().default(false), allSites: z.boolean().optional().default(false),
@@ -91,19 +97,24 @@ const bodySchema = z
const isHcEvent = (HC_EVENT_TYPES as readonly string[]).includes( const isHcEvent = (HC_EVENT_TYPES as readonly string[]).includes(
val.eventType val.eventType
); );
const isResourceEvent = (RESOURCE_EVENT_TYPES as readonly string[]).includes( const isResourceEvent = (
val.eventType RESOURCE_EVENT_TYPES as readonly string[]
); ).includes(val.eventType);
if (isSiteEvent && !val.allSites && val.siteIds.length === 0) { if (isSiteEvent && !val.allSites && val.siteIds.length === 0) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: "At least one siteId is required for site event types when allSites is false", message:
"At least one siteId is required for site event types when allSites is false",
path: ["siteIds"] path: ["siteIds"]
}); });
} }
if (isHcEvent && !val.allHealthChecks && val.healthCheckIds.length === 0) { if (
isHcEvent &&
!val.allHealthChecks &&
val.healthCheckIds.length === 0
) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: message:
@@ -128,10 +139,15 @@ const bodySchema = z
}); });
} }
if (isResourceEvent && !val.allResources && val.resourceIds.length === 0) { if (
isResourceEvent &&
!val.allResources &&
val.resourceIds.length === 0
) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: "At least one resourceId is required for resource event types when allResources is false", message:
"At least one resourceId is required for resource event types when allResources is false",
path: ["resourceIds"] path: ["resourceIds"]
}); });
} }
@@ -147,7 +163,8 @@ const bodySchema = z
if (isResourceEvent && val.healthCheckIds.length > 0) { if (isResourceEvent && val.healthCheckIds.length > 0) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: "healthCheckIds must not be set for resource event types", message:
"healthCheckIds must not be set for resource event types",
path: ["healthCheckIds"] path: ["healthCheckIds"]
}); });
} }
@@ -163,16 +180,13 @@ const bodySchema = z
if (isHcEvent && val.resourceIds.length > 0) { if (isHcEvent && val.resourceIds.length > 0) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: "resourceIds must not be set for health check event types", message:
"resourceIds must not be set for health check event types",
path: ["resourceIds"] path: ["resourceIds"]
}); });
} }
}); });
export type CreateAlertRuleResponse = {
alertRuleId: number;
};
registry.registerPath({ registry.registerPath({
method: "put", method: "put",
path: "/org/{orgId}/alert-rule", path: "/org/{orgId}/alert-rule",
@@ -287,9 +301,7 @@ export async function createAlertRule(
// Create the email action pivot row and recipients if any recipients // Create the email action pivot row and recipients if any recipients
// were supplied (userIds, roleIds, or raw emails). // were supplied (userIds, roleIds, or raw emails).
const hasRecipients = const hasRecipients =
userIds.length > 0 || userIds.length > 0 || roleIds.length > 0 || emails.length > 0;
roleIds.length > 0 ||
emails.length > 0;
if (hasRecipients) { if (hasRecipients) {
const [emailActionRow] = await db const [emailActionRow] = await db

View File

@@ -32,7 +32,7 @@ import { OpenAPITags, registry } from "@server/openApi";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import { decrypt } from "@server/lib/crypto"; import { decrypt } from "@server/lib/crypto";
import config from "@server/lib/config"; import config from "@server/lib/config";
import { WebhookAlertConfig } from "#private/lib/alerts/types"; import { GetAlertRuleResponse, WebhookAlertConfig } from "@server/routers/alertRule/types";
const paramsSchema = z const paramsSchema = z
.object({ .object({
@@ -41,43 +41,6 @@ const paramsSchema = z
}) })
.strict(); .strict();
export type GetAlertRuleResponse = {
alertRuleId: number;
orgId: string;
name: string;
eventType:
| "site_online"
| "site_offline"
| "site_toggle"
| "health_check_healthy"
| "health_check_unhealthy"
| "health_check_toggle"
| "resource_healthy"
| "resource_unhealthy"
| "resource_toggle";
enabled: boolean;
cooldownSeconds: number;
lastTriggeredAt: number | null;
createdAt: number;
updatedAt: number;
siteIds: number[];
healthCheckIds: number[];
resourceIds: number[];
recipients: {
recipientId: number;
userId: string | null;
roleId: number | null;
email: string | null;
}[];
webhookActions: {
webhookActionId: number;
webhookUrl: string;
enabled: boolean;
lastSentAt: number | null;
config: WebhookAlertConfig | null;
}[];
};
registry.registerPath({ registry.registerPath({
method: "get", method: "get",
path: "/org/{orgId}/alert-rule/{alertRuleId}", path: "/org/{orgId}/alert-rule/{alertRuleId}",

View File

@@ -14,14 +14,20 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { alertRules, alertSites, alertHealthChecks, alertResources } from "@server/db"; import {
alertRules,
alertSites,
alertHealthChecks,
alertResources
} from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { and, eq, inArray, like, sql } from "drizzle-orm"; import { and, asc, desc, eq, inArray, like, or, sql } from "drizzle-orm";
import { ListAlertRulesResponse } from "@server/routers/alertRule/types";
const paramsSchema = z.strictObject({ const paramsSchema = z.strictObject({
orgId: z.string().nonempty() orgId: z.string().nonempty()
@@ -50,30 +56,35 @@ const querySchema = z.strictObject({
.string() .string()
.optional() .optional()
.transform((v) => (v !== undefined ? Number(v) : undefined)) .transform((v) => (v !== undefined ? Number(v) : undefined))
.pipe(z.number().int().positive().optional()) .pipe(z.number().int().positive().optional()),
healthCheckId: z
.string()
.optional()
.transform((v) => (v !== undefined ? Number(v) : undefined))
.pipe(z.number().int().positive().optional()),
sort_by: z.enum(["name", "last_triggered_at"]).optional(),
order: z.enum(["asc", "desc"]).optional().default("asc"),
enabled: z.enum(["true", "false"]).optional()
}); });
export type ListAlertRulesResponse = { const SITE_ALERT_EVENT_TYPES = [
alertRules: { "site_online",
alertRuleId: number; "site_offline",
orgId: string; "site_toggle"
name: string; ] as const;
eventType: string;
enabled: boolean; const RESOURCE_ALERT_EVENT_TYPES = [
cooldownSeconds: number; "resource_healthy",
lastTriggeredAt: number | null; "resource_unhealthy",
createdAt: number; "resource_degraded",
updatedAt: number; "resource_toggle"
siteIds: number[]; ] as const;
healthCheckIds: number[];
resourceIds: number[]; const HEALTH_CHECK_ALERT_EVENT_TYPES = [
}[]; "health_check_healthy",
pagination: { "health_check_unhealthy",
total: number; "health_check_toggle"
limit: number; ] as const;
offset: number;
};
};
registry.registerPath({ registry.registerPath({
method: "get", method: "get",
@@ -113,70 +124,137 @@ export async function listAlertRules(
) )
); );
} }
const { limit, offset, query, siteId, resourceId } = parsedQuery.data; const {
limit,
offset,
query,
siteId,
resourceId,
healthCheckId,
sort_by,
order,
enabled: enabledFilter
} = parsedQuery.data;
// Resolve siteId filter → matching alertRuleIds const explicitSiteRuleIds: number[] =
let siteFilterRuleIds: number[] | null = null; siteId !== undefined
if (siteId !== undefined) { ? (
const rows = await db await db
.select({ alertRuleId: alertSites.alertRuleId }) .select({ alertRuleId: alertSites.alertRuleId })
.from(alertSites) .from(alertSites)
.where(eq(alertSites.siteId, siteId)); .where(eq(alertSites.siteId, siteId))
siteFilterRuleIds = rows.map((r) => r.alertRuleId); ).map((r) => r.alertRuleId)
if (siteFilterRuleIds.length === 0) { : [];
return response<ListAlertRulesResponse>(res, {
data: {
alertRules: [],
pagination: { total: 0, limit, offset }
},
success: true,
error: false,
message: "Alert rules retrieved successfully",
status: HttpCode.OK
});
}
}
// Resolve resourceId filter → matching alertRuleIds const explicitResourceRuleIds: number[] =
let resourceFilterRuleIds: number[] | null = null; resourceId !== undefined
if (resourceId !== undefined) { ? (
const rows = await db await db
.select({ alertRuleId: alertResources.alertRuleId }) .select({
.from(alertResources) alertRuleId: alertResources.alertRuleId
.where(eq(alertResources.resourceId, resourceId)); })
resourceFilterRuleIds = rows.map((r) => r.alertRuleId); .from(alertResources)
if (resourceFilterRuleIds.length === 0) { .where(eq(alertResources.resourceId, resourceId))
return response<ListAlertRulesResponse>(res, { ).map((r) => r.alertRuleId)
data: { : [];
alertRules: [],
pagination: { total: 0, limit, offset } const explicitHealthCheckRuleIds: number[] =
}, healthCheckId !== undefined
success: true, ? (
error: false, await db
message: "Alert rules retrieved successfully", .select({
status: HttpCode.OK alertRuleId: alertHealthChecks.alertRuleId
}); })
} .from(alertHealthChecks)
} .where(
eq(alertHealthChecks.healthCheckId, healthCheckId)
)
).map((r) => r.alertRuleId)
: [];
const allSitesWildcardClause = and(
eq(alertRules.allSites, true),
inArray(alertRules.eventType, SITE_ALERT_EVENT_TYPES)
);
const siteScopeClause =
siteId !== undefined
? explicitSiteRuleIds.length > 0
? or(
allSitesWildcardClause,
inArray(alertRules.alertRuleId, explicitSiteRuleIds)
)
: allSitesWildcardClause
: undefined;
const allResourcesWildcardClause = and(
eq(alertRules.allResources, true),
inArray(alertRules.eventType, RESOURCE_ALERT_EVENT_TYPES)
);
const resourceScopeClause =
resourceId !== undefined
? explicitResourceRuleIds.length > 0
? or(
allResourcesWildcardClause,
inArray(
alertRules.alertRuleId,
explicitResourceRuleIds
)
)
: allResourcesWildcardClause
: undefined;
const allHealthChecksWildcardClause = and(
eq(alertRules.allHealthChecks, true),
inArray(alertRules.eventType, HEALTH_CHECK_ALERT_EVENT_TYPES)
);
const healthCheckScopeClause =
healthCheckId !== undefined
? explicitHealthCheckRuleIds.length > 0
? or(
allHealthChecksWildcardClause,
inArray(
alertRules.alertRuleId,
explicitHealthCheckRuleIds
)
)
: allHealthChecksWildcardClause
: undefined;
const whereClause = and( const whereClause = and(
eq(alertRules.orgId, orgId), eq(alertRules.orgId, orgId),
query query
? like(sql`LOWER(${alertRules.name})`, `%${query.toLowerCase()}%`) ? like(
sql`LOWER(${alertRules.name})`,
`%${query.toLowerCase()}%`
)
: undefined, : undefined,
siteFilterRuleIds !== null siteScopeClause,
? inArray(alertRules.alertRuleId, siteFilterRuleIds) resourceScopeClause,
: undefined, healthCheckScopeClause,
resourceFilterRuleIds !== null enabledFilter !== undefined
? inArray(alertRules.alertRuleId, resourceFilterRuleIds) ? eq(alertRules.enabled, enabledFilter === "true")
: undefined : undefined
); );
const orderByClause =
sort_by === "name"
? order === "asc"
? asc(alertRules.name)
: desc(alertRules.name)
: sort_by === "last_triggered_at"
? order === "asc"
? sql`${alertRules.lastTriggeredAt} ASC NULLS FIRST`
: sql`${alertRules.lastTriggeredAt} DESC NULLS LAST`
: sql`${alertRules.createdAt} DESC`;
const list = await db const list = await db
.select() .select()
.from(alertRules) .from(alertRules)
.where(whereClause) .where(whereClause)
.orderBy(sql`${alertRules.createdAt} DESC`) .orderBy(orderByClause)
.limit(limit) .limit(limit)
.offset(offset); .offset(offset);
@@ -202,9 +280,7 @@ export async function listAlertRules(
? await db ? await db
.select() .select()
.from(alertHealthChecks) .from(alertHealthChecks)
.where( .where(inArray(alertHealthChecks.alertRuleId, ruleIds))
inArray(alertHealthChecks.alertRuleId, ruleIds)
)
: []; : [];
const resourceRows = const resourceRows =
@@ -271,4 +347,4 @@ export async function listAlertRules(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
); );
} }
} }

View File

@@ -30,8 +30,10 @@ import {
userOrgRoles, userOrgRoles,
siteProvisioningKeyOrg, siteProvisioningKeyOrg,
siteProvisioningKeys, siteProvisioningKeys,
alertRules,
targetHealthCheck
} from "@server/db"; } from "@server/db";
import { and, eq } from "drizzle-orm"; import { and, eq, isNull } from "drizzle-orm";
/** /**
* Get the maximum allowed retention days for a given tier * Get the maximum allowed retention days for a given tier
@@ -318,6 +320,14 @@ async function disableFeature(
await disableSiteProvisioningKeys(orgId); await disableSiteProvisioningKeys(orgId);
break; break;
case TierFeature.AlertingRules:
await disableAlertingRules(orgId);
break;
case TierFeature.StandaloneHealthChecks:
await disableStandaloneHealthChecks(orgId);
break;
default: default:
logger.warn( logger.warn(
`Unknown feature ${feature} for org ${orgId}, skipping` `Unknown feature ${feature} for org ${orgId}, skipping`
@@ -360,8 +370,7 @@ async function disableFullRbac(orgId: string): Promise<void> {
async function disableSiteProvisioningKeys(orgId: string): Promise<void> { async function disableSiteProvisioningKeys(orgId: string): Promise<void> {
const rows = await db const rows = await db
.select({ .select({
siteProvisioningKeyId: siteProvisioningKeyId: siteProvisioningKeyOrg.siteProvisioningKeyId
siteProvisioningKeyOrg.siteProvisioningKeyId
}) })
.from(siteProvisioningKeyOrg) .from(siteProvisioningKeyOrg)
.where(eq(siteProvisioningKeyOrg.orgId, orgId)); .where(eq(siteProvisioningKeyOrg.orgId, orgId));
@@ -525,6 +534,29 @@ async function disablePasswordExpirationPolicies(orgId: string): Promise<void> {
logger.info(`Disabled password expiration policies for org ${orgId}`); logger.info(`Disabled password expiration policies for org ${orgId}`);
} }
async function disableAlertingRules(orgId: string): Promise<void> {
await db
.update(alertRules)
.set({ enabled: false })
.where(eq(alertRules.orgId, orgId));
logger.info(`Disabled all alert rules for org ${orgId}`);
}
async function disableStandaloneHealthChecks(orgId: string): Promise<void> {
await db
.update(targetHealthCheck)
.set({ hcEnabled: false })
.where(
and(
eq(targetHealthCheck.orgId, orgId),
isNull(targetHealthCheck.targetId)
)
);
logger.info(`Disabled standalone health checks for org ${orgId}`);
}
async function disableAutoProvisioning(orgId: string): Promise<void> { async function disableAutoProvisioning(orgId: string): Promise<void> {
// Get all IDP IDs for this org through the idpOrg join table // Get all IDP IDs for this org through the idpOrg join table
const orgIdps = await db const orgIdps = await db

View File

@@ -12,9 +12,10 @@
*/ */
import Stripe from "stripe"; import Stripe from "stripe";
import { customers, db } from "@server/db"; import { customers, db, subscriptions } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import logger from "@server/logger"; import logger from "@server/logger";
import { generateId } from "@server/auth/sessions/app";
export async function handleCustomerCreated( export async function handleCustomerCreated(
customer: Stripe.Customer customer: Stripe.Customer
@@ -38,14 +39,31 @@ export async function handleCustomerCreated(
return; return;
} }
await db.insert(customers).values({ await db.transaction(async (trx) => {
customerId: customer.id, await trx.insert(customers).values({
orgId: customer.metadata.orgId, customerId: customer.id,
email: customer.email || null, orgId: customer.metadata.orgId,
name: customer.name || null, email: customer.email || null,
createdAt: customer.created, name: customer.name || null,
updatedAt: customer.created createdAt: customer.created,
updatedAt: customer.created
});
// Insert a 14-day trial subscription at tier3
const now = Math.floor(Date.now() / 1000);
const trialExpiresAt = now + 10 * 24 * 60 * 60;
const subscriptionId = `trial-${generateId(15)}`;
await trx.insert(subscriptions).values({
subscriptionId,
customerId: customer.id,
status: "active",
type: "tier3",
createdAt: now,
expiresAt: trialExpiresAt,
trial: true
});
}); });
logger.info(`Customer with ID ${customer.id} created successfully.`); logger.info(`Customer with ID ${customer.id} created successfully.`);
} catch (error) { } catch (error) {
logger.error( logger.error(

View File

@@ -174,6 +174,19 @@ export async function handleSubscriptionCreated(
// TODO: update user in Sendy // TODO: update user in Sendy
} }
} }
// delete the trial subscrition if we have one
await db
.delete(subscriptions)
.where(
and(
eq(
subscriptions.customerId,
subscription.customer as string
),
eq(subscriptions.trial, true)
)
);
} else if (type === "license") { } else if (type === "license") {
logger.debug( logger.debug(
`License subscription created for org ${customer.orgId}, no lifecycle handling needed.` `License subscription created for org ${customer.orgId}, no lifecycle handling needed.`

View File

@@ -15,7 +15,6 @@ import { Certificate, certificates, db, domains } from "@server/db";
import logger from "@server/logger"; import logger from "@server/logger";
import { Transaction } from "@server/db"; import { Transaction } from "@server/db";
import { eq, or, and, like } from "drizzle-orm"; import { eq, or, and, like } from "drizzle-orm";
import privateConfig from "#private/lib/config";
/** /**
* Checks if a certificate exists for the given domain. * Checks if a certificate exists for the given domain.
@@ -27,10 +26,6 @@ export async function createCertificate(
domain: string, domain: string,
trx: Transaction | typeof db trx: Transaction | typeof db
) { ) {
if (!privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) {
return;
}
const [domainRecord] = await trx const [domainRecord] = await trx
.select() .select()
.from(domains) .from(domains)
@@ -42,18 +37,25 @@ export async function createCertificate(
} }
let existing: Certificate[] = []; let existing: Certificate[] = [];
if (domainRecord.type == "ns") { if (domainRecord.type == "ns" || domainRecord.type == "wildcard") {
const domainLevelDown = domain.split(".").slice(1).join("."); const domainLevelDown = domain.split(".").slice(1).join(".");
const wildcardPrefixed = `*.${domainLevelDown}`;
existing = await trx existing = await trx
.select() .select()
.from(certificates) .from(certificates)
.where( .where(
and( and(
eq(certificates.domainId, domainId), eq(certificates.domainId, domainId),
eq(certificates.wildcard, true), // only NS domains can have wildcard certs
or( or(
eq(certificates.domain, domain), eq(certificates.domain, domain),
eq(certificates.domain, domainLevelDown) and(
eq(certificates.wildcard, true),
or(
eq(certificates.domain, domainLevelDown),
eq(certificates.domain, wildcardPrefixed)
)
)
) )
) )
); );
@@ -75,11 +77,38 @@ export async function createCertificate(
return; return;
} }
let domainToWrite = domain;
if (
domainRecord.type == "wildcard" && // this is to fix the wildcard certs for traefik in self hosted NOT ON THE CLOUD
domainRecord.preferWildcardCert &&
!domain.startsWith("*.")
) {
// in this case traefik is going to generate a domain one level down so we need to store it that way
const parts = domain.split(".");
if (parts.length > 2) {
domainToWrite = parts.slice(1).join(".");
domainToWrite = `*.${domainToWrite}`;
}
} else if (domainRecord.type == "ns") {
// first if we have a * in the domain for this case we dont want to include it because it will mess with the cert generator so remove it
if (domain.startsWith("*.")) {
domain = domain.slice(2);
}
const parts = domain.split(".");
if (parts.length > 2) {
domainToWrite = parts.slice(1).join(".");
}
}
// No cert found, create a new one in pending state // No cert found, create a new one in pending state
await trx.insert(certificates).values({ await trx.insert(certificates).values({
domain, domain: domainToWrite,
domainId, domainId,
wildcard: domainRecord.type == "ns", // we can only create wildcard certs for NS domains wildcard:
domainRecord.type == "ns" ||
(domainRecord.type == "wildcard" &&
domainRecord.preferWildcardCert), // we can only create wildcard certs for NS domains
status: "pending", status: "pending",
updatedAt: Math.floor(Date.now() / 1000), updatedAt: Math.floor(Date.now() / 1000),
createdAt: Math.floor(Date.now() / 1000) createdAt: Math.floor(Date.now() / 1000)

View File

@@ -40,9 +40,12 @@ async function query(domainId: string, domain: string) {
throw new Error(`Domain with ID ${domainId} not found`); throw new Error(`Domain with ID ${domainId} not found`);
} }
const domainType = domainRecord.type;
let existing: any[] = []; let existing: any[] = [];
if (domainRecord.type == "ns") { if (domainRecord.type == "ns" || domainRecord.type == "wildcard") {
const domainLevelDown = domain.split(".").slice(1).join("."); const domainLevelDown = domain.split(".").slice(1).join(".");
const wildcardPrefixed = `*.${domainLevelDown}`;
existing = await db existing = await db
.select({ .select({
@@ -61,10 +64,15 @@ async function query(domainId: string, domain: string) {
.where( .where(
and( and(
eq(certificates.domainId, domainId), eq(certificates.domainId, domainId),
eq(certificates.wildcard, true), // only NS domains can have wildcard certs
or( or(
eq(certificates.domain, domain), eq(certificates.domain, domain),
eq(certificates.domain, domainLevelDown) and(
eq(certificates.wildcard, true),
or(
eq(certificates.domain, domainLevelDown),
eq(certificates.domain, wildcardPrefixed)
)
)
) )
) )
); );
@@ -92,7 +100,7 @@ async function query(domainId: string, domain: string) {
); );
} }
return existing.length > 0 ? existing[0] : null; return existing.length > 0 ? { ...existing[0], domainType } : null;
} }
registry.registerPath({ registry.registerPath({

View File

@@ -13,3 +13,4 @@
export * from "./getCertificate"; export * from "./getCertificate";
export * from "./restartCertificate"; export * from "./restartCertificate";
export * from "./syncCertToNewts";

View File

@@ -0,0 +1,68 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025-2026 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 { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { pushCertUpdateToAffectedNewts } from "#private/lib/acmeCertSync";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { fromError } from "zod-validation-error";
const bodySchema = z.object({
domain: z.string().min(1),
domainId: z.string().nullable().optional().default(null)
});
export async function syncCertToNewts(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const parsed = bodySchema.safeParse(req.body);
if (!parsed.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsed.error).toString()
)
);
}
const { domain, domainId } = parsed.data;
logger.debug(
`syncCertToNewts: received request to push cert update for domain "${domain}" (domainId: ${domainId ?? "none"})`
);
try {
await pushCertUpdateToAffectedNewts(domain, domainId, null, null);
res.status(HttpCode.OK).json({
data: null,
success: true,
error: false,
message: `Certificate update pushed to affected newts for domain "${domain}"`
});
} catch (err) {
logger.error(
`syncCertToNewts: error pushing cert update for domain "${domain}": ${err}`
);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Failed to push certificate update to affected newts"
)
);
}
}

View File

@@ -165,7 +165,6 @@ authenticated.get(
authenticated.get( authenticated.get(
"/org/:orgId/certificate/:domainId/:domain", "/org/:orgId/certificate/:domainId/:domain",
verifyValidLicense,
verifyOrgAccess, verifyOrgAccess,
verifyCertificateAccess, verifyCertificateAccess,
verifyUserHasAction(ActionsEnum.getCertificate), verifyUserHasAction(ActionsEnum.getCertificate),

View File

@@ -13,13 +13,16 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, targetHealthCheck } from "@server/db"; import { db, targetHealthCheck, newts, sites } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { addStandaloneHealthCheck } from "@server/routers/newt/targets";
import { fireHealthCheckUnhealthyAlert } from "@server/lib/alerts";
const paramsSchema = z.strictObject({ const paramsSchema = z.strictObject({
orgId: z.string().nonempty() orgId: z.string().nonempty()
@@ -27,6 +30,7 @@ const paramsSchema = z.strictObject({
const bodySchema = z.strictObject({ const bodySchema = z.strictObject({
name: z.string().nonempty(), name: z.string().nonempty(),
siteId: z.number().int().positive(),
hcEnabled: z.boolean().default(false), hcEnabled: z.boolean().default(false),
hcMode: z.string().default("http"), hcMode: z.string().default("http"),
hcHostname: z.string().optional(), hcHostname: z.string().optional(),
@@ -36,7 +40,7 @@ const bodySchema = z.strictObject({
hcMethod: z.string().default("GET"), hcMethod: z.string().default("GET"),
hcInterval: z.number().int().positive().default(30), hcInterval: z.number().int().positive().default(30),
hcUnhealthyInterval: z.number().int().positive().default(30), hcUnhealthyInterval: z.number().int().positive().default(30),
hcTimeout: z.number().int().positive().default(5), hcTimeout: z.number().int().positive().default(1),
hcHeaders: z.string().optional().nullable(), hcHeaders: z.string().optional().nullable(),
hcFollowRedirects: z.boolean().default(true), hcFollowRedirects: z.boolean().default(true),
hcStatus: z.number().int().optional().nullable(), hcStatus: z.number().int().optional().nullable(),
@@ -97,6 +101,7 @@ export async function createHealthCheck(
const { const {
name, name,
siteId,
hcEnabled, hcEnabled,
hcMode, hcMode,
hcHostname, hcHostname,
@@ -120,6 +125,7 @@ export async function createHealthCheck(
.values({ .values({
targetId: null, targetId: null,
orgId, orgId,
siteId,
name, name,
hcEnabled, hcEnabled,
hcMode, hcMode,
@@ -136,10 +142,45 @@ export async function createHealthCheck(
hcStatus: hcStatus ?? null, hcStatus: hcStatus ?? null,
hcTlsServerName: hcTlsServerName ?? null, hcTlsServerName: hcTlsServerName ?? null,
hcHealthyThreshold, hcHealthyThreshold,
hcUnhealthyThreshold hcUnhealthyThreshold,
hcHealth: "unhealthy"
}) })
.returning(); .returning();
await fireHealthCheckUnhealthyAlert(
record.orgId,
record.targetHealthCheckId,
record.name || "",
undefined,
undefined,
false // dont send the alert because we just want to create the alert, not notify users yet
);
// Push health check to newt if the site is a newt site
if (siteId) {
const [site] = await db
.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
if (site && site.type === "newt") {
const [newt] = await db
.select()
.from(newts)
.where(eq(newts.siteId, site.siteId))
.limit(1);
if (newt) {
await addStandaloneHealthCheck(
newt.newtId,
record,
newt.version
);
}
}
}
return response<CreateHealthCheckResponse>(res, { return response<CreateHealthCheckResponse>(res, {
data: { data: {
targetHealthCheckId: record.targetHealthCheckId targetHealthCheckId: record.targetHealthCheckId

View File

@@ -13,7 +13,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, targetHealthCheck } from "@server/db"; import { db, targetHealthCheck, newts, sites } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -21,6 +21,7 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { and, eq, isNull } from "drizzle-orm"; import { and, eq, isNull } from "drizzle-orm";
import { removeStandaloneHealthCheck } from "@server/routers/newt/targets";
const paramsSchema = z const paramsSchema = z
.object({ .object({
@@ -91,6 +92,21 @@ export async function deleteHealthCheck(
) )
); );
// Remove health check from newt if the site is a newt site
const [newt] = await db
.select()
.from(newts)
.where(eq(newts.siteId, existing.siteId))
.limit(1);
if (newt) {
await removeStandaloneHealthCheck(
newt.newtId,
healthCheckId,
newt.version
);
}
return response<null>(res, { return response<null>(res, {
data: null, data: null,
success: true, success: true,

View File

@@ -1,14 +1,25 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025-2026 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 { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, statusHistory } from "@server/db";
import { and, eq, gte, asc } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { import {
computeBuckets, getCachedStatusHistory,
statusHistoryQuerySchema, statusHistoryQuerySchema,
StatusHistoryResponse StatusHistoryResponse
} from "@server/lib/statusHistory"; } from "@server/lib/statusHistory";
@@ -42,43 +53,14 @@ export async function getHealthCheckStatusHistory(
); );
} }
const entityType = "healthCheck"; const entityType = "health_check";
const entityId = parsedParams.data.healthCheckId const entityId = parsedParams.data.healthCheckId;
const { days } = parsedQuery.data; const { days } = parsedQuery.data;
const nowSec = Math.floor(Date.now() / 1000); const data = await getCachedStatusHistory(entityType, entityId, days);
const startSec = nowSec - days * 86400;
const events = await db
.select()
.from(statusHistory)
.where(
and(
eq(statusHistory.entityType, entityType),
eq(statusHistory.entityId, entityId),
gte(statusHistory.timestamp, startSec)
)
)
.orderBy(asc(statusHistory.timestamp));
const { buckets, totalDowntime } = computeBuckets(events, days);
const totalWindow = days * 86400;
const overallUptime =
totalWindow > 0
? Math.max(
0,
((totalWindow - totalDowntime) / totalWindow) * 100
)
: 100;
return response<StatusHistoryResponse>(res, { return response<StatusHistoryResponse>(res, {
data: { data,
entityType,
entityId,
days: buckets,
overallUptimePercent: Math.round(overallUptime * 100) / 100,
totalDowntimeSeconds: totalDowntime
},
success: true, success: true,
error: false, error: false,
message: "Status history retrieved successfully", message: "Status history retrieved successfully",
@@ -90,4 +72,4 @@ export async function getHealthCheckStatusHistory(
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
); );
} }
} }

View File

@@ -11,13 +11,13 @@
* This file is not licensed under the AGPLv3. * This file is not licensed under the AGPLv3.
*/ */
import { db, targetHealthCheck, targets, resources } from "@server/db"; import { db, targetHealthCheck, targets, resources, sites } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { and, eq, like, sql } from "drizzle-orm"; import { and, eq, exists, isNotNull, like, sql } from "drizzle-orm";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { z } from "zod"; import { z } from "zod";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
@@ -40,7 +40,23 @@ const querySchema = z.object({
.default("0") .default("0")
.transform(Number) .transform(Number)
.pipe(z.int().nonnegative()), .pipe(z.int().nonnegative()),
query: z.string().optional() query: z.string().optional(),
hcMode: z.enum(["http", "tcp", "snmp", "ping"]).optional(),
siteId: z
.string()
.optional()
.transform((s) => (s == null || s === "" ? undefined : Number(s)))
.pipe(z.union([z.undefined(), z.number().int().positive()])),
resourceId: z
.string()
.optional()
.transform((s) => (s == null || s === "" ? undefined : Number(s)))
.pipe(z.union([z.undefined(), z.number().int().positive()])),
hcHealth: z.enum(["healthy", "unhealthy", "unknown"]).optional(),
hcEnabled: z
.enum(["true", "false"])
.optional()
.transform((v) => (v === undefined ? undefined : v === "true"))
}); });
registry.registerPath({ registry.registerPath({
@@ -81,15 +97,46 @@ export async function listHealthChecks(
) )
); );
} }
const { limit, offset, query } = parsedQuery.data; const {
limit,
offset,
query,
hcMode,
siteId,
resourceId,
hcHealth,
hcEnabled
} = parsedQuery.data;
const resourceIdFilter = resourceId
? exists(
db
.select()
.from(targets)
.where(
and(
eq(targets.targetId, targetHealthCheck.targetId),
eq(targets.resourceId, resourceId)
)
)
)
: undefined;
const whereClause = and( const whereClause = and(
eq(targetHealthCheck.orgId, orgId), eq(targetHealthCheck.orgId, orgId),
isNotNull(targetHealthCheck.hcMode), // filter out the null ones attached to targets
query query
? like( ? like(
sql`LOWER(${targetHealthCheck.name})`, sql`LOWER(${targetHealthCheck.name})`,
`%${query.toLowerCase()}%` `%${query.toLowerCase()}%`
) )
: undefined,
hcMode ? eq(targetHealthCheck.hcMode, hcMode) : undefined,
siteId ? eq(targetHealthCheck.siteId, siteId) : undefined,
resourceIdFilter,
hcHealth ? eq(targetHealthCheck.hcHealth, hcHealth) : undefined,
hcEnabled !== undefined
? eq(targetHealthCheck.hcEnabled, hcEnabled)
: undefined : undefined
); );
@@ -97,6 +144,9 @@ export async function listHealthChecks(
.select({ .select({
targetHealthCheckId: targetHealthCheck.targetHealthCheckId, targetHealthCheckId: targetHealthCheck.targetHealthCheckId,
name: targetHealthCheck.name, name: targetHealthCheck.name,
siteId: targetHealthCheck.siteId,
siteName: sites.name,
siteNiceId: sites.niceId,
hcEnabled: targetHealthCheck.hcEnabled, hcEnabled: targetHealthCheck.hcEnabled,
hcHealth: targetHealthCheck.hcHealth, hcHealth: targetHealthCheck.hcHealth,
hcMode: targetHealthCheck.hcMode, hcMode: targetHealthCheck.hcMode,
@@ -121,6 +171,7 @@ export async function listHealthChecks(
.from(targetHealthCheck) .from(targetHealthCheck)
.leftJoin(targets, eq(targetHealthCheck.targetId, targets.targetId)) .leftJoin(targets, eq(targetHealthCheck.targetId, targets.targetId))
.leftJoin(resources, eq(targets.resourceId, resources.resourceId)) .leftJoin(resources, eq(targets.resourceId, resources.resourceId))
.leftJoin(sites, eq(targetHealthCheck.siteId, sites.siteId))
.where(whereClause) .where(whereClause)
.orderBy(sql`${targetHealthCheck.targetHealthCheckId} DESC`) .orderBy(sql`${targetHealthCheck.targetHealthCheckId} DESC`)
.limit(limit) .limit(limit)
@@ -136,6 +187,9 @@ export async function listHealthChecks(
healthChecks: list.map((row) => ({ healthChecks: list.map((row) => ({
targetHealthCheckId: row.targetHealthCheckId, targetHealthCheckId: row.targetHealthCheckId,
name: row.name ?? "", name: row.name ?? "",
siteId: row.siteId ?? null,
siteName: row.siteName ?? null,
siteNiceId: row.siteNiceId ?? null,
hcEnabled: row.hcEnabled, hcEnabled: row.hcEnabled,
hcHealth: (row.hcHealth ?? "unknown") as hcHealth: (row.hcHealth ?? "unknown") as
| "unknown" | "unknown"

View File

@@ -13,7 +13,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, targetHealthCheck } from "@server/db"; import { db, targetHealthCheck, newts, sites } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -21,6 +21,12 @@ import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
import { and, eq, isNull } from "drizzle-orm"; import { and, eq, isNull } from "drizzle-orm";
import { addStandaloneHealthCheck } from "@server/routers/newt/targets";
import {
fireHealthCheckUnhealthyAlert,
fireHealthCheckUnknownAlert,
fireHealthCheckHealthyAlert
} from "@server/lib/alerts";
const paramsSchema = z const paramsSchema = z
.object({ .object({
@@ -34,6 +40,7 @@ const paramsSchema = z
const bodySchema = z.strictObject({ const bodySchema = z.strictObject({
name: z.string().nonempty().optional(), name: z.string().nonempty().optional(),
siteId: z.number().int().positive().optional(),
hcEnabled: z.boolean().optional(), hcEnabled: z.boolean().optional(),
hcMode: z.string().optional(), hcMode: z.string().optional(),
hcHostname: z.string().optional(), hcHostname: z.string().optional(),
@@ -55,6 +62,7 @@ const bodySchema = z.strictObject({
export type UpdateHealthCheckResponse = { export type UpdateHealthCheckResponse = {
targetHealthCheckId: number; targetHealthCheckId: number;
name: string | null; name: string | null;
siteId: number | null;
hcEnabled: boolean; hcEnabled: boolean;
hcHealth: string | null; hcHealth: string | null;
hcMode: string | null; hcMode: string | null;
@@ -125,10 +133,7 @@ export async function updateHealthCheck(
.from(targetHealthCheck) .from(targetHealthCheck)
.where( .where(
and( and(
eq( eq(targetHealthCheck.targetHealthCheckId, healthCheckId),
targetHealthCheck.targetHealthCheckId,
healthCheckId
),
eq(targetHealthCheck.orgId, orgId), eq(targetHealthCheck.orgId, orgId),
isNull(targetHealthCheck.targetId) isNull(targetHealthCheck.targetId)
) )
@@ -145,6 +150,7 @@ export async function updateHealthCheck(
const { const {
name, name,
siteId,
hcEnabled, hcEnabled,
hcMode, hcMode,
hcHostname, hcHostname,
@@ -165,7 +171,19 @@ export async function updateHealthCheck(
const updateData: Record<string, unknown> = {}; const updateData: Record<string, unknown> = {};
const [existingHealthCheck] = await db
.select()
.from(targetHealthCheck)
.where(
and(
eq(targetHealthCheck.targetHealthCheckId, healthCheckId),
eq(targetHealthCheck.orgId, orgId)
)
)
.limit(1);
if (name !== undefined) updateData.name = name; if (name !== undefined) updateData.name = name;
if (siteId !== undefined) updateData.siteId = siteId;
if (hcEnabled !== undefined) updateData.hcEnabled = hcEnabled; if (hcEnabled !== undefined) updateData.hcEnabled = hcEnabled;
if (hcMode !== undefined) updateData.hcMode = hcMode; if (hcMode !== undefined) updateData.hcMode = hcMode;
if (hcHostname !== undefined) updateData.hcHostname = hcHostname; if (hcHostname !== undefined) updateData.hcHostname = hcHostname;
@@ -188,24 +206,92 @@ export async function updateHealthCheck(
if (hcUnhealthyThreshold !== undefined) if (hcUnhealthyThreshold !== undefined)
updateData.hcUnhealthyThreshold = hcUnhealthyThreshold; updateData.hcUnhealthyThreshold = hcUnhealthyThreshold;
const hcEnabledTurnedOn =
parsedBody.data.hcEnabled === true &&
existingHealthCheck.hcEnabled === false;
let hcHealthValue: "unknown" | "healthy" | "unhealthy" | undefined;
if (
parsedBody.data.hcEnabled === false ||
parsedBody.data.hcEnabled === null
) {
hcHealthValue = "unknown";
} else if (hcEnabledTurnedOn) {
hcHealthValue = "unhealthy";
} else {
hcHealthValue = undefined;
}
if (hcHealthValue) {
updateData.hcHealth = hcHealthValue;
}
const [updated] = await db const [updated] = await db
.update(targetHealthCheck) .update(targetHealthCheck)
.set(updateData) .set(updateData)
.where( .where(
and( and(
eq( eq(targetHealthCheck.targetHealthCheckId, healthCheckId),
targetHealthCheck.targetHealthCheckId,
healthCheckId
),
eq(targetHealthCheck.orgId, orgId), eq(targetHealthCheck.orgId, orgId),
isNull(targetHealthCheck.targetId) isNull(targetHealthCheck.targetId)
) )
) )
.returning(); .returning();
if (
updated.hcHealth === "unhealthy" &&
existingHealthCheck.hcHealth !== "unhealthy"
) {
await fireHealthCheckUnhealthyAlert(
updated.orgId,
updated.targetHealthCheckId,
updated.name || "",
undefined,
undefined,
false // dont send the alert because we just want to create the alert, not notify users yet
);
} else if (
updated.hcHealth === "unknown" &&
existingHealthCheck.hcHealth !== "unknown"
) {
// if the health is unknown, we want to fire an alert to notify users to enable health checks
await fireHealthCheckUnknownAlert(
updated.orgId,
updated.targetHealthCheckId,
updated.name,
undefined,
undefined,
false // dont send the alert because we just want to create the alert, not notify users yet
);
} else if (
updated.hcHealth === "healthy" &&
existingHealthCheck.hcHealth !== "healthy"
) {
await fireHealthCheckHealthyAlert(
updated.orgId,
updated.targetHealthCheckId,
updated.name,
undefined,
undefined,
false // dont send the alert because we just want to create the alert, not notify users yet
);
}
// Push updated health check to newt if the site is a newt site
const [newt] = await db
.select()
.from(newts)
.where(eq(newts.siteId, updated.siteId))
.limit(1);
if (newt) {
await addStandaloneHealthCheck(newt.newtId, updated, newt.version);
}
return response<UpdateHealthCheckResponse>(res, { return response<UpdateHealthCheckResponse>(res, {
data: { data: {
targetHealthCheckId: updated.targetHealthCheckId, targetHealthCheckId: updated.targetHealthCheckId,
siteId: updated.siteId ?? null,
name: updated.name ?? null, name: updated.name ?? null,
hcEnabled: updated.hcEnabled, hcEnabled: updated.hcEnabled,
hcHealth: updated.hcHealth ?? null, hcHealth: updated.hcHealth ?? null,

View File

@@ -50,7 +50,7 @@ import {
userOrgRoles, userOrgRoles,
roles roles
} from "@server/db"; } from "@server/db";
import { eq, and, inArray, isNotNull, ne } from "drizzle-orm"; import { eq, and, inArray, isNotNull, ne, or, sql } from "drizzle-orm";
import { response } from "@server/lib/response"; import { response } from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
@@ -492,7 +492,15 @@ hybridRouter.get(
); );
} }
const [result] = await db // Build wildcard domain candidates for the requested domain.
// e.g. "me.example.test.com" -> ["*.example.test.com", "*.test.com"]
const domainParts = domain.split(".");
const wildcardCandidates: string[] = [];
for (let i = 1; i < domainParts.length; i++) {
wildcardCandidates.push(`*.${domainParts.slice(i).join(".")}`);
}
const potentialResults = await db
.select() .select()
.from(resources) .from(resources)
.leftJoin( .leftJoin(
@@ -515,10 +523,28 @@ hybridRouter.get(
) )
) )
.innerJoin(orgs, eq(orgs.orgId, resources.orgId)) .innerJoin(orgs, eq(orgs.orgId, resources.orgId))
.where(eq(resources.fullDomain, domain)) .where(
.limit(1); or(
// Exact match
eq(resources.fullDomain, domain),
// Wildcard match
wildcardCandidates.length > 0
? and(
eq(resources.wildcard, true),
inArray(resources.fullDomain, wildcardCandidates)
)
: sql`false`
)
);
// Prefer exact match over wildcard match
const exactMatch = potentialResults.find(
(r) => r.resources?.fullDomain === domain
);
const result = exactMatch ?? potentialResults[0];
if ( if (
result &&
await checkExitNodeOrg( await checkExitNodeOrg(
remoteExitNode.exitNodeId, remoteExitNode.exitNodeId,
result.resources.orgId result.resources.orgId

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