mirror of
https://github.com/fosrl/pangolin.git
synced 2026-04-01 15:36:38 +00:00
Compare commits
992 Commits
1.15.1
...
1.17.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fc1aa9191 | ||
|
|
f34a3559be | ||
|
|
ddf417f4ca | ||
|
|
f2abbf01e5 | ||
|
|
d08be59055 | ||
|
|
44bb87e4ac | ||
|
|
1d2f1405aa | ||
|
|
ff64a79014 | ||
|
|
f6cdadbc2d | ||
|
|
546769ca66 | ||
|
|
d07996d435 | ||
|
|
467808f174 | ||
|
|
64d3c6b2d9 | ||
|
|
75193bb0a2 | ||
|
|
82ba2bd809 | ||
|
|
8559942c5c | ||
|
|
a7fefc84a8 | ||
|
|
c8e83fedeb | ||
|
|
4bf148a4bf | ||
|
|
44664faf3c | ||
|
|
322c136d1f | ||
|
|
3b8dd45a73 | ||
|
|
c1bd36231d | ||
|
|
2cee723f0e | ||
|
|
edfeec900d | ||
|
|
958bde2090 | ||
|
|
29b272f5d5 | ||
|
|
9162ac6d91 | ||
|
|
fe30bb280e | ||
|
|
a1e9396999 | ||
|
|
2a1c290dff | ||
|
|
d155d7e31b | ||
|
|
3dc258da16 | ||
|
|
0db1397f2f | ||
|
|
0254fb1695 | ||
|
|
954b492aa9 | ||
|
|
8aadc10530 | ||
|
|
6ea719c50f | ||
|
|
b50886179a | ||
|
|
e06f2f47b1 | ||
|
|
ed8c8bedcd | ||
|
|
1711e39219 | ||
|
|
a73879ec7a | ||
|
|
45c613dec4 | ||
|
|
5150a2c386 | ||
|
|
ca0dd09964 | ||
|
|
5e0e4f1452 | ||
|
|
3ed72dd96b | ||
|
|
b8d7d5c910 | ||
|
|
673b8b7af5 | ||
|
|
a651e50759 | ||
|
|
6484e8e302 | ||
|
|
b01d266629 | ||
|
|
4465b05404 | ||
|
|
d1182c3a59 | ||
|
|
cb6c47678b | ||
|
|
8106620a19 | ||
|
|
be3e066843 | ||
|
|
e345c6ee6e | ||
|
|
073b89b355 | ||
|
|
5cad07f8ad | ||
|
|
f9d872558e | ||
|
|
c5015d02ae | ||
|
|
48013228c1 | ||
|
|
dbafffe73d | ||
|
|
61cbcb2a06 | ||
|
|
89c1ad5d98 | ||
|
|
b343ca6290 | ||
|
|
b913466671 | ||
|
|
9054f4f9c3 | ||
|
|
3915024d9a | ||
|
|
7d1085b43f | ||
|
|
7c2477cccc | ||
|
|
5aecb5fb90 | ||
|
|
f86d040ee4 | ||
|
|
ed32717b3f | ||
|
|
aab8462134 | ||
|
|
04943fb4a6 | ||
|
|
e0c96e7224 | ||
|
|
caacd1e677 | ||
|
|
c995c5a674 | ||
|
|
1e9544af07 | ||
|
|
c20dfdabfb | ||
|
|
11a6f1f47f | ||
|
|
fcf92d4e2c | ||
|
|
77cef554be | ||
|
|
9dc9b6a2c3 | ||
|
|
9808a48da0 | ||
|
|
8a6960d9c3 | ||
|
|
d966ef66e1 | ||
|
|
ed97cf5d97 | ||
|
|
a3b088f8d2 | ||
|
|
2828dee94c | ||
|
|
bdc45887f9 | ||
|
|
ee6fb34906 | ||
|
|
bff2ba7cc2 | ||
|
|
8e821b397f | ||
|
|
6f71af278e | ||
|
|
757bb39622 | ||
|
|
00ef6d617f | ||
|
|
d1b2105c80 | ||
|
|
50ee28b1f7 | ||
|
|
ba529ad14e | ||
|
|
6ab0555148 | ||
|
|
c6f269b3fa | ||
|
|
8e160902af | ||
|
|
7bcb852dba | ||
|
|
ed604c8810 | ||
|
|
bea20674a8 | ||
|
|
177926932b | ||
|
|
04dfbd0a14 | ||
|
|
06f840a680 | ||
|
|
5ddcfeb506 | ||
|
|
a143b7de7c | ||
|
|
63372b174f | ||
|
|
ad7d68d2b4 | ||
|
|
e05af54f76 | ||
|
|
914e95e47f | ||
|
|
13eadeaa8f | ||
|
|
19a686b3e4 | ||
|
|
d046084e84 | ||
|
|
e13a076939 | ||
|
|
b4ca6432db | ||
|
|
5b9efc3c5f | ||
|
|
6d7a19b0a0 | ||
|
|
6b3a6fa380 | ||
|
|
e2a65b4b74 | ||
|
|
1f01108b62 | ||
|
|
c80c7df1d0 | ||
|
|
99a064b77a | ||
|
|
9b84623d0c | ||
|
|
6bb6cf8a48 | ||
|
|
348fcbcabf | ||
|
|
1f4cde5f7f | ||
|
|
3e3b02021c | ||
|
|
17eb93d045 | ||
|
|
660420ddef | ||
|
|
395cab795c | ||
|
|
0fecbe704b | ||
|
|
ce59a8a52b | ||
|
|
2091b5f359 | ||
|
|
62c63ddcaa | ||
|
|
dfd604c781 | ||
|
|
3525b367b3 | ||
|
|
0b5b6ed5a3 | ||
|
|
6fe9494df4 | ||
|
|
b2eab95a3b | ||
|
|
38d30b0214 | ||
|
|
c96c5e8ae8 | ||
|
|
6f71e9f0f2 | ||
|
|
d17ec6dc1f | ||
|
|
212b7a104f | ||
|
|
d21dfb750e | ||
|
|
c36a019f5d | ||
|
|
cf2dfdea5b | ||
|
|
985e1bb9ab | ||
|
|
fff38aac85 | ||
|
|
7db58f920c | ||
|
|
e9b16b8801 | ||
|
|
5a2a97b23a | ||
|
|
5b894e8682 | ||
|
|
84925f724d | ||
|
|
7b78b91449 | ||
|
|
f9bff5954f | ||
|
|
2c6e9507b5 | ||
|
|
6471571bc6 | ||
|
|
fe40ea58c1 | ||
|
|
0d4edcd1c7 | ||
|
|
7d8797840a | ||
|
|
19f8c1772f | ||
|
|
37d331e813 | ||
|
|
c660df55cd | ||
|
|
60982bf19f | ||
|
|
7c8b865379 | ||
|
|
3cca0c09c0 | ||
|
|
85335bfecc | ||
|
|
7c2b4f422a | ||
|
|
ad2a0ae127 | ||
|
|
871f14ef3a | ||
|
|
6c2c620c99 | ||
|
|
f643abf19a | ||
|
|
a1729033cf | ||
|
|
7311766512 | ||
|
|
17105f3a51 | ||
|
|
edcfbd26e4 | ||
|
|
0c4d9ea164 | ||
|
|
a5a5224f5c | ||
|
|
8773f7c0a7 | ||
|
|
f385bc2d22 | ||
|
|
a8c9d2e7e6 | ||
|
|
db3f90318b | ||
|
|
2d4d0df5ca | ||
|
|
569ebc671d | ||
|
|
8c8e4e6233 | ||
|
|
c7901ef74b | ||
|
|
be3bd72c1b | ||
|
|
73d1f9288d | ||
|
|
fb7e9f6898 | ||
|
|
38e4b3077f | ||
|
|
312cdc563b | ||
|
|
48ff6dd705 | ||
|
|
695e831090 | ||
|
|
046b431bb8 | ||
|
|
ce2704fc1a | ||
|
|
7e89b36188 | ||
|
|
222dd6bba3 | ||
|
|
ca9ab65228 | ||
|
|
ee4e8f7029 | ||
|
|
f86a1eb32b | ||
|
|
ffd648ed74 | ||
|
|
b2b72169fd | ||
|
|
76746fb6e1 | ||
|
|
6258787c73 | ||
|
|
720080e487 | ||
|
|
46ad1317e4 | ||
|
|
cd28720e46 | ||
|
|
38af02ad3c | ||
|
|
5eed547f91 | ||
|
|
d363ee02ed | ||
|
|
594ee31f43 | ||
|
|
56e25d01ae | ||
|
|
e0fa5607e5 | ||
|
|
572c9bf319 | ||
|
|
52cac4aa21 | ||
|
|
e358d12765 | ||
|
|
02697e27a4 | ||
|
|
ce58e71c44 | ||
|
|
d9766b0f99 | ||
|
|
eeaa1d56ad | ||
|
|
e7f5bc585c | ||
|
|
4f26fb7750 | ||
|
|
cdbc190bfc | ||
|
|
1b1f9ab4cf | ||
|
|
2efe6cfdb3 | ||
|
|
517c607ecf | ||
|
|
802e8f7a22 | ||
|
|
c7cfe2efcb | ||
|
|
ae1f36f39a | ||
|
|
a479ef28ac | ||
|
|
ce2cf50b5a | ||
|
|
f48d01acde | ||
|
|
991fed93ee | ||
|
|
26ab63d0e4 | ||
|
|
e15703164d | ||
|
|
8f33e25782 | ||
|
|
722595c131 | ||
|
|
4843268537 | ||
|
|
c9be84a8a8 | ||
|
|
03288d2a60 | ||
|
|
f60ae13e4e | ||
|
|
e72697f8b8 | ||
|
|
0c3dc1ad14 | ||
|
|
840fe86f78 | ||
|
|
e079927a5b | ||
|
|
63379964fa | ||
|
|
0cfaf6ed7f | ||
|
|
043ee9e9d2 | ||
|
|
1d5dfd6db2 | ||
|
|
b63e3e5888 | ||
|
|
4f82470506 | ||
|
|
40e21b6f28 | ||
|
|
67fab1928d | ||
|
|
eb98374566 | ||
|
|
1169b68619 | ||
|
|
6c83e78256 | ||
|
|
d3bfd67738 | ||
|
|
0908f0f057 | ||
|
|
2785449c7a | ||
|
|
d2419ba572 | ||
|
|
d44292cf33 | ||
|
|
435cae06a2 | ||
|
|
18ed38889f | ||
|
|
aed86ce4ba | ||
|
|
2c2be50b19 | ||
|
|
e2db4c6246 | ||
|
|
c4839fee08 | ||
|
|
965b7026f0 | ||
|
|
e14e15fcbb | ||
|
|
4ca5acf158 | ||
|
|
ea41fcc566 | ||
|
|
5736c1d8ce | ||
|
|
d142366dd9 | ||
|
|
bab09dff95 | ||
|
|
23d3345ab9 | ||
|
|
09a64815d4 | ||
|
|
6d5f969798 | ||
|
|
10349932f4 | ||
|
|
9c430b37aa | ||
|
|
ad3fe2fa76 | ||
|
|
863eb8efe9 | ||
|
|
86bba494fe | ||
|
|
1a43f1ef4b | ||
|
|
75ab074805 | ||
|
|
dc4e0253de | ||
|
|
47a99e35ee | ||
|
|
cccf236042 | ||
|
|
63fd63c65c | ||
|
|
beee1d692d | ||
|
|
fde786ca84 | ||
|
|
3086fdd064 | ||
|
|
6c30f6db31 | ||
|
|
84b082e194 | ||
|
|
f021b73458 | ||
|
|
74f4751bcc | ||
|
|
e5bce4e180 | ||
|
|
9b0e7b381c | ||
|
|
90afe5a7ac | ||
|
|
b24de85157 | ||
|
|
eda43dffe1 | ||
|
|
82c9a1eb70 | ||
|
|
a3d4553d14 | ||
|
|
1cc5f59f66 | ||
|
|
4e2d88efdd | ||
|
|
4975cabb2c | ||
|
|
225591094f | ||
|
|
82f88f2cd3 | ||
|
|
99e6bd31b6 | ||
|
|
5c50590d7b | ||
|
|
072c89e704 | ||
|
|
dbdff6812d | ||
|
|
42b9d5158d | ||
|
|
2ba225299e | ||
|
|
cc841d5640 | ||
|
|
fa0818d3fa | ||
|
|
dec358c4cd | ||
|
|
5455d1c118 | ||
|
|
ae39084a75 | ||
|
|
e98f873f81 | ||
|
|
e9a2a7e752 | ||
|
|
06015d5191 | ||
|
|
af688d2a23 | ||
|
|
7d0b3ec6b5 | ||
|
|
cf5fb8dc33 | ||
|
|
27d20eb1bc | ||
|
|
2e2684c695 | ||
|
|
7e2fd8f49d | ||
|
|
9a0a255445 | ||
|
|
91b7ceb2cf | ||
|
|
d5a37436c0 | ||
|
|
be609b5000 | ||
|
|
0503c6e66e | ||
|
|
d4b830b9bb | ||
|
|
14d6ff25a7 | ||
|
|
1f62f305ce | ||
|
|
9405b0b70a | ||
|
|
a26ee4ac1a | ||
|
|
cebcf3e337 | ||
|
|
4cfcc64481 | ||
|
|
1a2069a6d9 | ||
|
|
2a5c9465e9 | ||
|
|
f36b66e397 | ||
|
|
8c6d44677d | ||
|
|
1bfff630bf | ||
|
|
ebcef28b05 | ||
|
|
e87e12898c | ||
|
|
d60ab281cf | ||
|
|
483d54a9f0 | ||
|
|
0ab6ff9148 | ||
|
|
c73a39f797 | ||
|
|
c87b6872e5 | ||
|
|
f315c8bc43 | ||
|
|
20fa1519fd | ||
|
|
54430afc40 | ||
|
|
7990d08fee | ||
|
|
e9042d9e2e | ||
|
|
24a15841e4 | ||
|
|
bb8f6e09fd | ||
|
|
04bc8ab694 | ||
|
|
6ac8335cf2 | ||
|
|
4c6144f8fb | ||
|
|
255003794e | ||
|
|
119d5c79a0 | ||
|
|
8e2d7c25df | ||
|
|
753dee3023 | ||
|
|
cac0272952 | ||
|
|
ee5b74f9fc | ||
|
|
1362b72cd3 | ||
|
|
35b1566962 | ||
|
|
a4bcce5a0c | ||
|
|
c03f1946e8 | ||
|
|
c11e107758 | ||
|
|
3b4e49f63a | ||
|
|
ea7253f7e8 | ||
|
|
8a529f7946 | ||
|
|
e76612e018 | ||
|
|
e1f99985d8 | ||
|
|
e0c2735635 | ||
|
|
8e6b4e243d | ||
|
|
2623fa8f02 | ||
|
|
7ff92d32cd | ||
|
|
c7f691b20a | ||
|
|
db042e520e | ||
|
|
4cab693cfc | ||
|
|
c9515ae77c | ||
|
|
d14de86f65 | ||
|
|
f6ee9db730 | ||
|
|
94353aea44 | ||
|
|
b01fcc70fe | ||
|
|
ed95f10fcc | ||
|
|
35fed74e49 | ||
|
|
64bae5b142 | ||
|
|
6cf1b9b010 | ||
|
|
dae169540b | ||
|
|
19f9dda490 | ||
|
|
a060c8029f | ||
|
|
aca9d1e070 | ||
|
|
cdf79edb00 | ||
|
|
df53dfc936 | ||
|
|
8e2e09ab81 | ||
|
|
1eac7cbccd | ||
|
|
ddaaed65e4 | ||
|
|
8e633c21c7 | ||
|
|
e7c4ef44d8 | ||
|
|
3d71470bd2 | ||
|
|
dd627a222e | ||
|
|
62cc20fa1c | ||
|
|
0450fc9f57 | ||
|
|
c58aaf5ba6 | ||
|
|
655522d4e2 | ||
|
|
225475dcae | ||
|
|
ccb977fdfb | ||
|
|
280cbb6e22 | ||
|
|
c20babcb53 | ||
|
|
768eebe2cd | ||
|
|
44e3eedffa | ||
|
|
bb189874cb | ||
|
|
34dadd0e16 | ||
|
|
87b5cd9988 | ||
|
|
6a537a23e8 | ||
|
|
e63a6e9b77 | ||
|
|
7ce589c4f2 | ||
|
|
75a909784a | ||
|
|
244f497a9c | ||
|
|
e58f0c9f07 | ||
|
|
5f18c06e03 | ||
|
|
f36cf06e26 | ||
|
|
27d52646a0 | ||
|
|
4dd8080c55 | ||
|
|
0b35d4f2e3 | ||
|
|
54a9fb9e54 | ||
|
|
60a9e68f02 | ||
|
|
ad374298e3 | ||
|
|
c5dc4e6127 | ||
|
|
291ad831c5 | ||
|
|
0a018f0ca8 | ||
|
|
6673eeb1bb | ||
|
|
4641f0b9ef | ||
|
|
a4487964e5 | ||
|
|
fe42fdd1ec | ||
|
|
375211f184 | ||
|
|
66c377a5c9 | ||
|
|
50c2aa0111 | ||
|
|
fdeb891137 | ||
|
|
6a6e3a43b1 | ||
|
|
b0a34fa21b | ||
|
|
5c4de03588 | ||
|
|
72bf6f3c41 | ||
|
|
ad9289e0c1 | ||
|
|
b0cb0e5a99 | ||
|
|
8347203bbe | ||
|
|
4aa1186aed | ||
|
|
eed87af61d | ||
|
|
daeea8e7ea | ||
|
|
0d63a15715 | ||
|
|
fa2e229ada | ||
|
|
81c1a1da9c | ||
|
|
5d9700d84c | ||
|
|
f8a8cdaa5f | ||
|
|
e23e446476 | ||
|
|
fa097df50b | ||
|
|
75f34ff127 | ||
|
|
c9586b4d93 | ||
|
|
52937a6d90 | ||
|
|
186c131cce | ||
|
|
8de3f9a440 | ||
|
|
ea49e179f9 | ||
|
|
485f4f1c8e | ||
|
|
5fb35d12d7 | ||
|
|
ec8a9fe3d2 | ||
|
|
411a34e15e | ||
|
|
3df71fd2bc | ||
|
|
5e1f6085e3 | ||
|
|
53fc7ab6e3 | ||
|
|
7779ed24fe | ||
|
|
6e4193dae3 | ||
|
|
f138609f48 | ||
|
|
98154b5de3 | ||
|
|
6322fd9eef | ||
|
|
1c0949e957 | ||
|
|
52f26396ac | ||
|
|
c3847e6001 | ||
|
|
5cf13a963d | ||
|
|
b017877826 | ||
|
|
959f68b520 | ||
|
|
14cab3fdb8 | ||
|
|
b8d468f6de | ||
|
|
fc66394243 | ||
|
|
8fca243c9a | ||
|
|
388f710379 | ||
|
|
ba3ab4362b | ||
|
|
e18c9afc2d | ||
|
|
a9b4a86c4a | ||
|
|
200ea502dd | ||
|
|
de36db97eb | ||
|
|
30283b044f | ||
|
|
055bed8a07 | ||
|
|
12b5c2ab34 | ||
|
|
dd78674888 | ||
|
|
0d0df63847 | ||
|
|
3ab00d9da8 | ||
|
|
3e6e72c5c7 | ||
|
|
5d8a55f08c | ||
|
|
81c569aae4 | ||
|
|
88fd3fc4da | ||
|
|
2282d3ae39 | ||
|
|
c4dcec463a | ||
|
|
5b7f893ad7 | ||
|
|
2ede0d498a | ||
|
|
f518e8a0ff | ||
|
|
767284408a | ||
|
|
eef51f3b84 | ||
|
|
69b7114a49 | ||
|
|
0ea38ea568 | ||
|
|
c600da71e3 | ||
|
|
c64dd14b1a | ||
|
|
8ea6d9fa67 | ||
|
|
978ac8f53c | ||
|
|
49a326cde7 | ||
|
|
63e208f4ec | ||
|
|
f50d1549b0 | ||
|
|
55e24df671 | ||
|
|
b37e1d0cc0 | ||
|
|
afa26c0dd4 | ||
|
|
c71f46ede5 | ||
|
|
20e547a0f6 | ||
|
|
2edebaddc2 | ||
|
|
119e1d4867 | ||
|
|
63e30d3378 | ||
|
|
d6fe04ec4e | ||
|
|
b8a364af6a | ||
|
|
5ef808d4a2 | ||
|
|
848d4d91e6 | ||
|
|
a502780c9b | ||
|
|
418e099804 | ||
|
|
06258aa386 | ||
|
|
d7608b1cc8 | ||
|
|
cb86ad4104 | ||
|
|
8cd51df1e1 | ||
|
|
8ef7220766 | ||
|
|
b5333a3686 | ||
|
|
e6e92dbc0f | ||
|
|
01fdd41a10 | ||
|
|
6af06a38ae | ||
|
|
5d9c66d22d | ||
|
|
81f5a4b127 | ||
|
|
da3e68a20b | ||
|
|
8712c1719e | ||
|
|
593c5db0e8 | ||
|
|
b28391feae | ||
|
|
5f8df6d4cd | ||
|
|
c36efe7f14 | ||
|
|
cf97b6df9c | ||
|
|
720d3a8135 | ||
|
|
9c42458fa5 | ||
|
|
6d9b129ac9 | ||
|
|
e17ec798d4 | ||
|
|
58ac499f30 | ||
|
|
f07f0092ad | ||
|
|
bcd3475d17 | ||
|
|
7c04526088 | ||
|
|
2d7ab68576 | ||
|
|
218a4893b6 | ||
|
|
266bf261aa | ||
|
|
63694032e8 | ||
|
|
b77aaedb58 | ||
|
|
a316d0301f | ||
|
|
dcd499720e | ||
|
|
e18fe21eca | ||
|
|
2970b51fb8 | ||
|
|
b9236ff52e | ||
|
|
38eb0ec7ed | ||
|
|
ecba4a0b80 | ||
|
|
e6da18c952 | ||
|
|
12941ac5ae | ||
|
|
11085bda63 | ||
|
|
c03211cc53 | ||
|
|
2867459600 | ||
|
|
32b24db9bf | ||
|
|
660bf9ff87 | ||
|
|
78c4ddebba | ||
|
|
f2dfadb37b | ||
|
|
3f2bdf081f | ||
|
|
d6ba34aeea | ||
|
|
b622aca221 | ||
|
|
6442eb12fb | ||
|
|
01c15afa74 | ||
|
|
4e88f1f38a | ||
|
|
13ab505f4d | ||
|
|
7d112aab27 | ||
|
|
b786497299 | ||
|
|
eedf57af89 | ||
|
|
7a01a4e090 | ||
|
|
874794c996 | ||
|
|
5e37c4e85f | ||
|
|
4e7eac368f | ||
|
|
e8398cb221 | ||
|
|
9460e28c7b | ||
|
|
756f3f32ca | ||
|
|
362981ad19 | ||
|
|
fa4f7e4ac2 | ||
|
|
c6bca4e2ab | ||
|
|
e28b361e05 | ||
|
|
a18691011b | ||
|
|
c4a6403cba | ||
|
|
1851bf941a | ||
|
|
b7ab3c2e92 | ||
|
|
ce1ad032ba | ||
|
|
8446c68e1b | ||
|
|
40ed388b0f | ||
|
|
ce1693aa2f | ||
|
|
11d16a1552 | ||
|
|
0ac54a2c88 | ||
|
|
b7d8b32123 | ||
|
|
5987f6b2cd | ||
|
|
7ad76f5683 | ||
|
|
09a9457021 | ||
|
|
d8b45396e3 | ||
|
|
ca4643ec36 | ||
|
|
e2f78ba476 | ||
|
|
5d92190d50 | ||
|
|
2b0d6de986 | ||
|
|
057f82a561 | ||
|
|
719d2a5ffe | ||
|
|
d4bff9d5cb | ||
|
|
19fcc1f93b | ||
|
|
d45ea127c2 | ||
|
|
f591cf8601 | ||
|
|
6661a76aa8 | ||
|
|
a2ed22bfcc | ||
|
|
e370f8891a | ||
|
|
8a83e32c42 | ||
|
|
831eb6325c | ||
|
|
4d6240c987 | ||
|
|
79cf7c84dc | ||
|
|
b71f582329 | ||
|
|
8315d4b6ae | ||
|
|
b8c3cc751a | ||
|
|
d00262dc31 | ||
|
|
952d0c74d0 | ||
|
|
ffbea7af59 | ||
|
|
3debc6c8d3 | ||
|
|
5092eb58fb | ||
|
|
f0b9240575 | ||
|
|
9cf59c409e | ||
|
|
971c375398 | ||
|
|
ac4439c5ae | ||
|
|
bfd5aa30a7 | ||
|
|
9737170665 | ||
|
|
922a040466 | ||
|
|
9eacefb155 | ||
|
|
33f0782f3a | ||
|
|
e6a5cef945 | ||
|
|
4c8edb80b3 | ||
|
|
d4668fae99 | ||
|
|
ddfe55e3ae | ||
|
|
761a5f1d4c | ||
|
|
1fbcad8787 | ||
|
|
aba586e605 | ||
|
|
27b21b5ad4 | ||
|
|
b6e54dab17 | ||
|
|
1f8e89772d | ||
|
|
843b13ed57 | ||
|
|
be89e5ca55 | ||
|
|
5f3657fd56 | ||
|
|
494162400e | ||
|
|
ab65bb6a8a | ||
|
|
333625f199 | ||
|
|
dbfd715381 | ||
|
|
f1d989964e | ||
|
|
b701629498 | ||
|
|
8250946325 | ||
|
|
71f63d8e6f | ||
|
|
dd5e834db0 | ||
|
|
970ecb52f0 | ||
|
|
62ea1b40e1 | ||
|
|
3b0fd5c592 | ||
|
|
b7616026dd | ||
|
|
16ad60b89a | ||
|
|
db7971d2f7 | ||
|
|
f3f8bd3125 | ||
|
|
516fd0ee8f | ||
|
|
8d6700d493 | ||
|
|
9d4ace9b3e | ||
|
|
2800655e33 | ||
|
|
91eecee11d | ||
|
|
899e5aa395 | ||
|
|
d5820c4902 | ||
|
|
a91c002274 | ||
|
|
4d142b93dd | ||
|
|
04dcf57ff3 | ||
|
|
975550c755 | ||
|
|
a964a80d85 | ||
|
|
22c3b8f116 | ||
|
|
c4b1831cfe | ||
|
|
cdb6813384 | ||
|
|
b14b68d83c | ||
|
|
3c2f930e6b | ||
|
|
ca9c7ce555 | ||
|
|
c2e95a0607 | ||
|
|
2767ee9e80 | ||
|
|
d998a8087f | ||
|
|
fdce016921 | ||
|
|
c73d70933b | ||
|
|
e9d0ad6e37 | ||
|
|
a35586f762 | ||
|
|
f527c30923 | ||
|
|
94e70219cf | ||
|
|
6496763aae | ||
|
|
a409ec269b | ||
|
|
bc7bc8da66 | ||
|
|
52484c774e | ||
|
|
4e1e0cade1 | ||
|
|
fda5904dac | ||
|
|
69ecc22318 | ||
|
|
bff9d33ee6 | ||
|
|
edf506953b | ||
|
|
5e11746549 | ||
|
|
1ae315e303 | ||
|
|
758b03ab25 | ||
|
|
e756fad573 | ||
|
|
3547450b03 | ||
|
|
733f6692c6 | ||
|
|
2d83160b16 | ||
|
|
256fa880dd | ||
|
|
b08c5f5c67 | ||
|
|
d0862a2d26 | ||
|
|
e97340ed52 | ||
|
|
e27c81eea6 | ||
|
|
7f7f3d43b2 | ||
|
|
4b1b772098 | ||
|
|
f66b88490f | ||
|
|
18f9157169 | ||
|
|
6eb82a807b | ||
|
|
bf57a97833 | ||
|
|
e9e2093220 | ||
|
|
c3540da2e3 | ||
|
|
d228cf56dd | ||
|
|
8f4cecd963 | ||
|
|
66adff44bb | ||
|
|
be41c094dc | ||
|
|
273848ca18 | ||
|
|
1e9dbead3b | ||
|
|
aeaa8ba133 | ||
|
|
24654af635 | ||
|
|
e88a21d6db | ||
|
|
bcd01badaf | ||
|
|
8e063506e0 | ||
|
|
84f5d6137a | ||
|
|
0a8565f5e8 | ||
|
|
bd8da25a46 | ||
|
|
a841f588dd | ||
|
|
75a4362ce3 | ||
|
|
e763e001e5 | ||
|
|
69475a0ae7 | ||
|
|
53e14c2ad7 | ||
|
|
1edc33148a | ||
|
|
a4cbfc74e4 | ||
|
|
c0d25aeb02 | ||
|
|
40f49bf6da | ||
|
|
0bfce87dc6 | ||
|
|
2a0655e9de | ||
|
|
a86cfa5934 | ||
|
|
54b77523c5 | ||
|
|
ba06c8928d | ||
|
|
c8a4ac1ed4 | ||
|
|
143acbae48 | ||
|
|
937f6fdae8 | ||
|
|
ba7239ac08 | ||
|
|
2e748274c0 | ||
|
|
eab2750953 | ||
|
|
17b6cb0c73 | ||
|
|
98a4c453c1 | ||
|
|
6475dceab9 | ||
|
|
040a945774 | ||
|
|
47743a5fa8 | ||
|
|
d47d6de985 | ||
|
|
37818b8594 | ||
|
|
3b184acddd | ||
|
|
9c80404d17 | ||
|
|
aaa7082f9d | ||
|
|
a45b45b2ce | ||
|
|
e4bfbd267e | ||
|
|
65b4dcc672 | ||
|
|
36fc30b524 | ||
|
|
e724ed9137 | ||
|
|
7ca992af05 | ||
|
|
37f1c714ac | ||
|
|
397a43fb60 | ||
|
|
45e0a648c6 | ||
|
|
7336aa81d9 | ||
|
|
d727c10d98 | ||
|
|
321d77a317 | ||
|
|
19b8a6b737 | ||
|
|
f2e69dfb96 | ||
|
|
8207e49317 | ||
|
|
b75600b9ea | ||
|
|
7b01f1bef6 | ||
|
|
e7bd2c0001 | ||
|
|
a26076e9db | ||
|
|
9711a0fb8e | ||
|
|
accc670411 | ||
|
|
071c41a54f | ||
|
|
35ba6c19c3 | ||
|
|
14c8348166 | ||
|
|
7d6ee72025 | ||
|
|
ea0e770b57 | ||
|
|
193b7ff21e | ||
|
|
d814ad9f3e | ||
|
|
da8b620c75 | ||
|
|
911b5e6814 | ||
|
|
f991fd9c71 | ||
|
|
652e4c922d | ||
|
|
4364e3fbc1 | ||
|
|
a783fdecbc | ||
|
|
16f67455a2 | ||
|
|
0850a28d20 | ||
|
|
5ca598139e | ||
|
|
df1bf09163 | ||
|
|
50bc8d3e9c | ||
|
|
86d089024e | ||
|
|
d5c1cf594d | ||
|
|
a0b5731e69 | ||
|
|
ceb359d614 | ||
|
|
a49a9f8e3b | ||
|
|
766606b08d | ||
|
|
fed56c1959 | ||
|
|
ae6ed8ad97 | ||
|
|
c1ca0b8e2c | ||
|
|
569dc735ce | ||
|
|
dd11c2c871 | ||
|
|
8def4a2b68 | ||
|
|
13a5f24b07 | ||
|
|
0989d6353e | ||
|
|
4139a7b73f | ||
|
|
be60d66ce3 | ||
|
|
0a33043874 | ||
|
|
96d1d983e5 | ||
|
|
7ffb260d7c | ||
|
|
ce74489df5 | ||
|
|
342b188fae | ||
|
|
fa6fee7b55 | ||
|
|
c53d5a4d7d | ||
|
|
521e905724 | ||
|
|
4623090050 | ||
|
|
dd9e5cc541 | ||
|
|
626be6a347 | ||
|
|
56327ed503 | ||
|
|
6d1665004b | ||
|
|
59b8119fbd | ||
|
|
9ff863db5e | ||
|
|
e2ac6e6d4d | ||
|
|
df4101875a | ||
|
|
3f5c788d48 | ||
|
|
45cd4df6e5 | ||
|
|
94ac3ec76e | ||
|
|
af7263a0b1 | ||
|
|
035396f95c | ||
|
|
f318f6304b | ||
|
|
9d0ff472e5 | ||
|
|
d27482e812 | ||
|
|
d5b6de70da | ||
|
|
69c2212ea0 | ||
|
|
10be9bcd56 | ||
|
|
f531def0d2 | ||
|
|
ed40eae655 | ||
|
|
ba5ae6ed04 | ||
|
|
d6ade102dc | ||
|
|
0a6301697e | ||
|
|
13b4fc6725 | ||
|
|
c94d246c24 | ||
|
|
5b779ba9fe | ||
|
|
3ba2cb19a9 | ||
|
|
a095dddd01 | ||
|
|
1b5cfaa49b | ||
|
|
66f3fabbae | ||
|
|
0be8fb7931 | ||
|
|
431e6ffaae | ||
|
|
7d8185e0ee | ||
|
|
dff45748bd | ||
|
|
da514ef314 | ||
|
|
7f73cde794 | ||
|
|
b0af0d9cd5 | ||
|
|
e6464929ff | ||
|
|
122053939d | ||
|
|
8429197b07 | ||
|
|
44f2081882 | ||
|
|
300b4a3706 | ||
|
|
81ef2db7f8 | ||
|
|
c41e8be3e8 | ||
|
|
41bab0ce0b | ||
|
|
5f26b9eeea | ||
|
|
1cca69ad23 | ||
|
|
410ed3949b | ||
|
|
efc6ef3075 | ||
|
|
63f7dd1d20 | ||
|
|
57b8c69983 | ||
|
|
aad060810a | ||
|
|
9222b00a6f | ||
|
|
ff61b22e7e | ||
|
|
577cb91343 | ||
|
|
1889386f64 | ||
|
|
5d7f082ebf | ||
|
|
db6327c4ff | ||
|
|
fd7f6b2b99 | ||
|
|
49435398a8 | ||
|
|
e101ac341b | ||
|
|
6cfc7b7c69 | ||
|
|
313acabc86 | ||
|
|
34cced872f | ||
|
|
ac09e3aaf9 | ||
|
|
9f2fd34e99 | ||
|
|
67b63d3084 | ||
|
|
4a31a7b84b | ||
|
|
538b601b1e | ||
|
|
588f064c25 | ||
|
|
d521e79662 | ||
|
|
ccddb9244d | ||
|
|
0547396213 | ||
|
|
6c85171091 | ||
|
|
a8f6b6c1da | ||
|
|
f899326189 | ||
|
|
0f4d1d2a74 | ||
|
|
941d5c08e3 | ||
|
|
db9f74158b | ||
|
|
b4c01349d1 | ||
|
|
165bbd3584 | ||
|
|
ffb253e0e9 | ||
|
|
e5e9fe456f | ||
|
|
c63589b204 | ||
|
|
11408c2656 | ||
|
|
7d4aed8819 | ||
|
|
609ffccd67 | ||
|
|
508369a59d | ||
|
|
748af1d8cb | ||
|
|
26a91cd5e1 | ||
|
|
48dd4d5913 | ||
|
|
d309ec249e | ||
|
|
72d46b7352 | ||
|
|
4613aae47d | ||
|
|
1bc4480d84 | ||
|
|
b5d76f73e8 | ||
|
|
a5c7913e77 | ||
|
|
34b914f509 | ||
|
|
5a3d75ca12 | ||
|
|
158d7b23d8 | ||
|
|
67949b4968 | ||
|
|
1fc40b3017 | ||
|
|
bb1a375484 | ||
|
|
bf5dd3b0a1 | ||
|
|
e4d4c62833 | ||
|
|
20ae903d7f | ||
|
|
f5f757e4bd | ||
|
|
13c011895d | ||
|
|
bd8d0e3392 | ||
|
|
5ad564d21b | ||
|
|
8f8775cb93 | ||
|
|
37695827aa | ||
|
|
7a72d209ea | ||
|
|
cda6b67bef | ||
|
|
066305b095 | ||
|
|
f2ba4b270f | ||
|
|
89695df012 | ||
|
|
b0566d3c6f | ||
|
|
5dda8c384f | ||
|
|
b04385a340 | ||
|
|
d374ea6ea6 | ||
|
|
01a2820390 | ||
|
|
c89c1a03da | ||
|
|
873408270e | ||
|
|
8fec8f35bc | ||
|
|
141c846fe2 | ||
|
|
cb569ff14d | ||
|
|
1497469016 | ||
|
|
e356a6d33b | ||
|
|
38ac4c5980 | ||
|
|
ed3ee64e4b | ||
|
|
12aea2901d | ||
|
|
5ff56467ea | ||
|
|
3a8718a4b0 | ||
|
|
8c15855fc3 | ||
|
|
3d4df906cf | ||
|
|
e051142334 |
@@ -28,7 +28,9 @@ LICENSE
|
||||
CONTRIBUTING.md
|
||||
dist
|
||||
.git
|
||||
migrations/
|
||||
server/migrations/
|
||||
config/
|
||||
build.ts
|
||||
tsconfig.json
|
||||
tsconfig.json
|
||||
Dockerfile*
|
||||
drizzle.config.ts
|
||||
|
||||
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@@ -44,19 +44,9 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
dev-patch-updates:
|
||||
dependency-type: "development"
|
||||
patch-updates:
|
||||
update-types:
|
||||
- "patch"
|
||||
dev-minor-updates:
|
||||
dependency-type: "development"
|
||||
minor-updates:
|
||||
update-types:
|
||||
- "minor"
|
||||
prod-patch-updates:
|
||||
dependency-type: "production"
|
||||
update-types:
|
||||
- "patch"
|
||||
prod-minor-updates:
|
||||
dependency-type: "production"
|
||||
update-types:
|
||||
- "minor"
|
||||
161
.github/workflows/cicd.yml
vendored
161
.github/workflows/cicd.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: CI/CD Pipeline
|
||||
name: Public CICD Pipeline
|
||||
|
||||
# CI/CD workflow for building, publishing, mirroring, signing container images and building release binaries.
|
||||
# Actions are pinned to specific SHAs to reduce supply-chain risk. This workflow triggers on tag push events.
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Monitor storage space
|
||||
run: |
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Monitor storage space
|
||||
run: |
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
@@ -201,10 +201,10 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
@@ -256,7 +256,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Extract tag name
|
||||
id: get-tag
|
||||
@@ -264,9 +264,9 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
with:
|
||||
go-version: 1.24
|
||||
go-version: 1.25
|
||||
|
||||
- name: Update version in package.json
|
||||
run: |
|
||||
@@ -289,25 +289,17 @@ jobs:
|
||||
echo "LATEST_BADGER_TAG=$LATEST_TAG" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Update install/main.go
|
||||
run: |
|
||||
PANGOLIN_VERSION=${{ env.TAG }}
|
||||
GERBIL_VERSION=${{ env.LATEST_GERBIL_TAG }}
|
||||
BADGER_VERSION=${{ env.LATEST_BADGER_TAG }}
|
||||
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"$PANGOLIN_VERSION\"/" install/main.go
|
||||
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"$GERBIL_VERSION\"/" install/main.go
|
||||
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"$BADGER_VERSION\"/" install/main.go
|
||||
echo "Updated install/main.go with Pangolin version $PANGOLIN_VERSION, Gerbil version $GERBIL_VERSION, and Badger version $BADGER_VERSION"
|
||||
cat install/main.go
|
||||
shell: bash
|
||||
|
||||
- name: Build installer
|
||||
working-directory: install
|
||||
run: |
|
||||
make go-build-release
|
||||
make go-build-release \
|
||||
PANGOLIN_VERSION=${{ env.TAG }} \
|
||||
GERBIL_VERSION=${{ env.LATEST_GERBIL_TAG }} \
|
||||
BADGER_VERSION=${{ env.LATEST_BADGER_TAG }}
|
||||
shell: bash
|
||||
|
||||
- name: Upload artifacts from /install/bin
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: install-bin
|
||||
path: install/bin/
|
||||
@@ -415,7 +407,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Login to GitHub Container Registry (for cosign)
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -423,7 +415,7 @@ jobs:
|
||||
|
||||
- name: Install cosign
|
||||
# cosign is used to sign and verify container images (key and keyless)
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
||||
|
||||
- name: Dual-sign and verify (GHCR & Docker Hub)
|
||||
# Sign each image by digest using keyless (OIDC) and key-based signing,
|
||||
@@ -440,6 +432,10 @@ jobs:
|
||||
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
|
||||
IS_RC="false"
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
@@ -471,49 +467,92 @@ jobs:
|
||||
for BASE_IMAGE in "${GHCR_IMAGE}" "${DOCKERHUB_IMAGE}"; do
|
||||
for IMAGE_TAG in "${IMAGE_TAGS[@]}"; do
|
||||
echo "Processing ${BASE_IMAGE}:${IMAGE_TAG}"
|
||||
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}"
|
||||
# Wrap the entire tag processing in error handling
|
||||
(
|
||||
set -e
|
||||
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}"
|
||||
echo "==> cosign sign (keyless) --recursive ${REF}"
|
||||
cosign sign --recursive "${REF}"
|
||||
|
||||
echo "==> cosign sign (key) --recursive ${REF}"
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}"
|
||||
echo "==> cosign sign (key) --recursive ${REF}"
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}"
|
||||
|
||||
# Retry wrapper for verification to handle registry propagation delays
|
||||
retry_verify() {
|
||||
local cmd="$1"
|
||||
local attempts=6
|
||||
local delay=5
|
||||
local i=1
|
||||
until eval "$cmd"; do
|
||||
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
|
||||
}
|
||||
# Retry wrapper for verification to handle registry propagation delays
|
||||
retry_verify() {
|
||||
local cmd="$1"
|
||||
local attempts=6
|
||||
local delay=5
|
||||
local i=1
|
||||
until eval "$cmd"; do
|
||||
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}"
|
||||
retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${REF}' -o text"
|
||||
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}"
|
||||
retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${REF}' -o text"
|
||||
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
|
||||
|
||||
echo "✓ Successfully signed and verified ${BASE_IMAGE}:${IMAGE_TAG}"
|
||||
# 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
|
||||
|
||||
echo "All images signed and verified successfully!"
|
||||
# Report summary
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Sign and Verify Summary"
|
||||
echo "=========================================="
|
||||
echo "Successful: ${#SUCCESSFUL_TAGS[@]}"
|
||||
echo "Failed: ${#FAILED_TAGS[@]}"
|
||||
echo ""
|
||||
|
||||
if [ ${#FAILED_TAGS[@]} -gt 0 ]; then
|
||||
echo "Failed tags:"
|
||||
for tag in "${FAILED_TAGS[@]}"; do
|
||||
echo " - $tag"
|
||||
done
|
||||
echo ""
|
||||
echo "⚠️ WARNING: Some tags failed to sign/verify, but continuing anyway"
|
||||
else
|
||||
echo "✓ All images signed and verified successfully!"
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
post-run:
|
||||
@@ -531,7 +570,7 @@ jobs:
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
|
||||
426
.github/workflows/cicd.yml.backup
vendored
426
.github/workflows/cicd.yml.backup
vendored
@@ -1,426 +0,0 @@
|
||||
name: CI/CD Pipeline
|
||||
|
||||
# CI/CD workflow for building, publishing, mirroring, signing container images and building release binaries.
|
||||
# Actions are pinned to specific SHAs to reduce supply-chain risk. This workflow triggers on tag push events.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # for GHCR push
|
||||
id-token: write # for Cosign Keyless (OIDC) Signing
|
||||
|
||||
# Required secrets:
|
||||
# - DOCKER_HUB_USERNAME / DOCKER_HUB_ACCESS_TOKEN: push to Docker Hub
|
||||
# - GITHUB_TOKEN: used for GHCR login and OIDC keyless signing
|
||||
# - COSIGN_PRIVATE_KEY / COSIGN_PASSWORD / COSIGN_PUBLIC_KEY: for key-based signing
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+"
|
||||
- "[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
pre-run:
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Verify AWS identity
|
||||
run: aws sts get-caller-identity
|
||||
|
||||
- name: Start EC2 instances
|
||||
run: |
|
||||
aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
|
||||
aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }}
|
||||
echo "EC2 instances started"
|
||||
|
||||
|
||||
release-arm:
|
||||
name: Build and Release (ARM64)
|
||||
runs-on: [self-hosted, linux, arm64, us-east-1]
|
||||
needs: [pre-run]
|
||||
if: >-
|
||||
${{
|
||||
needs.pre-run.result == 'success'
|
||||
}}
|
||||
# Job-level timeout to avoid runaway or stuck runs
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
# Target images
|
||||
DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Monitor storage space
|
||||
run: |
|
||||
THRESHOLD=75
|
||||
USED_SPACE=$(df / | grep / | awk '{ print $5 }' | sed 's/%//g')
|
||||
echo "Used space: $USED_SPACE%"
|
||||
if [ "$USED_SPACE" -ge "$THRESHOLD" ]; then
|
||||
echo "Used space is below the threshold of 75% free. Running Docker system prune."
|
||||
echo y | docker system prune -a
|
||||
else
|
||||
echo "Storage space is above the threshold. No action needed."
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Extract tag name
|
||||
id: get-tag
|
||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Update version in package.json
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
|
||||
cat server/lib/consts.ts
|
||||
shell: bash
|
||||
|
||||
- name: Check if release candidate
|
||||
id: check-rc
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
echo "IS_RC=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "IS_RC=false" >> $GITHUB_ENV
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build and push Docker images (Docker Hub - ARM64)
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
if [ "$IS_RC" = "true" ]; then
|
||||
make build-rc-arm tag=$TAG
|
||||
else
|
||||
make build-release-arm tag=$TAG
|
||||
fi
|
||||
echo "Built & pushed ARM64 images to: ${{ env.DOCKERHUB_IMAGE }}:${TAG}"
|
||||
shell: bash
|
||||
|
||||
release-amd:
|
||||
name: Build and Release (AMD64)
|
||||
runs-on: [self-hosted, linux, x64, us-east-1]
|
||||
needs: [pre-run]
|
||||
if: >-
|
||||
${{
|
||||
needs.pre-run.result == 'success'
|
||||
}}
|
||||
# Job-level timeout to avoid runaway or stuck runs
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
# Target images
|
||||
DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Monitor storage space
|
||||
run: |
|
||||
THRESHOLD=75
|
||||
USED_SPACE=$(df / | grep / | awk '{ print $5 }' | sed 's/%//g')
|
||||
echo "Used space: $USED_SPACE%"
|
||||
if [ "$USED_SPACE" -ge "$THRESHOLD" ]; then
|
||||
echo "Used space is below the threshold of 75% free. Running Docker system prune."
|
||||
echo y | docker system prune -a
|
||||
else
|
||||
echo "Storage space is above the threshold. No action needed."
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Extract tag name
|
||||
id: get-tag
|
||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Update version in package.json
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
|
||||
cat server/lib/consts.ts
|
||||
shell: bash
|
||||
|
||||
- name: Check if release candidate
|
||||
id: check-rc
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
echo "IS_RC=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "IS_RC=false" >> $GITHUB_ENV
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build and push Docker images (Docker Hub - AMD64)
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
if [ "$IS_RC" = "true" ]; then
|
||||
make build-rc-amd tag=$TAG
|
||||
else
|
||||
make build-release-amd tag=$TAG
|
||||
fi
|
||||
echo "Built & pushed AMD64 images to: ${{ env.DOCKERHUB_IMAGE }}:${TAG}"
|
||||
shell: bash
|
||||
|
||||
create-manifest:
|
||||
name: Create Multi-Arch Manifests
|
||||
runs-on: [self-hosted, linux, x64, us-east-1]
|
||||
needs: [release-arm, release-amd]
|
||||
if: >-
|
||||
${{
|
||||
needs.release-arm.result == 'success' &&
|
||||
needs.release-amd.result == 'success'
|
||||
}}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Extract tag name
|
||||
id: get-tag
|
||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Check if release candidate
|
||||
id: check-rc
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
if [[ "$TAG" == *"-rc."* ]]; then
|
||||
echo "IS_RC=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "IS_RC=false" >> $GITHUB_ENV
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Create multi-arch manifests
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
if [ "$IS_RC" = "true" ]; then
|
||||
make create-manifests-rc tag=$TAG
|
||||
else
|
||||
make create-manifests tag=$TAG
|
||||
fi
|
||||
echo "Created multi-arch manifests for tag: ${TAG}"
|
||||
shell: bash
|
||||
|
||||
sign-and-package:
|
||||
name: Sign and Package
|
||||
runs-on: [self-hosted, linux, x64, us-east-1]
|
||||
needs: [release-arm, release-amd, create-manifest]
|
||||
if: >-
|
||||
${{
|
||||
needs.release-arm.result == 'success' &&
|
||||
needs.release-amd.result == 'success' &&
|
||||
needs.create-manifest.result == 'success'
|
||||
}}
|
||||
# Job-level timeout to avoid runaway or stuck runs
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
# Target images
|
||||
DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Extract tag name
|
||||
id: get-tag
|
||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version: 1.24
|
||||
|
||||
- name: Update version in package.json
|
||||
run: |
|
||||
TAG=${{ env.TAG }}
|
||||
sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
|
||||
cat server/lib/consts.ts
|
||||
shell: bash
|
||||
|
||||
- name: Pull latest Gerbil version
|
||||
id: get-gerbil-tag
|
||||
run: |
|
||||
LATEST_TAG=$(curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name')
|
||||
echo "LATEST_GERBIL_TAG=$LATEST_TAG" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Pull latest Badger version
|
||||
id: get-badger-tag
|
||||
run: |
|
||||
LATEST_TAG=$(curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name')
|
||||
echo "LATEST_BADGER_TAG=$LATEST_TAG" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Update install/main.go
|
||||
run: |
|
||||
PANGOLIN_VERSION=${{ env.TAG }}
|
||||
GERBIL_VERSION=${{ env.LATEST_GERBIL_TAG }}
|
||||
BADGER_VERSION=${{ env.LATEST_BADGER_TAG }}
|
||||
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"$PANGOLIN_VERSION\"/" install/main.go
|
||||
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"$GERBIL_VERSION\"/" install/main.go
|
||||
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"$BADGER_VERSION\"/" install/main.go
|
||||
echo "Updated install/main.go with Pangolin version $PANGOLIN_VERSION, Gerbil version $GERBIL_VERSION, and Badger version $BADGER_VERSION"
|
||||
cat install/main.go
|
||||
shell: bash
|
||||
|
||||
- name: Build installer
|
||||
working-directory: install
|
||||
run: |
|
||||
make go-build-release
|
||||
|
||||
- name: Upload artifacts from /install/bin
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: install-bin
|
||||
path: install/bin/
|
||||
|
||||
- name: Install skopeo + jq
|
||||
# skopeo: copy/inspect images between registries
|
||||
# jq: JSON parsing tool used to extract digest values
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y skopeo jq
|
||||
skopeo --version
|
||||
shell: bash
|
||||
|
||||
- name: Login to GHCR
|
||||
env:
|
||||
REGISTRY_AUTH_FILE: ${{ runner.temp }}/containers/auth.json
|
||||
run: |
|
||||
mkdir -p "$(dirname "$REGISTRY_AUTH_FILE")"
|
||||
skopeo login ghcr.io -u "${{ github.actor }}" -p "${{ secrets.GITHUB_TOKEN }}"
|
||||
shell: bash
|
||||
|
||||
- name: Copy tag from Docker Hub to GHCR
|
||||
# Mirror the already-built image (all architectures) to GHCR so we can sign it
|
||||
# Wait a bit for both architectures to be available in Docker Hub manifest
|
||||
env:
|
||||
REGISTRY_AUTH_FILE: ${{ runner.temp }}/containers/auth.json
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG=${{ env.TAG }}
|
||||
echo "Waiting for multi-arch manifest to be ready..."
|
||||
sleep 30
|
||||
echo "Copying ${{ env.DOCKERHUB_IMAGE }}:${TAG} -> ${{ env.GHCR_IMAGE }}:${TAG}"
|
||||
skopeo copy --all --retry-times 3 \
|
||||
docker://$DOCKERHUB_IMAGE:$TAG \
|
||||
docker://$GHCR_IMAGE:$TAG
|
||||
shell: bash
|
||||
|
||||
- name: Login to GitHub Container Registry (for cosign)
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install cosign
|
||||
# cosign is used to sign and verify container images (key and keyless)
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
|
||||
- name: Dual-sign and verify (GHCR & Docker Hub)
|
||||
# Sign each image by digest using keyless (OIDC) and key-based signing,
|
||||
# then verify both the public key signature and the keyless OIDC signature.
|
||||
env:
|
||||
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"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
issuer="https://token.actions.githubusercontent.com"
|
||||
id_regex="^https://github.com/${{ github.repository }}/.+" # accept this repo (all workflows/refs)
|
||||
|
||||
for IMAGE in "${GHCR_IMAGE}" "${DOCKERHUB_IMAGE}"; do
|
||||
echo "Processing ${IMAGE}:${TAG}"
|
||||
|
||||
DIGEST="$(skopeo inspect --retry-times 3 docker://${IMAGE}:${TAG} | jq -r '.Digest')"
|
||||
REF="${IMAGE}@${DIGEST}"
|
||||
echo "Resolved digest: ${REF}"
|
||||
|
||||
echo "==> cosign sign (keyless) --recursive ${REF}"
|
||||
cosign sign --recursive "${REF}"
|
||||
|
||||
echo "==> cosign sign (key) --recursive ${REF}"
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}"
|
||||
|
||||
echo "==> cosign verify (public key) ${REF}"
|
||||
cosign verify --key env://COSIGN_PUBLIC_KEY "${REF}" -o text
|
||||
|
||||
echo "==> cosign verify (keyless policy) ${REF}"
|
||||
cosign verify \
|
||||
--certificate-oidc-issuer "${issuer}" \
|
||||
--certificate-identity-regexp "${id_regex}" \
|
||||
"${REF}" -o text
|
||||
done
|
||||
shell: bash
|
||||
|
||||
post-run:
|
||||
needs: [pre-run, release-arm, release-amd, create-manifest, sign-and-package]
|
||||
if: >-
|
||||
${{
|
||||
always() &&
|
||||
needs.pre-run.result == 'success' &&
|
||||
(needs.release-arm.result == 'success' || needs.release-arm.result == 'skipped' || needs.release-arm.result == 'failure') &&
|
||||
(needs.release-amd.result == 'success' || needs.release-amd.result == 'skipped' || needs.release-amd.result == 'failure') &&
|
||||
(needs.create-manifest.result == 'success' || needs.create-manifest.result == 'skipped' || needs.create-manifest.result == 'failure') &&
|
||||
(needs.sign-and-package.result == 'success' || needs.sign-and-package.result == 'skipped' || needs.sign-and-package.result == 'failure')
|
||||
}}
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Verify AWS identity
|
||||
run: aws sts get-caller-identity
|
||||
|
||||
- name: Stop EC2 instances
|
||||
run: |
|
||||
aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
|
||||
aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }}
|
||||
echo "EC2 instances stopped"
|
||||
6
.github/workflows/linting.yml
vendored
6
.github/workflows/linting.yml
vendored
@@ -21,12 +21,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: '22'
|
||||
node-version: '24'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
2
.github/workflows/mirror.yaml
vendored
2
.github/workflows/mirror.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
skopeo --version
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
||||
|
||||
- name: Input check
|
||||
run: |
|
||||
|
||||
2
.github/workflows/restart-runners.yml
vendored
2
.github/workflows/restart-runners.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
|
||||
45
.github/workflows/saas.yml
vendored
45
.github/workflows/saas.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: CI/CD Pipeline
|
||||
name: SAAS Pipeline
|
||||
|
||||
# CI/CD workflow for building, publishing, mirroring, signing container images and building release binaries.
|
||||
# Actions are pinned to specific SHAs to reduce supply-chain risk. This workflow triggers on tag push events.
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
@@ -54,7 +54,42 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Download MaxMind GeoLite2 databases
|
||||
env:
|
||||
MAXMIND_LICENSE_KEY: ${{ secrets.MAXMIND_LICENSE_KEY }}
|
||||
run: |
|
||||
echo "Downloading MaxMind GeoLite2 databases..."
|
||||
|
||||
# Download GeoLite2-Country
|
||||
curl -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" \
|
||||
-o GeoLite2-Country.tar.gz
|
||||
|
||||
# Download GeoLite2-ASN
|
||||
curl -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" \
|
||||
-o GeoLite2-ASN.tar.gz
|
||||
|
||||
# Extract the .mmdb files
|
||||
tar -xzf GeoLite2-Country.tar.gz --strip-components=1 --wildcards '*.mmdb'
|
||||
tar -xzf GeoLite2-ASN.tar.gz --strip-components=1 --wildcards '*.mmdb'
|
||||
|
||||
# Verify files exist
|
||||
if [ ! -f "GeoLite2-Country.mmdb" ]; then
|
||||
echo "ERROR: Failed to download GeoLite2-Country.mmdb"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "GeoLite2-ASN.mmdb" ]; then
|
||||
echo "ERROR: Failed to download GeoLite2-ASN.mmdb"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up tar files
|
||||
rm -f GeoLite2-Country.tar.gz GeoLite2-ASN.tar.gz
|
||||
|
||||
echo "MaxMind databases downloaded successfully"
|
||||
ls -lh GeoLite2-*.mmdb
|
||||
|
||||
- name: Monitor storage space
|
||||
run: |
|
||||
@@ -69,7 +104,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.aws_account_id }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
@@ -110,7 +145,7 @@ jobs:
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
|
||||
2
.github/workflows/stale-bot.yml
vendored
2
.github/workflows/stale-bot.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
|
||||
20
.github/workflows/test.yml
vendored
20
.github/workflows/test.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: '22'
|
||||
node-version: '24'
|
||||
|
||||
- name: Copy config file
|
||||
run: cp config/config.example.yml config/config.yml
|
||||
@@ -34,10 +34,10 @@ jobs:
|
||||
run: npm run set:oss
|
||||
|
||||
- name: Generate database migrations
|
||||
run: npm run db:sqlite:generate
|
||||
run: npm run db:generate
|
||||
|
||||
- name: Apply database migrations
|
||||
run: npm run db:sqlite:push
|
||||
run: npm run db:push
|
||||
|
||||
- name: Test with tsc
|
||||
run: npx tsc --noEmit
|
||||
@@ -62,10 +62,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Copy config file
|
||||
run: cp config/config.example.yml config/config.yml
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build Docker image sqlite
|
||||
run: make dev-build-sqlite
|
||||
@@ -74,10 +71,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Copy config file
|
||||
run: cp config/config.example.yml config/config.yml
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build Docker image pg
|
||||
run: make dev-build-pg
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -51,4 +51,6 @@ dynamic/
|
||||
scratch/
|
||||
tsconfig.json
|
||||
hydrateSaas.ts
|
||||
CLAUDE.md
|
||||
CLAUDE.md
|
||||
drizzle.config.ts
|
||||
server/setup/migrations.ts
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -10,7 +10,7 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
|
||||
104
Dockerfile
104
Dockerfile
@@ -1,78 +1,52 @@
|
||||
FROM node:24-alpine AS builder
|
||||
|
||||
# OCI Image Labels - Build Args for dynamic values
|
||||
ARG VERSION="dev"
|
||||
ARG REVISION=""
|
||||
ARG CREATED=""
|
||||
ARG LICENSE="AGPL-3.0"
|
||||
# FROM node:24-slim AS base
|
||||
FROM public.ecr.aws/docker/library/node:24-slim AS base
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ARG BUILD=oss
|
||||
ARG DATABASE=sqlite
|
||||
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Derive title and description based on BUILD type
|
||||
ARG IMAGE_TITLE="Pangolin"
|
||||
ARG IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere"
|
||||
|
||||
RUN apk add --no-cache curl tzdata python3 make g++
|
||||
|
||||
# COPY package.json package-lock.json ./
|
||||
COPY package*.json ./
|
||||
|
||||
FROM base AS builder-dev
|
||||
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN echo "export * from \"./$DATABASE\";" > server/db/index.ts
|
||||
RUN echo "export const driver: \"pg\" | \"sqlite\" = \"$DATABASE\";" >> server/db/index.ts
|
||||
ARG BUILD=oss
|
||||
ARG DATABASE=sqlite
|
||||
|
||||
RUN echo "export const build = \"$BUILD\" as \"saas\" | \"enterprise\" | \"oss\";" > server/build.ts
|
||||
RUN if [ "$BUILD" = "oss" ]; then rm -rf server/private; fi && \
|
||||
npm run set:$DATABASE && \
|
||||
npm run set:$BUILD && \
|
||||
npm run db:generate && \
|
||||
npm run build && \
|
||||
npm run build:cli && \
|
||||
test -f dist/server.mjs
|
||||
|
||||
# Copy the appropriate TypeScript configuration based on build type
|
||||
RUN if [ "$BUILD" = "oss" ]; then cp tsconfig.oss.json tsconfig.json; \
|
||||
elif [ "$BUILD" = "saas" ]; then cp tsconfig.saas.json tsconfig.json; \
|
||||
elif [ "$BUILD" = "enterprise" ]; then cp tsconfig.enterprise.json tsconfig.json; \
|
||||
fi
|
||||
# Create placeholder files for MaxMind databases to avoid COPY errors
|
||||
# Real files should be present for saas builds, placeholders for oss builds
|
||||
RUN touch /app/GeoLite2-Country.mmdb /app/GeoLite2-ASN.mmdb
|
||||
|
||||
# if the build is oss then remove the server/private directory
|
||||
RUN if [ "$BUILD" = "oss" ]; then rm -rf server/private; fi
|
||||
FROM base AS builder
|
||||
|
||||
RUN if [ "$DATABASE" = "pg" ]; then npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema --out init; else npx drizzle-kit generate --dialect $DATABASE --schema ./server/db/$DATABASE/schema --out init; fi
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
RUN mkdir -p dist
|
||||
RUN npm run next:build
|
||||
RUN node esbuild.mjs -e server/index.ts -o dist/server.mjs -b $BUILD
|
||||
RUN if [ "$DATABASE" = "pg" ]; then \
|
||||
node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs; \
|
||||
else \
|
||||
node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs; \
|
||||
fi
|
||||
|
||||
# test to make sure the build output is there and error if not
|
||||
RUN test -f dist/server.mjs
|
||||
|
||||
RUN npm run build:cli
|
||||
|
||||
# Prune dev dependencies and clean up to prepare for copy to runner
|
||||
RUN npm prune --omit=dev && npm cache clean --force
|
||||
|
||||
FROM node:24-alpine AS runner
|
||||
# FROM node:24-slim AS runner
|
||||
FROM public.ecr.aws/docker/library/node:24-slim AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Only curl and tzdata needed at runtime - no build tools!
|
||||
RUN apk add --no-cache curl tzdata
|
||||
RUN apt-get update && apt-get install -y curl tzdata && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy pre-built node_modules from builder (already pruned to production only)
|
||||
# This includes the compiled native modules like better-sqlite3
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/init ./dist/init
|
||||
COPY --from=builder /app/package.json ./package.json
|
||||
|
||||
COPY --from=builder-dev /app/.next/standalone ./
|
||||
COPY --from=builder-dev /app/.next/static ./.next/static
|
||||
COPY --from=builder-dev /app/dist ./dist
|
||||
COPY --from=builder-dev /app/server/migrations ./dist/init
|
||||
|
||||
COPY ./cli/wrapper.sh /usr/local/bin/pangctl
|
||||
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs
|
||||
|
||||
@@ -81,6 +55,28 @@ COPY server/db/ios_models.json ./dist/ios_models.json
|
||||
COPY server/db/mac_models.json ./dist/mac_models.json
|
||||
COPY public ./public
|
||||
|
||||
# Copy MaxMind databases for SaaS builds
|
||||
ARG BUILD=oss
|
||||
|
||||
RUN mkdir -p ./maxmind
|
||||
|
||||
# Copy MaxMind databases (placeholders exist for oss builds, real files for saas)
|
||||
COPY --from=builder-dev /app/GeoLite2-Country.mmdb ./maxmind/GeoLite2-Country.mmdb
|
||||
COPY --from=builder-dev /app/GeoLite2-ASN.mmdb ./maxmind/GeoLite2-ASN.mmdb
|
||||
|
||||
# Remove MaxMind databases for non-saas builds (keep only for saas)
|
||||
RUN if [ "$BUILD" != "saas" ]; then rm -rf ./maxmind; fi
|
||||
|
||||
# OCI Image Labels - Build Args for dynamic values
|
||||
ARG VERSION="dev"
|
||||
ARG REVISION=""
|
||||
ARG CREATED=""
|
||||
ARG LICENSE="AGPL-3.0"
|
||||
|
||||
# Derive title and description based on BUILD type
|
||||
ARG IMAGE_TITLE="Pangolin"
|
||||
ARG IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere"
|
||||
|
||||
# OCI Image Labels
|
||||
# https://github.com/opencontainers/image-spec/blob/main/annotations.md
|
||||
LABEL org.opencontainers.image.source="https://github.com/fosrl/pangolin" \
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
FROM node:22-alpine
|
||||
FROM node:24-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache python3 make g++
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
|
||||
13
README.md
13
README.md
@@ -43,7 +43,7 @@
|
||||
|
||||
<p align="center">
|
||||
<strong>
|
||||
Start testing Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
|
||||
Get started with Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
@@ -60,9 +60,9 @@ Pangolin is an open-source, identity-based remote access platform built on WireG
|
||||
|
||||
| <img width=500 /> | Description |
|
||||
|-----------------|--------------|
|
||||
| **Pangolin Cloud** | Fully managed service with instant setup and pay-as-you-go pricing — no infrastructure required. Or, self-host your own [remote node](https://docs.pangolin.net/manage/remote-node/understanding-nodes) and connect to our control plane. |
|
||||
| **Self-Host: Community Edition** | Free, open source, and licensed under AGPL-3. |
|
||||
| **Self-Host: Enterprise Edition** | Licensed under Fossorial Commercial License. Free for personal and hobbyist use, and for businesses earning under \$100K USD annually. |
|
||||
| **Pangolin Cloud** | Fully managed service with instant setup and pay-as-you-go pricing — no infrastructure required. Or, self-host your own [remote node](https://docs.pangolin.net/manage/remote-node/nodes) and connect to our control plane. |
|
||||
|
||||
## Key Features
|
||||
|
||||
@@ -85,17 +85,16 @@ Download the Pangolin client for your platform:
|
||||
|
||||
## Get Started
|
||||
|
||||
### Sign up now
|
||||
|
||||
Create an account at [app.pangolin.net](https://app.pangolin.net) to get started with Pangolin Cloud. A generous free tier is available.
|
||||
|
||||
### Check out the docs
|
||||
|
||||
We encourage everyone to read the full documentation first, which is
|
||||
available at [docs.pangolin.net](https://docs.pangolin.net). This README provides only a very brief subset of
|
||||
the docs to illustrate some basic ideas.
|
||||
|
||||
### Sign up and try now
|
||||
|
||||
For Pangolin's managed service, you will first need to create an account at
|
||||
[app.pangolin.net](https://app.pangolin.net). We have a generous free tier to get started.
|
||||
|
||||
## Licensing
|
||||
|
||||
Pangolin is dual licensed under the AGPL-3 and the [Fossorial Commercial License](https://pangolin.net/fcl.html). For inquiries about commercial licensing, please contact us at [contact@pangolin.net](mailto:contact@pangolin.net).
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
If you discover a security vulnerability, please follow the steps below to responsibly disclose it to us:
|
||||
|
||||
1. **Do not create a public GitHub issue or discussion post.** This could put the security of other users at risk.
|
||||
2. Send a detailed report to [security@pangolin.net](mailto:security@pangolin.net) or send a **private** message to a maintainer on [Discord](https://discord.gg/HCJR8Xhme4). Include:
|
||||
2. Send a detailed report to [security@pangolin.net](mailto:security@pangolin.net) with the following information:
|
||||
|
||||
- Description and location of the vulnerability.
|
||||
- Potential impact of the vulnerability.
|
||||
|
||||
121
cli/commands/generateOrgCaKeys.ts
Normal file
121
cli/commands/generateOrgCaKeys.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { CommandModule } from "yargs";
|
||||
import { db, orgs } from "@server/db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { encrypt } from "@server/lib/crypto";
|
||||
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
|
||||
import { generateCA } from "@server/lib/sshCA";
|
||||
import fs from "fs";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
type GenerateOrgCaKeysArgs = {
|
||||
orgId: string;
|
||||
secret?: string;
|
||||
force?: boolean;
|
||||
};
|
||||
|
||||
export const generateOrgCaKeys: CommandModule<{}, GenerateOrgCaKeysArgs> = {
|
||||
command: "generate-org-ca-keys",
|
||||
describe:
|
||||
"Generate SSH CA public/private key pair for an organization and store them in the database (private key encrypted with server secret)",
|
||||
builder: (yargs) => {
|
||||
return yargs
|
||||
.option("orgId", {
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
describe: "The organization ID"
|
||||
})
|
||||
.option("secret", {
|
||||
type: "string",
|
||||
describe:
|
||||
"Server secret used to encrypt the CA private key. If omitted, read from config file (config.yml or config.yaml)."
|
||||
})
|
||||
.option("force", {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
describe:
|
||||
"Overwrite existing CA keys for the org if they already exist"
|
||||
});
|
||||
},
|
||||
handler: async (argv: {
|
||||
orgId: string;
|
||||
secret?: string;
|
||||
force?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
const { orgId, force } = argv;
|
||||
let secret = argv.secret;
|
||||
|
||||
if (!secret) {
|
||||
const configPath = fs.existsSync(configFilePath1)
|
||||
? configFilePath1
|
||||
: fs.existsSync(configFilePath2)
|
||||
? configFilePath2
|
||||
: null;
|
||||
|
||||
if (!configPath) {
|
||||
console.error(
|
||||
"Error: No server secret provided and config file not found. " +
|
||||
"Expected config.yml or config.yaml in the config directory, or pass --secret."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const configContent = fs.readFileSync(configPath, "utf8");
|
||||
const config = yaml.load(configContent) as {
|
||||
server?: { secret?: string };
|
||||
};
|
||||
|
||||
if (!config?.server?.secret) {
|
||||
console.error(
|
||||
"Error: No server.secret in config file. Pass --secret or set server.secret in config."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
secret = config.server.secret;
|
||||
}
|
||||
|
||||
const [org] = await db
|
||||
.select({
|
||||
orgId: orgs.orgId,
|
||||
sshCaPrivateKey: orgs.sshCaPrivateKey,
|
||||
sshCaPublicKey: orgs.sshCaPublicKey
|
||||
})
|
||||
.from(orgs)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.limit(1);
|
||||
|
||||
if (!org) {
|
||||
console.error(`Error: Organization with orgId "${orgId}" not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (org.sshCaPrivateKey != null || org.sshCaPublicKey != null) {
|
||||
if (!force) {
|
||||
console.error(
|
||||
"Error: This organization already has CA keys. Use --force to overwrite."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const ca = generateCA(`pangolin-ssh-ca-${orgId}`);
|
||||
const encryptedPrivateKey = encrypt(ca.privateKeyPem, secret);
|
||||
|
||||
await db
|
||||
.update(orgs)
|
||||
.set({
|
||||
sshCaPrivateKey: encryptedPrivateKey,
|
||||
sshCaPublicKey: ca.publicKeyOpenSSH
|
||||
})
|
||||
.where(eq(orgs.orgId, orgId));
|
||||
|
||||
console.log("SSH CA keys generated and stored for org:", orgId);
|
||||
console.log("\nPublic key (OpenSSH format):");
|
||||
console.log(ca.publicKeyOpenSSH);
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error("Error generating org CA keys:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import { clearExitNodes } from "./commands/clearExitNodes";
|
||||
import { rotateServerSecret } from "./commands/rotateServerSecret";
|
||||
import { clearLicenseKeys } from "./commands/clearLicenseKeys";
|
||||
import { deleteClient } from "./commands/deleteClient";
|
||||
import { generateOrgCaKeys } from "./commands/generateOrgCaKeys";
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.scriptName("pangctl")
|
||||
@@ -17,5 +18,6 @@ yargs(hideBin(process.argv))
|
||||
.command(rotateServerSecret)
|
||||
.command(clearLicenseKeys)
|
||||
.command(deleteClient)
|
||||
.command(generateOrgCaKeys)
|
||||
.demandCommand()
|
||||
.help().argv;
|
||||
|
||||
@@ -4,6 +4,12 @@ services:
|
||||
image: fosrl/pangolin:latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1g
|
||||
reservations:
|
||||
memory: 256m
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
|
||||
@@ -7,8 +7,8 @@ services:
|
||||
POSTGRES_DB: postgres # Default database name
|
||||
POSTGRES_USER: postgres # Default user
|
||||
POSTGRES_PASSWORD: password # Default password (change for production!)
|
||||
volumes:
|
||||
- ./config/postgres:/var/lib/postgresql/data
|
||||
# volumes:
|
||||
# - ./config/postgres:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432" # Map host port 5432 to container port 5432
|
||||
restart: no
|
||||
|
||||
10
esbuild.mjs
10
esbuild.mjs
@@ -6,6 +6,12 @@ import path from "path";
|
||||
import fs from "fs";
|
||||
// import { glob } from "glob";
|
||||
|
||||
// Read default build type from server/build.ts
|
||||
let build = "oss";
|
||||
const buildFile = fs.readFileSync(path.resolve("server/build.ts"), "utf8");
|
||||
const m = buildFile.match(/export\s+const\s+build\s*=\s*["'](oss|saas|enterprise)["']/);
|
||||
if (m) build = m[1];
|
||||
|
||||
const banner = `
|
||||
// patch __dirname
|
||||
// import { fileURLToPath } from "url";
|
||||
@@ -37,7 +43,7 @@ const argv = yargs(hideBin(process.argv))
|
||||
describe: "Build type (oss, saas, enterprise)",
|
||||
type: "string",
|
||||
choices: ["oss", "saas", "enterprise"],
|
||||
default: "oss"
|
||||
default: build
|
||||
})
|
||||
.help()
|
||||
.alias("help", "h").argv;
|
||||
@@ -275,7 +281,7 @@ esbuild
|
||||
})
|
||||
],
|
||||
sourcemap: "inline",
|
||||
target: "node22"
|
||||
target: "node24"
|
||||
})
|
||||
.then((result) => {
|
||||
// Check if there were any errors in the build result
|
||||
|
||||
@@ -1,41 +1,24 @@
|
||||
all: update-versions go-build-release put-back
|
||||
dev-all: dev-update-versions dev-build dev-clean
|
||||
all: go-build-release
|
||||
|
||||
# Build with version injection via ldflags
|
||||
# Versions can be passed via: make go-build-release PANGOLIN_VERSION=x.x.x GERBIL_VERSION=x.x.x BADGER_VERSION=x.x.x
|
||||
# Or fetched automatically if not provided (requires curl and jq)
|
||||
|
||||
PANGOLIN_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/pangolin/tags | jq -r '.[0].name')
|
||||
GERBIL_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name')
|
||||
BADGER_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name')
|
||||
|
||||
LDFLAGS = -X main.pangolinVersion=$(PANGOLIN_VERSION) \
|
||||
-X main.gerbilVersion=$(GERBIL_VERSION) \
|
||||
-X main.badgerVersion=$(BADGER_VERSION)
|
||||
|
||||
go-build-release:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/installer_linux_amd64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/installer_linux_arm64
|
||||
@echo "Building with versions - Pangolin: $(PANGOLIN_VERSION), Gerbil: $(GERBIL_VERSION), Badger: $(BADGER_VERSION)"
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/installer_linux_amd64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/installer_linux_arm64
|
||||
|
||||
clean:
|
||||
rm -f bin/installer_linux_amd64
|
||||
rm -f bin/installer_linux_arm64
|
||||
|
||||
update-versions:
|
||||
@echo "Fetching latest versions..."
|
||||
cp main.go main.go.bak && \
|
||||
$(MAKE) dev-update-versions
|
||||
|
||||
put-back:
|
||||
mv main.go.bak main.go
|
||||
|
||||
dev-update-versions:
|
||||
if [ -z "$(tag)" ]; then \
|
||||
PANGOLIN_VERSION=$$(curl -s https://api.github.com/repos/fosrl/pangolin/tags | jq -r '.[0].name'); \
|
||||
else \
|
||||
PANGOLIN_VERSION=$(tag); \
|
||||
fi && \
|
||||
GERBIL_VERSION=$$(curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name') && \
|
||||
BADGER_VERSION=$$(curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name') && \
|
||||
echo "Latest versions - Pangolin: $$PANGOLIN_VERSION, Gerbil: $$GERBIL_VERSION, Badger: $$BADGER_VERSION" && \
|
||||
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"$$PANGOLIN_VERSION\"/" main.go && \
|
||||
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"$$GERBIL_VERSION\"/" main.go && \
|
||||
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"$$BADGER_VERSION\"/" main.go && \
|
||||
echo "Updated main.go with latest versions"
|
||||
|
||||
dev-build: go-build-release
|
||||
|
||||
dev-clean:
|
||||
@echo "Restoring version values ..."
|
||||
sed -i "s/config.PangolinVersion = \".*\"/config.PangolinVersion = \"replaceme\"/" main.go && \
|
||||
sed -i "s/config.GerbilVersion = \".*\"/config.GerbilVersion = \"replaceme\"/" main.go && \
|
||||
sed -i "s/config.BadgerVersion = \".*\"/config.BadgerVersion = \"replaceme\"/" main.go
|
||||
@echo "Restored version strings in main.go"
|
||||
.PHONY: all go-build-release clean
|
||||
|
||||
@@ -99,11 +99,6 @@ func ReadAppConfig(configPath string) (*AppConfigValues, error) {
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// findPattern finds the start of a pattern in a string
|
||||
func findPattern(s, pattern string) int {
|
||||
return bytes.Index([]byte(s), []byte(pattern))
|
||||
}
|
||||
|
||||
func copyDockerService(sourceFile, destFile, serviceName string) error {
|
||||
// Read source file
|
||||
sourceData, err := os.ReadFile(sourceFile)
|
||||
@@ -118,19 +113,19 @@ func copyDockerService(sourceFile, destFile, serviceName string) error {
|
||||
}
|
||||
|
||||
// Parse source Docker Compose YAML
|
||||
var sourceCompose map[string]interface{}
|
||||
var sourceCompose map[string]any
|
||||
if err := yaml.Unmarshal(sourceData, &sourceCompose); err != nil {
|
||||
return fmt.Errorf("error parsing source Docker Compose file: %w", err)
|
||||
}
|
||||
|
||||
// Parse destination Docker Compose YAML
|
||||
var destCompose map[string]interface{}
|
||||
var destCompose map[string]any
|
||||
if err := yaml.Unmarshal(destData, &destCompose); err != nil {
|
||||
return fmt.Errorf("error parsing destination Docker Compose file: %w", err)
|
||||
}
|
||||
|
||||
// Get services section from source
|
||||
sourceServices, ok := sourceCompose["services"].(map[string]interface{})
|
||||
sourceServices, ok := sourceCompose["services"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("services section not found in source file or has invalid format")
|
||||
}
|
||||
@@ -142,10 +137,10 @@ func copyDockerService(sourceFile, destFile, serviceName string) error {
|
||||
}
|
||||
|
||||
// Get or create services section in destination
|
||||
destServices, ok := destCompose["services"].(map[string]interface{})
|
||||
destServices, ok := destCompose["services"].(map[string]any)
|
||||
if !ok {
|
||||
// If services section doesn't exist, create it
|
||||
destServices = make(map[string]interface{})
|
||||
destServices = make(map[string]any)
|
||||
destCompose["services"] = destServices
|
||||
}
|
||||
|
||||
@@ -187,17 +182,21 @@ func backupConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func MarshalYAMLWithIndent(data interface{}, indent int) ([]byte, error) {
|
||||
func MarshalYAMLWithIndent(data any, indent int) (resp []byte, err error) {
|
||||
buffer := new(bytes.Buffer)
|
||||
encoder := yaml.NewEncoder(buffer)
|
||||
encoder.SetIndent(indent)
|
||||
|
||||
err := encoder.Encode(data)
|
||||
if err != nil {
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer encoder.Close()
|
||||
defer func() {
|
||||
if cerr := encoder.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -209,7 +208,7 @@ func replaceInFile(filepath, oldStr, newStr string) error {
|
||||
}
|
||||
|
||||
// Replace the string
|
||||
newContent := strings.Replace(string(content), oldStr, newStr, -1)
|
||||
newContent := strings.ReplaceAll(string(content), oldStr, newStr)
|
||||
|
||||
// Write the modified content back to the file
|
||||
err = os.WriteFile(filepath, []byte(newContent), 0644)
|
||||
@@ -228,28 +227,28 @@ func CheckAndAddTraefikLogVolume(composePath string) error {
|
||||
}
|
||||
|
||||
// Parse YAML into a generic map
|
||||
var compose map[string]interface{}
|
||||
var compose map[string]any
|
||||
if err := yaml.Unmarshal(data, &compose); err != nil {
|
||||
return fmt.Errorf("error parsing compose file: %w", err)
|
||||
}
|
||||
|
||||
// Get services section
|
||||
services, ok := compose["services"].(map[string]interface{})
|
||||
services, ok := compose["services"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("services section not found or invalid")
|
||||
}
|
||||
|
||||
// Get traefik service
|
||||
traefik, ok := services["traefik"].(map[string]interface{})
|
||||
traefik, ok := services["traefik"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("traefik service not found or invalid")
|
||||
}
|
||||
|
||||
// Check volumes
|
||||
logVolume := "./config/traefik/logs:/var/log/traefik"
|
||||
var volumes []interface{}
|
||||
var volumes []any
|
||||
|
||||
if existingVolumes, ok := traefik["volumes"].([]interface{}); ok {
|
||||
if existingVolumes, ok := traefik["volumes"].([]any); ok {
|
||||
// Check if volume already exists
|
||||
for _, v := range existingVolumes {
|
||||
if v.(string) == logVolume {
|
||||
@@ -295,13 +294,13 @@ func MergeYAML(baseFile, overlayFile string) error {
|
||||
}
|
||||
|
||||
// Parse base YAML into a map
|
||||
var baseMap map[string]interface{}
|
||||
var baseMap map[string]any
|
||||
if err := yaml.Unmarshal(baseContent, &baseMap); err != nil {
|
||||
return fmt.Errorf("error parsing base YAML: %v", err)
|
||||
}
|
||||
|
||||
// Parse overlay YAML into a map
|
||||
var overlayMap map[string]interface{}
|
||||
var overlayMap map[string]any
|
||||
if err := yaml.Unmarshal(overlayContent, &overlayMap); err != nil {
|
||||
return fmt.Errorf("error parsing overlay YAML: %v", err)
|
||||
}
|
||||
@@ -324,8 +323,8 @@ func MergeYAML(baseFile, overlayFile string) error {
|
||||
}
|
||||
|
||||
// mergeMap recursively merges two maps
|
||||
func mergeMap(base, overlay map[string]interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
func mergeMap(base, overlay map[string]any) map[string]any {
|
||||
result := make(map[string]any)
|
||||
|
||||
// Copy all key-values from base map
|
||||
for k, v := range base {
|
||||
@@ -336,8 +335,8 @@ func mergeMap(base, overlay map[string]interface{}) map[string]interface{} {
|
||||
for k, v := range overlay {
|
||||
// If both maps have the same key and both values are maps, merge recursively
|
||||
if baseVal, ok := base[k]; ok {
|
||||
if baseMap, isBaseMap := baseVal.(map[string]interface{}); isBaseMap {
|
||||
if overlayMap, isOverlayMap := v.(map[string]interface{}); isOverlayMap {
|
||||
if baseMap, isBaseMap := baseVal.(map[string]any); isBaseMap {
|
||||
if overlayMap, isOverlayMap := v.(map[string]any); isOverlayMap {
|
||||
result[k] = mergeMap(baseMap, overlayMap)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -81,11 +81,17 @@ entryPoints:
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http3:
|
||||
advertisedPort: 443
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
middlewares:
|
||||
- crowdsec@file
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
|
||||
@@ -4,6 +4,12 @@ services:
|
||||
image: docker.io/fosrl/pangolin:{{if .IsEnterprise}}ee-{{end}}{{.PangolinVersion}}
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1g
|
||||
reservations:
|
||||
memory: 256m
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
@@ -32,15 +38,14 @@ services:
|
||||
- 51820:51820/udp
|
||||
- 21820:21820/udp
|
||||
- 443:443
|
||||
- 443:443/udp # For http3 QUIC if desired
|
||||
- 80:80
|
||||
{{end}}
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.6
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
{{if .InstallGerbil}}
|
||||
network_mode: service:gerbil # Ports appear on the gerbil service
|
||||
{{end}}{{if not .InstallGerbil}}
|
||||
{{if .InstallGerbil}} network_mode: service:gerbil # Ports appear on the gerbil service{{end}}{{if not .InstallGerbil}}
|
||||
ports:
|
||||
- 443:443
|
||||
- 80:80
|
||||
|
||||
@@ -40,6 +40,8 @@ entryPoints:
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http3:
|
||||
advertisedPort: 443
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
|
||||
@@ -144,12 +144,13 @@ func installDocker() error {
|
||||
}
|
||||
|
||||
func startDockerService() error {
|
||||
if runtime.GOOS == "linux" {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
cmd := exec.Command("systemctl", "enable", "--now", "docker")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
} else if runtime.GOOS == "darwin" {
|
||||
case "darwin":
|
||||
// On macOS, Docker is usually started via the Docker Desktop application
|
||||
fmt.Println("Please start Docker Desktop manually on macOS.")
|
||||
return nil
|
||||
@@ -302,7 +303,7 @@ func pullContainers(containerType SupportedContainer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||
return fmt.Errorf("unsupported container type: %s", containerType)
|
||||
}
|
||||
|
||||
// startContainers starts the containers using the appropriate command.
|
||||
@@ -325,7 +326,7 @@ func startContainers(containerType SupportedContainer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||
return fmt.Errorf("unsupported container type: %s", containerType)
|
||||
}
|
||||
|
||||
// stopContainers stops the containers using the appropriate command.
|
||||
@@ -347,7 +348,7 @@ func stopContainers(containerType SupportedContainer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||
return fmt.Errorf("unsupported container type: %s", containerType)
|
||||
}
|
||||
|
||||
// restartContainer restarts a specific container using the appropriate command.
|
||||
@@ -369,5 +370,5 @@ func restartContainer(container string, containerType SupportedContainer) error
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||
return fmt.Errorf("unsupported container type: %s", containerType)
|
||||
}
|
||||
|
||||
@@ -27,9 +27,18 @@ func installCrowdsec(config Config) error {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.MkdirAll("config/crowdsec/db", 0755)
|
||||
os.MkdirAll("config/crowdsec/acquis.d", 0755)
|
||||
os.MkdirAll("config/traefik/logs", 0755)
|
||||
if err := os.MkdirAll("config/crowdsec/db", 0755); err != nil {
|
||||
fmt.Printf("Error creating config files: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.MkdirAll("config/crowdsec/acquis.d", 0755); err != nil {
|
||||
fmt.Printf("Error creating config files: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.MkdirAll("config/traefik/logs", 0755); err != nil {
|
||||
fmt.Printf("Error creating config files: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil {
|
||||
fmt.Printf("Error copying docker service: %v\n", err)
|
||||
@@ -153,34 +162,34 @@ func CheckAndAddCrowdsecDependency(composePath string) error {
|
||||
}
|
||||
|
||||
// Parse YAML into a generic map
|
||||
var compose map[string]interface{}
|
||||
var compose map[string]any
|
||||
if err := yaml.Unmarshal(data, &compose); err != nil {
|
||||
return fmt.Errorf("error parsing compose file: %w", err)
|
||||
}
|
||||
|
||||
// Get services section
|
||||
services, ok := compose["services"].(map[string]interface{})
|
||||
services, ok := compose["services"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("services section not found or invalid")
|
||||
}
|
||||
|
||||
// Get traefik service
|
||||
traefik, ok := services["traefik"].(map[string]interface{})
|
||||
traefik, ok := services["traefik"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("traefik service not found or invalid")
|
||||
}
|
||||
|
||||
// Get dependencies
|
||||
dependsOn, ok := traefik["depends_on"].(map[string]interface{})
|
||||
dependsOn, ok := traefik["depends_on"].(map[string]any)
|
||||
if ok {
|
||||
// Append the new block for crowdsec
|
||||
dependsOn["crowdsec"] = map[string]interface{}{
|
||||
dependsOn["crowdsec"] = map[string]any{
|
||||
"condition": "service_healthy",
|
||||
}
|
||||
} else {
|
||||
// No dependencies exist, create it
|
||||
traefik["depends_on"] = map[string]interface{}{
|
||||
"crowdsec": map[string]interface{}{
|
||||
traefik["depends_on"] = map[string]any{
|
||||
"crowdsec": map[string]any{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
module installer
|
||||
|
||||
go 1.24.0
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
golang.org/x/term v0.39.0
|
||||
github.com/charmbracelet/huh v1.0.0
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
golang.org/x/term v0.41.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.40.0 // indirect
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/catppuccin/go v0.3.0 // indirect
|
||||
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect
|
||||
github.com/charmbracelet/bubbletea v1.3.6 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.9.3 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,7 +1,80 @@
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
|
||||
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
|
||||
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
||||
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws=
|
||||
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw=
|
||||
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
|
||||
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/huh v1.0.0 h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw=
|
||||
github.com/charmbracelet/huh v1.0.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
|
||||
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
|
||||
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
|
||||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
|
||||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
|
||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||
github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=
|
||||
github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
260
install/input.go
260
install/input.go
@@ -1,92 +1,208 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func readString(reader *bufio.Reader, prompt string, defaultValue string) string {
|
||||
// pangolinTheme is the custom theme using brand colors
|
||||
var pangolinTheme = ThemePangolin()
|
||||
|
||||
// isAccessibleMode checks if we should use accessible mode (simple prompts)
|
||||
// This is true for: non-TTY, TERM=dumb, or ACCESSIBLE env var set
|
||||
func isAccessibleMode() bool {
|
||||
// Check if stdin is not a terminal (piped input, CI, etc.)
|
||||
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
return true
|
||||
}
|
||||
// Check for dumb terminal
|
||||
if os.Getenv("TERM") == "dumb" {
|
||||
return true
|
||||
}
|
||||
// Check for explicit accessible mode request
|
||||
if os.Getenv("ACCESSIBLE") != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// handleAbort checks if the error is a user abort (Ctrl+C) and exits if so
|
||||
func handleAbort(err error) {
|
||||
if err != nil && errors.Is(err, huh.ErrUserAborted) {
|
||||
fmt.Println("\nInstallation cancelled.")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// runField runs a single field with the Pangolin theme, handling accessible mode
|
||||
func runField(field huh.Field) error {
|
||||
if isAccessibleMode() {
|
||||
return field.RunAccessible(os.Stdout, os.Stdin)
|
||||
}
|
||||
form := huh.NewForm(huh.NewGroup(field)).WithTheme(pangolinTheme)
|
||||
return form.Run()
|
||||
}
|
||||
|
||||
func readString(prompt string, defaultValue string) string {
|
||||
var value string
|
||||
|
||||
title := prompt
|
||||
if defaultValue != "" {
|
||||
fmt.Printf("%s (default: %s): ", prompt, defaultValue)
|
||||
} else {
|
||||
fmt.Print(prompt + ": ")
|
||||
title = fmt.Sprintf("%s (default: %s)", prompt, defaultValue)
|
||||
}
|
||||
input, _ := reader.ReadString('\n')
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
return defaultValue
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
func readStringNoDefault(reader *bufio.Reader, prompt string) string {
|
||||
fmt.Print(prompt + ": ")
|
||||
input, _ := reader.ReadString('\n')
|
||||
return strings.TrimSpace(input)
|
||||
}
|
||||
input := huh.NewInput().
|
||||
Title(title).
|
||||
Value(&value)
|
||||
|
||||
func readPassword(prompt string, reader *bufio.Reader) string {
|
||||
if term.IsTerminal(int(syscall.Stdin)) {
|
||||
fmt.Print(prompt + ": ")
|
||||
// Read password without echo if we're in a terminal
|
||||
password, err := term.ReadPassword(int(syscall.Stdin))
|
||||
fmt.Println() // Add a newline since ReadPassword doesn't add one
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
input := strings.TrimSpace(string(password))
|
||||
if input == "" {
|
||||
return readPassword(prompt, reader)
|
||||
}
|
||||
return input
|
||||
} else {
|
||||
// Fallback to reading from stdin if not in a terminal
|
||||
return readString(reader, prompt, "")
|
||||
// If no default value, this field is required
|
||||
if defaultValue == "" {
|
||||
input = input.Validate(func(s string) error {
|
||||
if s == "" {
|
||||
return fmt.Errorf("this field is required")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool {
|
||||
defaultStr := "no"
|
||||
if defaultValue {
|
||||
defaultStr = "yes"
|
||||
}
|
||||
for {
|
||||
input := readString(reader, prompt+" (yes/no)", defaultStr)
|
||||
lower := strings.ToLower(input)
|
||||
if lower == "yes" {
|
||||
return true
|
||||
} else if lower == "no" {
|
||||
return false
|
||||
} else {
|
||||
fmt.Println("Please enter 'yes' or 'no'.")
|
||||
}
|
||||
}
|
||||
}
|
||||
err := runField(input)
|
||||
handleAbort(err)
|
||||
|
||||
func readBoolNoDefault(reader *bufio.Reader, prompt string) bool {
|
||||
for {
|
||||
input := readStringNoDefault(reader, prompt+" (yes/no)")
|
||||
lower := strings.ToLower(input)
|
||||
if lower == "yes" {
|
||||
return true
|
||||
} else if lower == "no" {
|
||||
return false
|
||||
} else {
|
||||
fmt.Println("Please enter 'yes' or 'no'.")
|
||||
}
|
||||
if value == "" {
|
||||
value = defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func readInt(reader *bufio.Reader, prompt string, defaultValue int) int {
|
||||
input := readString(reader, prompt, fmt.Sprintf("%d", defaultValue))
|
||||
if input == "" {
|
||||
return defaultValue
|
||||
// Print the answer so it remains visible in terminal history (skip in accessible mode as it already shows)
|
||||
if !isAccessibleMode() {
|
||||
fmt.Printf("%s: %s\n", prompt, value)
|
||||
}
|
||||
value := defaultValue
|
||||
fmt.Sscanf(input, "%d", &value)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func readPassword(prompt string) string {
|
||||
var value string
|
||||
|
||||
for {
|
||||
input := huh.NewInput().
|
||||
Title(prompt).
|
||||
Value(&value).
|
||||
EchoMode(huh.EchoModePassword).
|
||||
Validate(func(s string) error {
|
||||
if s == "" {
|
||||
return fmt.Errorf("password is required")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
err := runField(input)
|
||||
handleAbort(err)
|
||||
|
||||
if value != "" {
|
||||
// Print confirmation without revealing the password
|
||||
if !isAccessibleMode() {
|
||||
fmt.Printf("%s: %s\n", prompt, "********")
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readBool(prompt string, defaultValue bool) bool {
|
||||
var value = defaultValue
|
||||
|
||||
confirm := huh.NewConfirm().
|
||||
Title(prompt).
|
||||
Value(&value).
|
||||
Affirmative("Yes").
|
||||
Negative("No")
|
||||
|
||||
err := runField(confirm)
|
||||
handleAbort(err)
|
||||
|
||||
// Print the answer so it remains visible in terminal history
|
||||
if !isAccessibleMode() {
|
||||
answer := "No"
|
||||
if value {
|
||||
answer = "Yes"
|
||||
}
|
||||
fmt.Printf("%s: %s\n", prompt, answer)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func readBoolNoDefault(prompt string) bool {
|
||||
var value bool
|
||||
|
||||
confirm := huh.NewConfirm().
|
||||
Title(prompt).
|
||||
Value(&value).
|
||||
Affirmative("Yes").
|
||||
Negative("No")
|
||||
|
||||
err := runField(confirm)
|
||||
handleAbort(err)
|
||||
|
||||
// Print the answer so it remains visible in terminal history
|
||||
if !isAccessibleMode() {
|
||||
answer := "No"
|
||||
if value {
|
||||
answer = "Yes"
|
||||
}
|
||||
fmt.Printf("%s: %s\n", prompt, answer)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func readInt(prompt string, defaultValue int) int {
|
||||
var value string
|
||||
|
||||
title := fmt.Sprintf("%s (default: %d)", prompt, defaultValue)
|
||||
|
||||
input := huh.NewInput().
|
||||
Title(title).
|
||||
Value(&value).
|
||||
Validate(func(s string) error {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("please enter a valid number")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
err := runField(input)
|
||||
handleAbort(err)
|
||||
|
||||
if value == "" {
|
||||
// Print the answer so it remains visible in terminal history
|
||||
if !isAccessibleMode() {
|
||||
fmt.Printf("%s: %d\n", prompt, defaultValue)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
result, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
if !isAccessibleMode() {
|
||||
fmt.Printf("%s: %d\n", prompt, defaultValue)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Print the answer so it remains visible in terminal history
|
||||
if !isAccessibleMode() {
|
||||
fmt.Printf("%s: %d\n", prompt, result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
318
install/main.go
318
install/main.go
@@ -1,29 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"embed"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DO NOT EDIT THIS FUNCTION; IT MATCHED BY REGEX IN CICD
|
||||
// Version variables injected at build time via -ldflags
|
||||
var (
|
||||
pangolinVersion string
|
||||
gerbilVersion string
|
||||
badgerVersion string
|
||||
)
|
||||
|
||||
func loadVersions(config *Config) {
|
||||
config.PangolinVersion = "replaceme"
|
||||
config.GerbilVersion = "replaceme"
|
||||
config.BadgerVersion = "replaceme"
|
||||
config.PangolinVersion = pangolinVersion
|
||||
config.GerbilVersion = gerbilVersion
|
||||
config.BadgerVersion = badgerVersion
|
||||
}
|
||||
|
||||
//go:embed config/*
|
||||
@@ -81,14 +87,19 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
var config Config
|
||||
var alreadyInstalled = false
|
||||
|
||||
// Determine installation directory
|
||||
installDir := findOrSelectInstallDirectory()
|
||||
if err := os.Chdir(installDir); err != nil {
|
||||
fmt.Printf("Error changing to installation directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// check if there is already a config file
|
||||
if _, err := os.Stat("config/config.yml"); err != nil {
|
||||
config = collectUserInput(reader)
|
||||
config = collectUserInput()
|
||||
|
||||
loadVersions(&config)
|
||||
config.DoCrowdsecInstall = false
|
||||
@@ -101,7 +112,10 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
moveFile("config/docker-compose.yml", "docker-compose.yml")
|
||||
if err := moveFile("config/docker-compose.yml", "docker-compose.yml"); err != nil {
|
||||
fmt.Printf("Error moving docker-compose.yml: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("\nConfiguration files created successfully!")
|
||||
|
||||
@@ -116,13 +130,17 @@ func main() {
|
||||
|
||||
fmt.Println("\n=== Starting installation ===")
|
||||
|
||||
if readBool(reader, "Would you like to install and start the containers?", true) {
|
||||
if readBool("Would you like to install and start the containers?", true) {
|
||||
|
||||
config.InstallationContainerType = podmanOrDocker(reader)
|
||||
config.InstallationContainerType = podmanOrDocker()
|
||||
|
||||
if !isDockerInstalled() && runtime.GOOS == "linux" && config.InstallationContainerType == Docker {
|
||||
if readBool(reader, "Docker is not installed. Would you like to install it?", true) {
|
||||
installDocker()
|
||||
if readBool("Docker is not installed. Would you like to install it?", true) {
|
||||
if err := installDocker(); err != nil {
|
||||
fmt.Printf("Error installing Docker: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// try to start docker service but ignore errors
|
||||
if err := startDockerService(); err != nil {
|
||||
fmt.Println("Error starting Docker service:", err)
|
||||
@@ -131,7 +149,7 @@ func main() {
|
||||
}
|
||||
// wait 10 seconds for docker to start checking if docker is running every 2 seconds
|
||||
fmt.Println("Waiting for Docker to start...")
|
||||
for i := 0; i < 5; i++ {
|
||||
for range 5 {
|
||||
if isDockerRunning() {
|
||||
fmt.Println("Docker is running!")
|
||||
break
|
||||
@@ -166,7 +184,7 @@ func main() {
|
||||
fmt.Println("\n=== MaxMind Database Update ===")
|
||||
if _, err := os.Stat("config/GeoLite2-Country.mmdb"); err == nil {
|
||||
fmt.Println("MaxMind GeoLite2 Country database found.")
|
||||
if readBool(reader, "Would you like to update the MaxMind database to the latest version?", false) {
|
||||
if readBool("Would you like to update the MaxMind database to the latest version?", false) {
|
||||
if err := downloadMaxMindDatabase(); err != nil {
|
||||
fmt.Printf("Error updating MaxMind database: %v\n", err)
|
||||
fmt.Println("You can try updating it manually later if needed.")
|
||||
@@ -174,7 +192,7 @@ func main() {
|
||||
}
|
||||
} else {
|
||||
fmt.Println("MaxMind GeoLite2 Country database not found.")
|
||||
if readBool(reader, "Would you like to download the MaxMind GeoLite2 database for geoblocking functionality?", false) {
|
||||
if readBool("Would you like to download the MaxMind GeoLite2 database for geoblocking functionality?", false) {
|
||||
if err := downloadMaxMindDatabase(); err != nil {
|
||||
fmt.Printf("Error downloading MaxMind database: %v\n", err)
|
||||
fmt.Println("You can try downloading it manually later if needed.")
|
||||
@@ -191,11 +209,11 @@ func main() {
|
||||
if !checkIsCrowdsecInstalledInCompose() {
|
||||
fmt.Println("\n=== CrowdSec Install ===")
|
||||
// check if crowdsec is installed
|
||||
if readBool(reader, "Would you like to install CrowdSec?", false) {
|
||||
if readBool("Would you like to install CrowdSec?", false) {
|
||||
fmt.Println("This installer constitutes a minimal viable CrowdSec deployment. CrowdSec will add extra complexity to your Pangolin installation and may not work to the best of its abilities out of the box. Users are expected to implement configuration adjustments on their own to achieve the best security posture. Consult the CrowdSec documentation for detailed configuration instructions.")
|
||||
|
||||
// BUG: crowdsec installation will be skipped if the user chooses to install on the first installation.
|
||||
if readBool(reader, "Are you willing to manage CrowdSec?", false) {
|
||||
if readBool("Are you willing to manage CrowdSec?", false) {
|
||||
if config.DashboardDomain == "" {
|
||||
traefikConfig, err := ReadTraefikConfig("config/traefik/traefik_config.yml")
|
||||
if err != nil {
|
||||
@@ -224,8 +242,8 @@ func main() {
|
||||
fmt.Printf("Let's Encrypt Email: %s\n", config.LetsEncryptEmail)
|
||||
fmt.Printf("Badger Version: %s\n", config.BadgerVersion)
|
||||
|
||||
if !readBool(reader, "Are these values correct?", true) {
|
||||
config = collectUserInput(reader)
|
||||
if !readBool("Are these values correct?", true) {
|
||||
config = collectUserInput()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +252,7 @@ func main() {
|
||||
if detectedType == Undefined {
|
||||
// If detection fails, prompt the user
|
||||
fmt.Println("Unable to detect container type from existing installation.")
|
||||
config.InstallationContainerType = podmanOrDocker(reader)
|
||||
config.InstallationContainerType = podmanOrDocker()
|
||||
} else {
|
||||
config.InstallationContainerType = detectedType
|
||||
fmt.Printf("Detected container type: %s\n", config.InstallationContainerType)
|
||||
@@ -276,8 +294,119 @@ func main() {
|
||||
fmt.Printf("\nTo complete the initial setup, please visit:\nhttps://%s/auth/initial-setup\n", config.DashboardDomain)
|
||||
}
|
||||
|
||||
func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
inputContainer := readString(reader, "Would you like to run Pangolin as Docker or Podman containers?", "docker")
|
||||
func hasExistingInstall(dir string) bool {
|
||||
configPath := filepath.Join(dir, "config", "config.yml")
|
||||
_, err := os.Stat(configPath)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func findOrSelectInstallDirectory() string {
|
||||
const defaultInstallDir = "/opt/pangolin"
|
||||
|
||||
// Get current working directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting current directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 1. Check current directory for existing install
|
||||
if hasExistingInstall(cwd) {
|
||||
fmt.Printf("Found existing Pangolin installation in current directory: %s\n", cwd)
|
||||
return cwd
|
||||
}
|
||||
|
||||
// 2. Check default location (/opt/pangolin) for existing install
|
||||
if cwd != defaultInstallDir && hasExistingInstall(defaultInstallDir) {
|
||||
fmt.Printf("\nFound existing Pangolin installation at: %s\n", defaultInstallDir)
|
||||
if readBool(fmt.Sprintf("Would you like to use the existing installation at %s?", defaultInstallDir), true) {
|
||||
return defaultInstallDir
|
||||
}
|
||||
}
|
||||
|
||||
// 3. No existing install found, prompt for installation directory
|
||||
fmt.Println("\n=== Installation Directory ===")
|
||||
fmt.Println("No existing Pangolin installation detected.")
|
||||
|
||||
installDir := readString("Enter the installation directory", defaultInstallDir)
|
||||
|
||||
// Expand ~ to home directory if present
|
||||
if strings.HasPrefix(installDir, "~") {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting home directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
installDir = filepath.Join(home, installDir[1:])
|
||||
}
|
||||
|
||||
// Convert to absolute path
|
||||
absPath, err := filepath.Abs(installDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Error resolving path: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
installDir = absPath
|
||||
|
||||
// Check if directory exists
|
||||
if _, err := os.Stat(installDir); os.IsNotExist(err) {
|
||||
// Directory doesn't exist, create it
|
||||
if readBool(fmt.Sprintf("Directory %s does not exist. Create it?", installDir), true) {
|
||||
if err := os.MkdirAll(installDir, 0755); err != nil {
|
||||
fmt.Printf("Error creating directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Created directory: %s\n", installDir)
|
||||
|
||||
// Offer to change ownership if running via sudo
|
||||
changeDirectoryOwnership(installDir)
|
||||
} else {
|
||||
fmt.Println("Installation cancelled.")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Installation directory: %s\n", installDir)
|
||||
return installDir
|
||||
}
|
||||
|
||||
func changeDirectoryOwnership(dir string) {
|
||||
// Check if we're running via sudo by looking for SUDO_USER
|
||||
sudoUser := os.Getenv("SUDO_USER")
|
||||
if sudoUser == "" || os.Geteuid() != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
sudoUID := os.Getenv("SUDO_UID")
|
||||
sudoGID := os.Getenv("SUDO_GID")
|
||||
|
||||
if sudoUID == "" || sudoGID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\nRunning as root via sudo (original user: %s)\n", sudoUser)
|
||||
if readBool(fmt.Sprintf("Would you like to change ownership of %s to user '%s'? This makes it easier to manage config files without sudo.", dir, sudoUser), true) {
|
||||
uid, err := strconv.Atoi(sudoUID)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: Could not parse SUDO_UID: %v\n", err)
|
||||
return
|
||||
}
|
||||
gid, err := strconv.Atoi(sudoGID)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: Could not parse SUDO_GID: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.Chown(dir, uid, gid); err != nil {
|
||||
fmt.Printf("Warning: Could not change ownership: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Changed ownership of %s to %s\n", dir, sudoUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func podmanOrDocker() SupportedContainer {
|
||||
inputContainer := readString("Would you like to run Pangolin as Docker or Podman containers?", "docker")
|
||||
|
||||
chosenContainer := Docker
|
||||
if strings.EqualFold(inputContainer, "docker") {
|
||||
@@ -289,7 +418,8 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if chosenContainer == Podman {
|
||||
switch chosenContainer {
|
||||
case Podman:
|
||||
if !isPodmanInstalled() {
|
||||
fmt.Println("Podman or podman-compose is not installed. Please install both manually. Automated installation will be available in a later release.")
|
||||
os.Exit(1)
|
||||
@@ -298,7 +428,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
if err := exec.Command("bash", "-c", "cat /etc/sysctl.d/99-podman.conf 2>/dev/null | grep 'net.ipv4.ip_unprivileged_port_start=' || cat /etc/sysctl.conf 2>/dev/null | grep 'net.ipv4.ip_unprivileged_port_start='").Run(); err != nil {
|
||||
fmt.Println("Would you like to configure ports >= 80 as unprivileged ports? This enables podman containers to listen on low-range ports.")
|
||||
fmt.Println("Pangolin will experience startup issues if this is not configured, because it needs to listen on port 80/443 by default.")
|
||||
approved := readBool(reader, "The installer is about to execute \"echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system\". Approve?", true)
|
||||
approved := readBool("The installer is about to execute \"echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system\". Approve?", true)
|
||||
if approved {
|
||||
if os.Geteuid() != 0 {
|
||||
fmt.Println("You need to run the installer as root for such a configuration.")
|
||||
@@ -310,7 +440,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
// Linux only.
|
||||
|
||||
if err := run("bash", "-c", "echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system"); err != nil {
|
||||
fmt.Printf("Error configuring unprivileged ports: %v\n", err)
|
||||
fmt.Printf("Error configuring unprivileged ports: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
@@ -320,7 +450,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
fmt.Println("Unprivileged ports have been configured.")
|
||||
}
|
||||
|
||||
} else if chosenContainer == Docker {
|
||||
case Docker:
|
||||
// check if docker is not installed and the user is root
|
||||
if !isDockerInstalled() {
|
||||
if os.Geteuid() != 0 {
|
||||
@@ -335,7 +465,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
fmt.Println("The installer will not be able to run docker commands without running it as root.")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
// This shouldn't happen unless there's a third container runtime.
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -343,35 +473,35 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||
return chosenContainer
|
||||
}
|
||||
|
||||
func collectUserInput(reader *bufio.Reader) Config {
|
||||
func collectUserInput() Config {
|
||||
config := Config{}
|
||||
|
||||
// Basic configuration
|
||||
fmt.Println("\n=== Basic Configuration ===")
|
||||
|
||||
config.IsEnterprise = readBoolNoDefault(reader, "Do you want to install the Enterprise version of Pangolin? The EE is free for personal use or for businesses making less than 100k USD annually.")
|
||||
config.IsEnterprise = readBoolNoDefault("Do you want to install the Enterprise version of Pangolin? The EE is free for personal use or for businesses making less than 100k USD annually.")
|
||||
|
||||
config.BaseDomain = readString(reader, "Enter your base domain (no subdomain e.g. example.com)", "")
|
||||
config.BaseDomain = readString("Enter your base domain (no subdomain e.g. example.com)", "")
|
||||
|
||||
// Set default dashboard domain after base domain is collected
|
||||
defaultDashboardDomain := ""
|
||||
if config.BaseDomain != "" {
|
||||
defaultDashboardDomain = "pangolin." + config.BaseDomain
|
||||
}
|
||||
config.DashboardDomain = readString(reader, "Enter the domain for the Pangolin dashboard", defaultDashboardDomain)
|
||||
config.LetsEncryptEmail = readString(reader, "Enter email for Let's Encrypt certificates", "")
|
||||
config.InstallGerbil = readBool(reader, "Do you want to use Gerbil to allow tunneled connections", true)
|
||||
config.DashboardDomain = readString("Enter the domain for the Pangolin dashboard", defaultDashboardDomain)
|
||||
config.LetsEncryptEmail = readString("Enter email for Let's Encrypt certificates", "")
|
||||
config.InstallGerbil = readBool("Do you want to use Gerbil to allow tunneled connections", true)
|
||||
|
||||
// Email configuration
|
||||
fmt.Println("\n=== Email Configuration ===")
|
||||
config.EnableEmail = readBool(reader, "Enable email functionality (SMTP)", false)
|
||||
config.EnableEmail = readBool("Enable email functionality (SMTP)", false)
|
||||
|
||||
if config.EnableEmail {
|
||||
config.EmailSMTPHost = readString(reader, "Enter SMTP host", "")
|
||||
config.EmailSMTPPort = readInt(reader, "Enter SMTP port (default 587)", 587)
|
||||
config.EmailSMTPUser = readString(reader, "Enter SMTP username", "")
|
||||
config.EmailSMTPPass = readString(reader, "Enter SMTP password", "") // Should this be readPassword?
|
||||
config.EmailNoReply = readString(reader, "Enter no-reply email address (often the same as SMTP username)", "")
|
||||
config.EmailSMTPHost = readString("Enter SMTP host", "")
|
||||
config.EmailSMTPPort = readInt("Enter SMTP port (default 587)", 587)
|
||||
config.EmailSMTPUser = readString("Enter SMTP username", "")
|
||||
config.EmailSMTPPass = readPassword("Enter SMTP password")
|
||||
config.EmailNoReply = readString("Enter no-reply email address (often the same as SMTP username)", "")
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
@@ -392,8 +522,8 @@ func collectUserInput(reader *bufio.Reader) Config {
|
||||
|
||||
fmt.Println("\n=== Advanced Configuration ===")
|
||||
|
||||
config.EnableIPv6 = readBool(reader, "Is your server IPv6 capable?", true)
|
||||
config.EnableGeoblocking = readBool(reader, "Do you want to download the MaxMind GeoLite2 database for geoblocking functionality?", true)
|
||||
config.EnableIPv6 = readBool("Is your server IPv6 capable?", true)
|
||||
config.EnableGeoblocking = readBool("Do you want to download the MaxMind GeoLite2 database for geoblocking functionality?", true)
|
||||
|
||||
if config.DashboardDomain == "" {
|
||||
fmt.Println("Error: Dashboard Domain name is required")
|
||||
@@ -404,15 +534,23 @@ func collectUserInput(reader *bufio.Reader) Config {
|
||||
}
|
||||
|
||||
func createConfigFiles(config Config) error {
|
||||
os.MkdirAll("config", 0755)
|
||||
os.MkdirAll("config/letsencrypt", 0755)
|
||||
os.MkdirAll("config/db", 0755)
|
||||
os.MkdirAll("config/logs", 0755)
|
||||
if err := os.MkdirAll("config", 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll("config/letsencrypt", 0755); err != nil {
|
||||
return fmt.Errorf("failed to create letsencrypt directory: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll("config/db", 0755); err != nil {
|
||||
return fmt.Errorf("failed to create db directory: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll("config/logs", 0755); err != nil {
|
||||
return fmt.Errorf("failed to create logs directory: %v", err)
|
||||
}
|
||||
|
||||
// Walk through all embedded files
|
||||
err := fs.WalkDir(configFiles, "config", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
err := fs.WalkDir(configFiles, "config", func(path string, d fs.DirEntry, walkErr error) (err error) {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
|
||||
// Skip the root fs directory itself
|
||||
@@ -463,7 +601,11 @@ func createConfigFiles(config Config) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create %s: %v", path, err)
|
||||
}
|
||||
defer outFile.Close()
|
||||
defer func() {
|
||||
if cerr := outFile.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
// Execute template
|
||||
if err := tmpl.Execute(outFile, config); err != nil {
|
||||
@@ -479,18 +621,26 @@ func createConfigFiles(config Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
func copyFile(src, dst string) (err error) {
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer source.Close()
|
||||
defer func() {
|
||||
if cerr := source.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destination.Close()
|
||||
defer func() {
|
||||
if cerr := destination.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = io.Copy(destination, source)
|
||||
return err
|
||||
@@ -561,22 +711,24 @@ func showSetupTokenInstructions(containerType SupportedContainer, dashboardDomai
|
||||
fmt.Println("To get your setup token, you need to:")
|
||||
fmt.Println("")
|
||||
fmt.Println("1. Start the containers")
|
||||
if containerType == Docker {
|
||||
switch containerType {
|
||||
case Docker:
|
||||
fmt.Println(" docker compose up -d")
|
||||
} else if containerType == Podman {
|
||||
case Podman:
|
||||
fmt.Println(" podman-compose up -d")
|
||||
} else {
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("2. Wait for the Pangolin container to start and generate the token")
|
||||
fmt.Println("")
|
||||
fmt.Println("3. Check the container logs for the setup token")
|
||||
if containerType == Docker {
|
||||
switch containerType {
|
||||
case Docker:
|
||||
fmt.Println(" docker logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'")
|
||||
} else if containerType == Podman {
|
||||
case Podman:
|
||||
fmt.Println(" podman logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'")
|
||||
} else {
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("4. Look for output like")
|
||||
fmt.Println(" === SETUP TOKEN GENERATED ===")
|
||||
@@ -592,43 +744,12 @@ func showSetupTokenInstructions(containerType SupportedContainer, dashboardDomai
|
||||
}
|
||||
|
||||
func generateRandomSecretKey() string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
const length = 32
|
||||
|
||||
var seededRand *rand.Rand = rand.New(
|
||||
rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func getPublicIP() string {
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Get("https://ifconfig.io/ip")
|
||||
secret := make([]byte, 32)
|
||||
_, err := rand.Read(secret)
|
||||
if err != nil {
|
||||
return ""
|
||||
panic(fmt.Sprintf("Failed to generate random secret key: %v", err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
ip := strings.TrimSpace(string(body))
|
||||
|
||||
// Validate that it's a valid IP address
|
||||
if net.ParseIP(ip) != nil {
|
||||
return ip
|
||||
}
|
||||
|
||||
return ""
|
||||
return base64.StdEncoding.EncodeToString(secret)
|
||||
}
|
||||
|
||||
// Run external commands with stdio/stderr attached.
|
||||
@@ -643,10 +764,7 @@ func checkPortsAvailable(port int) error {
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"ERROR: port %d is occupied or cannot be bound: %w\n\n",
|
||||
port, err,
|
||||
)
|
||||
return fmt.Errorf("ERROR: port %d is occupied or cannot be bound: %w", port, err)
|
||||
}
|
||||
if closeErr := ln.Close(); closeErr != nil {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
|
||||
51
install/theme.go
Normal file
51
install/theme.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Pangolin brand colors (converted from oklch to hex)
|
||||
var (
|
||||
// Primary orange/amber - oklch(0.6717 0.1946 41.93)
|
||||
primaryColor = lipgloss.AdaptiveColor{Light: "#D97706", Dark: "#F59E0B"}
|
||||
// Muted foreground
|
||||
mutedColor = lipgloss.AdaptiveColor{Light: "#737373", Dark: "#A3A3A3"}
|
||||
// Success green
|
||||
successColor = lipgloss.AdaptiveColor{Light: "#16A34A", Dark: "#22C55E"}
|
||||
// Error red - oklch(0.577 0.245 27.325)
|
||||
errorColor = lipgloss.AdaptiveColor{Light: "#DC2626", Dark: "#EF4444"}
|
||||
// Normal text
|
||||
normalFg = lipgloss.AdaptiveColor{Light: "#171717", Dark: "#FAFAFA"}
|
||||
)
|
||||
|
||||
// ThemePangolin returns a huh theme using Pangolin brand colors
|
||||
func ThemePangolin() *huh.Theme {
|
||||
t := huh.ThemeBase()
|
||||
|
||||
// Focused state styles
|
||||
t.Focused.Base = t.Focused.Base.BorderForeground(primaryColor)
|
||||
t.Focused.Title = t.Focused.Title.Foreground(primaryColor).Bold(true)
|
||||
t.Focused.Description = t.Focused.Description.Foreground(mutedColor)
|
||||
t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(errorColor)
|
||||
t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(errorColor)
|
||||
t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(primaryColor)
|
||||
t.Focused.NextIndicator = t.Focused.NextIndicator.Foreground(primaryColor)
|
||||
t.Focused.PrevIndicator = t.Focused.PrevIndicator.Foreground(primaryColor)
|
||||
t.Focused.Option = t.Focused.Option.Foreground(normalFg)
|
||||
t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(primaryColor)
|
||||
t.Focused.SelectedPrefix = lipgloss.NewStyle().Foreground(successColor).SetString("✓ ")
|
||||
t.Focused.UnselectedPrefix = lipgloss.NewStyle().Foreground(mutedColor).SetString(" ")
|
||||
t.Focused.FocusedButton = t.Focused.FocusedButton.Foreground(lipgloss.Color("#FFFFFF")).Background(primaryColor)
|
||||
t.Focused.BlurredButton = t.Focused.BlurredButton.Foreground(normalFg).Background(lipgloss.AdaptiveColor{Light: "#E5E5E5", Dark: "#404040"})
|
||||
t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(primaryColor)
|
||||
t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(primaryColor)
|
||||
|
||||
// Blurred state inherits from focused but with hidden border
|
||||
t.Blurred = t.Focused
|
||||
t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder())
|
||||
t.Blurred.Title = t.Blurred.Title.Foreground(mutedColor).Bold(false)
|
||||
t.Blurred.TextInput.Prompt = t.Blurred.TextInput.Prompt.Foreground(mutedColor)
|
||||
|
||||
return t
|
||||
}
|
||||
115
license.py
Normal file
115
license.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
# --- Configuration ---
|
||||
# The header text to be added to the files.
|
||||
HEADER_TEXT = """/*
|
||||
* This file is part of a proprietary work.
|
||||
*
|
||||
* Copyright (c) 2025 Fossorial, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is licensed under the Fossorial Commercial License.
|
||||
* You may not use this file except in compliance with the License.
|
||||
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
*
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
"""
|
||||
|
||||
def should_add_header(file_path):
|
||||
"""
|
||||
Checks if a file should receive the commercial license header.
|
||||
Returns True if 'private' is in the path or file content.
|
||||
"""
|
||||
# Check if 'private' is in the file path (case-insensitive)
|
||||
if 'server/private' in file_path.lower():
|
||||
return True
|
||||
|
||||
# Check if 'private' is in the file content (case-insensitive)
|
||||
# try:
|
||||
# with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
# content = f.read()
|
||||
# if 'private' in content.lower():
|
||||
# return True
|
||||
# except Exception as e:
|
||||
# print(f"Could not read file {file_path}: {e}")
|
||||
|
||||
return False
|
||||
|
||||
def process_directory(root_dir):
|
||||
"""
|
||||
Recursively scans a directory and adds headers to qualifying .ts or .tsx files,
|
||||
skipping any 'node_modules' directories.
|
||||
"""
|
||||
print(f"Scanning directory: {root_dir}")
|
||||
files_processed = 0
|
||||
headers_added = 0
|
||||
|
||||
for root, dirs, files in os.walk(root_dir):
|
||||
# --- MODIFICATION ---
|
||||
# Exclude 'node_modules' directories from the scan to improve performance.
|
||||
if 'node_modules' in dirs:
|
||||
dirs.remove('node_modules')
|
||||
|
||||
for file in files:
|
||||
if file.endswith('.ts') or file.endswith('.tsx'):
|
||||
file_path = os.path.join(root, file)
|
||||
files_processed += 1
|
||||
|
||||
try:
|
||||
with open(file_path, 'r+', encoding='utf-8') as f:
|
||||
original_content = f.read()
|
||||
has_header = original_content.startswith(HEADER_TEXT.strip())
|
||||
|
||||
if should_add_header(file_path):
|
||||
# Add header only if it's not already there
|
||||
if not has_header:
|
||||
f.seek(0, 0) # Go to the beginning of the file
|
||||
f.write(HEADER_TEXT.strip() + '\n\n' + original_content)
|
||||
print(f"Added header to: {file_path}")
|
||||
headers_added += 1
|
||||
else:
|
||||
print(f"Header already exists in: {file_path}")
|
||||
else:
|
||||
# Remove header if it exists but shouldn't be there
|
||||
if has_header:
|
||||
# Find the end of the header and remove it (including following newlines)
|
||||
header_with_newlines = HEADER_TEXT.strip() + '\n\n'
|
||||
if original_content.startswith(header_with_newlines):
|
||||
content_without_header = original_content[len(header_with_newlines):]
|
||||
else:
|
||||
# Handle case where there might be different newline patterns
|
||||
header_end = len(HEADER_TEXT.strip())
|
||||
# Skip any newlines after the header
|
||||
while header_end < len(original_content) and original_content[header_end] in '\n\r':
|
||||
header_end += 1
|
||||
content_without_header = original_content[header_end:]
|
||||
|
||||
f.seek(0)
|
||||
f.write(content_without_header)
|
||||
f.truncate()
|
||||
print(f"Removed header from: {file_path}")
|
||||
headers_added += 1 # Reusing counter for modifications
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing file {file_path}: {e}")
|
||||
|
||||
print("\n--- Scan Complete ---")
|
||||
print(f"Total .ts or .tsx files found: {files_processed}")
|
||||
print(f"Files modified (headers added/removed): {headers_added}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Get the target directory from the command line arguments.
|
||||
# If no directory is provided, it uses the current directory ('.').
|
||||
if len(sys.argv) > 1:
|
||||
target_directory = sys.argv[1]
|
||||
else:
|
||||
target_directory = '.' # Default to current directory
|
||||
|
||||
if not os.path.isdir(target_directory):
|
||||
print(f"Error: Directory '{target_directory}' not found.")
|
||||
sys.exit(1)
|
||||
|
||||
process_directory(os.path.abspath(target_directory))
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Вие сте част от {count, plural, =0 {нула организации} one {една организация} other {# организации}}.",
|
||||
"componentsInvalidKey": "Засечен е невалиден или изтекъл лиценз. Проверете лицензионните условия, за да се възползвате от всички функционалности.",
|
||||
"dismiss": "Отхвърляне",
|
||||
"subscriptionViolationMessage": "Превишихте ограничението на текущия си план. Коригирайте проблема, като премахнете сайтове, потребители или други ресурси, за да оставате в рамките на плана си.",
|
||||
"subscriptionViolationViewBilling": "Преглед на фактурирането",
|
||||
"componentsLicenseViolation": "Нарушение на лиценза: Сървърът използва {usedSites} сайта, което надвишава лицензионния лимит от {maxSites} сайта. Проверете лицензионните условия, за да се възползвате от всички функционалности.",
|
||||
"componentsSupporterMessage": "Благодарим ви, че подкрепяте Pangolin като {tier}!",
|
||||
"inviteErrorNotValid": "Съжаляваме, но изглежда, че поканата, до която се опитвате да получите достъп, не е приета или вече не е валидна.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Създаване на връзка",
|
||||
"resourcesNotFound": "Не са намерени ресурси",
|
||||
"resourceSearch": "Търсене на ресурси",
|
||||
"machineSearch": "Търсене на машини",
|
||||
"machinesSearch": "Търсене на клиенти на машини...",
|
||||
"machineNotFound": "Не са намерени машини",
|
||||
"userDeviceSearch": "Търсене на устройства на потребителя",
|
||||
"userDevicesSearch": "Търсене на устройства на потребителя...",
|
||||
"openMenu": "Отваряне на менюто",
|
||||
"resource": "Ресурс",
|
||||
"title": "Заглавие",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Прокси заявки чрез HTTPS, използвайки напълно квалифицирано име на домейн.",
|
||||
"resourceRaw": "Суров TCP/UDP ресурс",
|
||||
"resourceRawDescription": "Прокси заявки чрез сурови TCP/UDP, използвайки порт номер.",
|
||||
"resourceRawDescriptionCloud": "Получавайте заявки чрез суров TCP/UDP с използване на портен номер. Изисква се сайтовете да се свързват към отдалечен възел.",
|
||||
"resourceCreate": "Създайте ресурс",
|
||||
"resourceCreateDescription": "Следвайте стъпките по-долу, за да създадете нов ресурс",
|
||||
"resourceSeeAll": "Вижте всички ресурси",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Изберете протокол",
|
||||
"resourcePortNumber": "Номер на порт",
|
||||
"resourcePortNumberDescription": "Външен номер на порт за прокси заявки.",
|
||||
"back": "Назад",
|
||||
"cancel": "Отмяна",
|
||||
"resourceConfig": "Конфигурационни фрагменти",
|
||||
"resourceConfigDescription": "Копирайте и поставете тези конфигурационни отрязъци, за да настроите TCP/UDP ресурса",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Възникна грешка при изтриването на организацията.",
|
||||
"orgDeleted": "Организацията е изтрита",
|
||||
"orgDeletedMessage": "Организацията и нейните данни са изтрити.",
|
||||
"deleteAccount": "Изтриване на профил",
|
||||
"deleteAccountDescription": "Перманентно изтрийте своя профил, всички организации, които притежавате, и всички данни в тези организации. Това не може да бъде отменено.",
|
||||
"deleteAccountButton": "Изтриване на профил",
|
||||
"deleteAccountConfirmTitle": "Изтрий профила",
|
||||
"deleteAccountConfirmMessage": "Това ще изтрие перманентно вашия профил, всички организации, които притежавате, и всички данни в тези организации. Това не може да бъде отменено.",
|
||||
"deleteAccountConfirmString": "изтриване на профил",
|
||||
"deleteAccountSuccess": "Профилът е изтрит",
|
||||
"deleteAccountSuccessMessage": "Вашият профил е изтрит.",
|
||||
"deleteAccountError": "Неуспешно изтриване на профил",
|
||||
"deleteAccountPreviewAccount": "Вашият профил",
|
||||
"deleteAccountPreviewOrgs": "Организации, които притежавате (и всички техни данни)",
|
||||
"orgMissing": "Липсва идентификатор на организация",
|
||||
"orgMissingMessage": "Невъзможност за регенериране на покана без идентификатор на организация.",
|
||||
"accessUsersManage": "Управление на потребители",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Изтрийте API ключа",
|
||||
"apiKeysManage": "Управление на API ключове",
|
||||
"apiKeysDescription": "API ключове се използват за удостоверяване с интеграционния API",
|
||||
"provisioningKeysTitle": "Ключ за осигуряване",
|
||||
"provisioningKeysManage": "Управление на ключове за осигуряване",
|
||||
"provisioningKeysDescription": "Ключовете за осигуряване се използват за удостоверяване на автоматичното осигуряване на сайта за вашата организация.",
|
||||
"provisioningManage": "Осигуряване",
|
||||
"provisioningDescription": "Управление на ключовете за осигуряване и преглед на чаканещите сайтове за одобрение.",
|
||||
"pendingSites": "Чаканещи сайтове",
|
||||
"siteApproveSuccess": "Сайтът е одобрен успешно",
|
||||
"siteApproveError": "Грешка при одобряването на сайта",
|
||||
"provisioningKeys": "Ключове за осигуряване",
|
||||
"searchProvisioningKeys": "Търсене на ключове за осигуряване...",
|
||||
"provisioningKeysAdd": "Генериране на ключ за осигуряване",
|
||||
"provisioningKeysErrorDelete": "Грешка при изтриване на ключ за осигуряване",
|
||||
"provisioningKeysErrorDeleteMessage": "Грешка при изтриване на ключ за осигуряване",
|
||||
"provisioningKeysQuestionRemove": "Сигурни ли сте, че искате да премахнете този ключ за осигуряване от организацията?",
|
||||
"provisioningKeysMessageRemove": "След като бъде премахнат, ключът няма да бъде използван за осигуряване на сайтове.",
|
||||
"provisioningKeysDeleteConfirm": "Потвърдете изтриването на ключ за осигуряване",
|
||||
"provisioningKeysDelete": "Изтриване на ключ за осигуряване",
|
||||
"provisioningKeysCreate": "Генериране на ключ за осигуряване",
|
||||
"provisioningKeysCreateDescription": "Генерирайте нов ключ за осигуряване за организацията",
|
||||
"provisioningKeysSeeAll": "Вижте всички ключове за осигуряване",
|
||||
"provisioningKeysSave": "Запазете ключа за осигуряване",
|
||||
"provisioningKeysSaveDescription": "Ще можете да видите това само веднъж. Копирайте го на сигурно място.",
|
||||
"provisioningKeysErrorCreate": "Грешка при създаване на ключ за осигуряване",
|
||||
"provisioningKeysList": "Нов ключ за осигуряване",
|
||||
"provisioningKeysMaxBatchSize": "Максимален размер на пакет",
|
||||
"provisioningKeysUnlimitedBatchSize": "Неограничен размер на партида (без лимит)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Неограничено",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Въведете валиден максимален размер на партида (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Валиден до",
|
||||
"provisioningKeysValidUntilHint": "Оставете празно за неограничено валидност.",
|
||||
"provisioningKeysValidUntilInvalid": "Въведете валидна дата и час.",
|
||||
"provisioningKeysNumUsed": "Брой използвания",
|
||||
"provisioningKeysLastUsed": "Последно използван",
|
||||
"provisioningKeysNoExpiry": "Без изтичане",
|
||||
"provisioningKeysNeverUsed": "Никога",
|
||||
"provisioningKeysEdit": "Редактиране на ключ за осигуряване",
|
||||
"provisioningKeysEditDescription": "Актуализирайте максималния размер на партида и времето на изтичане за този ключ.",
|
||||
"provisioningKeysApproveNewSites": "Одобрете нови сайтове",
|
||||
"provisioningKeysApproveNewSitesDescription": "Автоматично одобряване на сайтове, които се регистрират с този ключ.",
|
||||
"provisioningKeysUpdateError": "Грешка при актуализирането на ключа за осигуряване",
|
||||
"provisioningKeysUpdated": "Ключът за осигуряване е актуализиран",
|
||||
"provisioningKeysUpdatedDescription": "Вашите промени бяха запазени.",
|
||||
"provisioningKeysBannerTitle": "Ключове за осигуряване на сайта",
|
||||
"provisioningKeysBannerDescription": "Генерирайте ключ за осигуряване и го използвайте с Newt конектора за автоматично създаване на сайтове при първото стартиране — няма нужда от създаване на отделни идентификационни данни за всеки сайт.",
|
||||
"provisioningKeysBannerButtonText": "Научете повече",
|
||||
"pendingSitesBannerTitle": "Чакащи сайтове",
|
||||
"pendingSitesBannerDescription": "Сайтовете, които се свързват чрез ключ за осигуряване, се появяват тук за преглед. Одобрете всеки сайт, преди да стане активен и да получи достъп до вашите ресурси.",
|
||||
"pendingSitesBannerButtonText": "Научете повече",
|
||||
"apiKeysSettings": "Настройки на {apiKeyName}",
|
||||
"userTitle": "Управление на всички потребители",
|
||||
"userDescription": "Преглед и управление на всички потребители в системата",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Филтрирайте по състояние на одобрение",
|
||||
"approvalListEmpty": "Няма одобрения",
|
||||
"approvalState": "Състояние на одобрение",
|
||||
"approvalLoadMore": "Заредете още",
|
||||
"loadingApprovals": "Зарежда се одобрение",
|
||||
"approve": "Одобряване",
|
||||
"approved": "Одобрен",
|
||||
"denied": "Отказан",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Потребителят е запазен",
|
||||
"userSavedDescription": "Потребителят беше актуализиран.",
|
||||
"autoProvisioned": "Автоматично предоставено",
|
||||
"autoProvisionSettings": "Настройки за автоматично осигуряване",
|
||||
"autoProvisionedDescription": "Позволете този потребител да бъде автоматично управляван от доставчик на идентификационни данни",
|
||||
"accessControlsDescription": "Управлявайте какво може да достъпва и прави този потребител в организацията",
|
||||
"accessControlsSubmit": "Запазване на контролите за достъп",
|
||||
"singleRolePerUserPlanNotice": "Вашият план поддържа само една роля на потребител.",
|
||||
"singleRolePerUserEditionNotice": "Това издание поддържа само една роля на потребител.",
|
||||
"roles": "Роли",
|
||||
"accessUsersRoles": "Управление на потребители и роли",
|
||||
"accessUsersRolesDescription": "Поканете потребители и ги добавете към роли, за да управлявате достъпа до организацията",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Неуспешно превключване на ресурса",
|
||||
"resourcesErrorUpdateDescription": "Възникна грешка при актуализиране на ресурса",
|
||||
"access": "Достъп",
|
||||
"accessControl": "Контрол на достъпа",
|
||||
"shareLink": "{resource} Сподели връзка",
|
||||
"resourceSelect": "Изберете ресурс",
|
||||
"shareLinks": "Споделени връзки",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Ролята е премахната",
|
||||
"accessRoleRemovedDescription": "Ролята беше успешно премахната.",
|
||||
"accessRoleRequiredRemove": "Преди да изтриете тази роля, моля изберете нова роля, към която да прехвърлите настоящите членове.",
|
||||
"network": "Мрежа",
|
||||
"manage": "Управление",
|
||||
"sitesNotFound": "Няма намерени сайтове.",
|
||||
"pangolinServerAdmin": "Администратор на сървър - Панголин",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Увеличаване на броя на сайтовете",
|
||||
"idpManage": "Управление на доставчици на идентичност",
|
||||
"idpManageDescription": "Прегледайте и управлявайте доставчици на идентичност в системата",
|
||||
"idpGlobalModeBanner": "Доставчиците на идентичност (IdPs) за всяка организация са деактивирани на този сървър. Използват се глобални IdPs (споделени между всички организации). Управлявайте глобалните IdPs в <adminPanelLink>администраторския панел</adminPanelLink>. За да активирате IdPs за всяка организация, редактирайте конфигурацията на сървъра и задайте режима на IdP към org. <configDocsLink>Вижте документацията</configDocsLink>. Ако желаете да продължите да използвате глобалните IdPs и да премахнете това от настройките на организацията, изрично задайте режима на global в конфигурацията.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Доставчиците на идентичност (IdPs) за всяка организация са деактивирани на този сървър. Използват се глобални IdPs (споделени между всички организации). Управлявайте глобалните IdPs в <adminPanelLink>администраторския панел</adminPanelLink>. За да използвате доставчици на идентичност за всяка организация, трябва да надстроите до изданието Enterprise.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Доставчиците на идентичност (IdPs) за всяка организация са деактивирани на този сървър. Използват се глобални IdPs (споделени между всички организации). Управлявайте глобалните IdPs в <adminPanelLink>администраторския панел</adminPanelLink>. За да използвате доставчици на идентичност за всяка организация, е необходим лиценз за изданието Enterprise.",
|
||||
"idpDeletedDescription": "Доставчик на идентичност успешно изтрит",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Сигурни ли сте, че искате да изтриете доставчика за идентичност?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Настройка - Pangolin",
|
||||
"orgNameRequired": "Името на организацията е задължително",
|
||||
"orgIdRequired": "ID на организацията е задължително",
|
||||
"orgIdMaxLength": "ID на организация трябва да бъде най-много 32 символа",
|
||||
"orgErrorCreate": "Възникна грешка при създаване на организация",
|
||||
"pageNotFound": "Страницата не е намерена",
|
||||
"pageNotFoundDescription": "О, не! Страницата, която търсите, не съществува.",
|
||||
"overview": "Общ преглед",
|
||||
"home": "Начало",
|
||||
"accessControl": "Контрол на достъпа",
|
||||
"settings": "Настройки",
|
||||
"usersAll": "Всички потребители",
|
||||
"license": "Лиценз",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Получаване на потребител",
|
||||
"actionGetOrgUser": "Вземете потребител на организация",
|
||||
"actionListOrgDomains": "Изброяване на домейни на организация",
|
||||
"actionGetDomain": "Вземи домейн",
|
||||
"actionCreateOrgDomain": "Създай домейн",
|
||||
"actionUpdateOrgDomain": "Актуализирай домейн",
|
||||
"actionDeleteOrgDomain": "Изтрий домейн",
|
||||
"actionGetDNSRecords": "Вземи DNS записи",
|
||||
"actionRestartOrgDomain": "Рестартирай домейн",
|
||||
"actionCreateSite": "Създаване на сайт",
|
||||
"actionDeleteSite": "Изтриване на сайта",
|
||||
"actionGetSite": "Вземете сайт",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Въведете конфигурационния токен от сървърната конзола.",
|
||||
"setupTokenRequired": "Необходим е конфигурационен токен",
|
||||
"actionUpdateSite": "Актуализиране на сайт",
|
||||
"actionResetSiteBandwidth": "Нулиране на честотната лента на организацията",
|
||||
"actionListSiteRoles": "Изброяване на позволените роли за сайта",
|
||||
"actionCreateResource": "Създаване на ресурс",
|
||||
"actionDeleteResource": "Изтриване на ресурс",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Изтрийте потребител",
|
||||
"actionListUsers": "Изброяване на потребители",
|
||||
"actionAddUserRole": "Добавяне на роля на потребител",
|
||||
"actionSetUserOrgRoles": "Задайте роли на потребители",
|
||||
"actionGenerateAccessToken": "Генериране на токен за достъп",
|
||||
"actionDeleteAccessToken": "Изтриване на токен за достъп",
|
||||
"actionListAccessTokens": "Изброяване на токени за достъп",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Преглед на дневници",
|
||||
"noneSelected": "Нищо не е избрано",
|
||||
"orgNotFound2": "Няма намерени организации.",
|
||||
"searchProgress": "Търсене...",
|
||||
"searchPlaceholder": "Търсене...",
|
||||
"emptySearchOptions": "Няма намерени опции",
|
||||
"create": "Създаване",
|
||||
"orgs": "Организации",
|
||||
"loginError": "Възникна неочаквана грешка. Моля, опитайте отново.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Частно",
|
||||
"sidebarAccessControl": "Контрол на достъпа",
|
||||
"sidebarLogsAndAnalytics": "Дневници и анализи",
|
||||
"sidebarTeam": "Екип",
|
||||
"sidebarUsers": "Потребители",
|
||||
"sidebarAdmin": "Администратор",
|
||||
"sidebarInvitations": "Покани",
|
||||
"sidebarRoles": "Роли",
|
||||
"sidebarShareableLinks": "Връзки",
|
||||
"sidebarApiKeys": "API ключове",
|
||||
"sidebarProvisioning": "Осигуряване",
|
||||
"sidebarSettings": "Настройки",
|
||||
"sidebarAllUsers": "Всички потребители",
|
||||
"sidebarIdentityProviders": "Идентификационни доставчици",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Лог & Анализи",
|
||||
"sidebarBluePrints": "Чертежи",
|
||||
"sidebarOrganization": "Организация",
|
||||
"sidebarManagement": "Управление",
|
||||
"sidebarBillingAndLicenses": "Фактуриране & Лицензи",
|
||||
"sidebarLogsAnalytics": "Анализи",
|
||||
"blueprints": "Чертежи",
|
||||
"blueprintsDescription": "Прилагайте декларативни конфигурации и преглеждайте предишни изпълнения",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Парсирано съдържание (само за четене)",
|
||||
"enableDockerSocket": "Активиране на Docker Чернова",
|
||||
"enableDockerSocketDescription": "Активиране на Docker Socket маркировка за изтегляне на етикети на чернова. Пътят на гнездото трябва да бъде предоставен на Newt.",
|
||||
"enableDockerSocketLink": "Научете повече",
|
||||
"viewDockerContainers": "Преглед на Docker контейнери",
|
||||
"containersIn": "Контейнери в {siteName}",
|
||||
"selectContainerDescription": "Изберете контейнер, който да ползвате като име на хост за целта. Натиснете порт, за да ползвате порт",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Име на пространство: {namespace}",
|
||||
"domainPickerShowMore": "Покажи повече",
|
||||
"regionSelectorTitle": "Избор на регион",
|
||||
"domainPickerRemoteExitNodeWarning": "Предоставените домейни не се поддържат, когато сайтовете се свързват към отдалечени крайни възли. За да бъдат ресурсите налични на отдалечени възли, използвайте персонализиран домейн вместо това.",
|
||||
"regionSelectorInfo": "Изборът на регион ни помага да предоставим по-добра производителност за вашето местоположение. Не е необходимо да сте в същия регион като сървъра.",
|
||||
"regionSelectorPlaceholder": "Изберете регион",
|
||||
"regionSelectorComingSoon": "Очаква се скоро",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Преглед на лимитите за използване",
|
||||
"billingMonitorUsage": "Следете своята употреба спрямо конфигурираните лимити. Ако имате нужда от увеличаване на лимитите, моля свържете се с нас support@pangolin.net.",
|
||||
"billingDataUsage": "Използване на данни",
|
||||
"billingOnlineTime": "Време на работа на сайта",
|
||||
"billingUsers": "Активни потребители",
|
||||
"billingDomains": "Активни домейни",
|
||||
"billingRemoteExitNodes": "Активни самостоятелно хоствани възли",
|
||||
"billingSites": "Сайтове",
|
||||
"billingUsers": "Потребители",
|
||||
"billingDomains": "Домейни",
|
||||
"billingOrganizations": "Организации",
|
||||
"billingRemoteExitNodes": "Дистанционни възли",
|
||||
"billingNoLimitConfigured": "Няма конфигуриран лимит",
|
||||
"billingEstimatedPeriod": "Очакван период на фактуриране",
|
||||
"billingIncludedUsage": "Включено използване",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Неуспех при получаване на URL на портала",
|
||||
"billingPortalError": "Грешка в портала",
|
||||
"billingDataUsageInfo": "Таксува се за всички данни, прехвърляни през вашите защитени тунели, когато сте свързани към облака. Това включва както входящия, така и изходящия трафик за всички ваши сайтове. Когато достигнете лимита си, вашите сайтове ще бъдат прекъснати, докато не надстроите плана или не намалите използването. Данните не се таксуват при използване на възли.",
|
||||
"billingOnlineTimeInfo": "Таксува се на база колко време вашите сайтове остават свързани с облака. Пример: 44,640 минути се равняват на един сайт работещ 24/7 за цял месец. Когато достигнете лимита си, вашите сайтове ще бъдат прекъснати, докато не надстроите плана или не намалите използването. Времето не се таксува при използване на възли.",
|
||||
"billingUsersInfo": "Таксува се всеки потребител в организацията. Таксуването се изчислява ежедневно въз основа на броя на активните потребителски акаунти във вашата организация.",
|
||||
"billingDomainInfo": "Таксува се всеки домейн в организацията. Таксуването се изчислява ежедневно въз основа на броя на активните домейн акаунти във вашата организация.",
|
||||
"billingRemoteExitNodesInfo": "Таксува се всеки управляван възел в организацията. Таксуването се изчислява ежедневно въз основа на броя на активните управлявани възли във вашата организация.",
|
||||
"billingSInfo": "Колко сайта можете да използвате",
|
||||
"billingUsersInfo": "Колко потребители можете да използвате",
|
||||
"billingDomainInfo": "Колко домейни можете да използвате",
|
||||
"billingRemoteExitNodesInfo": "Колко дистанционни възли можете да използвате",
|
||||
"billingLicenseKeys": "Лицензионни ключове",
|
||||
"billingLicenseKeysDescription": "Управлявайте вашите абонаменти за лицензионни ключове",
|
||||
"billingLicenseSubscription": "Абонамент за лиценз",
|
||||
"billingInactive": "Неактивен",
|
||||
"billingLicenseItem": "Лицензионен елемент",
|
||||
"billingQuantity": "Количество",
|
||||
"billingTotal": "общо",
|
||||
"billingModifyLicenses": "Промяна на абонамента за лиценз",
|
||||
"domainNotFound": "Домейнът не е намерен",
|
||||
"domainNotFoundDescription": "Този ресурс е деактивиран, защото домейнът вече не съществува в нашата система. Моля, задайте нов домейн за този ресурс.",
|
||||
"failed": "Неуспешно",
|
||||
"createNewOrgDescription": "Създайте нова организация",
|
||||
"organization": "Организация",
|
||||
"primary": "Основно",
|
||||
"port": "Порт",
|
||||
"securityKeyManage": "Управление на ключове за защита",
|
||||
"securityKeyDescription": "Добавяне или премахване на ключове за защита за удостоверяване без парола",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Номерът на порта е задължителен за не-HTTP ресурси",
|
||||
"resourcePortNotAllowed": "Номерът на порта не трябва да бъде задаван за HTTP ресурси",
|
||||
"billingPricingCalculatorLink": "Калкулатор на цените",
|
||||
"billingYourPlan": "Вашият план",
|
||||
"billingViewOrModifyPlan": "Преглед или промяна на текущия ви план",
|
||||
"billingViewPlanDetails": "Преглед на подробности за плана",
|
||||
"billingUsageAndLimits": "Използване и граници",
|
||||
"billingViewUsageAndLimits": "Преглед на ограниченията на плана и текущото използване",
|
||||
"billingCurrentUsage": "Текущо използване",
|
||||
"billingMaximumLimits": "Максимални граници",
|
||||
"billingRemoteNodes": "Дистанционни възли",
|
||||
"billingUnlimited": "Неограничено",
|
||||
"billingPaidLicenseKeys": "Платени лицензионни ключове",
|
||||
"billingManageLicenseSubscription": "Управлявайте абонамента си за платени самостоятелно хоствани лицензионни ключове",
|
||||
"billingCurrentKeys": "Текущи ключове",
|
||||
"billingModifyCurrentPlan": "Промяна на текущия план",
|
||||
"billingConfirmUpgrade": "Потвърдете повишаването",
|
||||
"billingConfirmDowngrade": "Потвърдете понижението",
|
||||
"billingConfirmUpgradeDescription": "Предстои ви да повишите плана си. Прегледайте новите ограничения и цени по-долу.",
|
||||
"billingConfirmDowngradeDescription": "Предстои ви да понижите плана си. Прегледайте новите ограничения и цени по-долу.",
|
||||
"billingPlanIncludes": "Планът включва",
|
||||
"billingProcessing": "Процесиране...",
|
||||
"billingConfirmUpgradeButton": "Потвърдете повишаването",
|
||||
"billingConfirmDowngradeButton": "Потвърдете понижението",
|
||||
"billingLimitViolationWarning": "Използването надвишава новите планови ограничения",
|
||||
"billingLimitViolationDescription": "Текущото ви използване надвишава ограниченията на този план. След понижаване, всички действия ще бъдат деактивирани, докато не намалите използването в рамките на новите ограничения. Моля, прегледайте по-долу функциите, които в момента са извън ограниченията. Ограничения в нарушение:",
|
||||
"billingFeatureLossWarning": "Уведомление за наличност на функциите",
|
||||
"billingFeatureLossDescription": "Чрез понижението на плана, функциите, недостъпни в новия план, ще бъдат автоматично деактивирани. Някои настройки и конфигурации може да бъдат загубени. Моля, прегледайте ценовата матрица, за да разберете кои функции вече няма да са на разположение.",
|
||||
"billingUsageExceedsLimit": "Текущото използване ({current}) надвишава ограничението ({limit})",
|
||||
"billingPastDueTitle": "Плащането е просрочено",
|
||||
"billingPastDueDescription": "Вашето плащане е просрочено. Моля, актуализирайте метода на плащане, за да продължите да използвате настоящия си план. Ако проблемът не бъде разрешен, абонаментът ви ще бъде прекратен и ще бъдете прехвърлени на безплатния план.",
|
||||
"billingUnpaidTitle": "Абонаментът не е платен",
|
||||
"billingUnpaidDescription": "Вашият абонамент не е платен и сте прехвърлени на безплатния план. Моля, актуализирайте метода на плащане, за да възстановите вашия абонамент.",
|
||||
"billingIncompleteTitle": "Плащането е непълно",
|
||||
"billingIncompleteDescription": "Вашето плащане е непълно. Моля, завършете процеса на плащане, за да активирате вашия абонамент.",
|
||||
"billingIncompleteExpiredTitle": "Плащането е изтекло",
|
||||
"billingIncompleteExpiredDescription": "Вашето плащане никога не е завършено и е изтекло. Прехвърлени сте на безплатния план. Моля, абонирайте се отново, за да възстановите достъпа до платените функции.",
|
||||
"billingManageSubscription": "Управлявайте вашия абонамент",
|
||||
"billingResolvePaymentIssue": "Моля, разрешете проблема с плащането преди да извършите надграждане или понижение",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Съгласен съм с",
|
||||
"termsOfService": "условията за ползване",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Времето е в секунди",
|
||||
"requireDeviceApproval": "Изискват одобрение на устройства",
|
||||
"requireDeviceApprovalDescription": "Потребители с тази роля трябва да имат нови устройства одобрени от администратор преди да могат да се свържат и да имат достъп до ресурси.",
|
||||
"sshAccess": "SSH достъп",
|
||||
"roleAllowSsh": "Разреши SSH",
|
||||
"roleAllowSshAllow": "Разреши",
|
||||
"roleAllowSshDisallow": "Забрани",
|
||||
"roleAllowSshDescription": "Разреши на потребителите с тази роля да се свързват с ресурси чрез SSH. Когато е деактивирано, ролята не може да използва SSH достъп.",
|
||||
"sshSudoMode": "Sudo достъп",
|
||||
"sshSudoModeNone": "Няма",
|
||||
"sshSudoModeNoneDescription": "Потребителят не може да изпълнява команди с sudo.",
|
||||
"sshSudoModeFull": "Пълен Sudo",
|
||||
"sshSudoModeFullDescription": "Потребителят може да изпълнява всяка команда с sudo.",
|
||||
"sshSudoModeCommands": "Команди",
|
||||
"sshSudoModeCommandsDescription": "Потребителят може да изпълнява само определени команди с sudo.",
|
||||
"sshSudo": "Разреши sudo",
|
||||
"sshSudoCommands": "Sudo команди",
|
||||
"sshSudoCommandsDescription": "Списък, разделен със запетаи, с команди, които потребителят е позволено да изпълнява с sudo.",
|
||||
"sshCreateHomeDir": "Създай начална директория",
|
||||
"sshUnixGroups": "Unix групи",
|
||||
"sshUnixGroupsDescription": "Списък, разделен със запетаи, с Unix групи, към които да се добави потребителят на целевия хост.",
|
||||
"retryAttempts": "Опити за повторно",
|
||||
"expectedResponseCodes": "Очаквани кодове за отговор",
|
||||
"expectedResponseCodesDescription": "HTTP статус код, указващ здравословно състояние. Ако бъде оставено празно, между 200-300 се счита за здравословно.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Изходен възел",
|
||||
"country": "Държава",
|
||||
"rulesMatchCountry": "Понастоящем на базата на изходния IP",
|
||||
"region": "Регион",
|
||||
"selectRegion": "Изберете регион",
|
||||
"searchRegions": "Търсене на региони...",
|
||||
"noRegionFound": "Регионът не е намерен.",
|
||||
"rulesMatchRegion": "Изберете регионална групировка на държави",
|
||||
"rulesErrorInvalidRegion": "Невалиден регион",
|
||||
"rulesErrorInvalidRegionDescription": "Моля, изберете валиден регион.",
|
||||
"regionAfrica": "Африка",
|
||||
"regionNorthernAfrica": "Северна Африка",
|
||||
"regionEasternAfrica": "Източна Африка",
|
||||
"regionMiddleAfrica": "Централна Африка",
|
||||
"regionSouthernAfrica": "Южна Африка",
|
||||
"regionWesternAfrica": "Западна Африка",
|
||||
"regionAmericas": "Америките",
|
||||
"regionCaribbean": "Карибите",
|
||||
"regionCentralAmerica": "Централна Америка",
|
||||
"regionSouthAmerica": "Южна Америка",
|
||||
"regionNorthernAmerica": "Северна Америка",
|
||||
"regionAsia": "Азия",
|
||||
"regionCentralAsia": "Централна Азия",
|
||||
"regionEasternAsia": "Източна Азия",
|
||||
"regionSouthEasternAsia": "Югоизточна Азия",
|
||||
"regionSouthernAsia": "Южна Азия",
|
||||
"regionWesternAsia": "Западна Азия",
|
||||
"regionEurope": "Европа",
|
||||
"regionEasternEurope": "Източна Европа",
|
||||
"regionNorthernEurope": "Северна Европа",
|
||||
"regionSouthernEurope": "Южна Европа",
|
||||
"regionWesternEurope": "Западна Европа",
|
||||
"regionOceania": "Океания",
|
||||
"regionAustraliaAndNewZealand": "Австралия и Нова Зеландия",
|
||||
"regionMelanesia": "Меланезия",
|
||||
"regionMicronesia": "Микронезия",
|
||||
"regionPolynesia": "Полинезия",
|
||||
"managedSelfHosted": {
|
||||
"title": "Управлявано Самостоятелно-хоствано",
|
||||
"description": "По-надежден и по-нисък поддръжка на Самостоятелно-хостван Панголиин сървър с допълнителни екстри",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Невалидна стойност",
|
||||
"idpTypeLabel": "Тип на доставчика на идентичност",
|
||||
"roleMappingExpressionPlaceholder": "напр.: contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||
"roleMappingModeFixedRoles": "Фиксирани роли",
|
||||
"roleMappingModeMappingBuilder": "Строител на карти",
|
||||
"roleMappingModeRawExpression": "Необработено израз",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Изберете една или повече роли",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Въведете имена на роли (точно съвпадение на организацията)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Присвойте същият набор от роли на всеки автоматично осигурен потребител.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "За стандартните политики въведете имена на роли, които съществуват във всяка организация, където е осигурен потребител. Имената трябва да съвпадат точно.",
|
||||
"roleMappingClaimPath": "Път на иск",
|
||||
"roleMappingClaimPathPlaceholder": "групи",
|
||||
"roleMappingClaimPathDescription": "Път в съдържанието на маркера, който съдържа изходни стойности (например групи).",
|
||||
"roleMappingMatchValue": "Съвпадение на стойност",
|
||||
"roleMappingAssignRoles": "Присвояване на роли",
|
||||
"roleMappingAddMappingRule": "Добавяне на правило за картироване",
|
||||
"roleMappingRawExpressionResultDescription": "Изразът трябва да бъде оценен на низ или масив от низове.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Изразът трябва да бъде оценен на низ (едно име на роля).",
|
||||
"roleMappingMatchValuePlaceholder": "Съвпадение на стойност (например: администратор)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Въведете имена на роли (точно по организация)",
|
||||
"roleMappingBuilderFreeformRowHint": "Имената на ролите трябва да съвпадат с роля във всяка целева организация.",
|
||||
"roleMappingRemoveRule": "Премахни",
|
||||
"idpGoogleConfiguration": "Конфигурация на Google",
|
||||
"idpGoogleConfigurationDescription": "Конфигурирайте Google OAuth2 идентификационни данни",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 идентификационен клиент",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Сигурни ли сте, че искате да премахнете брандинга за страниците за автентификация?",
|
||||
"authPageBrandingDeleteConfirm": "Потвърждение на изтриване на брандинга.",
|
||||
"brandingLogoURL": "URL адрес на логото.",
|
||||
"brandingLogoURLOrPath": "URL или Път към лого",
|
||||
"brandingLogoPathDescription": "Въведете URL или локален път.",
|
||||
"brandingLogoURLDescription": "Въведете публично достъпен URL към вашето лого изображение.",
|
||||
"brandingPrimaryColor": "Основен цвят.",
|
||||
"brandingLogoWidth": "Ширина (px).",
|
||||
"brandingLogoHeight": "Височина (px).",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Назад към стандартния вход.",
|
||||
"orgAuthNoAccount": "Нямате профил?",
|
||||
"subscriptionRequiredToUse": "Необходим е абонамент, за да използвате тази функция.",
|
||||
"mustUpgradeToUse": "Трябва да повишите своя абонамент, за да използвате тази функция.",
|
||||
"subscriptionRequiredTierToUse": "Тази функция изисква <tierLink>{tier}</tierLink> или по-висок план.",
|
||||
"upgradeToTierToUse": "Повишете до <tierLink>{tier}</tierLink> или по-висок план, за да използвате тази функция.",
|
||||
"subscriptionTierTier1": "Домашен",
|
||||
"subscriptionTierTier2": "Екип",
|
||||
"subscriptionTierTier3": "Бизнес",
|
||||
"subscriptionTierEnterprise": "Предприятие",
|
||||
"idpDisabled": "Доставчиците на идентичност са деактивирани.",
|
||||
"orgAuthPageDisabled": "Страницата за удостоверяване на организацията е деактивирана.",
|
||||
"domainRestartedDescription": "Проверка на домейна е рестартирана успешно",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Получаване на лиценз",
|
||||
"description": "Изберете план и ни кажете как планирате да използвате Pangolin.",
|
||||
"chooseTier": "Изберете вашия план",
|
||||
"viewPricingLink": "Вижте цените, функциите и ограниченията",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Стартов",
|
||||
"description": "Предприятие, 25 потребители, 25 сайта и общностна поддръжка."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Скала",
|
||||
"description": "Предприятие, 50 потребители, 50 сайта и приоритетна поддръжка."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Само за лична употреба (безплатен лиценз — без плащане)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Продължете към плащане"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Грешка при плащането",
|
||||
"description": "Не можа да се започне плащането. Моля, опитайте отново."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Приоритет",
|
||||
"priorityDescription": "По-високите приоритетни маршрути се оценяват първи. Приоритет = 100 означава автоматично подреждане (системата решава). Използвайте друго число, за да наложите ръчен приоритет.",
|
||||
"instanceName": "Име на инстанция",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Колко дълго да се задържат логовете за достъп",
|
||||
"logRetentionActionLabel": "Задържане на логове за действия",
|
||||
"logRetentionActionDescription": "Колко дълго да се задържат логовете за действия",
|
||||
"logRetentionConnectionLabel": "Запазване на дневниците на връзките",
|
||||
"logRetentionConnectionDescription": "Колко дълго да се съхраняват дневниците на връзките",
|
||||
"logRetentionDisabled": "Деактивирано",
|
||||
"logRetention3Days": "3 дни",
|
||||
"logRetention7Days": "7 дни",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Край на следващата година",
|
||||
"actionLogsDescription": "Прегледайте историята на действията, извършени в тази организация",
|
||||
"accessLogsDescription": "Прегледайте заявките за удостоверяване на достъпа до ресурсите в тази организация",
|
||||
"licenseRequiredToUse": "Необходим е лиценз Enterprise, за да се използва тази функция.",
|
||||
"connectionLogs": "Логове на връзката",
|
||||
"connectionLogsDescription": "Вижте логовете на връзките за тунелите в тази организация",
|
||||
"sidebarLogsConnection": "Логове на връзката",
|
||||
"sidebarLogsStreaming": "Потоци",
|
||||
"sourceAddress": "Източен адрес",
|
||||
"destinationAddress": "Адрес на дестинация",
|
||||
"duration": "Продължителност",
|
||||
"licenseRequiredToUse": "Изисква се лиценз за <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> или <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> за използване на тази функция. <bookADemoLink>Резервирайте демонстрация или пробен POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> е необходим за използване на тази функция. Тази функция също е налична в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Резервирайте демонстрация или пробен POC</bookADemoLink>.",
|
||||
"certResolver": "Решавач на сертификати",
|
||||
"certResolverDescription": "Изберете решавач на сертификати за използване за този ресурс.",
|
||||
"selectCertResolver": "Изберете решавач на сертификати",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Контрол на достъпа.",
|
||||
"editInternalResourceDialogAccessControlDescription": "Контролирайте кои роли, потребители и клиентски машини имат достъп до този ресурс, когато са свързани. Администраторите винаги имат достъп.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Обхватът на портовете трябва да е \"*\" за всички портове или списък от разделени със запетая портове и диапазони (например: \"80,443,8000-9000\"). Портовете трябва да са между 1 и 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "Локация на SSH Auth Daemon",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Изберете къде ще работи демонът за SSH удостоверение: на сайта (Newt) или на отдалечен хост.",
|
||||
"internalResourceAuthDaemonDescription": "Демонът за SSH удостоверение управлява подписването на SSH ключове и PAM удостоверение за този ресурс. Изберете дали да работи на сайта (Newt) или на отделен отдалечен хост. Вижте <docsLink>документацията</docsLink> за повече информация.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Изберете стратегия",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Местоположение",
|
||||
"internalResourceAuthDaemonSite": "На сайта",
|
||||
"internalResourceAuthDaemonSiteDescription": "Демонът за удостоверение работи на сайта (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Отдалечен хост",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Демонът за удостоверение работи на хост, който не е сайтът.",
|
||||
"internalResourceAuthDaemonPort": "Порт на демона (незадължителен)",
|
||||
"orgAuthWhatsThis": "Къде мога да намеря идентификатора на организацията си?",
|
||||
"learnMore": "Научете повече.",
|
||||
"backToHome": "Връщане към началната страница.",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Активирана защитна стена.",
|
||||
"autoUpdatesEnabled": "Активирани автоматични актуализации.",
|
||||
"tpmAvailable": "TPM е на разположение.",
|
||||
"windowsAntivirusEnabled": "Активирана антивирусна програма",
|
||||
"macosSipEnabled": "Protection на системната цялост (SIP).",
|
||||
"macosGatekeeperEnabled": "Gatekeeper.",
|
||||
"macosFirewallStealthMode": "Скрит режим на защитната стена.",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Активирайте одобрения на устройства",
|
||||
"approvalsEmptyStateStep2Description": "Редактирайте ролята и активирайте опцията 'Изискване на одобрения за устройства'. Потребители с тази роля ще трябва администраторско одобрение за нови устройства.",
|
||||
"approvalsEmptyStatePreviewDescription": "Преглед: Когато е активирано, чакащите заявки за устройства ще се появят тук за преглед",
|
||||
"approvalsEmptyStateButtonText": "Управлявайте роли"
|
||||
"approvalsEmptyStateButtonText": "Управлявайте роли",
|
||||
"domainErrorTitle": "Имаме проблем с проверката на вашия домейн",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Конфигурирайте картографирането на ролите и организационните политики на раздела <policiesTabLink>Настройки за автоматично осигуряване</policiesTabLink>.",
|
||||
"streamingTitle": "Събитийни потоци",
|
||||
"streamingDescription": "Предавайте събития от вашата организация до външни дестинации в реално време.",
|
||||
"streamingUnnamedDestination": "Неименувана дестинация",
|
||||
"streamingNoUrlConfigured": "Не е конфигуриран URL",
|
||||
"streamingAddDestination": "Добавяне на дестинация",
|
||||
"streamingHttpWebhookTitle": "HTTP Уеб хук",
|
||||
"streamingHttpWebhookDescription": "Изпратете събития до всяка HTTP крайна точка с гъвкаво удостоверяване и шаблониране.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Предавайте събития на хранилище, съвместимо с S3. Очаквайте скоро.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Пресочвайте събития директно към вашият акаунт в Datadog. Очаквайте скоро.",
|
||||
"streamingTypePickerDescription": "Изберете вид на дестинацията, за да започнете.",
|
||||
"streamingFailedToLoad": "Неуспешно зареждане на дестинации",
|
||||
"streamingUnexpectedError": "Възникна неочаквана грешка.",
|
||||
"streamingFailedToUpdate": "Неуспешно актуализиране на дестинация",
|
||||
"streamingDeletedSuccess": "Дестинацията беше изтрита успешно",
|
||||
"streamingFailedToDelete": "Неуспешно изтриване на дестинацията",
|
||||
"streamingDeleteTitle": "Изтриване на дестинация",
|
||||
"streamingDeleteButtonText": "Изтриване на дестинация",
|
||||
"streamingDeleteDialogAreYouSure": "Сигурни ли сте, че искате да изтриете",
|
||||
"streamingDeleteDialogThisDestination": "тази дестинация",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Всички конфигурации ще бъдат премахнати завинаги.",
|
||||
"httpDestEditTitle": "Редактиране на дестинация",
|
||||
"httpDestAddTitle": "Добавяне на HTTP дестинация",
|
||||
"httpDestEditDescription": "Актуализирайте конфигурацията за този HTTP събитий.",
|
||||
"httpDestAddDescription": "Конфигурирайте нов HTTP крайна точка, за да получавате събития на вашата организация.",
|
||||
"httpDestTabSettings": "Настройки",
|
||||
"httpDestTabHeaders": "Заглавки",
|
||||
"httpDestTabBody": "Тяло",
|
||||
"httpDestTabLogs": "Логове",
|
||||
"httpDestNamePlaceholder": "Моята HTTP дестинация",
|
||||
"httpDestUrlLabel": "Дестинация URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL адресът трябва да използва http или https",
|
||||
"httpDestUrlErrorHttpsRequired": "SSL е необходимо за облачни инсталации",
|
||||
"httpDestUrlErrorInvalid": "Въведете валиден URL (напр. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Удостоверяване",
|
||||
"httpDestAuthDescription": "Изберете как заявленията ви се удостоверяват.",
|
||||
"httpDestAuthNoneTitle": "Без удостоверяване",
|
||||
"httpDestAuthNoneDescription": "Изпращане на заявки без заглавие за удостоверяване.",
|
||||
"httpDestAuthBearerTitle": "Bearer Токен",
|
||||
"httpDestAuthBearerDescription": "Добавя заглавие за удостоверяване Bearer <token> към всяка заявка.",
|
||||
"httpDestAuthBearerPlaceholder": "Вашият API ключ или токен",
|
||||
"httpDestAuthBasicTitle": "Основно удостоверяване",
|
||||
"httpDestAuthBasicDescription": "Добавя заглавие за удостоверяване Basic <credentials> към всяка заявка. Осигурете идентификационни данни като потребителско име:парола.",
|
||||
"httpDestAuthBasicPlaceholder": "потребителско име:парола",
|
||||
"httpDestAuthCustomTitle": "Персонализирано заглавие",
|
||||
"httpDestAuthCustomDescription": "Посочете персонализирано име и стойност на заглавието за удостоверяване (например X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Имя на заглавието (напр. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Стойност на заглавието",
|
||||
"httpDestCustomHeadersTitle": "Персонализирани заглавия за HTTP",
|
||||
"httpDestCustomHeadersDescription": "Добавяне на персонализирани заглавия към всяка изходяща заявка. Полезно за статични токени или персонални Content-Type. По подразбиране се изпраща Content-Type: application/json.",
|
||||
"httpDestNoHeadersConfigured": "Персонализирани заглавия не са конфигурирани. Кликнете \"Добавяне на заглавие\" да добавите такова.",
|
||||
"httpDestHeaderNamePlaceholder": "Име на заглавието",
|
||||
"httpDestHeaderValuePlaceholder": "Стойност на заглавието",
|
||||
"httpDestAddHeader": "Добавяне на заглавие",
|
||||
"httpDestBodyTemplateTitle": "Шаблон на персонализирано тяло",
|
||||
"httpDestBodyTemplateDescription": "Управлявайте структурата на JSON съобщението, изпратено до вашата крайна точка. Ако е деактивирано, по подразбиране се изпраща JSON обект за всяко събитие.",
|
||||
"httpDestEnableBodyTemplate": "Активиране на персонализиран шаблон на тяло",
|
||||
"httpDestBodyTemplateLabel": "Шаблон за тяло (JSON)",
|
||||
"httpDestBodyTemplateHint": "Използвайте шаблонни променливи за позоваване на полетата на събитията в съобщението си.",
|
||||
"httpDestPayloadFormatTitle": "Формат на полезния товар",
|
||||
"httpDestPayloadFormatDescription": "Как се сериализират събитията във всеки заявка.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON масив",
|
||||
"httpDestFormatJsonArrayDescription": "Една заявка на партида, тялото е JSON масив. Съвместим с повечето общи уеб куки и Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Една заявка на партида, тялото е ново линии отделени JSON — един обект на ред, няма външен масив. Изисквано от Splunk HEC, Elastic / OpenSearch и Grafana.",
|
||||
"httpDestFormatSingleTitle": "Едно събитие на заявка",
|
||||
"httpDestFormatSingleDescription": "Изпращат се отделни HTTP POST за всяко индивидуално събитие. Използвайте само за крайни точки, които не могат да обработват партиди.",
|
||||
"httpDestLogTypesTitle": "Видове логове",
|
||||
"httpDestLogTypesDescription": "Изберете кои видове журнални записи ще се предават към тази дестинация. Предаването ще се прави само за активирани видове журнални записи.",
|
||||
"httpDestAccessLogsTitle": "Логове за достъп",
|
||||
"httpDestAccessLogsDescription": "Опити за достъп до ресурс, включително удостоверени и отказани заявки.",
|
||||
"httpDestActionLogsTitle": "Логове на действия",
|
||||
"httpDestActionLogsDescription": "Административни действия, извършени от потребители в организацията.",
|
||||
"httpDestConnectionLogsTitle": "Логове на връзката",
|
||||
"httpDestConnectionLogsDescription": "Събития на свързване и прекъсване на сайта и тунела, включително свръзки и прекъсвания.",
|
||||
"httpDestRequestLogsTitle": "Заявки за логове",
|
||||
"httpDestRequestLogsDescription": "Регистри за HTTP заявките към проксирани ресурси, включително метод, път и код на отговор.",
|
||||
"httpDestSaveChanges": "Запази промените",
|
||||
"httpDestCreateDestination": "Създаване на дестинация",
|
||||
"httpDestUpdatedSuccess": "Дестинацията беше актуализирана успешно",
|
||||
"httpDestCreatedSuccess": "Дестинацията беше създадена успешно",
|
||||
"httpDestUpdateFailed": "Неуспешно актуализиране на дестинацията",
|
||||
"httpDestCreateFailed": "Неуспешно създаване на дестинацията"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Jste členem {count, plural, =0 {0 organizací} one {1 organizace} other {# organizací}}.",
|
||||
"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",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Děkujeme, že podporujete Pangolin jako {tier}!",
|
||||
"inviteErrorNotValid": "Je nám líto, ale vypadá to, že pozvánka, ke které se snažíte získat přístup, nebyla přijata nebo již není platná.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Vytvořit odkaz",
|
||||
"resourcesNotFound": "Nebyly nalezeny žádné zdroje",
|
||||
"resourceSearch": "Vyhledat zdroje",
|
||||
"machineSearch": "Vyhledávací stroje",
|
||||
"machinesSearch": "Hledat klienty stroje...",
|
||||
"machineNotFound": "Nebyly nalezeny žádné stroje",
|
||||
"userDeviceSearch": "Hledat uživatelská zařízení",
|
||||
"userDevicesSearch": "Hledat uživatelská zařízení...",
|
||||
"openMenu": "Otevřít nabídku",
|
||||
"resource": "Zdroj",
|
||||
"title": "Název",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy požadavky přes HTTPS pomocí plně kvalifikovaného názvu domény.",
|
||||
"resourceRaw": "Surový TCP/UDP zdroj",
|
||||
"resourceRawDescription": "Proxy požadavky přes nezpracovaný TCP/UDP pomocí čísla portu.",
|
||||
"resourceRawDescriptionCloud": "Proxy požadavky na syrové TCP/UDP pomocí čísla portu. Vyžaduje připojení stránek ke vzdálenému uzlu.",
|
||||
"resourceCreate": "Vytvořit zdroj",
|
||||
"resourceCreateDescription": "Postupujte podle níže uvedených kroků, abyste vytvořili a připojili nový zdroj",
|
||||
"resourceSeeAll": "Zobrazit všechny zdroje",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Vybrat protokol",
|
||||
"resourcePortNumber": "Číslo portu",
|
||||
"resourcePortNumberDescription": "Externí port k požadavkům proxy serveru.",
|
||||
"back": "Zpět",
|
||||
"cancel": "Zrušit",
|
||||
"resourceConfig": "Konfigurační snippety",
|
||||
"resourceConfigDescription": "Zkopírujte a vložte tyto konfigurační textové bloky pro nastavení TCP/UDP zdroje",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Došlo k chybě při odstraňování organizace.",
|
||||
"orgDeleted": "Organizace odstraněna",
|
||||
"orgDeletedMessage": "Organizace a její data byla smazána.",
|
||||
"deleteAccount": "Odstranit účet",
|
||||
"deleteAccountDescription": "Trvale smazat svůj účet, všechny organizace, které vlastníte, a všechna data těchto organizací. Tuto akci nelze vrátit zpět.",
|
||||
"deleteAccountButton": "Odstranit účet",
|
||||
"deleteAccountConfirmTitle": "Odstranit účet",
|
||||
"deleteAccountConfirmMessage": "Toto trvale vymaže váš účet, všechny organizace, které vlastníte, a všechna data v rámci těchto organizací. Tuto akci nelze vrátit zpět.",
|
||||
"deleteAccountConfirmString": "smazat účet",
|
||||
"deleteAccountSuccess": "Účet odstraněn",
|
||||
"deleteAccountSuccessMessage": "Váš účet byl odstraněn.",
|
||||
"deleteAccountError": "Nepodařilo se odstranit účet",
|
||||
"deleteAccountPreviewAccount": "Váš účet",
|
||||
"deleteAccountPreviewOrgs": "Organizace, které vlastníte (a všechny jejich údaje)",
|
||||
"orgMissing": "Chybí ID organizace",
|
||||
"orgMissingMessage": "Nelze obnovit pozvánku bez ID organizace.",
|
||||
"accessUsersManage": "Spravovat uživatele",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Odstranit klíč API",
|
||||
"apiKeysManage": "Správa API klíčů",
|
||||
"apiKeysDescription": "API klíče se používají k ověření s integračním API",
|
||||
"provisioningKeysTitle": "Zajišťovací klíč",
|
||||
"provisioningKeysManage": "Spravovat zajišťovací klíče",
|
||||
"provisioningKeysDescription": "Zajišťovací klíče slouží k ověření automatického poskytování služeb vaší organizaci.",
|
||||
"provisioningManage": "Zajištění",
|
||||
"provisioningDescription": "Spravovat klíče pro nastavení a zkontrolovat čekající stránky čekající na schválení.",
|
||||
"pendingSites": "Nevyřízené weby",
|
||||
"siteApproveSuccess": "Web byl úspěšně schválen",
|
||||
"siteApproveError": "Chyba při schvalování webu",
|
||||
"provisioningKeys": "Poskytovací klíče",
|
||||
"searchProvisioningKeys": "Hledat klíče k zajišťování...",
|
||||
"provisioningKeysAdd": "Generovat zajišťovací klíč",
|
||||
"provisioningKeysErrorDelete": "Chyba při odstraňování klíče pro úpravu",
|
||||
"provisioningKeysErrorDeleteMessage": "Chyba při odstraňování klíče pro úpravu",
|
||||
"provisioningKeysQuestionRemove": "Jste si jisti, že chcete odstranit tento konfigurační klíč z organizace?",
|
||||
"provisioningKeysMessageRemove": "Jakmile je klíč odstraněn, nelze již použít pro poskytování služeb.",
|
||||
"provisioningKeysDeleteConfirm": "Potvrdit odstranění zajišťovacího klíče",
|
||||
"provisioningKeysDelete": "Odstranit zajišťovací klíč",
|
||||
"provisioningKeysCreate": "Generovat zajišťovací klíč",
|
||||
"provisioningKeysCreateDescription": "Vygenerovat nový klíč pro organizaci",
|
||||
"provisioningKeysSeeAll": "Zobrazit všechny doplňovací klíče",
|
||||
"provisioningKeysSave": "Uložit konfigurační klíč",
|
||||
"provisioningKeysSaveDescription": "Toto můžete vidět pouze jednou. Zkopírujte ho na bezpečné místo.",
|
||||
"provisioningKeysErrorCreate": "Chyba při vytváření doplňovacího klíče",
|
||||
"provisioningKeysList": "Nový klíč pro poskytování informací",
|
||||
"provisioningKeysMaxBatchSize": "Maximální velikost dávky",
|
||||
"provisioningKeysUnlimitedBatchSize": "Neomezená velikost šarže (bez omezení)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Bez omezení",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Zadejte platnou maximální velikost šarže (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Platné do",
|
||||
"provisioningKeysValidUntilHint": "Ponechte prázdné, pokud vyprší platnost.",
|
||||
"provisioningKeysValidUntilInvalid": "Zadejte platné datum a čas.",
|
||||
"provisioningKeysNumUsed": "Časy použití",
|
||||
"provisioningKeysLastUsed": "Naposledy použito",
|
||||
"provisioningKeysNoExpiry": "Bez vypršení platnosti",
|
||||
"provisioningKeysNeverUsed": "Nikdy",
|
||||
"provisioningKeysEdit": "Upravit zajišťovací klíč",
|
||||
"provisioningKeysEditDescription": "Aktualizujte maximální velikost dávky a dobu vypršení platnosti tohoto klíče.",
|
||||
"provisioningKeysApproveNewSites": "Schválit nové stránky",
|
||||
"provisioningKeysApproveNewSitesDescription": "Automaticky schvalovat weby, které se registrují pomocí tohoto klíče.",
|
||||
"provisioningKeysUpdateError": "Chyba při aktualizaci klíče",
|
||||
"provisioningKeysUpdated": "Zajišťovací klíč byl aktualizován",
|
||||
"provisioningKeysUpdatedDescription": "Vaše změny byly uloženy.",
|
||||
"provisioningKeysBannerTitle": "Klíče pro poskytování webu",
|
||||
"provisioningKeysBannerDescription": "Vygenerujte konfigurační klíč a používejte jej pomocí nového konektoru k automatickému vytváření stránek při prvním startu – není třeba nastavovat samostatné přihlašovací údaje pro každý web.",
|
||||
"provisioningKeysBannerButtonText": "Zjistit více",
|
||||
"pendingSitesBannerTitle": "Nevyřízené weby",
|
||||
"pendingSitesBannerDescription": "Zde se zobrazují stránky, které se připojují pomocí doplňovacího klíče. Schválte každý web předtím, než bude aktivní, a získejte přístup k vašim zdrojům.",
|
||||
"pendingSitesBannerButtonText": "Zjistit více",
|
||||
"apiKeysSettings": "Nastavení {apiKeyName}",
|
||||
"userTitle": "Spravovat všechny uživatele",
|
||||
"userDescription": "Zobrazit a spravovat všechny uživatele v systému",
|
||||
@@ -322,7 +390,7 @@
|
||||
"userQuestionRemove": "Jste si jisti, že chcete trvale odstranit uživatele ze serveru?",
|
||||
"licenseKey": "Licenční klíč",
|
||||
"valid": "Valid",
|
||||
"numberOfSites": "Počet stránek",
|
||||
"numberOfSites": "Počet lokalit",
|
||||
"licenseKeySearch": "Hledat licenční klíče...",
|
||||
"licenseKeyAdd": "Přidat licenční klíč",
|
||||
"type": "Typ",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtrovat podle státu schválení",
|
||||
"approvalListEmpty": "Žádná schválení",
|
||||
"approvalState": "Země schválení",
|
||||
"approvalLoadMore": "Načíst více",
|
||||
"loadingApprovals": "Načítání schválení",
|
||||
"approve": "Schválit",
|
||||
"approved": "Schváleno",
|
||||
"denied": "Zamítnuto",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Uživatel uložen",
|
||||
"userSavedDescription": "Uživatel byl aktualizován.",
|
||||
"autoProvisioned": "Automaticky poskytnuto",
|
||||
"autoProvisionSettings": "Automatická nastavení",
|
||||
"autoProvisionedDescription": "Povolit tomuto uživateli automaticky spravovat poskytovatel identity",
|
||||
"accessControlsDescription": "Spravovat co může tento uživatel přistupovat a dělat v organizaci",
|
||||
"accessControlsSubmit": "Uložit kontroly přístupu",
|
||||
"singleRolePerUserPlanNotice": "Váš plán podporuje pouze jednu roli na uživatele.",
|
||||
"singleRolePerUserEditionNotice": "Tato verze podporuje pouze jednu roli na uživatele.",
|
||||
"roles": "Role",
|
||||
"accessUsersRoles": "Spravovat uživatele a role",
|
||||
"accessUsersRolesDescription": "Pozvěte uživatele a přidejte je do rolí pro správu přístupu k organizaci",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Nepodařilo se přepnout zdroj",
|
||||
"resourcesErrorUpdateDescription": "Došlo k chybě při aktualizaci zdroje",
|
||||
"access": "Přístup",
|
||||
"accessControl": "Kontrola přístupu",
|
||||
"shareLink": "{resource} Sdílet odkaz",
|
||||
"resourceSelect": "Vyberte zdroj",
|
||||
"shareLinks": "Sdílet odkazy",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Role odstraněna",
|
||||
"accessRoleRemovedDescription": "Role byla úspěšně odstraněna.",
|
||||
"accessRoleRequiredRemove": "Před odstraněním této role vyberte novou roli, do které chcete převést existující členy.",
|
||||
"network": "Síť",
|
||||
"manage": "Spravovat",
|
||||
"sitesNotFound": "Nebyly nalezeny žádné stránky.",
|
||||
"pangolinServerAdmin": "Správce serveru - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Zvýšit počet stránek",
|
||||
"idpManage": "Spravovat poskytovatele identity",
|
||||
"idpManageDescription": "Zobrazit a spravovat poskytovatele identity v systému",
|
||||
"idpGlobalModeBanner": "Poskytovatelé identity (IdP) pro každou organizaci jsou na tomto serveru zakázáni. Používá globální IdP (sdílené napříč všemi organizacemi). Správa globálních IdP v <adminPanelLink>admin panelu</adminPanelLink>. Chcete-li povolit IdP pro každou organizaci, upravte konfiguraci serveru a nastavte IdP režim na org. <configDocsLink>Viz dokumentace</configDocsLink>. Pokud chcete pokračovat v používání globálních IdP a zmizet z nastavení organizace, explicitně nastavte režim na globální v konfiguraci.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Poskytovatelé identity (IdP) pro každou organizaci jsou na tomto serveru zakázáni. Používá globální IdP (sdílené napříč všemi organizacemi). Spravujte globální IdP v <adminPanelLink>admin panelu</adminPanelLink>. Chcete-li použít poskytovatele identity pro každou organizaci, musíte přejít na Enterprise vydání.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Poskytovatelé identity (IdP) pro každou organizaci jsou na tomto serveru zakázáni. Používá globální IdP (sdílené napříč všemi organizacemi). Správa globálních IdP v <adminPanelLink>admin panelu</adminPanelLink>. Chcete-li použít poskytovatele identity pro každou organizaci, je vyžadována Enterprise licence.",
|
||||
"idpDeletedDescription": "Poskytovatel identity byl úspěšně odstraněn",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Jste si jisti, že chcete trvale odstranit poskytovatele identity?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Setup - Pangolin",
|
||||
"orgNameRequired": "Je vyžadován název organizace",
|
||||
"orgIdRequired": "Je vyžadováno ID organizace",
|
||||
"orgIdMaxLength": "ID organizace musí mít nejvýše 32 znaků",
|
||||
"orgErrorCreate": "Při vytváření org došlo k chybě",
|
||||
"pageNotFound": "Stránka nenalezena",
|
||||
"pageNotFoundDescription": "Jejda! Stránka, kterou hledáte, neexistuje.",
|
||||
"overview": "Přehled",
|
||||
"home": "Domů",
|
||||
"accessControl": "Kontrola přístupu",
|
||||
"settings": "Nastavení",
|
||||
"usersAll": "Všichni uživatelé",
|
||||
"license": "Licence",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Získat uživatele",
|
||||
"actionGetOrgUser": "Získat uživatele organizace",
|
||||
"actionListOrgDomains": "Seznam domén organizace",
|
||||
"actionGetDomain": "Získat doménu",
|
||||
"actionCreateOrgDomain": "Vytvořit doménu",
|
||||
"actionUpdateOrgDomain": "Aktualizovat doménu",
|
||||
"actionDeleteOrgDomain": "Odstranit doménu",
|
||||
"actionGetDNSRecords": "Získat záznamy DNS",
|
||||
"actionRestartOrgDomain": "Restartovat doménu",
|
||||
"actionCreateSite": "Vytvořit lokalitu",
|
||||
"actionDeleteSite": "Odstranění lokality",
|
||||
"actionGetSite": "Získat web",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Zadejte nastavovací token z konzole serveru.",
|
||||
"setupTokenRequired": "Je vyžadován token nastavení",
|
||||
"actionUpdateSite": "Aktualizovat stránku",
|
||||
"actionResetSiteBandwidth": "Resetovat šířku pásma organizace",
|
||||
"actionListSiteRoles": "Seznam povolených rolí webu",
|
||||
"actionCreateResource": "Vytvořit zdroj",
|
||||
"actionDeleteResource": "Odstranit dokument",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Odstranit uživatele",
|
||||
"actionListUsers": "Seznam uživatelů",
|
||||
"actionAddUserRole": "Přidat uživatelskou roli",
|
||||
"actionSetUserOrgRoles": "Nastavit uživatelské role",
|
||||
"actionGenerateAccessToken": "Generovat přístupový token",
|
||||
"actionDeleteAccessToken": "Odstranit přístupový token",
|
||||
"actionListAccessTokens": "Seznam přístupových tokenů",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Zobrazit logy",
|
||||
"noneSelected": "Není vybráno",
|
||||
"orgNotFound2": "Nebyly nalezeny žádné organizace.",
|
||||
"searchProgress": "Hledat...",
|
||||
"searchPlaceholder": "Hledat...",
|
||||
"emptySearchOptions": "Nebyly nalezeny žádné možnosti",
|
||||
"create": "Vytvořit",
|
||||
"orgs": "Organizace",
|
||||
"loginError": "Došlo k neočekávané chybě. Zkuste to prosím znovu.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Soukromé",
|
||||
"sidebarAccessControl": "Kontrola přístupu",
|
||||
"sidebarLogsAndAnalytics": "Logy & Analytika",
|
||||
"sidebarTeam": "Tým",
|
||||
"sidebarUsers": "Uživatelé",
|
||||
"sidebarAdmin": "Admin",
|
||||
"sidebarInvitations": "Pozvánky",
|
||||
"sidebarRoles": "Role",
|
||||
"sidebarShareableLinks": "Odkazy",
|
||||
"sidebarApiKeys": "API klíče",
|
||||
"sidebarProvisioning": "Zajištění",
|
||||
"sidebarSettings": "Nastavení",
|
||||
"sidebarAllUsers": "Všichni uživatelé",
|
||||
"sidebarIdentityProviders": "Poskytovatelé identity",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Log & Analytics",
|
||||
"sidebarBluePrints": "Plány",
|
||||
"sidebarOrganization": "Organizace",
|
||||
"sidebarManagement": "Správa",
|
||||
"sidebarBillingAndLicenses": "Fakturace a licence",
|
||||
"sidebarLogsAnalytics": "Analytici",
|
||||
"blueprints": "Plány",
|
||||
"blueprintsDescription": "Použít deklarativní konfigurace a zobrazit předchozí běhy",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Parsed content (Pouze pro čtení)",
|
||||
"enableDockerSocket": "Povolit Docker plán",
|
||||
"enableDockerSocketDescription": "Povolte seškrábání štítků na Docker Socket pro popisky plánů. Nová cesta musí být k dispozici.",
|
||||
"enableDockerSocketLink": "Zjistit více",
|
||||
"viewDockerContainers": "Zobrazit kontejnery Dockeru",
|
||||
"containersIn": "Kontejnery v {siteName}",
|
||||
"selectContainerDescription": "Vyberte jakýkoli kontejner pro použití jako název hostitele pro tento cíl. Klikněte na port pro použití portu.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Jmenný prostor: {namespace}",
|
||||
"domainPickerShowMore": "Zobrazit více",
|
||||
"regionSelectorTitle": "Vybrat region",
|
||||
"domainPickerRemoteExitNodeWarning": "Poskytnuté domény nejsou podporovány, když se stránky připojují k vzdáleným výstupním uzlům. Pro dostupné zdroje na vzdálených uzlech použijte vlastní doménu.",
|
||||
"regionSelectorInfo": "Výběr regionu nám pomáhá poskytovat lepší výkon pro vaši polohu. Nemusíte být ve stejném regionu jako váš server.",
|
||||
"regionSelectorPlaceholder": "Vyberte region",
|
||||
"regionSelectorComingSoon": "Již brzy",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Přehled omezení použití",
|
||||
"billingMonitorUsage": "Sledujte vaše využití pomocí nastavených limitů. Pokud potřebujete zvýšit limity, kontaktujte nás prosím support@pangolin.net.",
|
||||
"billingDataUsage": "Využití dat",
|
||||
"billingOnlineTime": "Stránka online čas",
|
||||
"billingUsers": "Aktivní uživatelé",
|
||||
"billingDomains": "Aktivní domény",
|
||||
"billingRemoteExitNodes": "Aktivní Samostatně hostované uzly",
|
||||
"billingSites": "Stránky",
|
||||
"billingUsers": "Uživatelé",
|
||||
"billingDomains": "Domény",
|
||||
"billingOrganizations": "Tělo",
|
||||
"billingRemoteExitNodes": "Vzdálené uzly",
|
||||
"billingNoLimitConfigured": "Žádný limit nenastaven",
|
||||
"billingEstimatedPeriod": "Odhadované období fakturace",
|
||||
"billingIncludedUsage": "Zahrnuto využití",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Nepodařilo se získat URL portálu",
|
||||
"billingPortalError": "Chyba portálu",
|
||||
"billingDataUsageInfo": "Pokud jste připojeni k cloudu, jsou vám účtována všechna data přenášená prostřednictvím zabezpečených tunelů. To zahrnuje příchozí i odchozí provoz na všech vašich stránkách. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezmenšíte jeho používání. Data nejsou nabírána při používání uzlů.",
|
||||
"billingOnlineTimeInfo": "Platíte na základě toho, jak dlouho budou vaše stránky připojeny k cloudu. Například, 44,640 minut se rovná jedné stránce 24/7 po celý měsíc. Jakmile dosáhnete svého limitu, vaše stránky se odpojí, dokud neaktualizujete svůj tarif nebo nezkrátíte jeho používání. Čas není vybírán při používání uzlů.",
|
||||
"billingUsersInfo": "Každý uživatel v organizaci je účtován denně. Fakturace je počítána na základě počtu aktivních uživatelských účtů na Vašem org.",
|
||||
"billingDomainInfo": "Objednávka je účtována za každou doménu v organizaci. Fakturace je počítána denně na základě počtu aktivních doménových účtů na Vašem org.",
|
||||
"billingRemoteExitNodesInfo": "Za každý spravovaný uzel v organizaci se vám účtuje denně. Fakturace je vypočítávána na základě počtu aktivních spravovaných uzlů ve vašem org.",
|
||||
"billingSInfo": "Kolik stránek můžete použít",
|
||||
"billingUsersInfo": "Kolik uživatelů můžete použít",
|
||||
"billingDomainInfo": "Kolik domén můžete použít",
|
||||
"billingRemoteExitNodesInfo": "Kolik vzdálených uzlů můžete použít",
|
||||
"billingLicenseKeys": "Licenční klíče",
|
||||
"billingLicenseKeysDescription": "Spravovat předplatné licenčního klíče",
|
||||
"billingLicenseSubscription": "Předplatné licence",
|
||||
"billingInactive": "Neaktivní",
|
||||
"billingLicenseItem": "Položka licence",
|
||||
"billingQuantity": "Množství",
|
||||
"billingTotal": "celkem",
|
||||
"billingModifyLicenses": "Upravit předplatné licence",
|
||||
"domainNotFound": "Doména nenalezena",
|
||||
"domainNotFoundDescription": "Tento dokument je zakázán, protože doména již neexistuje náš systém. Nastavte prosím novou doménu pro tento dokument.",
|
||||
"failed": "Selhalo",
|
||||
"createNewOrgDescription": "Vytvořit novou organizaci",
|
||||
"organization": "Organizace",
|
||||
"primary": "Primární",
|
||||
"port": "Přístav",
|
||||
"securityKeyManage": "Správa bezpečnostních klíčů",
|
||||
"securityKeyDescription": "Přidat nebo odebrat bezpečnostní klíče pro bezheslou autentizaci",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Pro neHTTP zdroje je vyžadováno číslo portu",
|
||||
"resourcePortNotAllowed": "Číslo portu by nemělo být nastaveno pro HTTP zdroje",
|
||||
"billingPricingCalculatorLink": "Cenová kalkulačka",
|
||||
"billingYourPlan": "Váš plán",
|
||||
"billingViewOrModifyPlan": "Zobrazit nebo upravit aktuální tarif",
|
||||
"billingViewPlanDetails": "Zobrazit detaily plánu",
|
||||
"billingUsageAndLimits": "Limity a použití",
|
||||
"billingViewUsageAndLimits": "Zobrazit limity vašeho plánu a aktuální využití",
|
||||
"billingCurrentUsage": "Aktuální využití",
|
||||
"billingMaximumLimits": "Maximální limity",
|
||||
"billingRemoteNodes": "Vzdálené uzly",
|
||||
"billingUnlimited": "Bez omezení",
|
||||
"billingPaidLicenseKeys": "Placené licenční klíče",
|
||||
"billingManageLicenseSubscription": "Spravujte své předplatné za placené samohostované licenční klíče",
|
||||
"billingCurrentKeys": "Aktuální klíče",
|
||||
"billingModifyCurrentPlan": "Upravit aktuální tarif",
|
||||
"billingConfirmUpgrade": "Potvrdit aktualizaci",
|
||||
"billingConfirmDowngrade": "Potvrdit downgrade",
|
||||
"billingConfirmUpgradeDescription": "Chystáte se povýšit svůj tarif. Přečtěte si nové limity a ceny.",
|
||||
"billingConfirmDowngradeDescription": "Chystáte se snížit svůj tarif. Přečtěte si nové limity a ceny níže.",
|
||||
"billingPlanIncludes": "Plán zahrnuje",
|
||||
"billingProcessing": "Zpracovávám...",
|
||||
"billingConfirmUpgradeButton": "Potvrdit aktualizaci",
|
||||
"billingConfirmDowngradeButton": "Potvrdit downgrade",
|
||||
"billingLimitViolationWarning": "Využití překročilo limity nového plánu",
|
||||
"billingLimitViolationDescription": "Vaše současné využití překračuje meze tohoto plánu. Po ponížení budou všechny akce zakázány, dokud nesnížíte využití v rámci nových limitů. Přečtěte si prosím níže uvedené funkce překračující limity. Limity při porušení:",
|
||||
"billingFeatureLossWarning": "Upozornění na dostupnost funkce",
|
||||
"billingFeatureLossDescription": "Po pomenutí budou funkce v novém plánu automaticky zakázány. Některá nastavení a konfigurace mohou být ztraceny. Zkontrolujte cenovou matrici, abyste pochopili, které funkce již nebudou k dispozici.",
|
||||
"billingUsageExceedsLimit": "Aktuální využití ({current}) překračuje limit ({limit})",
|
||||
"billingPastDueTitle": "Poslední splatnost platby",
|
||||
"billingPastDueDescription": "Vaše platba je již splatná. Chcete-li pokračovat v používání aktuálních tarifů, aktualizujte prosím způsob platby. Pokud nebude vyřešeno, Vaše předplatné bude zrušeno a budete vráceno na úroveň zdarma.",
|
||||
"billingUnpaidTitle": "Předplatné nezaplaceno",
|
||||
"billingUnpaidDescription": "Vaše předplatné není zaplaceno a byli jste vráceni do bezplatné úrovně. Aktualizujte prosím svou platební metodu pro obnovení předplatného.",
|
||||
"billingIncompleteTitle": "Platba nedokončena",
|
||||
"billingIncompleteDescription": "Vaše platba je neúplná. Pro aktivaci předplatného prosím dokončete platební proces.",
|
||||
"billingIncompleteExpiredTitle": "Platba vypršela",
|
||||
"billingIncompleteExpiredDescription": "Vaše platba nebyla nikdy dokončena a vypršela. Byli jste vráceni na úroveň zdarma. Prosím, přihlašte se znovu pro obnovení přístupu k placeným funkcím.",
|
||||
"billingManageSubscription": "Spravujte své předplatné",
|
||||
"billingResolvePaymentIssue": "Vyřešte prosím problém s platbou před upgradem nebo upgradem",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Souhlasím s",
|
||||
"termsOfService": "podmínky služby",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Čas je v sekundách",
|
||||
"requireDeviceApproval": "Vyžadovat schválení zařízení",
|
||||
"requireDeviceApprovalDescription": "Uživatelé s touto rolí potřebují nová zařízení schválená správcem, než se mohou připojit a přistupovat ke zdrojům.",
|
||||
"sshAccess": "SSH přístup",
|
||||
"roleAllowSsh": "Povolit SSH",
|
||||
"roleAllowSshAllow": "Povolit",
|
||||
"roleAllowSshDisallow": "Zakázat",
|
||||
"roleAllowSshDescription": "Povolit uživatelům s touto rolí připojení k zdrojům přes SSH. Je-li zakázáno, role nemůže používat přístup SSH.",
|
||||
"sshSudoMode": "Súdánský přístup",
|
||||
"sshSudoModeNone": "Nic",
|
||||
"sshSudoModeNoneDescription": "Uživatel nemůže spouštět příkazy se sudo.",
|
||||
"sshSudoModeFull": "Úplný Súdán",
|
||||
"sshSudoModeFullDescription": "Uživatel může spustit libovolný příkaz se sudo.",
|
||||
"sshSudoModeCommands": "Příkazy",
|
||||
"sshSudoModeCommandsDescription": "Uživatel může spustit pouze zadané příkazy s sudo.",
|
||||
"sshSudo": "Povolit sudo",
|
||||
"sshSudoCommands": "Sudo příkazy",
|
||||
"sshSudoCommandsDescription": "Čárkami oddělený seznam příkazů, které může uživatel spouštět s sudo.",
|
||||
"sshCreateHomeDir": "Vytvořit domovský adresář",
|
||||
"sshUnixGroups": "Unixové skupiny",
|
||||
"sshUnixGroupsDescription": "Čárkou oddělené skupiny Unix přidají uživatele do cílového hostitele.",
|
||||
"retryAttempts": "Opakovat pokusy",
|
||||
"expectedResponseCodes": "Očekávané kódy odezvy",
|
||||
"expectedResponseCodesDescription": "HTTP kód stavu, který označuje zdravý stav. Ponecháte-li prázdné, 200-300 je považováno za zdravé.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Ukončit uzel",
|
||||
"country": "L 343, 22.12.2009, s. 1).",
|
||||
"rulesMatchCountry": "Aktuálně založené na zdrojové IP adrese",
|
||||
"region": "Oblasti",
|
||||
"selectRegion": "Vyberte region",
|
||||
"searchRegions": "Hledat regiony...",
|
||||
"noRegionFound": "Nebyl nalezen žádný region.",
|
||||
"rulesMatchRegion": "Vyberte regionální seskupení zemí",
|
||||
"rulesErrorInvalidRegion": "Neplatný region",
|
||||
"rulesErrorInvalidRegionDescription": "Vyberte prosím platný region.",
|
||||
"regionAfrica": "Afrika",
|
||||
"regionNorthernAfrica": "Severní Afrika",
|
||||
"regionEasternAfrica": "Východní Afrika",
|
||||
"regionMiddleAfrica": "Střední Afrika",
|
||||
"regionSouthernAfrica": "Jižní Afrika",
|
||||
"regionWesternAfrica": "Západní Afrika",
|
||||
"regionAmericas": "Ameriky",
|
||||
"regionCaribbean": "Karibské",
|
||||
"regionCentralAmerica": "Střední Amerika",
|
||||
"regionSouthAmerica": "Jižní Amerika",
|
||||
"regionNorthernAmerica": "Severní Amerika",
|
||||
"regionAsia": "Asie",
|
||||
"regionCentralAsia": "Střední Asie",
|
||||
"regionEasternAsia": "Východní Asie",
|
||||
"regionSouthEasternAsia": "jihovýchodní Asie",
|
||||
"regionSouthernAsia": "Jižní Asie",
|
||||
"regionWesternAsia": "Západní Asie",
|
||||
"regionEurope": "L 347, 20.12.2013, s. 965).",
|
||||
"regionEasternEurope": "Východní Evropa",
|
||||
"regionNorthernEurope": "Severní Evropa",
|
||||
"regionSouthernEurope": "Jižní Evropa",
|
||||
"regionWesternEurope": "Západní Evropa",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Austrálie a Nový Zéland",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Spravované vlastní hostování",
|
||||
"description": "Spolehlivější a nízko udržovaný Pangolinův server s dalšími zvony a bičkami",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Neplatná hodnota",
|
||||
"idpTypeLabel": "Typ poskytovatele identity",
|
||||
"roleMappingExpressionPlaceholder": "např. obsahuje(skupiny, 'admin') && 'Admin' || 'Member'",
|
||||
"roleMappingModeFixedRoles": "Pevné role",
|
||||
"roleMappingModeMappingBuilder": "Tvorba mapování",
|
||||
"roleMappingModeRawExpression": "Surový výraz",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Vyberte jednu nebo více rolí",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Napište názvy rolí (shoda podle organizace)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Přiřadit stejnou roli nastavenou každému uživateli automatického poskytování.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Pro výchozí zásady zadejte názvy rolí, které existují v každé organizaci, kde jsou uživatelé poskytováni. Jména musí přesně odpovídat.",
|
||||
"roleMappingClaimPath": "Cesta k žádosti",
|
||||
"roleMappingClaimPathPlaceholder": "skupiny",
|
||||
"roleMappingClaimPathDescription": "Cesta k užitečnému zatížení tokenu, která obsahuje zdrojové hodnoty (například skupiny).",
|
||||
"roleMappingMatchValue": "Hodnota zápasu",
|
||||
"roleMappingAssignRoles": "Přiřadit role",
|
||||
"roleMappingAddMappingRule": "Přidat pravidlo pro mapování",
|
||||
"roleMappingRawExpressionResultDescription": "Výraz se musí vyhodnotit do pole řetězce nebo řetězce.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Výraz musí být vyhodnocen na řetězec (jediný název role).",
|
||||
"roleMappingMatchValuePlaceholder": "Hodnota zápasu (například: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Napište názvy rolí (exact per org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Názvy rolí musí odpovídat roli v každé cílové organizaci.",
|
||||
"roleMappingRemoveRule": "Odstranit",
|
||||
"idpGoogleConfiguration": "Konfigurace Google",
|
||||
"idpGoogleConfigurationDescription": "Konfigurace přihlašovacích údajů Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Jste si jisti, že chcete odstranit branding autentizačních stránek?",
|
||||
"authPageBrandingDeleteConfirm": "Potvrzení odstranění brandingu",
|
||||
"brandingLogoURL": "URL loga",
|
||||
"brandingLogoURLOrPath": "URL nebo cesta k logu",
|
||||
"brandingLogoPathDescription": "Zadejte URL nebo místní cestu.",
|
||||
"brandingLogoURLDescription": "Zadejte veřejně přístupnou adresu URL vašeho loga.",
|
||||
"brandingPrimaryColor": "Primární barva",
|
||||
"brandingLogoWidth": "Šířka (px)",
|
||||
"brandingLogoHeight": "Výška (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Zpět ke standardnímu přihlášení",
|
||||
"orgAuthNoAccount": "Nemáte účet?",
|
||||
"subscriptionRequiredToUse": "Pro použití této funkce je vyžadováno předplatné.",
|
||||
"mustUpgradeToUse": "Pro použití této funkce musíte aktualizovat své předplatné.",
|
||||
"subscriptionRequiredTierToUse": "Tato funkce vyžaduje <tierLink>{tier}</tierLink> nebo vyšší.",
|
||||
"upgradeToTierToUse": "Pro použití této funkce upgradujte na <tierLink>{tier}</tierLink> nebo vyšší.",
|
||||
"subscriptionTierTier1": "Domů",
|
||||
"subscriptionTierTier2": "Tým",
|
||||
"subscriptionTierTier3": "Podniky",
|
||||
"subscriptionTierEnterprise": "Podniky",
|
||||
"idpDisabled": "Poskytovatelé identit jsou zakázáni.",
|
||||
"orgAuthPageDisabled": "Ověřovací stránka organizace je zakázána.",
|
||||
"domainRestartedDescription": "Ověření domény bylo úspěšně restartováno",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Získat licenci",
|
||||
"description": "Vyberte si plán a řekněte nám, jak plánujete používat Pangolin.",
|
||||
"chooseTier": "Vyberte si svůj plán",
|
||||
"viewPricingLink": "Zobrazit ceny, funkce a limity",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Počáteční",
|
||||
"description": "Firemní funkce, 25 uživatelů, 25 stránek a komunitní podpory."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Měřítko",
|
||||
"description": "Podnikové funkce, 50 uživatelů, 50 míst a prioritní podpory."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Pouze osobní použití (bezplatná licence – bez platby)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Pokračovat do pokladny"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Chyba při objednávce",
|
||||
"description": "Nelze spustit objednávku. Zkuste to prosím znovu."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Priorita",
|
||||
"priorityDescription": "Vyšší priorita je vyhodnocena jako první. Priorita = 100 znamená automatické řazení (rozhodnutí systému). Pro vynucení manuální priority použijte jiné číslo.",
|
||||
"instanceName": "Název instance",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Jak dlouho uchovávat přístupové záznamy",
|
||||
"logRetentionActionLabel": "Uchovávání protokolu akcí",
|
||||
"logRetentionActionDescription": "Jak dlouho uchovávat záznamy akcí",
|
||||
"logRetentionConnectionLabel": "Uchovávání protokolu připojení",
|
||||
"logRetentionConnectionDescription": "Jak dlouho uchovávat protokoly připojení",
|
||||
"logRetentionDisabled": "Zakázáno",
|
||||
"logRetention3Days": "3 dny",
|
||||
"logRetention7Days": "7 dní",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Konec následujícího roku",
|
||||
"actionLogsDescription": "Zobrazit historii akcí provedených v této organizaci",
|
||||
"accessLogsDescription": "Zobrazit žádosti o ověření přístupu pro zdroje v této organizaci",
|
||||
"licenseRequiredToUse": "Pro použití této funkce je vyžadována licence pro podnikání.",
|
||||
"connectionLogs": "Protokoly připojení",
|
||||
"connectionLogsDescription": "Zobrazit protokoly připojení pro tunely v této organizaci",
|
||||
"sidebarLogsConnection": "Protokoly připojení",
|
||||
"sidebarLogsStreaming": "Streamování",
|
||||
"sourceAddress": "Zdrojová adresa",
|
||||
"destinationAddress": "Cílová adresa",
|
||||
"duration": "Doba trvání",
|
||||
"licenseRequiredToUse": "Pro použití této funkce je vyžadována licence <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> nebo <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> . <bookADemoLink>Zarezervujte si demo nebo POC zkušební verzi</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> je vyžadována pro použití této funkce. Tato funkce je také k dispozici v <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Rezervujte si demo nebo POC zkušební verzi</bookADemoLink>.",
|
||||
"certResolver": "Oddělovač certifikátů",
|
||||
"certResolverDescription": "Vyberte řešitele certifikátů pro tento dokument.",
|
||||
"selectCertResolver": "Vyberte řešič certifikátů",
|
||||
@@ -2267,7 +2521,7 @@
|
||||
"terms": "Výrazy",
|
||||
"privacy": "Soukromí",
|
||||
"security": "Zabezpečení",
|
||||
"docs": "Dokumenty",
|
||||
"docs": "Dokumentace",
|
||||
"deviceActivation": "Aktivace zařízení",
|
||||
"deviceCodeInvalidFormat": "Kód musí být 9 znaků (např. A1AJ-N5JD)",
|
||||
"deviceCodeInvalidOrExpired": "Neplatný nebo prošlý kód",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Řízení přístupu",
|
||||
"editInternalResourceDialogAccessControlDescription": "Kontrolujte, které role, uživatelé a klienti mohou přistupovat k tomuto prostředku, když jsou připojeni. Admini mají vždy přístup.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Rozsah portů musí být \"*\" pro všechny porty, nebo seznam portů a rozsahů oddělených čárkou (např. \"80,443,8000-9000\"). Porty musí být mezi 1 a 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Auth Démon umístění",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Zvolte, kde běží SSH autentizační démon: na stránce (Newt) nebo na vzdáleném serveru.",
|
||||
"internalResourceAuthDaemonDescription": "SSH autentizační daemon zpracovává podpis SSH klíče a PAM autentizaci tohoto zdroje. Vyberte si, zda běží na webu (Newt) nebo na samostatném vzdáleném serveru. Více informací najdete v <docsLink>dokumentaci</docsLink>.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Vybrat strategii",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Poloha",
|
||||
"internalResourceAuthDaemonSite": "Na stránce",
|
||||
"internalResourceAuthDaemonSiteDescription": "Auth daemon běží na webu (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Vzdálený server",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Auth daemon běží na hostitele, který není web.",
|
||||
"internalResourceAuthDaemonPort": "Daemon port (volitelné)",
|
||||
"orgAuthWhatsThis": "Kde najdu ID mé organizace?",
|
||||
"learnMore": "Zjistit více",
|
||||
"backToHome": "Zpět na domovskou stránku",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Firewall povolen",
|
||||
"autoUpdatesEnabled": "Automatické aktualizace povoleny",
|
||||
"tpmAvailable": "TPM k dispozici",
|
||||
"windowsAntivirusEnabled": "Antivirus povolen",
|
||||
"macosSipEnabled": "Ochrana systémové integrity (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Režim neviditelnosti firewallu",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Povolit schválení zařízení",
|
||||
"approvalsEmptyStateStep2Description": "Upravte roli a povolte možnost 'Vyžadovat schválení zařízení'. Uživatelé s touto rolí budou potřebovat schválení pro nová zařízení správce.",
|
||||
"approvalsEmptyStatePreviewDescription": "Náhled: Pokud je povoleno, čekající na zařízení se zde zobrazí žádosti o recenzi",
|
||||
"approvalsEmptyStateButtonText": "Spravovat role"
|
||||
"approvalsEmptyStateButtonText": "Spravovat role",
|
||||
"domainErrorTitle": "Máme problém s ověřením tvé domény",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Nastavte pravidla mapování rolí a organizace na kartě <policiesTabLink>Automatická úprava nastavení</policiesTabLink>.",
|
||||
"streamingTitle": "Streamování událostí",
|
||||
"streamingDescription": "Streamujte události z vaší organizace do externích destinací v reálném čase.",
|
||||
"streamingUnnamedDestination": "Nepojmenovaný cíl",
|
||||
"streamingNoUrlConfigured": "Není nakonfigurována žádná URL",
|
||||
"streamingAddDestination": "Přidat cíl",
|
||||
"streamingHttpWebhookTitle": "HTTP webový háček",
|
||||
"streamingHttpWebhookDescription": "Odeslat události na libovolný HTTP koncový bod s pružnou autentizací a šablonou.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Streamujte události do úložiště, které je kompatibilní se S3. Brzy přijde.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Přeposlat události přímo do vašeho účtu Datadog účtu. Brzy přijde.",
|
||||
"streamingTypePickerDescription": "Vyberte cílový typ pro začátek.",
|
||||
"streamingFailedToLoad": "Nepodařilo se načíst destinace",
|
||||
"streamingUnexpectedError": "Došlo k neočekávané chybě.",
|
||||
"streamingFailedToUpdate": "Nepodařilo se aktualizovat cíl",
|
||||
"streamingDeletedSuccess": "Cíl byl úspěšně odstraněn",
|
||||
"streamingFailedToDelete": "Nepodařilo se odstranit cíl",
|
||||
"streamingDeleteTitle": "Odstranit cíl",
|
||||
"streamingDeleteButtonText": "Odstranit cíl",
|
||||
"streamingDeleteDialogAreYouSure": "Jste si jisti, že chcete odstranit",
|
||||
"streamingDeleteDialogThisDestination": "tato destinace",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Všechny konfigurace budou trvale odstraněny.",
|
||||
"httpDestEditTitle": "Upravit cíl",
|
||||
"httpDestAddTitle": "Přidat cíl HTTP",
|
||||
"httpDestEditDescription": "Aktualizovat konfiguraci pro tuto destinaci HTTP události",
|
||||
"httpDestAddDescription": "Konfigurace nového koncového bodu HTTP pro příjem událostí vaší organizace.",
|
||||
"httpDestTabSettings": "Nastavení",
|
||||
"httpDestTabHeaders": "Záhlaví",
|
||||
"httpDestTabBody": "Tělo",
|
||||
"httpDestTabLogs": "Logy",
|
||||
"httpDestNamePlaceholder": "Moje HTTP cíl",
|
||||
"httpDestUrlLabel": "Cílová adresa URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL musí používat http nebo https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS je vyžadován při nasazení do cloudu",
|
||||
"httpDestUrlErrorInvalid": "Zadejte platnou URL (např. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Autentifikace",
|
||||
"httpDestAuthDescription": "Zvolte, jak jsou požadavky na tvůj koncový bod ověřeny.",
|
||||
"httpDestAuthNoneTitle": "Žádné ověření",
|
||||
"httpDestAuthNoneDescription": "Odešle žádosti bez záhlaví autorizace.",
|
||||
"httpDestAuthBearerTitle": "Token na doručitele",
|
||||
"httpDestAuthBearerDescription": "Přidá autorizaci: Hlavička Bearer <token> ke každému požadavku.",
|
||||
"httpDestAuthBearerPlaceholder": "Váš API klíč nebo token",
|
||||
"httpDestAuthBasicTitle": "Základní ověření",
|
||||
"httpDestAuthBasicDescription": "Přidá autorizaci: Základní <credentials> hlavička. Poskytněte přihlašovací údaje jako uživatelské jméno:password.",
|
||||
"httpDestAuthBasicPlaceholder": "uživatelské jméno:heslo",
|
||||
"httpDestAuthCustomTitle": "Vlastní záhlaví",
|
||||
"httpDestAuthCustomDescription": "Zadejte název a hodnotu vlastního HTTP hlavičky pro ověření (např. X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Název záhlaví (např. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Hodnota záhlaví",
|
||||
"httpDestCustomHeadersTitle": "Vlastní HTTP hlavičky",
|
||||
"httpDestCustomHeadersDescription": "Přidat vlastní hlavičky ke každému odchozímu požadavku. Užitečné pro statické tokeny nebo vlastní Typ obsahu. Ve výchozím nastavení je typ obsahu: application/json.",
|
||||
"httpDestNoHeadersConfigured": "Nejsou nakonfigurovány žádné vlastní záhlaví. Pro přidání klikněte na \"Přidat záhlaví\".",
|
||||
"httpDestHeaderNamePlaceholder": "Název záhlaví",
|
||||
"httpDestHeaderValuePlaceholder": "Hodnota",
|
||||
"httpDestAddHeader": "Přidat záhlaví",
|
||||
"httpDestBodyTemplateTitle": "Vlastní šablona těla",
|
||||
"httpDestBodyTemplateDescription": "Ovládá strukturu užitečného zatížení JSON odeslanou na váš koncový bod. Pokud je vypnuto, je pro každou událost zaslán výchozí objekt JSON.",
|
||||
"httpDestEnableBodyTemplate": "Povolit vlastní šablonu těla",
|
||||
"httpDestBodyTemplateLabel": "Šablona těla (JSON)",
|
||||
"httpDestBodyTemplateHint": "Použijte šablonové proměnné pro referenční pole události ve vašem užitečném zatížení.",
|
||||
"httpDestPayloadFormatTitle": "Formát datového zatížení",
|
||||
"httpDestPayloadFormatDescription": "Jak jsou události serializovány v každém žádajícím subjektu.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON pole",
|
||||
"httpDestFormatJsonArrayDescription": "Jeden požadavek na každou šarži, tělo je pole JSON. Kompatibilní s většinou generických webových háčků a Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Jeden požadavek na každou šarži, tělo je nově ohraničené JSON – jeden objekt na jednu čáru, bez vnějšího pole. Vyžaduje Splunk HEC, Elastic / OpenSearch, a Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Jedna událost na požadavek",
|
||||
"httpDestFormatSingleDescription": "Odešle samostatnou HTTP POST pro každou jednotlivou událost. Používejte pouze pro koncové body, které nemohou zpracovávat dávky.",
|
||||
"httpDestLogTypesTitle": "Typy protokolů",
|
||||
"httpDestLogTypesDescription": "Vyberte, které typy logů jsou přesměrovány do této destinace. Budou streamovány pouze povolené typy logů.",
|
||||
"httpDestAccessLogsTitle": "Protokoly přístupu",
|
||||
"httpDestAccessLogsDescription": "Pokusy o přístup k dokumentům, včetně ověřených a zamítnutých požadavků.",
|
||||
"httpDestActionLogsTitle": "Záznamy akcí",
|
||||
"httpDestActionLogsDescription": "Správní opatření prováděná uživateli v rámci organizace.",
|
||||
"httpDestConnectionLogsTitle": "Protokoly připojení",
|
||||
"httpDestConnectionLogsDescription": "Události týkající se připojení lokality a tunelu, včetně připojení a odpojení.",
|
||||
"httpDestRequestLogsTitle": "Záznamy požadavků",
|
||||
"httpDestRequestLogsDescription": "HTTP záznamy požadavků pro proxy zdroje, včetně metod, cesty a kódu odpovědi.",
|
||||
"httpDestSaveChanges": "Uložit změny",
|
||||
"httpDestCreateDestination": "Vytvořit cíl",
|
||||
"httpDestUpdatedSuccess": "Cíl byl úspěšně aktualizován",
|
||||
"httpDestCreatedSuccess": "Cíl byl úspěšně vytvořen",
|
||||
"httpDestUpdateFailed": "Nepodařilo se aktualizovat cíl",
|
||||
"httpDestCreateFailed": "Nepodařilo se vytvořit cíl"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} one {einer Organisation} other {# Organisationen}}.",
|
||||
"componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!",
|
||||
"inviteErrorNotValid": "Es tut uns leid, aber es sieht so aus, als wäre die Einladung, auf die du zugreifen möchtest, entweder nicht angenommen worden oder nicht mehr gültig.",
|
||||
@@ -97,7 +99,7 @@
|
||||
"siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren",
|
||||
"siteSettingDescription": "Standorteinstellungen konfigurieren",
|
||||
"siteSetting": "{siteName} Einstellungen",
|
||||
"siteNewtTunnel": "Neuer Standort (empfohlen)",
|
||||
"siteNewtTunnel": "Newt Standort (empfohlen)",
|
||||
"siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in jedes Netzwerk zu erstellen. Keine zusätzliche Einrichtung.",
|
||||
"siteWg": "Einfacher WireGuard Tunnel",
|
||||
"siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.",
|
||||
@@ -107,7 +109,7 @@
|
||||
"siteSeeAll": "Alle Standorte anzeigen",
|
||||
"siteTunnelDescription": "Legen Sie fest, wie Sie sich mit dem Standort verbinden möchten",
|
||||
"siteNewtCredentials": "Zugangsdaten",
|
||||
"siteNewtCredentialsDescription": "So wird sich die Seite mit dem Server authentifizieren",
|
||||
"siteNewtCredentialsDescription": "So wird sich der Standort mit dem Server authentifizieren",
|
||||
"remoteNodeCredentialsDescription": "So wird sich der entfernte Node mit dem Server authentifizieren",
|
||||
"siteCredentialsSave": "Anmeldedaten speichern",
|
||||
"siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Link erstellen",
|
||||
"resourcesNotFound": "Keine Ressourcen gefunden",
|
||||
"resourceSearch": "Suche Ressourcen",
|
||||
"machineSearch": "Maschinen suchen",
|
||||
"machinesSearch": "Suche Maschinen-Klienten...",
|
||||
"machineNotFound": "Keine Maschinen gefunden",
|
||||
"userDeviceSearch": "Benutzergeräte durchsuchen",
|
||||
"userDevicesSearch": "Benutzergeräte durchsuchen...",
|
||||
"openMenu": "Menü öffnen",
|
||||
"resource": "Ressource",
|
||||
"title": "Titel",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy-Anfragen über HTTPS mit einem voll qualifizierten Domain-Namen.",
|
||||
"resourceRaw": "Direkte TCP/UDP Ressource (raw)",
|
||||
"resourceRawDescription": "Proxy-Anfragen über rohes TCP/UDP mit einer Portnummer.",
|
||||
"resourceRawDescriptionCloud": "Proxy-Anfragen über rohe TCP/UDP mit Portnummer. Benötigt Sites, um sich mit einem entfernten Knoten zu verbinden.",
|
||||
"resourceCreate": "Ressource erstellen",
|
||||
"resourceCreateDescription": "Folgen Sie den Schritten unten, um eine neue Ressource zu erstellen",
|
||||
"resourceSeeAll": "Alle Ressourcen anzeigen",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Wählen Sie ein Protokoll",
|
||||
"resourcePortNumber": "Portnummer",
|
||||
"resourcePortNumberDescription": "Die externe Portnummer für Proxy-Anfragen.",
|
||||
"back": "Zurück",
|
||||
"cancel": "Abbrechen",
|
||||
"resourceConfig": "Konfiguration Snippets",
|
||||
"resourceConfigDescription": "Kopieren und fügen Sie diese Konfigurations-Snippets ein, um die TCP/UDP Ressource einzurichten",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Beim Löschen der Organisation ist ein Fehler aufgetreten.",
|
||||
"orgDeleted": "Organisation gelöscht",
|
||||
"orgDeletedMessage": "Die Organisation und ihre Daten wurden gelöscht.",
|
||||
"deleteAccount": "Konto löschen",
|
||||
"deleteAccountDescription": "Lösche dein Konto, alle Organisationen, die du besitzt, und alle Daten innerhalb dieser Organisationen. Dies kann nicht rückgängig gemacht werden.",
|
||||
"deleteAccountButton": "Konto löschen",
|
||||
"deleteAccountConfirmTitle": "Konto löschen",
|
||||
"deleteAccountConfirmMessage": "Dies wird Ihr Konto dauerhaft löschen, alle Organisationen, die Sie besitzen, und alle Daten innerhalb dieser Organisationen. Dies kann nicht rückgängig gemacht werden.",
|
||||
"deleteAccountConfirmString": "Konto löschen",
|
||||
"deleteAccountSuccess": "Konto gelöscht",
|
||||
"deleteAccountSuccessMessage": "Ihr Konto wurde gelöscht.",
|
||||
"deleteAccountError": "Konto konnte nicht gelöscht werden",
|
||||
"deleteAccountPreviewAccount": "Ihr Konto",
|
||||
"deleteAccountPreviewOrgs": "Organisationen, die Sie besitzen (und ihre Daten)",
|
||||
"orgMissing": "Organisations-ID fehlt",
|
||||
"orgMissingMessage": "Einladung kann ohne Organisations-ID nicht neu generiert werden.",
|
||||
"accessUsersManage": "Benutzer verwalten",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "API-Schlüssel löschen",
|
||||
"apiKeysManage": "API-Schlüssel verwalten",
|
||||
"apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet",
|
||||
"provisioningKeysTitle": "Bereitstellungsschlüssel",
|
||||
"provisioningKeysManage": "Bereitstellungsschlüssel verwalten",
|
||||
"provisioningKeysDescription": "Bereitstellungsschlüssel werden verwendet, um die automatisierte Bereitstellung von Seiten für Ihr Unternehmen zu authentifizieren.",
|
||||
"provisioningManage": "Bereitstellung",
|
||||
"provisioningDescription": "Bereitstellungsschlüssel verwalten und ausstehende Seiten prüfen, die noch auf Genehmigung warten.",
|
||||
"pendingSites": "Ausstehende Seiten",
|
||||
"siteApproveSuccess": "Site erfolgreich freigegeben",
|
||||
"siteApproveError": "Fehler beim Bestätigen der Seite",
|
||||
"provisioningKeys": "Bereitstellungsschlüssel",
|
||||
"searchProvisioningKeys": "Bereitstellungsschlüssel suchen...",
|
||||
"provisioningKeysAdd": "Bereitstellungsschlüssel generieren",
|
||||
"provisioningKeysErrorDelete": "Fehler beim Löschen des Bereitstellungsschlüssels",
|
||||
"provisioningKeysErrorDeleteMessage": "Fehler beim Löschen des Bereitstellungsschlüssels",
|
||||
"provisioningKeysQuestionRemove": "Sind Sie sicher, dass Sie diesen Bereitstellungsschlüssel aus der Organisation entfernen möchten?",
|
||||
"provisioningKeysMessageRemove": "Einmal entfernt, kann der Schlüssel nicht mehr für die Bereitstellung der Site verwendet werden.",
|
||||
"provisioningKeysDeleteConfirm": "Bereitstellungsschlüssel löschen bestätigen",
|
||||
"provisioningKeysDelete": "Bereitstellungsschlüssel löschen",
|
||||
"provisioningKeysCreate": "Bereitstellungsschlüssel generieren",
|
||||
"provisioningKeysCreateDescription": "Einen neuen Bereitstellungsschlüssel für die Organisation generieren",
|
||||
"provisioningKeysSeeAll": "Alle Bereitstellungsschlüssel anzeigen",
|
||||
"provisioningKeysSave": "Bereitstellungsschlüssel speichern",
|
||||
"provisioningKeysSaveDescription": "Sie können dies nur einmal sehen. Kopieren Sie es an einen sicheren Ort.",
|
||||
"provisioningKeysErrorCreate": "Fehler beim Erstellen des Bereitstellungsschlüssels",
|
||||
"provisioningKeysList": "Neuer Bereitstellungsschlüssel",
|
||||
"provisioningKeysMaxBatchSize": "Max. Batch-Größe",
|
||||
"provisioningKeysUnlimitedBatchSize": "Unbegrenzte Batch-Größe (kein Limit)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Unbegrenzt",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Geben Sie eine gültige maximale Batchgröße ein (1–1.000.000).",
|
||||
"provisioningKeysValidUntil": "Gültig bis",
|
||||
"provisioningKeysValidUntilHint": "Leer lassen für keine Verjährung.",
|
||||
"provisioningKeysValidUntilInvalid": "Geben Sie ein gültiges Datum und Zeit ein.",
|
||||
"provisioningKeysNumUsed": "Verwendete Zeiten",
|
||||
"provisioningKeysLastUsed": "Zuletzt verwendet",
|
||||
"provisioningKeysNoExpiry": "Kein Ablauf",
|
||||
"provisioningKeysNeverUsed": "Nie",
|
||||
"provisioningKeysEdit": "Bereitstellungsschlüssel bearbeiten",
|
||||
"provisioningKeysEditDescription": "Aktualisieren Sie die maximale Batch-Größe und Ablaufzeit für diesen Schlüssel.",
|
||||
"provisioningKeysApproveNewSites": "Neue Seiten genehmigen",
|
||||
"provisioningKeysApproveNewSitesDescription": "Sites, die sich mit diesem Schlüssel registrieren, automatisch freigeben.",
|
||||
"provisioningKeysUpdateError": "Fehler beim Aktualisieren des Bereitstellungsschlüssels",
|
||||
"provisioningKeysUpdated": "Bereitstellungsschlüssel aktualisiert",
|
||||
"provisioningKeysUpdatedDescription": "Ihre Änderungen wurden gespeichert.",
|
||||
"provisioningKeysBannerTitle": "Website-Bereitstellungsschlüssel",
|
||||
"provisioningKeysBannerDescription": "Generieren Sie einen Bereitstellungsschlüssel und verwenden Sie ihn mit dem Newt-Konnektor, um beim ersten Start automatisch Sites zu erstellen – keine Notwendigkeit, separate Anmeldeinformationen für jede Seite einzurichten.",
|
||||
"provisioningKeysBannerButtonText": "Mehr erfahren",
|
||||
"pendingSitesBannerTitle": "Ausstehende Seiten",
|
||||
"pendingSitesBannerDescription": "Sites, die sich mit einem Bereitstellungsschlüssel verbinden, erscheinen hier zur Überprüfung. Bestätigen Sie jede Site, bevor sie aktiv wird und erhalten Zugriff auf Ihre Ressourcen.",
|
||||
"pendingSitesBannerButtonText": "Mehr erfahren",
|
||||
"apiKeysSettings": "{apiKeyName} Einstellungen",
|
||||
"userTitle": "Alle Benutzer verwalten",
|
||||
"userDescription": "Alle Benutzer im System anzeigen und verwalten",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtern nach Genehmigungsstatus",
|
||||
"approvalListEmpty": "Keine Genehmigungen",
|
||||
"approvalState": "Genehmigungsstatus",
|
||||
"approvalLoadMore": "Mehr laden",
|
||||
"loadingApprovals": "Genehmigungen werden geladen",
|
||||
"approve": "Bestätigen",
|
||||
"approved": "Genehmigt",
|
||||
"denied": "Verweigert",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Benutzer gespeichert",
|
||||
"userSavedDescription": "Der Benutzer wurde aktualisiert.",
|
||||
"autoProvisioned": "Automatisch bereitgestellt",
|
||||
"autoProvisionSettings": "Auto-Bereitstellungseinstellungen",
|
||||
"autoProvisionedDescription": "Erlaube diesem Benutzer die automatische Verwaltung durch Identitätsanbieter",
|
||||
"accessControlsDescription": "Verwalten Sie, worauf dieser Benutzer in der Organisation zugreifen und was er tun kann",
|
||||
"accessControlsSubmit": "Zugriffskontrollen speichern",
|
||||
"singleRolePerUserPlanNotice": "Ihr Plan unterstützt nur eine Rolle pro Benutzer.",
|
||||
"singleRolePerUserEditionNotice": "Diese Ausgabe unterstützt nur eine Rolle pro Benutzer.",
|
||||
"roles": "Rollen",
|
||||
"accessUsersRoles": "Benutzer & Rollen verwalten",
|
||||
"accessUsersRolesDescription": "Lade Benutzer ein und füge sie zu Rollen hinzu, um den Zugriff auf die Organisation zu verwalten",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Fehler beim Umschalten der Ressource",
|
||||
"resourcesErrorUpdateDescription": "Beim Aktualisieren der Ressource ist ein Fehler aufgetreten",
|
||||
"access": "Zugriff",
|
||||
"accessControl": "Zugriffskontrolle",
|
||||
"shareLink": "{resource} Freigabe-Link",
|
||||
"resourceSelect": "Ressource auswählen",
|
||||
"shareLinks": "Freigabe-Links",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rolle entfernt",
|
||||
"accessRoleRemovedDescription": "Die Rolle wurde erfolgreich entfernt.",
|
||||
"accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.",
|
||||
"network": "Netzwerk",
|
||||
"manage": "Verwalten",
|
||||
"sitesNotFound": "Keine Standorte gefunden.",
|
||||
"pangolinServerAdmin": "Server-Admin - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Anzahl der Standorte erhöhen",
|
||||
"idpManage": "Identitätsanbieter verwalten",
|
||||
"idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten",
|
||||
"idpGlobalModeBanner": "Identitätsanbieter (IdPs) pro Organisation sind auf diesem Server deaktiviert. Es verwendet globale IdPs (geteilt über alle Organisationen). Verwalten Sie globale IdPs im <adminPanelLink>Admin-Panel</adminPanelLink>. Um IdPs pro Organisation zu aktivieren, bearbeiten Sie die Server-Konfiguration und setzen Sie den IdP-Modus auf org. <configDocsLink>Siehe Dokumentation</configDocsLink>. Wenn Sie weiterhin globale IdPs verwenden und diese in den Organisationseinstellungen verschwinden lassen wollen, setzen Sie den Modus explizit auf global in der Konfiguration.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Identitätsanbieter (IdPs) pro Organisation sind auf diesem Server deaktiviert. Es verwendet globale IdPs (geteilt in allen Organisationen). Globale IdPs im <adminPanelLink>Admin-Panel</adminPanelLink>verwalten. Um Identitätsanbieter pro Organisation nutzen zu können, müssen Sie zur Enterprise Edition upgraden.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Identitätsanbieter (IdPs) pro Organisation sind auf diesem Server deaktiviert. Es verwendet globale IdPs (geteilt in allen Organisationen). Globale IdPs im <adminPanelLink>Admin-Panel</adminPanelLink>verwalten. Um Identitätsanbieter pro Organisation zu verwenden, ist eine Enterprise-Lizenz erforderlich.",
|
||||
"idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Sind Sie sicher, dass Sie den Identitätsanbieter dauerhaft löschen möchten?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Einrichtung - Pangolin",
|
||||
"orgNameRequired": "Organisationsname ist erforderlich",
|
||||
"orgIdRequired": "Organisations-ID ist erforderlich",
|
||||
"orgIdMaxLength": "Organisations-ID darf höchstens 32 Zeichen lang sein",
|
||||
"orgErrorCreate": "Beim Erstellen der Organisation ist ein Fehler aufgetreten",
|
||||
"pageNotFound": "Seite nicht gefunden",
|
||||
"pageNotFoundDescription": "Hoppla! Die gesuchte Seite existiert nicht.",
|
||||
"overview": "Übersicht",
|
||||
"home": "Startseite",
|
||||
"accessControl": "Zugriffskontrolle",
|
||||
"settings": "Einstellungen",
|
||||
"usersAll": "Alle Benutzer",
|
||||
"license": "Lizenz",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Benutzer abrufen",
|
||||
"actionGetOrgUser": "Organisationsbenutzer abrufen",
|
||||
"actionListOrgDomains": "Organisationsdomains auflisten",
|
||||
"actionGetDomain": "Domain abrufen",
|
||||
"actionCreateOrgDomain": "Domain erstellen",
|
||||
"actionUpdateOrgDomain": "Domain aktualisieren",
|
||||
"actionDeleteOrgDomain": "Domain löschen",
|
||||
"actionGetDNSRecords": "DNS-Einträge abrufen",
|
||||
"actionRestartOrgDomain": "Domain neu starten",
|
||||
"actionCreateSite": "Standort erstellen",
|
||||
"actionDeleteSite": "Standort löschen",
|
||||
"actionGetSite": "Standort abrufen",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Geben Sie das Setup-Token von der Serverkonsole ein.",
|
||||
"setupTokenRequired": "Setup-Token ist erforderlich",
|
||||
"actionUpdateSite": "Standorte aktualisieren",
|
||||
"actionResetSiteBandwidth": "Organisations-Bandbreite zurücksetzen",
|
||||
"actionListSiteRoles": "Erlaubte Standort-Rollen auflisten",
|
||||
"actionCreateResource": "Ressource erstellen",
|
||||
"actionDeleteResource": "Ressource löschen",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Benutzer entfernen",
|
||||
"actionListUsers": "Benutzer auflisten",
|
||||
"actionAddUserRole": "Benutzerrolle hinzufügen",
|
||||
"actionSetUserOrgRoles": "Benutzerrollen festlegen",
|
||||
"actionGenerateAccessToken": "Zugriffstoken generieren",
|
||||
"actionDeleteAccessToken": "Zugriffstoken löschen",
|
||||
"actionListAccessTokens": "Zugriffstoken auflisten",
|
||||
@@ -1147,9 +1233,9 @@
|
||||
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
|
||||
"actionCreateClient": "Client erstellen",
|
||||
"actionDeleteClient": "Client löschen",
|
||||
"actionArchiveClient": "Kunde archivieren",
|
||||
"actionArchiveClient": "Client archivieren",
|
||||
"actionUnarchiveClient": "Client dearchivieren",
|
||||
"actionBlockClient": "Klient sperren",
|
||||
"actionBlockClient": "Client sperren",
|
||||
"actionUnblockClient": "Client entsperren",
|
||||
"actionUpdateClient": "Client aktualisieren",
|
||||
"actionListClients": "Clients auflisten",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Logs anzeigen",
|
||||
"noneSelected": "Keine ausgewählt",
|
||||
"orgNotFound2": "Keine Organisationen gefunden.",
|
||||
"searchProgress": "Suche...",
|
||||
"searchPlaceholder": "Suche...",
|
||||
"emptySearchOptions": "Keine Optionen gefunden",
|
||||
"create": "Erstellen",
|
||||
"orgs": "Organisationen",
|
||||
"loginError": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privat",
|
||||
"sidebarAccessControl": "Zugriffskontrolle",
|
||||
"sidebarLogsAndAnalytics": "Protokolle & Analysen",
|
||||
"sidebarTeam": "Team",
|
||||
"sidebarUsers": "Benutzer",
|
||||
"sidebarAdmin": "Admin",
|
||||
"sidebarInvitations": "Einladungen",
|
||||
"sidebarRoles": "Rollen",
|
||||
"sidebarShareableLinks": "Links",
|
||||
"sidebarApiKeys": "API-Schlüssel",
|
||||
"sidebarProvisioning": "Bereitstellung",
|
||||
"sidebarSettings": "Einstellungen",
|
||||
"sidebarAllUsers": "Alle Benutzer",
|
||||
"sidebarIdentityProviders": "Identitätsanbieter",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Log & Analytik",
|
||||
"sidebarBluePrints": "Blaupausen",
|
||||
"sidebarOrganization": "Organisation",
|
||||
"sidebarManagement": "Management",
|
||||
"sidebarBillingAndLicenses": "Abrechnung & Lizenzen",
|
||||
"sidebarLogsAnalytics": "Analytik",
|
||||
"blueprints": "Blaupausen",
|
||||
"blueprintsDescription": "Deklarative Konfigurationen anwenden und vorherige Abläufe anzeigen",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Analysierte Inhalte (Nur lesen)",
|
||||
"enableDockerSocket": "Docker Blueprint aktivieren",
|
||||
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blueprintbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
|
||||
"enableDockerSocketLink": "Mehr erfahren",
|
||||
"viewDockerContainers": "Docker Container anzeigen",
|
||||
"containersIn": "Container in {siteName}",
|
||||
"selectContainerDescription": "Wählen Sie einen Container, der als Hostname für dieses Ziel verwendet werden soll. Klicken Sie auf einen Port, um einen Port zu verwenden.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Namespace: {namespace}",
|
||||
"domainPickerShowMore": "Mehr anzeigen",
|
||||
"regionSelectorTitle": "Region auswählen",
|
||||
"domainPickerRemoteExitNodeWarning": "Angegebene Domains werden nicht unterstützt, wenn sich Websites mit externen Exit-Knoten verbinden. Damit Ressourcen auf entfernten Knoten verfügbar sind, verwenden Sie stattdessen eine eigene Domain.",
|
||||
"regionSelectorInfo": "Das Auswählen einer Region hilft uns, eine bessere Leistung für Ihren Standort bereitzustellen. Sie müssen sich nicht in derselben Region wie Ihr Server befinden.",
|
||||
"regionSelectorPlaceholder": "Wähle eine Region",
|
||||
"regionSelectorComingSoon": "Kommt bald",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Übersicht über Nutzungsgrenzen",
|
||||
"billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@pangolin.net.",
|
||||
"billingDataUsage": "Datenverbrauch",
|
||||
"billingOnlineTime": "Online-Zeit der Seite",
|
||||
"billingUsers": "Aktive Benutzer",
|
||||
"billingDomains": "Aktive Domains",
|
||||
"billingRemoteExitNodes": "Aktive selbstgehostete Nodes",
|
||||
"billingSites": "Seiten",
|
||||
"billingUsers": "Benutzergeräte",
|
||||
"billingDomains": "Domänen",
|
||||
"billingOrganizations": "Orden",
|
||||
"billingRemoteExitNodes": "Entfernte Knoten",
|
||||
"billingNoLimitConfigured": "Kein Limit konfiguriert",
|
||||
"billingEstimatedPeriod": "Geschätzter Abrechnungszeitraum",
|
||||
"billingIncludedUsage": "Inklusive Nutzung",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Fehler beim Abrufen der Portal-URL",
|
||||
"billingPortalError": "Portalfehler",
|
||||
"billingDataUsageInfo": "Wenn Sie mit der Cloud verbunden sind, werden alle Daten über Ihre sicheren Tunnel belastet. Dies schließt eingehenden und ausgehenden Datenverkehr über alle Ihre Websites ein. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Daten werden nicht belastet, wenn Sie Knoten verwenden.",
|
||||
"billingOnlineTimeInfo": "Sie werden belastet, abhängig davon, wie lange Ihre Seiten mit der Cloud verbunden bleiben. Zum Beispiel 44.640 Minuten entspricht einer Site, die 24 Stunden am Tag des Monats läuft. Wenn Sie Ihr Limit erreichen, werden Ihre Seiten die Verbindung trennen, bis Sie Ihr Paket upgraden oder die Nutzung verringern. Die Zeit wird nicht belastet, wenn Sie Knoten verwenden.",
|
||||
"billingUsersInfo": "Sie werden für jeden Benutzer in der Organisation berechnet. Die Abrechnung wird täglich anhand der Anzahl der aktiven Benutzerkonten in Ihrer Org berechnet.",
|
||||
"billingDomainInfo": "Sie werden für jede Domain in der Organisation berechnet. Die Abrechnung wird täglich anhand der Anzahl der aktiven Domain-Konten in Ihrer Org berechnet.",
|
||||
"billingRemoteExitNodesInfo": "Sie werden für jeden verwalteten Knoten in der Organisation berechnet. Die Abrechnung wird täglich anhand der Anzahl der aktiven verwalteten Knoten in Ihrer Org berechnet.",
|
||||
"billingSInfo": "Anzahl der Sites die Sie verwenden können",
|
||||
"billingUsersInfo": "Wie viele Benutzer Sie verwenden können",
|
||||
"billingDomainInfo": "Wie viele Domains Sie verwenden können",
|
||||
"billingRemoteExitNodesInfo": "Wie viele entfernte Knoten Sie verwenden können",
|
||||
"billingLicenseKeys": "Lizenzschlüssel",
|
||||
"billingLicenseKeysDescription": "Verwalten Sie Ihre Lizenzschlüssel Abonnements",
|
||||
"billingLicenseSubscription": "Lizenzabonnement",
|
||||
"billingInactive": "Inaktiv",
|
||||
"billingLicenseItem": "Lizenz-Element",
|
||||
"billingQuantity": "Menge",
|
||||
"billingTotal": "gesamt",
|
||||
"billingModifyLicenses": "Lizenzabonnement ändern",
|
||||
"domainNotFound": "Domain nicht gefunden",
|
||||
"domainNotFoundDescription": "Diese Ressource ist deaktiviert, weil die Domain nicht mehr in unserem System existiert. Bitte setzen Sie eine neue Domain für diese Ressource.",
|
||||
"failed": "Fehlgeschlagen",
|
||||
"createNewOrgDescription": "Eine neue Organisation erstellen",
|
||||
"organization": "Organisation",
|
||||
"primary": "Primär",
|
||||
"port": "Port",
|
||||
"securityKeyManage": "Sicherheitsschlüssel verwalten",
|
||||
"securityKeyDescription": "Sicherheitsschlüssel für passwortlose Authentifizierung hinzufügen oder entfernen",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Portnummer ist für nicht-HTTP-Ressourcen erforderlich",
|
||||
"resourcePortNotAllowed": "Portnummer sollte für HTTP-Ressourcen nicht gesetzt werden",
|
||||
"billingPricingCalculatorLink": "Preisrechner",
|
||||
"billingYourPlan": "Ihr Plan",
|
||||
"billingViewOrModifyPlan": "Zeige oder ändere dein aktuelles Paket",
|
||||
"billingViewPlanDetails": "Plan Details anzeigen",
|
||||
"billingUsageAndLimits": "Nutzung und Einschränkungen",
|
||||
"billingViewUsageAndLimits": "Schau dir die Grenzen und die aktuelle Nutzung deines Plans an",
|
||||
"billingCurrentUsage": "Aktuelle Nutzung",
|
||||
"billingMaximumLimits": "Maximale Grenzen",
|
||||
"billingRemoteNodes": "Entfernte Knoten",
|
||||
"billingUnlimited": "Unbegrenzt",
|
||||
"billingPaidLicenseKeys": "Bezahlte Lizenzschlüssel",
|
||||
"billingManageLicenseSubscription": "Verwalten Sie Ihr Abonnement für kostenpflichtige selbstgehostete Lizenzschlüssel",
|
||||
"billingCurrentKeys": "Aktuelle Tasten",
|
||||
"billingModifyCurrentPlan": "Aktuelles Paket ändern",
|
||||
"billingConfirmUpgrade": "Upgrade bestätigen",
|
||||
"billingConfirmDowngrade": "Downgrade bestätigen",
|
||||
"billingConfirmUpgradeDescription": "Sie sind dabei, Ihr Paket zu aktualisieren. Schauen Sie sich die neuen Limits und Preise unten an.",
|
||||
"billingConfirmDowngradeDescription": "Sie sind dabei, Ihren Plan herunterzustufen. Überprüfen Sie die neuen Limits und Preise unten.",
|
||||
"billingPlanIncludes": "Plan beinhaltet",
|
||||
"billingProcessing": "Verarbeitung...",
|
||||
"billingConfirmUpgradeButton": "Upgrade bestätigen",
|
||||
"billingConfirmDowngradeButton": "Downgrade bestätigen",
|
||||
"billingLimitViolationWarning": "Nutzung überschreitet neue Plan-Grenzen",
|
||||
"billingLimitViolationDescription": "Ihre aktuelle Nutzung überschreitet die Grenzen dieses Plans. Nach dem Downgrade werden alle Aktionen deaktiviert, bis Sie die Nutzung innerhalb der neuen Grenzen reduzieren. Bitte überprüfen Sie die Funktionen unten, die derzeit über den Grenzen liegen. Grenzwerte verletzen:",
|
||||
"billingFeatureLossWarning": "Verfügbarkeitshinweis",
|
||||
"billingFeatureLossDescription": "Durch Herabstufung werden Funktionen, die im neuen Paket nicht verfügbar sind, automatisch deaktiviert. Einige Einstellungen und Konfigurationen können verloren gehen. Bitte überprüfen Sie die Preismatrix um zu verstehen, welche Funktionen nicht mehr verfügbar sein werden.",
|
||||
"billingUsageExceedsLimit": "Aktuelle Nutzung ({current}) überschreitet das Limit ({limit})",
|
||||
"billingPastDueTitle": "Zahlung vergangene Fälligkeit",
|
||||
"billingPastDueDescription": "Ihre Zahlung ist abgelaufen. Bitte aktualisieren Sie Ihre Zahlungsmethode, um die aktuellen Funktionen Ihres Pakets weiter zu nutzen. Wenn nicht geklärt, wird Ihr Abonnement abgebrochen und Sie werden auf die kostenlose Stufe zurückgekehrt.",
|
||||
"billingUnpaidTitle": "Unbezahltes Abonnement",
|
||||
"billingUnpaidDescription": "Dein Abonnement ist unbezahlt und du wurdest auf die kostenlose Stufe zurückgekehrt. Bitte aktualisiere deine Zahlungsmethode, um dein Abonnement wiederherzustellen.",
|
||||
"billingIncompleteTitle": "Zahlung unvollständig",
|
||||
"billingIncompleteDescription": "Ihre Zahlung ist unvollständig. Bitte schließen Sie den Zahlungsvorgang ab, um Ihr Abonnement zu aktivieren.",
|
||||
"billingIncompleteExpiredTitle": "Zahlung abgelaufen",
|
||||
"billingIncompleteExpiredDescription": "Deine Zahlung wurde nie abgeschlossen und ist abgelaufen. Du wurdest zur kostenlosen Stufe zurückgekehrt. Bitte melde dich erneut an, um den Zugriff auf kostenpflichtige Funktionen wiederherzustellen.",
|
||||
"billingManageSubscription": "Verwalten Sie Ihr Abonnement",
|
||||
"billingResolvePaymentIssue": "Bitte beheben Sie Ihr Zahlungsproblem vor dem Upgrade oder Herabstufen",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Ich stimme den",
|
||||
"termsOfService": "Nutzungsbedingungen zu",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Zeit ist in Sekunden",
|
||||
"requireDeviceApproval": "Gerätegenehmigungen erforderlich",
|
||||
"requireDeviceApprovalDescription": "Benutzer mit dieser Rolle benötigen neue Geräte, die von einem Administrator genehmigt wurden, bevor sie sich verbinden und auf Ressourcen zugreifen können.",
|
||||
"sshAccess": "SSH-Zugriff",
|
||||
"roleAllowSsh": "SSH erlauben",
|
||||
"roleAllowSshAllow": "Erlauben",
|
||||
"roleAllowSshDisallow": "Nicht zulassen",
|
||||
"roleAllowSshDescription": "Benutzern mit dieser Rolle erlauben, sich über SSH mit Ressourcen zu verbinden. Wenn deaktiviert, kann die Rolle keinen SSH-Zugriff verwenden.",
|
||||
"sshSudoMode": "Sudo-Zugriff",
|
||||
"sshSudoModeNone": "Keine",
|
||||
"sshSudoModeNoneDescription": "Benutzer kann keine Befehle mit sudo ausführen.",
|
||||
"sshSudoModeFull": "Volles Sudo",
|
||||
"sshSudoModeFullDescription": "Benutzer kann jeden Befehl mit sudo ausführen.",
|
||||
"sshSudoModeCommands": "Befehle",
|
||||
"sshSudoModeCommandsDescription": "Benutzer kann nur die angegebenen Befehle mit sudo ausführen.",
|
||||
"sshSudo": "sudo erlauben",
|
||||
"sshSudoCommands": "Sudo-Befehle",
|
||||
"sshSudoCommandsDescription": "Kommagetrennte Liste von Befehlen, die der Benutzer mit sudo ausführen darf.",
|
||||
"sshCreateHomeDir": "Home-Verzeichnis erstellen",
|
||||
"sshUnixGroups": "Unix-Gruppen",
|
||||
"sshUnixGroupsDescription": "Durch Komma getrennte Unix-Gruppen, um den Benutzer auf dem Zielhost hinzuzufügen.",
|
||||
"retryAttempts": "Wiederholungsversuche",
|
||||
"expectedResponseCodes": "Erwartete Antwortcodes",
|
||||
"expectedResponseCodesDescription": "HTTP-Statuscode, der einen gesunden Zustand anzeigt. Wenn leer gelassen, wird 200-300 als gesund angesehen.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Exit-Node",
|
||||
"country": "Land",
|
||||
"rulesMatchCountry": "Derzeit basierend auf der Quell-IP",
|
||||
"region": "Region",
|
||||
"selectRegion": "Region wählen...",
|
||||
"searchRegions": "Regionen suchen...",
|
||||
"noRegionFound": "Keine Region gefunden.",
|
||||
"rulesMatchRegion": "Wählen Sie eine Regionalgruppe von Ländern",
|
||||
"rulesErrorInvalidRegion": "Ungültige Region",
|
||||
"rulesErrorInvalidRegionDescription": "Bitte wählen Sie eine gültige Region aus.",
|
||||
"regionAfrica": "Afrika",
|
||||
"regionNorthernAfrica": "Nordafrika",
|
||||
"regionEasternAfrica": "Ostafrika",
|
||||
"regionMiddleAfrica": "Zentralafrika",
|
||||
"regionSouthernAfrica": "Südliches Afrika",
|
||||
"regionWesternAfrica": "Westafrika",
|
||||
"regionAmericas": "Amerika",
|
||||
"regionCaribbean": "Karibik",
|
||||
"regionCentralAmerica": "Mittelamerika",
|
||||
"regionSouthAmerica": "Südamerika",
|
||||
"regionNorthernAmerica": "Nordamerika",
|
||||
"regionAsia": "Asien",
|
||||
"regionCentralAsia": "Zentralasien",
|
||||
"regionEasternAsia": "Ostasien",
|
||||
"regionSouthEasternAsia": "Südostasien",
|
||||
"regionSouthernAsia": "Südasien",
|
||||
"regionWesternAsia": "Westasien",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Osteuropa",
|
||||
"regionNorthernEurope": "Nordeuropa",
|
||||
"regionSouthernEurope": "Südeuropa",
|
||||
"regionWesternEurope": "Westeuropa",
|
||||
"regionOceania": "Ozeanien",
|
||||
"regionAustraliaAndNewZealand": "Australien und Neuseeland",
|
||||
"regionMelanesia": "Melanesien",
|
||||
"regionMicronesia": "Mikronesien",
|
||||
"regionPolynesia": "Polynesien",
|
||||
"managedSelfHosted": {
|
||||
"title": "Verwaltetes Selbsthosted",
|
||||
"description": "Zuverlässiger und wartungsarmer Pangolin Server mit zusätzlichen Glocken und Pfeifen",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Ungültiger Wert",
|
||||
"idpTypeLabel": "Identitätsanbietertyp",
|
||||
"roleMappingExpressionPlaceholder": "z. B. enthalten(Gruppen, 'admin') && 'Admin' || 'Mitglied'",
|
||||
"roleMappingModeFixedRoles": "Feste Rollen",
|
||||
"roleMappingModeMappingBuilder": "Mapping Builder",
|
||||
"roleMappingModeRawExpression": "Roher Ausdruck",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Wählen Sie eine oder mehrere Rollen",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Rollennamen eingeben (exakte Übereinstimmung pro Organisation)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Weisen Sie jedem auto-provisionierten Benutzer die gleiche Rolle zu.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Für Standardrichtlinien geben Sie Rollennamen ein, die in jeder Organisation existieren, in der Benutzer angegeben sind. Namen müssen exakt übereinstimmen.",
|
||||
"roleMappingClaimPath": "Pfad einfordern",
|
||||
"roleMappingClaimPathPlaceholder": "gruppen",
|
||||
"roleMappingClaimPathDescription": "Pfad in der Token Payload mit Quellwerten (zum Beispiel Gruppen).",
|
||||
"roleMappingMatchValue": "Match-Wert",
|
||||
"roleMappingAssignRoles": "Rollen zuweisen",
|
||||
"roleMappingAddMappingRule": "Zuordnungsregel hinzufügen",
|
||||
"roleMappingRawExpressionResultDescription": "Ausdruck muss zu einem String oder String Array ausgewertet werden.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Ausdruck muss zu einem String (einem einzigen Rollennamen) ausgewertet werden.",
|
||||
"roleMappingMatchValuePlaceholder": "Match-Wert (z. B.: Admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Rollennamen eingeben (exakt pro Ort)",
|
||||
"roleMappingBuilderFreeformRowHint": "Rollennamen müssen mit einer Rolle in jeder Zielorganisation übereinstimmen.",
|
||||
"roleMappingRemoveRule": "Entfernen",
|
||||
"idpGoogleConfiguration": "Google-Konfiguration",
|
||||
"idpGoogleConfigurationDescription": "Google OAuth2 Zugangsdaten konfigurieren",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Sind Sie sicher, dass Sie das Branding für Authentifizierungsseiten entfernen möchten?",
|
||||
"authPageBrandingDeleteConfirm": "Branding löschen bestätigen",
|
||||
"brandingLogoURL": "Logo URL",
|
||||
"brandingLogoURLOrPath": "Logo-URL oder Pfad",
|
||||
"brandingLogoPathDescription": "Geben Sie eine URL oder einen lokalen Pfad ein.",
|
||||
"brandingLogoURLDescription": "Geben Sie eine öffentlich zugängliche URL zu Ihrem Logobild ein.",
|
||||
"brandingPrimaryColor": "Primär-Farbe",
|
||||
"brandingLogoWidth": "Breite (px)",
|
||||
"brandingLogoHeight": "Höhe (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Zurück zum Standard Login",
|
||||
"orgAuthNoAccount": "Sie haben noch kein Konto?",
|
||||
"subscriptionRequiredToUse": "Um diese Funktion nutzen zu können, ist ein Abonnement erforderlich.",
|
||||
"mustUpgradeToUse": "Sie müssen Ihr Abonnement aktualisieren, um diese Funktion nutzen zu können.",
|
||||
"subscriptionRequiredTierToUse": "Diese Funktion erfordert <tierLink>{tier}</tierLink> oder höher.",
|
||||
"upgradeToTierToUse": "Upgrade auf <tierLink>{tier}</tierLink> oder höher, um diese Funktion zu nutzen.",
|
||||
"subscriptionTierTier1": "Zuhause",
|
||||
"subscriptionTierTier2": "Team",
|
||||
"subscriptionTierTier3": "Geschäftlich",
|
||||
"subscriptionTierEnterprise": "Firma",
|
||||
"idpDisabled": "Identitätsanbieter sind deaktiviert.",
|
||||
"orgAuthPageDisabled": "Organisations-Authentifizierungsseite ist deaktiviert.",
|
||||
"domainRestartedDescription": "Domain-Verifizierung erfolgreich neu gestartet",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Lizenz erhalten",
|
||||
"description": "Wählen Sie einen Plan und teilen Sie uns mit, wie Sie Pangolin verwenden möchten.",
|
||||
"chooseTier": "Wählen Sie Ihren Plan",
|
||||
"viewPricingLink": "Siehe Preise, Funktionen und Limits",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Starter",
|
||||
"description": "Enterprise Features, 25 Benutzer, 25 Sites und Community-Unterstützung."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Maßstab",
|
||||
"description": "Enterprise Features, 50 Benutzer, 50 Sites und Prioritätsunterstützung."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Nur persönliche Nutzung (kostenlose Lizenz — keine Kasse)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Weiter zur Kasse"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Checkout-Fehler",
|
||||
"description": "Kasse konnte nicht gestartet werden. Bitte versuchen Sie es erneut."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Priorität",
|
||||
"priorityDescription": "Die Routen mit höherer Priorität werden zuerst ausgewertet. Priorität = 100 bedeutet automatische Bestellung (Systementscheidung). Verwenden Sie eine andere Nummer, um manuelle Priorität zu erzwingen.",
|
||||
"instanceName": "Instanzname",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Wie lange Zugriffsprotokolle beibehalten werden sollen",
|
||||
"logRetentionActionLabel": "Aktionsprotokoll-Speicherung",
|
||||
"logRetentionActionDescription": "Dauer des Action-Logs",
|
||||
"logRetentionConnectionLabel": "Verbindungsprotokoll-Speicherung",
|
||||
"logRetentionConnectionDescription": "Wie lange Verbindungsprotokolle gespeichert werden sollen",
|
||||
"logRetentionDisabled": "Deaktiviert",
|
||||
"logRetention3Days": "3 Tage",
|
||||
"logRetention7Days": "7 Tage",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Ende des folgenden Jahres",
|
||||
"actionLogsDescription": "Verlauf der in dieser Organisation durchgeführten Aktionen anzeigen",
|
||||
"accessLogsDescription": "Zugriffsauth-Anfragen für Ressourcen in dieser Organisation anzeigen",
|
||||
"licenseRequiredToUse": "Um diese Funktion nutzen zu können, ist eine Enterprise-Lizenz erforderlich.",
|
||||
"connectionLogs": "Verbindungsprotokolle",
|
||||
"connectionLogsDescription": "Verbindungsprotokolle für Tunnel in dieser Organisation anzeigen",
|
||||
"sidebarLogsConnection": "Verbindungsprotokolle",
|
||||
"sidebarLogsStreaming": "Streaming",
|
||||
"sourceAddress": "Quelladresse",
|
||||
"destinationAddress": "Zieladresse",
|
||||
"duration": "Dauer",
|
||||
"licenseRequiredToUse": "Eine <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> Lizenz oder <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> wird benötigt, um diese Funktion nutzen zu können. <bookADemoLink>Buchen Sie eine Demo oder POC Testversion</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "Die <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> wird benötigt, um diese Funktion nutzen zu können. Diese Funktion ist auch in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>verfügbar. <bookADemoLink>Buchen Sie eine Demo oder POC Testversion</bookADemoLink>.",
|
||||
"certResolver": "Zertifikatsauflöser",
|
||||
"certResolverDescription": "Wählen Sie den Zertifikatslöser aus, der für diese Ressource verwendet werden soll.",
|
||||
"selectCertResolver": "Zertifikatsauflöser auswählen",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Zugriffskontrolle",
|
||||
"editInternalResourceDialogAccessControlDescription": "Kontrollieren Sie, welche Rollen, Benutzer und Maschinen-Clients Zugriff auf diese Ressource haben, wenn sie verbunden sind. Admins haben immer Zugriff.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Der Port-Bereich muss \"*\" für alle Ports sein, oder eine kommaseparierte Liste von Ports und Bereichen (z.B. \"80,443.8000-9000\"). Ports müssen zwischen 1 und 65535 liegen.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Auth-Daemon Standort",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Wählen Sie aus, wo der SSH-Authentifizierungs-Daemon läuft: auf der Site (Newt) oder auf einem entfernten Host.",
|
||||
"internalResourceAuthDaemonDescription": "Der SSH-Authentifizierungs-Daemon verarbeitet SSH-Schlüsselsignaturen und PAM-Authentifizierung für diese Ressource. Wählen Sie, ob sie auf der Website (Newt) oder auf einem separaten entfernten Host ausgeführt wird. Siehe <docsLink>die Dokumentation</docsLink> für mehr.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Strategie auswählen",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Standort",
|
||||
"internalResourceAuthDaemonSite": "Vor Ort",
|
||||
"internalResourceAuthDaemonSiteDescription": "Der Auth Daemon läuft auf der Seite (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Entfernter Host",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Der Auth Daemon läuft auf einem Host, der nicht die Site ist.",
|
||||
"internalResourceAuthDaemonPort": "Daemon-Port (optional)",
|
||||
"orgAuthWhatsThis": "Wo finde ich meine Organisations-ID?",
|
||||
"learnMore": "Mehr erfahren",
|
||||
"backToHome": "Zurück zur Startseite",
|
||||
@@ -2455,14 +2720,14 @@
|
||||
"errorUnarchivingDevice": "Fehler beim Entarchivieren des Geräts",
|
||||
"failedToUnarchiveDevice": "Fehler beim Entfernen des Geräts",
|
||||
"unarchive": "Archivieren",
|
||||
"archiveClient": "Kunde archivieren",
|
||||
"archiveClient": "Client archivieren",
|
||||
"archiveClientQuestion": "Sind Sie sicher, dass Sie diesen Client archivieren möchten?",
|
||||
"archiveClientMessage": "Der Client wird archiviert und aus der Liste Ihrer aktiven Clients entfernt.",
|
||||
"archiveClientConfirm": "Kunde archivieren",
|
||||
"blockClient": "Klient sperren",
|
||||
"archiveClientConfirm": "Client archivieren",
|
||||
"blockClient": "Client sperren",
|
||||
"blockClientQuestion": "Sind Sie sicher, dass Sie diesen Client blockieren möchten?",
|
||||
"blockClientMessage": "Das Gerät wird gezwungen, die Verbindung zu trennen, wenn es gerade verbunden ist. Sie können das Gerät später entsperren.",
|
||||
"blockClientConfirm": "Klient sperren",
|
||||
"blockClientConfirm": "Client sperren",
|
||||
"active": "Aktiv",
|
||||
"usernameOrEmail": "Benutzername oder E-Mail",
|
||||
"selectYourOrganization": "Wählen Sie Ihre Organisation",
|
||||
@@ -2503,13 +2768,14 @@
|
||||
"deviceModel": "Gerätemodell",
|
||||
"serialNumber": "Seriennummer",
|
||||
"hostname": "Hostname",
|
||||
"firstSeen": "Erster Blick",
|
||||
"firstSeen": "Zuerst gesehen",
|
||||
"lastSeen": "Zuletzt gesehen",
|
||||
"biometricsEnabled": "Biometrie aktiviert",
|
||||
"diskEncrypted": "Festplatte verschlüsselt",
|
||||
"firewallEnabled": "Firewall aktiviert",
|
||||
"autoUpdatesEnabled": "Automatische Updates aktiviert",
|
||||
"tpmAvailable": "TPM verfügbar",
|
||||
"windowsAntivirusEnabled": "Antivirus aktiviert",
|
||||
"macosSipEnabled": "Schutz der Systemintegrität (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Firewall Stealth-Modus",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Gerätegenehmigungen aktivieren",
|
||||
"approvalsEmptyStateStep2Description": "Bearbeite eine Rolle und aktiviere die Option 'Gerätegenehmigung erforderlich'. Benutzer mit dieser Rolle benötigen Administrator-Genehmigung für neue Geräte.",
|
||||
"approvalsEmptyStatePreviewDescription": "Vorschau: Wenn aktiviert, werden ausstehende Geräteanfragen hier zur Überprüfung angezeigt",
|
||||
"approvalsEmptyStateButtonText": "Rollen verwalten"
|
||||
"approvalsEmptyStateButtonText": "Rollen verwalten",
|
||||
"domainErrorTitle": "Wir haben Probleme mit der Überprüfung deiner Domain",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Konfigurieren Sie Rollenzuordnungs- und Organisationsrichtlinien auf der Registerkarte <policiesTabLink>Auto-Bereitstellungseinstellungen</policiesTabLink>.",
|
||||
"streamingTitle": "Event Streaming",
|
||||
"streamingDescription": "Streamen Sie Events aus Ihrem Unternehmen in Echtzeit zu externen Zielen.",
|
||||
"streamingUnnamedDestination": "Unbenanntes Ziel",
|
||||
"streamingNoUrlConfigured": "Keine URL konfiguriert",
|
||||
"streamingAddDestination": "Ziel hinzufügen",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookDescription": "Sende Ereignisse an jeden HTTP-Endpunkt mit flexibler Authentifizierung und Vorlage.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Streame Ereignisse in eine S3-kompatible Objekt-Speicher-Eimer. Kommt bald.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Events direkt an Ihr Datadog Konto weiterleiten. Kommen Sie bald.",
|
||||
"streamingTypePickerDescription": "Wählen Sie einen Zieltyp aus, um loszulegen.",
|
||||
"streamingFailedToLoad": "Fehler beim Laden der Ziele",
|
||||
"streamingUnexpectedError": "Ein unerwarteter Fehler ist aufgetreten.",
|
||||
"streamingFailedToUpdate": "Fehler beim Aktualisieren des Ziels",
|
||||
"streamingDeletedSuccess": "Ziel erfolgreich gelöscht",
|
||||
"streamingFailedToDelete": "Fehler beim Löschen des Ziels",
|
||||
"streamingDeleteTitle": "Ziel löschen",
|
||||
"streamingDeleteButtonText": "Ziel löschen",
|
||||
"streamingDeleteDialogAreYouSure": "Sind Sie sicher, dass Sie löschen möchten",
|
||||
"streamingDeleteDialogThisDestination": "dieses Ziel",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Alle Konfiguration wird dauerhaft entfernt.",
|
||||
"httpDestEditTitle": "Ziel bearbeiten",
|
||||
"httpDestAddTitle": "HTTP-Ziel hinzufügen",
|
||||
"httpDestEditDescription": "Aktualisiere die Konfiguration für dieses HTTP-Streaming-Ziel.",
|
||||
"httpDestAddDescription": "Konfigurieren Sie einen neuen HTTP-Endpunkt, um die Ereignisse Ihrer Organisation zu empfangen.",
|
||||
"httpDestTabSettings": "Einstellungen",
|
||||
"httpDestTabHeaders": "Kopfzeilen",
|
||||
"httpDestTabBody": "Körper",
|
||||
"httpDestTabLogs": "Logs",
|
||||
"httpDestNamePlaceholder": "Mein HTTP-Ziel",
|
||||
"httpDestUrlLabel": "Ziel-URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL muss http oder https verwenden",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS wird für Cloud-Deployment benötigt",
|
||||
"httpDestUrlErrorInvalid": "Geben Sie eine gültige URL ein (z.B. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Authentifizierung",
|
||||
"httpDestAuthDescription": "Legen Sie fest, wie Anfragen an Ihren Endpunkt authentifiziert werden.",
|
||||
"httpDestAuthNoneTitle": "Keine Authentifizierung",
|
||||
"httpDestAuthNoneDescription": "Sendet Anfragen ohne Autorisierungs-Header.",
|
||||
"httpDestAuthBearerTitle": "Bären-Token",
|
||||
"httpDestAuthBearerDescription": "Fügt eine Berechtigung hinzu: Bearer <token> Header zu jeder Anfrage.",
|
||||
"httpDestAuthBearerPlaceholder": "Ihr API-Schlüssel oder Token",
|
||||
"httpDestAuthBasicTitle": "Einfacher Auth",
|
||||
"httpDestAuthBasicDescription": "Fügt eine Autorisierung hinzu: Basic <credentials> Kopfzeile hinzu. Geben Sie Anmeldedaten als Benutzername:password an.",
|
||||
"httpDestAuthBasicPlaceholder": "benutzername:password",
|
||||
"httpDestAuthCustomTitle": "Eigene Kopfzeile",
|
||||
"httpDestAuthCustomDescription": "Geben Sie einen eigenen HTTP-Header-Namen und einen Wert für die Authentifizierung an (z.B. X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Headername (z.B. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Header-Wert",
|
||||
"httpDestCustomHeadersTitle": "Eigene HTTP-Header",
|
||||
"httpDestCustomHeadersDescription": "Fügen Sie jeder ausgehenden Anfrage benutzerdefinierte Kopfzeilen hinzu. Nützlich für statische Tokens oder einen benutzerdefinierten Content-Typ. Standardmäßig wird Content-Type: application/json gesendet.",
|
||||
"httpDestNoHeadersConfigured": "Keine benutzerdefinierten Header konfiguriert. Klicken Sie auf \"Header hinzufügen\", um einen hinzuzufügen.",
|
||||
"httpDestHeaderNamePlaceholder": "Header-Name",
|
||||
"httpDestHeaderValuePlaceholder": "Wert",
|
||||
"httpDestAddHeader": "Header hinzufügen",
|
||||
"httpDestBodyTemplateTitle": "Eigene Body-Vorlage",
|
||||
"httpDestBodyTemplateDescription": "Steuere die JSON-Payload-Struktur, die an deinen Endpunkt gesendet wurde. Wenn deaktiviert, wird für jede Veranstaltung ein Standard-JSON-Objekt gesendet.",
|
||||
"httpDestEnableBodyTemplate": "Eigene Körpervorlage aktivieren",
|
||||
"httpDestBodyTemplateLabel": "Body-Vorlage (JSON)",
|
||||
"httpDestBodyTemplateHint": "Verwenden Sie Template-Variablen, um Ereignisfelder in Ihrer Payload zu referenzieren.",
|
||||
"httpDestPayloadFormatTitle": "Payload-Format",
|
||||
"httpDestPayloadFormatDescription": "Wie Ereignisse in jedes Anfragegremium serialisiert werden.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON Array",
|
||||
"httpDestFormatJsonArrayDescription": "Eine Anfrage pro Stapel ist ein JSON-Array. Kompatibel mit den meisten generischen Webhooks und Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Eine Anfrage pro Batch, der Körper ist newline-getrenntes JSON — ein Objekt pro Zeile, kein äußeres Array. Benötigt von Splunk HEC, Elastic / OpenSearch, und Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Ein Ereignis pro Anfrage",
|
||||
"httpDestFormatSingleDescription": "Sendet eine separate HTTP-POST für jedes einzelne Ereignis. Nur für Endpunkte, die Batches nicht handhaben können.",
|
||||
"httpDestLogTypesTitle": "Log-Typen",
|
||||
"httpDestLogTypesDescription": "Wählen Sie, welche Log-Typen an dieses Ziel weitergeleitet werden. Nur aktivierte Log-Typen werden gestreamt.",
|
||||
"httpDestAccessLogsTitle": "Zugriffsprotokolle",
|
||||
"httpDestAccessLogsDescription": "Ressourcenzugriffe, einschließlich authentifizierter und abgelehnter Anfragen.",
|
||||
"httpDestActionLogsTitle": "Aktionsprotokolle",
|
||||
"httpDestActionLogsDescription": "Administrative Maßnahmen, die von Benutzern innerhalb der Organisation durchgeführt werden.",
|
||||
"httpDestConnectionLogsTitle": "Verbindungsprotokolle",
|
||||
"httpDestConnectionLogsDescription": "Site- und Tunnelverbindungen, einschließlich Verbindungen und Trennungen.",
|
||||
"httpDestRequestLogsTitle": "Logs anfordern",
|
||||
"httpDestRequestLogsDescription": "HTTP-Request-Protokolle für proxiierte Ressourcen, einschließlich Methode, Pfad und Antwort-Code.",
|
||||
"httpDestSaveChanges": "Änderungen speichern",
|
||||
"httpDestCreateDestination": "Ziel erstellen",
|
||||
"httpDestUpdatedSuccess": "Ziel erfolgreich aktualisiert",
|
||||
"httpDestCreatedSuccess": "Ziel erfolgreich erstellt",
|
||||
"httpDestUpdateFailed": "Fehler beim Aktualisieren des Ziels",
|
||||
"httpDestCreateFailed": "Fehler beim Erstellen des Ziels"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "You're a member of {count, plural, =0 {no organization} one {one organization} other {# organizations}}.",
|
||||
"componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Thank you for supporting Pangolin as a {tier}!",
|
||||
"inviteErrorNotValid": "We're sorry, but it looks like the invite you're trying to access has not been accepted or is no longer valid.",
|
||||
@@ -55,7 +57,7 @@
|
||||
"siteDescription": "Create and manage sites to enable connectivity to private networks",
|
||||
"sitesBannerTitle": "Connect Any Network",
|
||||
"sitesBannerDescription": "A site is a connection to a remote network that allows Pangolin to provide access to resources, whether public or private, to users anywhere. Install the site network connector (Newt) anywhere you can run a binary or container to establish the connection.",
|
||||
"sitesBannerButtonText": "Install Site",
|
||||
"sitesBannerButtonText": "Install Site Connector",
|
||||
"approvalsBannerTitle": "Approve or Deny Device Access",
|
||||
"approvalsBannerDescription": "Review and approve or deny device access requests from users. When device approvals are required, users must get admin approval before their devices can connect to your organization's resources.",
|
||||
"approvalsBannerButtonText": "Learn More",
|
||||
@@ -79,8 +81,8 @@
|
||||
"siteConfirmCopy": "I have copied the config",
|
||||
"searchSitesProgress": "Search sites...",
|
||||
"siteAdd": "Add Site",
|
||||
"siteInstallNewt": "Install Newt",
|
||||
"siteInstallNewtDescription": "Get Newt running on your system",
|
||||
"siteInstallNewt": "Install Site",
|
||||
"siteInstallNewtDescription": "Install the site connector for your system",
|
||||
"WgConfiguration": "WireGuard Configuration",
|
||||
"WgConfigurationDescription": "Use the following configuration to connect to the network",
|
||||
"operatingSystem": "Operating System",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Create Link",
|
||||
"resourcesNotFound": "No resources found",
|
||||
"resourceSearch": "Search resources",
|
||||
"machineSearch": "Search machines",
|
||||
"machinesSearch": "Search machine clients...",
|
||||
"machineNotFound": "No machines found",
|
||||
"userDeviceSearch": "Search user devices",
|
||||
"userDevicesSearch": "Search user devices...",
|
||||
"openMenu": "Open menu",
|
||||
"resource": "Resource",
|
||||
"title": "Title",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy requests over HTTPS using a fully qualified domain name.",
|
||||
"resourceRaw": "Raw TCP/UDP Resource",
|
||||
"resourceRawDescription": "Proxy requests over raw TCP/UDP using a port number.",
|
||||
"resourceRawDescriptionCloud": "Proxy requests over raw TCP/UDP using a port number. Requires sites to connect to a remote node.",
|
||||
"resourceCreate": "Create Resource",
|
||||
"resourceCreateDescription": "Follow the steps below to create a new resource",
|
||||
"resourceSeeAll": "See All Resources",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Select a protocol",
|
||||
"resourcePortNumber": "Port Number",
|
||||
"resourcePortNumberDescription": "The external port number to proxy requests.",
|
||||
"back": "Back",
|
||||
"cancel": "Cancel",
|
||||
"resourceConfig": "Configuration Snippets",
|
||||
"resourceConfigDescription": "Copy and paste these configuration snippets to set up the TCP/UDP resource",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "An error occurred while deleting the organization.",
|
||||
"orgDeleted": "Organization deleted",
|
||||
"orgDeletedMessage": "The organization and its data has been deleted.",
|
||||
"deleteAccount": "Delete Account",
|
||||
"deleteAccountDescription": "Permanently delete your account, all organizations you own, and all data within those organizations. This cannot be undone.",
|
||||
"deleteAccountButton": "Delete Account",
|
||||
"deleteAccountConfirmTitle": "Delete Account",
|
||||
"deleteAccountConfirmMessage": "This will permanently wipe your account, all organizations you own, and all data within those organizations. This cannot be undone.",
|
||||
"deleteAccountConfirmString": "delete account",
|
||||
"deleteAccountSuccess": "Account Deleted",
|
||||
"deleteAccountSuccessMessage": "Your account has been deleted.",
|
||||
"deleteAccountError": "Failed to delete account",
|
||||
"deleteAccountPreviewAccount": "Your Account",
|
||||
"deleteAccountPreviewOrgs": "Organizations you own (and all their data)",
|
||||
"orgMissing": "Organization ID Missing",
|
||||
"orgMissingMessage": "Unable to regenerate invitation without an organization ID.",
|
||||
"accessUsersManage": "Manage Users",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Delete API Key",
|
||||
"apiKeysManage": "Manage API Keys",
|
||||
"apiKeysDescription": "API keys are used to authenticate with the integration API",
|
||||
"provisioningKeysTitle": "Provisioning Key",
|
||||
"provisioningKeysManage": "Manage Provisioning Keys",
|
||||
"provisioningKeysDescription": "Provisioning keys are used to authenticate automated site provisioning for your organization.",
|
||||
"provisioningManage": "Provisioning",
|
||||
"provisioningDescription": "Manage provisioning keys and review pending sites awaiting approval.",
|
||||
"pendingSites": "Pending Sites",
|
||||
"siteApproveSuccess": "Site approved successfully",
|
||||
"siteApproveError": "Error approving site",
|
||||
"provisioningKeys": "Provisioning Keys",
|
||||
"searchProvisioningKeys": "Search provisioning keys...",
|
||||
"provisioningKeysAdd": "Generate Provisioning Key",
|
||||
"provisioningKeysErrorDelete": "Error deleting provisioning key",
|
||||
"provisioningKeysErrorDeleteMessage": "Error deleting provisioning key",
|
||||
"provisioningKeysQuestionRemove": "Are you sure you want to remove this provisioning key from the organization?",
|
||||
"provisioningKeysMessageRemove": "Once removed, the key can no longer be used for site provisioning.",
|
||||
"provisioningKeysDeleteConfirm": "Confirm Delete Provisioning Key",
|
||||
"provisioningKeysDelete": "Delete Provisioning key",
|
||||
"provisioningKeysCreate": "Generate Provisioning Key",
|
||||
"provisioningKeysCreateDescription": "Generate a new provisioning key for the organization",
|
||||
"provisioningKeysSeeAll": "See all provisioning keys",
|
||||
"provisioningKeysSave": "Save the provisioning key",
|
||||
"provisioningKeysSaveDescription": "You will only be able to see this once. Copy it to a secure place.",
|
||||
"provisioningKeysErrorCreate": "Error creating provisioning key",
|
||||
"provisioningKeysList": "New provisioning key",
|
||||
"provisioningKeysMaxBatchSize": "Max batch size",
|
||||
"provisioningKeysUnlimitedBatchSize": "Unlimited batch size (no limit)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Unlimited",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Enter a valid max batch size (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Valid until",
|
||||
"provisioningKeysValidUntilHint": "Leave empty for no expiration.",
|
||||
"provisioningKeysValidUntilInvalid": "Enter a valid date and time.",
|
||||
"provisioningKeysNumUsed": "Times used",
|
||||
"provisioningKeysLastUsed": "Last used",
|
||||
"provisioningKeysNoExpiry": "No expiration",
|
||||
"provisioningKeysNeverUsed": "Never",
|
||||
"provisioningKeysEdit": "Edit Provisioning Key",
|
||||
"provisioningKeysEditDescription": "Update the max batch size and expiration time for this key.",
|
||||
"provisioningKeysApproveNewSites": "Approve new sites",
|
||||
"provisioningKeysApproveNewSitesDescription": "Automatically approve sites that register with this key.",
|
||||
"provisioningKeysUpdateError": "Error updating provisioning key",
|
||||
"provisioningKeysUpdated": "Provisioning key updated",
|
||||
"provisioningKeysUpdatedDescription": "Your changes have been saved.",
|
||||
"provisioningKeysBannerTitle": "Site Provisioning Keys",
|
||||
"provisioningKeysBannerDescription": "Generate a provisioning key and use it with the Newt connector to automatically create sites on first startup — no need to set up separate credentials for each site.",
|
||||
"provisioningKeysBannerButtonText": "Learn More",
|
||||
"pendingSitesBannerTitle": "Pending Sites",
|
||||
"pendingSitesBannerDescription": "Sites that connect using a provisioning key appear here for review. Approve each site before it becomes active and gains access to your resources.",
|
||||
"pendingSitesBannerButtonText": "Learn More",
|
||||
"apiKeysSettings": "{apiKeyName} Settings",
|
||||
"userTitle": "Manage All Users",
|
||||
"userDescription": "View and manage all users in the system",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filter By Approval State",
|
||||
"approvalListEmpty": "No approvals",
|
||||
"approvalState": "Approval State",
|
||||
"approvalLoadMore": "Load more",
|
||||
"loadingApprovals": "Loading Approvals",
|
||||
"approve": "Approve",
|
||||
"approved": "Approved",
|
||||
"denied": "Denied",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "User saved",
|
||||
"userSavedDescription": "The user has been updated.",
|
||||
"autoProvisioned": "Auto Provisioned",
|
||||
"autoProvisionSettings": "Auto Provision Settings",
|
||||
"autoProvisionedDescription": "Allow this user to be automatically managed by identity provider",
|
||||
"accessControlsDescription": "Manage what this user can access and do in the organization",
|
||||
"accessControlsSubmit": "Save Access Controls",
|
||||
"singleRolePerUserPlanNotice": "Your plan only supports one role per user.",
|
||||
"singleRolePerUserEditionNotice": "This edition only supports one role per user.",
|
||||
"roles": "Roles",
|
||||
"accessUsersRoles": "Manage Users & Roles",
|
||||
"accessUsersRolesDescription": "Invite users and add them to roles to manage access to the organization",
|
||||
@@ -551,6 +624,8 @@
|
||||
"targetErrorInvalidPortDescription": "Please enter a valid port number",
|
||||
"targetErrorNoSite": "No site selected",
|
||||
"targetErrorNoSiteDescription": "Please select a site for the target",
|
||||
"targetTargetsCleared": "Targets cleared",
|
||||
"targetTargetsClearedDescription": "All targets have been removed from this resource",
|
||||
"targetCreated": "Target created",
|
||||
"targetCreatedDescription": "Target has been created successfully",
|
||||
"targetErrorCreate": "Failed to create target",
|
||||
@@ -634,6 +709,7 @@
|
||||
"resourcesErrorUpdate": "Failed to toggle resource",
|
||||
"resourcesErrorUpdateDescription": "An error occurred while updating the resource",
|
||||
"access": "Access",
|
||||
"accessControl": "Access Control",
|
||||
"shareLink": "{resource} Share Link",
|
||||
"resourceSelect": "Select resource",
|
||||
"shareLinks": "Share Links",
|
||||
@@ -774,6 +850,7 @@
|
||||
"accessRoleRemoved": "Role removed",
|
||||
"accessRoleRemovedDescription": "The role has been successfully removed.",
|
||||
"accessRoleRequiredRemove": "Before deleting this role, please select a new role to transfer existing members to.",
|
||||
"network": "Network",
|
||||
"manage": "Manage",
|
||||
"sitesNotFound": "No sites found.",
|
||||
"pangolinServerAdmin": "Server Admin - Pangolin",
|
||||
@@ -789,6 +866,9 @@
|
||||
"sitestCountIncrease": "Increase site count",
|
||||
"idpManage": "Manage Identity Providers",
|
||||
"idpManageDescription": "View and manage identity providers in the system",
|
||||
"idpGlobalModeBanner": "Identity providers (IdPs) per organization are disabled on this server. It is using global IdPs (shared across all organizations). Manage global IdPs in the <adminPanelLink>admin panel</adminPanelLink>. To enable IdPs per organization, edit the server config and set IdP mode to org. <configDocsLink>See the docs</configDocsLink>. If you want to continue using global IdPs and make this disappear from the organization settings, explicitly set the mode to global in the config.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Identity providers (IdPs) per organization are disabled on this server. It is using global IdPs (shared across all organizations). Manage global IdPs in the <adminPanelLink>admin panel</adminPanelLink>. To use identity providers per organization, you must upgrade to the Enterprise edition.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Identity providers (IdPs) per organization are disabled on this server. It is using global IdPs (shared across all organizations). Manage global IdPs in the <adminPanelLink>admin panel</adminPanelLink>. To use identity providers per organization, an Enterprise license is required.",
|
||||
"idpDeletedDescription": "Identity provider deleted successfully",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Are you sure you want to permanently delete the identity provider?",
|
||||
@@ -865,7 +945,7 @@
|
||||
"defaultMappingsRole": "Default Role Mapping",
|
||||
"defaultMappingsRoleDescription": "The result of this expression must return the role name as defined in the organization as a string.",
|
||||
"defaultMappingsOrg": "Default Organization Mapping",
|
||||
"defaultMappingsOrgDescription": "This expression must return the org ID or true for the user to be allowed to access the organization.",
|
||||
"defaultMappingsOrgDescription": "When set, this expression must return the organization ID or true for the user to access that organization. When unset, defining an organization policy for that org is enough: the user is allowed in as long as a valid role mapping can be resolved for them within the organization.",
|
||||
"defaultMappingsSubmit": "Save Default Mappings",
|
||||
"orgPoliciesEdit": "Edit Organization Policy",
|
||||
"org": "Organization",
|
||||
@@ -1012,12 +1092,12 @@
|
||||
"pangolinSetup": "Setup - Pangolin",
|
||||
"orgNameRequired": "Organization name is required",
|
||||
"orgIdRequired": "Organization ID is required",
|
||||
"orgIdMaxLength": "Organization ID must be at most 32 characters",
|
||||
"orgErrorCreate": "An error occurred while creating org",
|
||||
"pageNotFound": "Page Not Found",
|
||||
"pageNotFoundDescription": "Oops! The page you're looking for doesn't exist.",
|
||||
"overview": "Overview",
|
||||
"home": "Home",
|
||||
"accessControl": "Access Control",
|
||||
"settings": "Settings",
|
||||
"usersAll": "All Users",
|
||||
"license": "License",
|
||||
@@ -1080,6 +1160,12 @@
|
||||
"actionGetUser": "Get User",
|
||||
"actionGetOrgUser": "Get Organization User",
|
||||
"actionListOrgDomains": "List Organization Domains",
|
||||
"actionGetDomain": "Get Domain",
|
||||
"actionCreateOrgDomain": "Create Domain",
|
||||
"actionUpdateOrgDomain": "Update Domain",
|
||||
"actionDeleteOrgDomain": "Delete Domain",
|
||||
"actionGetDNSRecords": "Get DNS Records",
|
||||
"actionRestartOrgDomain": "Restart Domain",
|
||||
"actionCreateSite": "Create Site",
|
||||
"actionDeleteSite": "Delete Site",
|
||||
"actionGetSite": "Get Site",
|
||||
@@ -1091,6 +1177,7 @@
|
||||
"setupTokenDescription": "Enter the setup token from the server console.",
|
||||
"setupTokenRequired": "Setup token is required",
|
||||
"actionUpdateSite": "Update Site",
|
||||
"actionResetSiteBandwidth": "Reset Organization Bandwidth",
|
||||
"actionListSiteRoles": "List Allowed Site Roles",
|
||||
"actionCreateResource": "Create Resource",
|
||||
"actionDeleteResource": "Delete Resource",
|
||||
@@ -1120,6 +1207,7 @@
|
||||
"actionRemoveUser": "Remove User",
|
||||
"actionListUsers": "List Users",
|
||||
"actionAddUserRole": "Add User Role",
|
||||
"actionSetUserOrgRoles": "Set User Roles",
|
||||
"actionGenerateAccessToken": "Generate Access Token",
|
||||
"actionDeleteAccessToken": "Delete Access Token",
|
||||
"actionListAccessTokens": "List Access Tokens",
|
||||
@@ -1164,7 +1252,8 @@
|
||||
"actionViewLogs": "View Logs",
|
||||
"noneSelected": "None selected",
|
||||
"orgNotFound2": "No organizations found.",
|
||||
"searchProgress": "Search...",
|
||||
"searchPlaceholder": "Search...",
|
||||
"emptySearchOptions": "No options found",
|
||||
"create": "Create",
|
||||
"orgs": "Organizations",
|
||||
"loginError": "An unexpected error occurred. Please try again.",
|
||||
@@ -1228,12 +1317,14 @@
|
||||
"sidebarClientResources": "Private",
|
||||
"sidebarAccessControl": "Access Control",
|
||||
"sidebarLogsAndAnalytics": "Logs & Analytics",
|
||||
"sidebarTeam": "Team",
|
||||
"sidebarUsers": "Users",
|
||||
"sidebarAdmin": "Admin",
|
||||
"sidebarInvitations": "Invitations",
|
||||
"sidebarRoles": "Roles",
|
||||
"sidebarShareableLinks": "Links",
|
||||
"sidebarApiKeys": "API Keys",
|
||||
"sidebarProvisioning": "Provisioning",
|
||||
"sidebarSettings": "Settings",
|
||||
"sidebarAllUsers": "All Users",
|
||||
"sidebarIdentityProviders": "Identity Providers",
|
||||
@@ -1246,6 +1337,8 @@
|
||||
"sidebarLogAndAnalytics": "Log & Analytics",
|
||||
"sidebarBluePrints": "Blueprints",
|
||||
"sidebarOrganization": "Organization",
|
||||
"sidebarManagement": "Management",
|
||||
"sidebarBillingAndLicenses": "Billing & Licenses",
|
||||
"sidebarLogsAnalytics": "Analytics",
|
||||
"blueprints": "Blueprints",
|
||||
"blueprintsDescription": "Apply declarative configurations and view previous runs",
|
||||
@@ -1266,8 +1359,7 @@
|
||||
"contents": "Contents",
|
||||
"parsedContents": "Parsed Contents (Read Only)",
|
||||
"enableDockerSocket": "Enable Docker Blueprint",
|
||||
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
||||
"enableDockerSocketLink": "Learn More",
|
||||
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt. Read about how this works in <docsLink>the documentation</docsLink>.",
|
||||
"viewDockerContainers": "View Docker Containers",
|
||||
"containersIn": "Containers in {siteName}",
|
||||
"selectContainerDescription": "Select any container to use as a hostname for this target. Click a port to use a port.",
|
||||
@@ -1395,6 +1487,7 @@
|
||||
"domainPickerNamespace": "Namespace: {namespace}",
|
||||
"domainPickerShowMore": "Show More",
|
||||
"regionSelectorTitle": "Select Region",
|
||||
"domainPickerRemoteExitNodeWarning": "Provided domains are not supported when sites connect to remote exit nodes. For resources to be available on remote nodes, use a custom domain instead.",
|
||||
"regionSelectorInfo": "Selecting a region helps us provide better performance for your location. You do not have to be in the same region as your server.",
|
||||
"regionSelectorPlaceholder": "Choose a region",
|
||||
"regionSelectorComingSoon": "Coming Soon",
|
||||
@@ -1404,10 +1497,11 @@
|
||||
"billingUsageLimitsOverview": "Usage Limits Overview",
|
||||
"billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@pangolin.net.",
|
||||
"billingDataUsage": "Data Usage",
|
||||
"billingOnlineTime": "Site Online Time",
|
||||
"billingUsers": "Active Users",
|
||||
"billingDomains": "Active Domains",
|
||||
"billingRemoteExitNodes": "Active Self-hosted Nodes",
|
||||
"billingSites": "Sites",
|
||||
"billingUsers": "Users",
|
||||
"billingDomains": "Domains",
|
||||
"billingOrganizations": "Orgs",
|
||||
"billingRemoteExitNodes": "Remote Nodes",
|
||||
"billingNoLimitConfigured": "No limit configured",
|
||||
"billingEstimatedPeriod": "Estimated Billing Period",
|
||||
"billingIncludedUsage": "Included Usage",
|
||||
@@ -1432,15 +1526,24 @@
|
||||
"billingFailedToGetPortalUrl": "Failed to get portal URL",
|
||||
"billingPortalError": "Portal Error",
|
||||
"billingDataUsageInfo": "You're charged for all data transferred through your secure tunnels when connected to the cloud. This includes both incoming and outgoing traffic across all your sites. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Data is not charged when using nodes.",
|
||||
"billingOnlineTimeInfo": "You're charged based on how long your sites stay connected to the cloud. For example, 44,640 minutes equals one site running 24/7 for a full month. When you reach your limit, your sites will disconnect until you upgrade your plan or reduce usage. Time is not charged when using nodes.",
|
||||
"billingUsersInfo": "You're charged for each user in the organization. Billing is calculated daily based on the number of active user accounts in your org.",
|
||||
"billingDomainInfo": "You're charged for each domain in the organization. Billing is calculated daily based on the number of active domain accounts in your org.",
|
||||
"billingRemoteExitNodesInfo": "You're charged for each managed Node in the organization. Billing is calculated daily based on the number of active managed Nodes in your org.",
|
||||
"billingSInfo": "How many sites you can use",
|
||||
"billingUsersInfo": "How many users you can use",
|
||||
"billingDomainInfo": "How many domains you can use",
|
||||
"billingRemoteExitNodesInfo": "How many remote nodes you can use",
|
||||
"billingLicenseKeys": "License Keys",
|
||||
"billingLicenseKeysDescription": "Manage your license key subscriptions",
|
||||
"billingLicenseSubscription": "License Subscription",
|
||||
"billingInactive": "Inactive",
|
||||
"billingLicenseItem": "License Item",
|
||||
"billingQuantity": "Quantity",
|
||||
"billingTotal": "total",
|
||||
"billingModifyLicenses": "Modify License Subscription",
|
||||
"domainNotFound": "Domain Not Found",
|
||||
"domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.",
|
||||
"failed": "Failed",
|
||||
"createNewOrgDescription": "Create a new organization",
|
||||
"organization": "Organization",
|
||||
"primary": "Primary",
|
||||
"port": "Port",
|
||||
"securityKeyManage": "Manage Security Keys",
|
||||
"securityKeyDescription": "Add or remove security keys for passwordless authentication",
|
||||
@@ -1512,6 +1615,42 @@
|
||||
"resourcePortRequired": "Port number is required for non-HTTP resources",
|
||||
"resourcePortNotAllowed": "Port number should not be set for HTTP resources",
|
||||
"billingPricingCalculatorLink": "Pricing Calculator",
|
||||
"billingYourPlan": "Your Plan",
|
||||
"billingViewOrModifyPlan": "View or modify your current plan",
|
||||
"billingViewPlanDetails": "View Plan Details",
|
||||
"billingUsageAndLimits": "Usage and Limits",
|
||||
"billingViewUsageAndLimits": "View your plan's limits and current usage",
|
||||
"billingCurrentUsage": "Current Usage",
|
||||
"billingMaximumLimits": "Maximum Limits",
|
||||
"billingRemoteNodes": "Remote Nodes",
|
||||
"billingUnlimited": "Unlimited",
|
||||
"billingPaidLicenseKeys": "Paid License Keys",
|
||||
"billingManageLicenseSubscription": "Manage your subscription for paid self-hosted license keys",
|
||||
"billingCurrentKeys": "Current Keys",
|
||||
"billingModifyCurrentPlan": "Modify Current Plan",
|
||||
"billingConfirmUpgrade": "Confirm Upgrade",
|
||||
"billingConfirmDowngrade": "Confirm Downgrade",
|
||||
"billingConfirmUpgradeDescription": "You are about to upgrade your plan. Review the new limits and pricing below.",
|
||||
"billingConfirmDowngradeDescription": "You are about to downgrade your plan. Review the new limits and pricing below.",
|
||||
"billingPlanIncludes": "Plan Includes",
|
||||
"billingProcessing": "Processing...",
|
||||
"billingConfirmUpgradeButton": "Confirm Upgrade",
|
||||
"billingConfirmDowngradeButton": "Confirm Downgrade",
|
||||
"billingLimitViolationWarning": "Usage Exceeds New Plan Limits",
|
||||
"billingLimitViolationDescription": "Your current usage exceeds the limits of this plan. After downgrading, all actions will be disabled until you reduce usage within the new limits. Please review the features below that are currently over the limits. Limits in violation:",
|
||||
"billingFeatureLossWarning": "Feature Availability Notice",
|
||||
"billingFeatureLossDescription": "By downgrading, features not available in the new plan will be automatically disabled. Some settings and configurations may be lost. Please review the pricing matrix to understand which features will no longer be available.",
|
||||
"billingUsageExceedsLimit": "Current usage ({current}) exceeds limit ({limit})",
|
||||
"billingPastDueTitle": "Payment Past Due",
|
||||
"billingPastDueDescription": "Your payment is past due. Please update your payment method to continue using your current plan features. If not resolved, your subscription will be canceled and you'll be reverted to the free tier.",
|
||||
"billingUnpaidTitle": "Subscription Unpaid",
|
||||
"billingUnpaidDescription": "Your subscription is unpaid and you have been reverted to the free tier. Please update your payment method to restore your subscription.",
|
||||
"billingIncompleteTitle": "Payment Incomplete",
|
||||
"billingIncompleteDescription": "Your payment is incomplete. Please complete the payment process to activate your subscription.",
|
||||
"billingIncompleteExpiredTitle": "Payment Expired",
|
||||
"billingIncompleteExpiredDescription": "Your payment was never completed and has expired. You have been reverted to the free tier. Please subscribe again to restore access to paid features.",
|
||||
"billingManageSubscription": "Manage your subscription",
|
||||
"billingResolvePaymentIssue": "Please resolve your payment issue before upgrading or downgrading",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "I agree to the",
|
||||
"termsOfService": "terms of service",
|
||||
@@ -1536,8 +1675,8 @@
|
||||
"addressDescription": "The internal address of the client. Must fall within the organization's subnet.",
|
||||
"selectSites": "Select sites",
|
||||
"sitesDescription": "The client will have connectivity to the selected sites",
|
||||
"clientInstallOlm": "Install Olm",
|
||||
"clientInstallOlmDescription": "Get Olm running on your system",
|
||||
"clientInstallOlm": "Install Machine Client",
|
||||
"clientInstallOlmDescription": "Install the machine client for your system",
|
||||
"clientOlmCredentials": "Credentials",
|
||||
"clientOlmCredentialsDescription": "This is how the client will authenticate with the server",
|
||||
"olmEndpoint": "Endpoint",
|
||||
@@ -1585,6 +1724,24 @@
|
||||
"timeIsInSeconds": "Time is in seconds",
|
||||
"requireDeviceApproval": "Require Device Approvals",
|
||||
"requireDeviceApprovalDescription": "Users with this role need new devices approved by an admin before they can connect and access resources.",
|
||||
"sshAccess": "SSH Access",
|
||||
"roleAllowSsh": "Allow SSH",
|
||||
"roleAllowSshAllow": "Allow",
|
||||
"roleAllowSshDisallow": "Disallow",
|
||||
"roleAllowSshDescription": "Allow users with this role to connect to resources via SSH. When disabled, the role cannot use SSH access.",
|
||||
"sshSudoMode": "Sudo Access",
|
||||
"sshSudoModeNone": "None",
|
||||
"sshSudoModeNoneDescription": "User cannot run commands with sudo.",
|
||||
"sshSudoModeFull": "Full Sudo",
|
||||
"sshSudoModeFullDescription": "User can run any command with sudo.",
|
||||
"sshSudoModeCommands": "Commands",
|
||||
"sshSudoModeCommandsDescription": "User can run only the specified commands with sudo.",
|
||||
"sshSudo": "Allow sudo",
|
||||
"sshSudoCommands": "Sudo Commands",
|
||||
"sshSudoCommandsDescription": "Comma separated list of commands the user is allowed to run with sudo.",
|
||||
"sshCreateHomeDir": "Create Home Directory",
|
||||
"sshUnixGroups": "Unix Groups",
|
||||
"sshUnixGroupsDescription": "Comma separated Unix groups to add the user to on the target host.",
|
||||
"retryAttempts": "Retry Attempts",
|
||||
"expectedResponseCodes": "Expected Response Codes",
|
||||
"expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.",
|
||||
@@ -1793,6 +1950,40 @@
|
||||
"exitNode": "Exit Node",
|
||||
"country": "Country",
|
||||
"rulesMatchCountry": "Currently based on source IP",
|
||||
"region": "Region",
|
||||
"selectRegion": "Select region",
|
||||
"searchRegions": "Search regions...",
|
||||
"noRegionFound": "No region found.",
|
||||
"rulesMatchRegion": "Select a regional grouping of countries",
|
||||
"rulesErrorInvalidRegion": "Invalid region",
|
||||
"rulesErrorInvalidRegionDescription": "Please select a valid region.",
|
||||
"regionAfrica": "Africa",
|
||||
"regionNorthernAfrica": "Northern Africa",
|
||||
"regionEasternAfrica": "Eastern Africa",
|
||||
"regionMiddleAfrica": "Middle Africa",
|
||||
"regionSouthernAfrica": "Southern Africa",
|
||||
"regionWesternAfrica": "Western Africa",
|
||||
"regionAmericas": "Americas",
|
||||
"regionCaribbean": "Caribbean",
|
||||
"regionCentralAmerica": "Central America",
|
||||
"regionSouthAmerica": "South America",
|
||||
"regionNorthernAmerica": "Northern America",
|
||||
"regionAsia": "Asia",
|
||||
"regionCentralAsia": "Central Asia",
|
||||
"regionEasternAsia": "Eastern Asia",
|
||||
"regionSouthEasternAsia": "South-Eastern Asia",
|
||||
"regionSouthernAsia": "Southern Asia",
|
||||
"regionWesternAsia": "Western Asia",
|
||||
"regionEurope": "Europe",
|
||||
"regionEasternEurope": "Eastern Europe",
|
||||
"regionNorthernEurope": "Northern Europe",
|
||||
"regionSouthernEurope": "Southern Europe",
|
||||
"regionWesternEurope": "Western Europe",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australia and New Zealand",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Managed Self-Hosted",
|
||||
"description": "More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles",
|
||||
@@ -1841,6 +2032,25 @@
|
||||
"invalidValue": "Invalid value",
|
||||
"idpTypeLabel": "Identity Provider Type",
|
||||
"roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||
"roleMappingModeFixedRoles": "Fixed Roles",
|
||||
"roleMappingModeMappingBuilder": "Mapping Builder",
|
||||
"roleMappingModeRawExpression": "Raw Expression",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Select one or more roles",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Type role names (exact match per organization)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Assign the same role set to every auto-provisioned user.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "For default policies, type role names that exist in each organization where users are provisioned. Names must match exactly.",
|
||||
"roleMappingClaimPath": "Claim Path",
|
||||
"roleMappingClaimPathPlaceholder": "groups",
|
||||
"roleMappingClaimPathDescription": "Path in the token payload that contains source values (for example, groups).",
|
||||
"roleMappingMatchValue": "Match Value",
|
||||
"roleMappingAssignRoles": "Assign Roles",
|
||||
"roleMappingAddMappingRule": "Add Mapping Rule",
|
||||
"roleMappingRawExpressionResultDescription": "Expression must evaluate to a string or string array.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Expression must evaluate to a string (a single role name).",
|
||||
"roleMappingMatchValuePlaceholder": "Match value (for example: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Type role names (exact per org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Role names must match a role in each target organization.",
|
||||
"roleMappingRemoveRule": "Remove",
|
||||
"idpGoogleConfiguration": "Google Configuration",
|
||||
"idpGoogleConfigurationDescription": "Configure the Google OAuth2 credentials",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2087,9 @@
|
||||
"authPageBrandingQuestionRemove": "Are you sure you want to remove the branding for Auth Pages ?",
|
||||
"authPageBrandingDeleteConfirm": "Confirm Delete Branding",
|
||||
"brandingLogoURL": "Logo URL",
|
||||
"brandingLogoURLOrPath": "Logo URL or Path",
|
||||
"brandingLogoPathDescription": "Enter a URL or a local path.",
|
||||
"brandingLogoURLDescription": "Enter a publicly accessible URL to your logo image.",
|
||||
"brandingPrimaryColor": "Primary Color",
|
||||
"brandingLogoWidth": "Width (px)",
|
||||
"brandingLogoHeight": "Height (px)",
|
||||
@@ -1926,6 +2139,13 @@
|
||||
"orgAuthBackToSignIn": "Back to standard sign in",
|
||||
"orgAuthNoAccount": "Don't have an account?",
|
||||
"subscriptionRequiredToUse": "A subscription is required to use this feature.",
|
||||
"mustUpgradeToUse": "You must upgrade your subscription to use this feature.",
|
||||
"subscriptionRequiredTierToUse": "This feature requires <tierLink>{tier}</tierLink>.",
|
||||
"upgradeToTierToUse": "Upgrade to <tierLink>{tier}</tierLink> to use this feature.",
|
||||
"subscriptionTierTier1": "Home",
|
||||
"subscriptionTierTier2": "Team",
|
||||
"subscriptionTierTier3": "Business",
|
||||
"subscriptionTierEnterprise": "Enterprise",
|
||||
"idpDisabled": "Identity providers are disabled.",
|
||||
"orgAuthPageDisabled": "Organization auth page is disabled.",
|
||||
"domainRestartedDescription": "Domain verification restarted successfully",
|
||||
@@ -2011,10 +2231,10 @@
|
||||
"manageMachineClients": "Manage Machine Clients",
|
||||
"manageMachineClientsDescription": "Create and manage clients that servers and systems use to privately connect to resources",
|
||||
"machineClientsBannerTitle": "Servers & Automated Systems",
|
||||
"machineClientsBannerDescription": "Machine clients are for servers and automated systems that are not associated with a specific user. They authenticate with an ID and secret, and can run with Pangolin CLI, Olm CLI, or Olm as a container.",
|
||||
"machineClientsBannerDescription": "Machine clients are for servers and automated systems that are not associated with a specific user. They authenticate with an ID and secret, and can be deployed as a CLI or a container.",
|
||||
"machineClientsBannerPangolinCLI": "Pangolin CLI",
|
||||
"machineClientsBannerOlmCLI": "Olm CLI",
|
||||
"machineClientsBannerOlmContainer": "Olm Container",
|
||||
"machineClientsBannerOlmContainer": "Container",
|
||||
"clientsTableUserClients": "User",
|
||||
"clientsTableMachineClients": "Machine",
|
||||
"licenseTableValidUntil": "Valid Until",
|
||||
@@ -2113,6 +2333,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Get a license",
|
||||
"description": "Choose a plan and tell us how you plan to use Pangolin.",
|
||||
"chooseTier": "Choose your plan",
|
||||
"viewPricingLink": "See pricing, features, and limits",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Starter",
|
||||
"description": "Enterprise features, 25 users, 25 sites, and community support."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Scale",
|
||||
"description": "Enterprise features, 50 users, 50 sites, and priority support."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Personal use only (free license — no checkout)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Continue to Checkout"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Checkout error",
|
||||
"description": "Could not start checkout. Please try again."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Priority",
|
||||
"priorityDescription": "Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.",
|
||||
"instanceName": "Instance Name",
|
||||
@@ -2201,6 +2447,8 @@
|
||||
"logRetentionAccessDescription": "How long to retain access logs",
|
||||
"logRetentionActionLabel": "Action Log Retention",
|
||||
"logRetentionActionDescription": "How long to retain action logs",
|
||||
"logRetentionConnectionLabel": "Connection Log Retention",
|
||||
"logRetentionConnectionDescription": "How long to retain connection logs",
|
||||
"logRetentionDisabled": "Disabled",
|
||||
"logRetention3Days": "3 days",
|
||||
"logRetention7Days": "7 days",
|
||||
@@ -2211,7 +2459,15 @@
|
||||
"logRetentionEndOfFollowingYear": "End of following year",
|
||||
"actionLogsDescription": "View a history of actions performed in this organization",
|
||||
"accessLogsDescription": "View access auth requests for resources in this organization",
|
||||
"licenseRequiredToUse": "An Enterprise license is required to use this feature.",
|
||||
"connectionLogs": "Connection Logs",
|
||||
"connectionLogsDescription": "View connection logs for tunnels in this organization",
|
||||
"sidebarLogsConnection": "Connection Logs",
|
||||
"sidebarLogsStreaming": "Streaming",
|
||||
"sourceAddress": "Source Address",
|
||||
"destinationAddress": "Destination Address",
|
||||
"duration": "Duration",
|
||||
"licenseRequiredToUse": "An <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> license or <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is required to use this feature. <bookADemoLink>Book a free demo or POC trial to learn more</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "The <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Book a free demo or POC trial to learn more</bookADemoLink>.",
|
||||
"certResolver": "Certificate Resolver",
|
||||
"certResolverDescription": "Select the certificate resolver to use for this resource.",
|
||||
"selectCertResolver": "Select Certificate Resolver",
|
||||
@@ -2376,9 +2632,9 @@
|
||||
"remoteExitNodeRegenerateCredentialsConfirmation": "Are you sure you want to regenerate the credentials for this remote exit node?",
|
||||
"remoteExitNodeRegenerateCredentialsWarning": "This will regenerate the credentials. The remote exit node will stay connected until you manually restart it and use the new credentials.",
|
||||
"agent": "Agent",
|
||||
"personalUseOnly": "Personal Use Only",
|
||||
"loginPageLicenseWatermark": "This instance is licensed for personal use only.",
|
||||
"instanceIsUnlicensed": "This instance is unlicensed.",
|
||||
"personalUseOnly": "Personal Use Only",
|
||||
"loginPageLicenseWatermark": "This instance is licensed for personal use only.",
|
||||
"instanceIsUnlicensed": "This instance is unlicensed.",
|
||||
"portRestrictions": "Port Restrictions",
|
||||
"allPorts": "All",
|
||||
"custom": "Custom",
|
||||
@@ -2408,6 +2664,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Access Control",
|
||||
"editInternalResourceDialogAccessControlDescription": "Control which roles, users, and machine clients have access to this resource when connected. Admins always have access.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Port range must be \"*\" for all ports, or a comma-separated list of ports and ranges (e.g., \"80,443,8000-9000\"). Ports must be between 1 and 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon Location",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Choose where the SSH authentication daemon runs: on the site (Newt) or on a remote host.",
|
||||
"internalResourceAuthDaemonDescription": "The SSH authentication daemon handles SSH key signing and PAM authentication for this resource. Choose whether it runs on the site (Newt) or on a separate remote host. See <docsLink>the documentation</docsLink> for more.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Select Strategy",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Location",
|
||||
"internalResourceAuthDaemonSite": "On Site",
|
||||
"internalResourceAuthDaemonSiteDescription": "Auth daemon runs on the site (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Remote Host",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Auth daemon runs on this resource's destination - not the site.",
|
||||
"internalResourceAuthDaemonPort": "Daemon Port (optional)",
|
||||
"orgAuthWhatsThis": "Where can I find my organization ID?",
|
||||
"learnMore": "Learn more",
|
||||
"backToHome": "Go back to home",
|
||||
@@ -2421,7 +2688,7 @@
|
||||
"automaticModeDescription": " Show maintenance page only when all backend targets are down or unhealthy. Your resource continues working normally as long as at least one target is healthy.",
|
||||
"forced": "Forced",
|
||||
"forcedModeDescription": "Always show the maintenance page regardless of backend health. Use this for planned maintenance when you want to prevent all access.",
|
||||
"warning:" : "Warning:",
|
||||
"warning:": "Warning:",
|
||||
"forcedeModeWarning": "All traffic will be directed to the maintenance page. Your backend resources will not receive any requests.",
|
||||
"pageTitle": "Page Title",
|
||||
"pageTitleDescription": "The main heading displayed on the maintenance page",
|
||||
@@ -2537,5 +2804,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Enable Device Approvals",
|
||||
"approvalsEmptyStateStep2Description": "Edit a role and enable the 'Require Device Approvals' option. Users with this role will need admin approval for new devices.",
|
||||
"approvalsEmptyStatePreviewDescription": "Preview: When enabled, pending device requests will appear here for review",
|
||||
"approvalsEmptyStateButtonText": "Manage Roles"
|
||||
"approvalsEmptyStateButtonText": "Manage Roles",
|
||||
"domainErrorTitle": "We are having trouble verifying your domain",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configure role mapping and organization policies on the <policiesTabLink>Auto Provision Settings</policiesTabLink> tab.",
|
||||
"streamingTitle": "Event Streaming",
|
||||
"streamingDescription": "Stream events from your organization to external destinations in real time.",
|
||||
"streamingUnnamedDestination": "Unnamed destination",
|
||||
"streamingNoUrlConfigured": "No URL configured",
|
||||
"streamingAddDestination": "Add Destination",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookDescription": "Send events to any HTTP endpoint with flexible authentication and templating.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Stream events to an S3-compatible object storage bucket. Coming soon.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Forward events directly to your Datadog account. Coming soon.",
|
||||
"streamingTypePickerDescription": "Choose a destination type to get started.",
|
||||
"streamingFailedToLoad": "Failed to load destinations",
|
||||
"streamingUnexpectedError": "An unexpected error occurred.",
|
||||
"streamingFailedToUpdate": "Failed to update destination",
|
||||
"streamingDeletedSuccess": "Destination deleted successfully",
|
||||
"streamingFailedToDelete": "Failed to delete destination",
|
||||
"streamingDeleteTitle": "Delete Destination",
|
||||
"streamingDeleteButtonText": "Delete Destination",
|
||||
"streamingDeleteDialogAreYouSure": "Are you sure you want to delete",
|
||||
"streamingDeleteDialogThisDestination": "this destination",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? All configuration will be permanently removed.",
|
||||
"httpDestEditTitle": "Edit Destination",
|
||||
"httpDestAddTitle": "Add HTTP Destination",
|
||||
"httpDestEditDescription": "Update the configuration for this HTTP event streaming destination.",
|
||||
"httpDestAddDescription": "Configure a new HTTP endpoint to receive your organization's events.",
|
||||
"httpDestTabSettings": "Settings",
|
||||
"httpDestTabHeaders": "Headers",
|
||||
"httpDestTabBody": "Body",
|
||||
"httpDestTabLogs": "Logs",
|
||||
"httpDestNamePlaceholder": "My HTTP destination",
|
||||
"httpDestUrlLabel": "Destination URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL must use http or https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS is required on cloud deployments",
|
||||
"httpDestUrlErrorInvalid": "Enter a valid URL (e.g. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Authentication",
|
||||
"httpDestAuthDescription": "Choose how requests to your endpoint are authenticated.",
|
||||
"httpDestAuthNoneTitle": "No Authentication",
|
||||
"httpDestAuthNoneDescription": "Sends requests without an Authorization header.",
|
||||
"httpDestAuthBearerTitle": "Bearer Token",
|
||||
"httpDestAuthBearerDescription": "Adds an Authorization: Bearer <token> header to each request.",
|
||||
"httpDestAuthBearerPlaceholder": "Your API key or token",
|
||||
"httpDestAuthBasicTitle": "Basic Auth",
|
||||
"httpDestAuthBasicDescription": "Adds an Authorization: Basic <credentials> header. Provide credentials as username:password.",
|
||||
"httpDestAuthBasicPlaceholder": "username:password",
|
||||
"httpDestAuthCustomTitle": "Custom Header",
|
||||
"httpDestAuthCustomDescription": "Specify a custom HTTP header name and value for authentication (e.g. X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Header name (e.g. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Header value",
|
||||
"httpDestCustomHeadersTitle": "Custom HTTP Headers",
|
||||
"httpDestCustomHeadersDescription": "Add custom headers to every outgoing request. Useful for static tokens or a custom Content-Type. By default, Content-Type: application/json is sent.",
|
||||
"httpDestNoHeadersConfigured": "No custom headers configured. Click \"Add Header\" to add one.",
|
||||
"httpDestHeaderNamePlaceholder": "Header name",
|
||||
"httpDestHeaderValuePlaceholder": "Value",
|
||||
"httpDestAddHeader": "Add Header",
|
||||
"httpDestBodyTemplateTitle": "Custom Body Template",
|
||||
"httpDestBodyTemplateDescription": "Control the JSON payload structure sent to your endpoint. If disabled, a default JSON object is sent for each event.",
|
||||
"httpDestEnableBodyTemplate": "Enable custom body template",
|
||||
"httpDestBodyTemplateLabel": "Body Template (JSON)",
|
||||
"httpDestBodyTemplateHint": "Use template variables to reference event fields in your payload.",
|
||||
"httpDestPayloadFormatTitle": "Payload Format",
|
||||
"httpDestPayloadFormatDescription": "How events are serialised into each request body.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON Array",
|
||||
"httpDestFormatJsonArrayDescription": "One request per batch, body is a JSON array. Compatible with most generic webhooks and Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "One request per batch, body is newline-delimited JSON — one object per line, no outer array. Required by Splunk HEC, Elastic / OpenSearch, and Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "One Event Per Request",
|
||||
"httpDestFormatSingleDescription": "Sends a separate HTTP POST for each individual event. Use only for endpoints that cannot handle batches.",
|
||||
"httpDestLogTypesTitle": "Log Types",
|
||||
"httpDestLogTypesDescription": "Choose which log types are forwarded to this destination. Only enabled log types will be streamed.",
|
||||
"httpDestAccessLogsTitle": "Access Logs",
|
||||
"httpDestAccessLogsDescription": "Resource access attempts, including authenticated and denied requests.",
|
||||
"httpDestActionLogsTitle": "Action Logs",
|
||||
"httpDestActionLogsDescription": "Administrative actions performed by users within the organization.",
|
||||
"httpDestConnectionLogsTitle": "Connection Logs",
|
||||
"httpDestConnectionLogsDescription": "Site and tunnel connection events, including connects and disconnects.",
|
||||
"httpDestRequestLogsTitle": "Request Logs",
|
||||
"httpDestRequestLogsDescription": "HTTP request logs for proxied resources, including method, path, and response code.",
|
||||
"httpDestSaveChanges": "Save Changes",
|
||||
"httpDestCreateDestination": "Create Destination",
|
||||
"httpDestUpdatedSuccess": "Destination updated successfully",
|
||||
"httpDestCreatedSuccess": "Destination created successfully",
|
||||
"httpDestUpdateFailed": "Failed to update destination",
|
||||
"httpDestCreateFailed": "Failed to create destination"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Eres un miembro de {count, plural, =0 {ninguna organización} one {una organización} other {# organizaciones}}.",
|
||||
"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",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "¡Gracias por apoyar a Pangolin como {tier}!",
|
||||
"inviteErrorNotValid": "Lo sentimos, pero parece que la invitación a la que intentas acceder no ha sido aceptada o ya no es válida.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Crear enlace",
|
||||
"resourcesNotFound": "No se encontraron recursos",
|
||||
"resourceSearch": "Buscar recursos",
|
||||
"machineSearch": "Buscar máquinas",
|
||||
"machinesSearch": "Buscar clientes...",
|
||||
"machineNotFound": "No hay máquinas",
|
||||
"userDeviceSearch": "Buscar dispositivos de usuario",
|
||||
"userDevicesSearch": "Buscar dispositivos de usuario...",
|
||||
"openMenu": "Abrir menú",
|
||||
"resource": "Recurso",
|
||||
"title": "Título",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy proporciona solicitudes sobre HTTPS usando un nombre de dominio completamente calificado.",
|
||||
"resourceRaw": "Recurso TCP/UDP sin procesar",
|
||||
"resourceRawDescription": "Proxy proporciona solicitudes sobre TCP/UDP usando un número de puerto.",
|
||||
"resourceRawDescriptionCloud": "Las peticiones de proxy sobre TCP/UDP crudas usando un número de puerto. Requiere que los sitios se conecten a un nodo remoto.",
|
||||
"resourceCreate": "Crear Recurso",
|
||||
"resourceCreateDescription": "Siga los siguientes pasos para crear un nuevo recurso",
|
||||
"resourceSeeAll": "Ver todos los recursos",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Seleccionar un protocolo",
|
||||
"resourcePortNumber": "Número de puerto",
|
||||
"resourcePortNumberDescription": "El número de puerto externo a las solicitudes de proxy.",
|
||||
"back": "Atrás",
|
||||
"cancel": "Cancelar",
|
||||
"resourceConfig": "Fragmentos de configuración",
|
||||
"resourceConfigDescription": "Copia y pega estos fragmentos de configuración para configurar el recurso TCP/UDP",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Se ha producido un error al eliminar la organización.",
|
||||
"orgDeleted": "Organización eliminada",
|
||||
"orgDeletedMessage": "La organización y sus datos han sido eliminados.",
|
||||
"deleteAccount": "Eliminar cuenta",
|
||||
"deleteAccountDescription": "Elimina permanentemente tu cuenta, todas las organizaciones que posees y todos los datos dentro de esas organizaciones. Esto no se puede deshacer.",
|
||||
"deleteAccountButton": "Eliminar cuenta",
|
||||
"deleteAccountConfirmTitle": "Eliminar cuenta",
|
||||
"deleteAccountConfirmMessage": "Esto borrará permanentemente tu cuenta, todas las organizaciones que posees y todos los datos dentro de esas organizaciones. Esto no se puede deshacer.",
|
||||
"deleteAccountConfirmString": "eliminar cuenta",
|
||||
"deleteAccountSuccess": "Cuenta eliminada",
|
||||
"deleteAccountSuccessMessage": "Tu cuenta ha sido eliminada.",
|
||||
"deleteAccountError": "Error al eliminar la cuenta",
|
||||
"deleteAccountPreviewAccount": "Tu cuenta",
|
||||
"deleteAccountPreviewOrgs": "Organizaciones que tienes (y todos sus datos)",
|
||||
"orgMissing": "Falta el ID de la organización",
|
||||
"orgMissingMessage": "No se puede regenerar la invitación sin el ID de la organización.",
|
||||
"accessUsersManage": "Administrar usuarios",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Borrar Clave API",
|
||||
"apiKeysManage": "Administrar claves API",
|
||||
"apiKeysDescription": "Las claves API se utilizan para autenticar con la API de integración",
|
||||
"provisioningKeysTitle": "Clave de aprovisionamiento",
|
||||
"provisioningKeysManage": "Administrar Claves de Aprovisionamiento",
|
||||
"provisioningKeysDescription": "Las claves de aprovisionamiento se utilizan para autenticar la provisión automatizada del sitio para su organización.",
|
||||
"provisioningManage": "Aprovisionamiento",
|
||||
"provisioningDescription": "Administrar las claves de aprovisionamiento y revisar los sitios pendientes de aprobación.",
|
||||
"pendingSites": "Sitios pendientes",
|
||||
"siteApproveSuccess": "Sitio aprobado con éxito",
|
||||
"siteApproveError": "Error al aprobar el sitio",
|
||||
"provisioningKeys": "Claves de aprovisionamiento",
|
||||
"searchProvisioningKeys": "Buscar claves de suministro...",
|
||||
"provisioningKeysAdd": "Generar clave de aprovisionamiento",
|
||||
"provisioningKeysErrorDelete": "Error al eliminar la clave de aprovisionamiento",
|
||||
"provisioningKeysErrorDeleteMessage": "Error al eliminar la clave de aprovisionamiento",
|
||||
"provisioningKeysQuestionRemove": "¿Está seguro que desea eliminar esta clave de aprovisionamiento de la organización?",
|
||||
"provisioningKeysMessageRemove": "Una vez eliminada, la clave ya no se puede utilizar para la disposición del sitio.",
|
||||
"provisioningKeysDeleteConfirm": "Confirmar Eliminar Clave de Aprovisionamiento",
|
||||
"provisioningKeysDelete": "Eliminar clave de aprovisionamiento",
|
||||
"provisioningKeysCreate": "Generar clave de aprovisionamiento",
|
||||
"provisioningKeysCreateDescription": "Generar una nueva clave de aprovisionamiento para la organización",
|
||||
"provisioningKeysSeeAll": "Ver todas las claves de aprovisionamiento",
|
||||
"provisioningKeysSave": "Guardar la clave de aprovisionamiento",
|
||||
"provisioningKeysSaveDescription": "Sólo podrás verlo una vez. Copítalo a un lugar seguro.",
|
||||
"provisioningKeysErrorCreate": "Error al crear la clave de provisioning",
|
||||
"provisioningKeysList": "Nueva clave de aprovisionamiento",
|
||||
"provisioningKeysMaxBatchSize": "Tamaño máximo de lote",
|
||||
"provisioningKeysUnlimitedBatchSize": "Tamaño ilimitado del lote (sin límite)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Ilimitado",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Introduzca un tamaño máximo de lote válido (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Válido hasta",
|
||||
"provisioningKeysValidUntilHint": "Dejar vacío para no expirar.",
|
||||
"provisioningKeysValidUntilInvalid": "Introduzca una fecha y hora válidas.",
|
||||
"provisioningKeysNumUsed": "Tiempos usados",
|
||||
"provisioningKeysLastUsed": "Último uso",
|
||||
"provisioningKeysNoExpiry": "No expiración",
|
||||
"provisioningKeysNeverUsed": "Nunca",
|
||||
"provisioningKeysEdit": "Editar clave de aprovisionamiento",
|
||||
"provisioningKeysEditDescription": "Actualizar el tamaño máximo de lote y el tiempo de caducidad para esta clave.",
|
||||
"provisioningKeysApproveNewSites": "Aprobar nuevos sitios",
|
||||
"provisioningKeysApproveNewSitesDescription": "Aprobar automáticamente los sitios que se registran con esta clave.",
|
||||
"provisioningKeysUpdateError": "Error al actualizar la clave de aprovisionamiento",
|
||||
"provisioningKeysUpdated": "Clave de aprovisionamiento actualizada",
|
||||
"provisioningKeysUpdatedDescription": "Sus cambios han sido guardados.",
|
||||
"provisioningKeysBannerTitle": "Claves de aprovisionamiento del sitio",
|
||||
"provisioningKeysBannerDescription": "Generar una clave de aprovisionamiento y usarla con el conector Newt para crear automáticamente sitios en el primer inicio — no es necesario configurar credenciales separadas para cada sitio.",
|
||||
"provisioningKeysBannerButtonText": "Saber más",
|
||||
"pendingSitesBannerTitle": "Sitios pendientes",
|
||||
"pendingSitesBannerDescription": "Los sitios que se conectan usando una clave de aprovisionamiento aparecen aquí para su revisión. Aprobar cada sitio antes de que se active y obtenga acceso a sus recursos.",
|
||||
"pendingSitesBannerButtonText": "Saber más",
|
||||
"apiKeysSettings": "Ajustes {apiKeyName}",
|
||||
"userTitle": "Administrar todos los usuarios",
|
||||
"userDescription": "Ver y administrar todos los usuarios en el sistema",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtrar por estado de aprobación",
|
||||
"approvalListEmpty": "No hay aprobaciones",
|
||||
"approvalState": "Estado de aprobación",
|
||||
"approvalLoadMore": "Cargar más",
|
||||
"loadingApprovals": "Cargando aprobaciones",
|
||||
"approve": "Aprobar",
|
||||
"approved": "Aprobado",
|
||||
"denied": "Denegado",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Usuario guardado",
|
||||
"userSavedDescription": "El usuario ha sido actualizado.",
|
||||
"autoProvisioned": "Auto asegurado",
|
||||
"autoProvisionSettings": "Configuración de Auto Provision",
|
||||
"autoProvisionedDescription": "Permitir a este usuario ser administrado automáticamente por el proveedor de identidad",
|
||||
"accessControlsDescription": "Administrar lo que este usuario puede acceder y hacer en la organización",
|
||||
"accessControlsSubmit": "Guardar controles de acceso",
|
||||
"singleRolePerUserPlanNotice": "Tu plan sólo soporta un rol por usuario.",
|
||||
"singleRolePerUserEditionNotice": "Esta edición sólo soporta un rol por usuario.",
|
||||
"roles": "Roles",
|
||||
"accessUsersRoles": "Administrar usuarios y roles",
|
||||
"accessUsersRolesDescription": "Invitar usuarios y añadirlos a roles para administrar el acceso a la organización",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Error al cambiar el recurso",
|
||||
"resourcesErrorUpdateDescription": "Se ha producido un error al actualizar el recurso",
|
||||
"access": "Acceder",
|
||||
"accessControl": "Control de acceso",
|
||||
"shareLink": "{resource} Compartir Enlace",
|
||||
"resourceSelect": "Seleccionar recurso",
|
||||
"shareLinks": "Compartir enlaces",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rol eliminado",
|
||||
"accessRoleRemovedDescription": "El rol se ha eliminado correctamente.",
|
||||
"accessRoleRequiredRemove": "Antes de eliminar este rol, seleccione un nuevo rol al que transferir miembros existentes.",
|
||||
"network": "Red",
|
||||
"manage": "Gestionar",
|
||||
"sitesNotFound": "Sitios no encontrados.",
|
||||
"pangolinServerAdmin": "Admin Servidor - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Aumentar el número de sitios",
|
||||
"idpManage": "Administrar proveedores de identidad",
|
||||
"idpManageDescription": "Ver y administrar proveedores de identidad en el sistema",
|
||||
"idpGlobalModeBanner": "Los proveedores de identidad (IdPs) por organización están deshabilitados en este servidor. Está utilizando IdPs globales (compartidos entre todas las organizaciones). Administra los IdPs globales en el <adminPanelLink>panel de administración</adminPanelLink>. Para habilitar los IdPs por organización, edita la configuración del servidor y establece el modo de IdP en org. <configDocsLink>Consulta la documentación</configDocsLink>. Si deseas seguir utilizando IdPs globales y hacer que esto desaparezca de las configuraciones de la organización, establece explícitamente el modo en global en la configuración.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Los proveedores de identidad (IdPs) por organización están deshabilitados en este servidor. Está utilizando IdPs globales (compartidos entre todas las organizaciones). Administra los IdPs globales en el <adminPanelLink>panel de administración</adminPanelLink>. Para usar proveedores de identidad por organización, debes actualizar a la edición Empresarial.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Los proveedores de identidad (IdPs) por organización están deshabilitados en este servidor. Está utilizando identificadores globales (compartidos en todas las organizaciones). Gestionar identificaciones globales en el panel <adminPanelLink>de administración</adminPanelLink>. Para utilizar proveedores de identidad por organización, se requiere una licencia de empresa.",
|
||||
"idpDeletedDescription": "Proveedor de identidad eliminado correctamente",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "¿Está seguro que desea eliminar permanentemente el proveedor de identidad?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Configuración - Pangolin",
|
||||
"orgNameRequired": "El nombre de la organización es obligatorio",
|
||||
"orgIdRequired": "El ID de la organización es obligatorio",
|
||||
"orgIdMaxLength": "El ID de la organización debe tener como máximo 32 caracteres",
|
||||
"orgErrorCreate": "Se ha producido un error al crear el org",
|
||||
"pageNotFound": "Página no encontrada",
|
||||
"pageNotFoundDescription": "¡Vaya! La página que estás buscando no existe.",
|
||||
"overview": "Resumen",
|
||||
"home": "Inicio",
|
||||
"accessControl": "Control de acceso",
|
||||
"settings": "Ajustes",
|
||||
"usersAll": "Todos los usuarios",
|
||||
"license": "Licencia",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Obtener usuario",
|
||||
"actionGetOrgUser": "Obtener usuario de la organización",
|
||||
"actionListOrgDomains": "Listar dominios de la organización",
|
||||
"actionGetDomain": "Obtener dominio",
|
||||
"actionCreateOrgDomain": "Crear dominio",
|
||||
"actionUpdateOrgDomain": "Actualizar dominio",
|
||||
"actionDeleteOrgDomain": "Eliminar dominio",
|
||||
"actionGetDNSRecords": "Obtener registros DNS",
|
||||
"actionRestartOrgDomain": "Reiniciar dominio",
|
||||
"actionCreateSite": "Crear sitio",
|
||||
"actionDeleteSite": "Eliminar sitio",
|
||||
"actionGetSite": "Obtener sitio",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Ingrese el token de configuración desde la consola del servidor.",
|
||||
"setupTokenRequired": "Se requiere el token de configuración",
|
||||
"actionUpdateSite": "Actualizar sitio",
|
||||
"actionResetSiteBandwidth": "Restablecer ancho de banda de la organización",
|
||||
"actionListSiteRoles": "Lista de roles permitidos del sitio",
|
||||
"actionCreateResource": "Crear Recurso",
|
||||
"actionDeleteResource": "Eliminar Recurso",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Eliminar usuario",
|
||||
"actionListUsers": "Listar usuarios",
|
||||
"actionAddUserRole": "Añadir rol de usuario",
|
||||
"actionSetUserOrgRoles": "Establecer roles de usuario",
|
||||
"actionGenerateAccessToken": "Generar token de acceso",
|
||||
"actionDeleteAccessToken": "Eliminar token de acceso",
|
||||
"actionListAccessTokens": "Lista de Tokens de Acceso",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Ver registros",
|
||||
"noneSelected": "Ninguno seleccionado",
|
||||
"orgNotFound2": "No se encontraron organizaciones.",
|
||||
"searchProgress": "Buscar...",
|
||||
"searchPlaceholder": "Buscar...",
|
||||
"emptySearchOptions": "No se encontraron opciones",
|
||||
"create": "Crear",
|
||||
"orgs": "Organizaciones",
|
||||
"loginError": "Ocurrió un error inesperado. Por favor, inténtelo de nuevo.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privado",
|
||||
"sidebarAccessControl": "Control de acceso",
|
||||
"sidebarLogsAndAnalytics": "Registros y análisis",
|
||||
"sidebarTeam": "Equipo",
|
||||
"sidebarUsers": "Usuarios",
|
||||
"sidebarAdmin": "Admin",
|
||||
"sidebarInvitations": "Invitaciones",
|
||||
"sidebarRoles": "Roles",
|
||||
"sidebarShareableLinks": "Enlaces",
|
||||
"sidebarApiKeys": "Claves API",
|
||||
"sidebarProvisioning": "Aprovisionamiento",
|
||||
"sidebarSettings": "Ajustes",
|
||||
"sidebarAllUsers": "Todos los usuarios",
|
||||
"sidebarIdentityProviders": "Proveedores de identidad",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Registro y análisis",
|
||||
"sidebarBluePrints": "Planos",
|
||||
"sidebarOrganization": "Organización",
|
||||
"sidebarManagement": "Gestión",
|
||||
"sidebarBillingAndLicenses": "Facturación y licencias",
|
||||
"sidebarLogsAnalytics": "Analíticas",
|
||||
"blueprints": "Planos",
|
||||
"blueprintsDescription": "Aplicar configuraciones declarativas y ver ejecuciones anteriores",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Contenido analizado (Sólo lectura)",
|
||||
"enableDockerSocket": "Habilitar Plano Docker",
|
||||
"enableDockerSocketDescription": "Activar el raspado de etiquetas de Socket Docker para etiquetas de planos. La ruta del Socket debe proporcionarse a Newt.",
|
||||
"enableDockerSocketLink": "Saber más",
|
||||
"viewDockerContainers": "Ver contenedores Docker",
|
||||
"containersIn": "Contenedores en {siteName}",
|
||||
"selectContainerDescription": "Seleccione cualquier contenedor para usar como nombre de host para este objetivo. Haga clic en un puerto para usar un puerto.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Espacio de nombres: {namespace}",
|
||||
"domainPickerShowMore": "Mostrar más",
|
||||
"regionSelectorTitle": "Seleccionar Región",
|
||||
"domainPickerRemoteExitNodeWarning": "Los dominios suministrados no son compatibles cuando los sitios se conectan a nodos de salida remotos. Para que los recursos estén disponibles en nodos remotos, utilice un dominio personalizado en su lugar.",
|
||||
"regionSelectorInfo": "Seleccionar una región nos ayuda a brindar un mejor rendimiento para tu ubicación. No tienes que estar en la misma región que tu servidor.",
|
||||
"regionSelectorPlaceholder": "Elige una región",
|
||||
"regionSelectorComingSoon": "Próximamente",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Descripción general de los límites de uso",
|
||||
"billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@pangolin.net.",
|
||||
"billingDataUsage": "Uso de datos",
|
||||
"billingOnlineTime": "Tiempo en línea del sitio",
|
||||
"billingUsers": "Usuarios activos",
|
||||
"billingDomains": "Dominios activos",
|
||||
"billingRemoteExitNodes": "Nodos autogestionados activos",
|
||||
"billingSites": "Sitios",
|
||||
"billingUsers": "Usuarios",
|
||||
"billingDomains": "Dominios",
|
||||
"billingOrganizations": "Orgánico",
|
||||
"billingRemoteExitNodes": "Nodos remotos",
|
||||
"billingNoLimitConfigured": "No se ha configurado ningún límite",
|
||||
"billingEstimatedPeriod": "Período de facturación estimado",
|
||||
"billingIncludedUsage": "Uso incluido",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Error al obtener la URL del portal",
|
||||
"billingPortalError": "Error del portal",
|
||||
"billingDataUsageInfo": "Se le cobran todos los datos transferidos a través de sus túneles seguros cuando se conectan a la nube. Esto incluye tanto tráfico entrante como saliente a través de todos sus sitios. Cuando alcance su límite, sus sitios se desconectarán hasta que actualice su plan o reduzca el uso. Los datos no se cargan cuando se usan nodos.",
|
||||
"billingOnlineTimeInfo": "Se te cobrará en función del tiempo que tus sitios permanezcan conectados a la nube. Por ejemplo, 44.640 minutos equivale a un sitio que funciona 24/7 durante un mes completo. Cuando alcance su límite, sus sitios se desconectarán hasta que mejore su plan o reduzca el uso. No se cargará el tiempo al usar nodos.",
|
||||
"billingUsersInfo": "Se le cobra por cada usuario en la organización. La facturación se calcula diariamente según el número de cuentas de usuario activas en su órgano.",
|
||||
"billingDomainInfo": "Se le cobra por cada dominio en la organización. La facturación se calcula diariamente en función del número de cuentas de dominio activas en su órgano.",
|
||||
"billingRemoteExitNodesInfo": "Se le cobra por cada nodo administrado en la organización. La facturación se calcula diariamente en función del número de nodos activos gestionados en su órgano.",
|
||||
"billingSInfo": "Cuántos sitios puedes usar",
|
||||
"billingUsersInfo": "Cuántos usuarios puedes usar",
|
||||
"billingDomainInfo": "Cuántos dominios puedes usar",
|
||||
"billingRemoteExitNodesInfo": "Cuántos nodos remotos puedes usar",
|
||||
"billingLicenseKeys": "Claves de licencia",
|
||||
"billingLicenseKeysDescription": "Administrar las suscripciones de su clave de licencia",
|
||||
"billingLicenseSubscription": "Suscripción de licencia",
|
||||
"billingInactive": "Inactivo",
|
||||
"billingLicenseItem": "Licencia",
|
||||
"billingQuantity": "Cantidad",
|
||||
"billingTotal": "total",
|
||||
"billingModifyLicenses": "Modificar suscripción de licencia",
|
||||
"domainNotFound": "Dominio no encontrado",
|
||||
"domainNotFoundDescription": "Este recurso está deshabilitado porque el dominio ya no existe en nuestro sistema. Por favor, establece un nuevo dominio para este recurso.",
|
||||
"failed": "Fallido",
|
||||
"createNewOrgDescription": "Crear una nueva organización",
|
||||
"organization": "Organización",
|
||||
"primary": "Principal",
|
||||
"port": "Puerto",
|
||||
"securityKeyManage": "Gestionar llaves de seguridad",
|
||||
"securityKeyDescription": "Agregar o eliminar llaves de seguridad para autenticación sin contraseña",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Se requiere número de puerto para recursos no HTTP",
|
||||
"resourcePortNotAllowed": "El número de puerto no debe establecerse para recursos HTTP",
|
||||
"billingPricingCalculatorLink": "Calculadora de Precios",
|
||||
"billingYourPlan": "Su plan",
|
||||
"billingViewOrModifyPlan": "Ver o modificar su plan actual",
|
||||
"billingViewPlanDetails": "Ver detalles del plan",
|
||||
"billingUsageAndLimits": "Uso y límites",
|
||||
"billingViewUsageAndLimits": "Ver los límites de tu plan y el uso actual",
|
||||
"billingCurrentUsage": "Uso actual",
|
||||
"billingMaximumLimits": "Límites máximos",
|
||||
"billingRemoteNodes": "Nodos remotos",
|
||||
"billingUnlimited": "Ilimitado",
|
||||
"billingPaidLicenseKeys": "Claves de licencia pagadas",
|
||||
"billingManageLicenseSubscription": "Administra tu suscripción para las claves de licencia autoalojadas pagadas",
|
||||
"billingCurrentKeys": "Claves actuales",
|
||||
"billingModifyCurrentPlan": "Modificar plan actual",
|
||||
"billingConfirmUpgrade": "Confirmar actualización",
|
||||
"billingConfirmDowngrade": "Confirmar descenso",
|
||||
"billingConfirmUpgradeDescription": "Estás a punto de actualizar tu plan. Revisa los nuevos límites y precios a continuación.",
|
||||
"billingConfirmDowngradeDescription": "Está a punto de rebajar su plan. Revise los nuevos límites y los precios a continuación.",
|
||||
"billingPlanIncludes": "Plan Incluye",
|
||||
"billingProcessing": "Procesando...",
|
||||
"billingConfirmUpgradeButton": "Confirmar actualización",
|
||||
"billingConfirmDowngradeButton": "Confirmar descenso",
|
||||
"billingLimitViolationWarning": "El uso excede los nuevos límites del plan",
|
||||
"billingLimitViolationDescription": "Su uso actual excede los límites de este plan. Después de degradar, todas las acciones se desactivarán hasta que reduzca el uso dentro de los nuevos límites. Por favor, revisa las siguientes características que están actualmente por encima de los límites. Límites en violación:",
|
||||
"billingFeatureLossWarning": "Aviso de disponibilidad de funcionalidad",
|
||||
"billingFeatureLossDescription": "Al degradar, las características no disponibles en el nuevo plan se desactivarán automáticamente. Algunas configuraciones y configuraciones pueden perderse. Por favor, revise la matriz de precios para entender qué características ya no estarán disponibles.",
|
||||
"billingUsageExceedsLimit": "El uso actual ({current}) supera el límite ({limit})",
|
||||
"billingPastDueTitle": "Pago vencido",
|
||||
"billingPastDueDescription": "Su pago ha vencido. Por favor, actualice su método de pago para seguir utilizando las características actuales de su plan. Si no se resuelve, tu suscripción se cancelará y serás revertido al nivel gratuito.",
|
||||
"billingUnpaidTitle": "Suscripción no pagada",
|
||||
"billingUnpaidDescription": "Tu suscripción no está pagada y has sido revertido al nivel gratuito. Por favor, actualiza tu método de pago para restaurar tu suscripción.",
|
||||
"billingIncompleteTitle": "Pago incompleto",
|
||||
"billingIncompleteDescription": "Su pago está incompleto. Por favor, complete el proceso de pago para activar su suscripción.",
|
||||
"billingIncompleteExpiredTitle": "Pago expirado",
|
||||
"billingIncompleteExpiredDescription": "Tu pago nunca se completó y ha expirado. Has sido revertido al nivel gratuito. Suscríbete de nuevo para restaurar el acceso a las funciones de pago.",
|
||||
"billingManageSubscription": "Administra tu suscripción",
|
||||
"billingResolvePaymentIssue": "Por favor resuelva su problema de pago antes de actualizar o bajar de calificación",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Estoy de acuerdo con los",
|
||||
"termsOfService": "términos del servicio",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "El tiempo está en segundos",
|
||||
"requireDeviceApproval": "Requiere aprobaciones del dispositivo",
|
||||
"requireDeviceApprovalDescription": "Los usuarios con este rol necesitan nuevos dispositivos aprobados por un administrador antes de poder conectarse y acceder a los recursos.",
|
||||
"sshAccess": "Acceso a SSH",
|
||||
"roleAllowSsh": "Permitir SSH",
|
||||
"roleAllowSshAllow": "Permitir",
|
||||
"roleAllowSshDisallow": "Rechazar",
|
||||
"roleAllowSshDescription": "Permitir a los usuarios con este rol conectarse a recursos a través de SSH. Cuando está desactivado, el rol no puede usar acceso SSH.",
|
||||
"sshSudoMode": "Acceso Sudo",
|
||||
"sshSudoModeNone": "Ninguna",
|
||||
"sshSudoModeNoneDescription": "El usuario no puede ejecutar comandos con sudo.",
|
||||
"sshSudoModeFull": "Sudo completo",
|
||||
"sshSudoModeFullDescription": "El usuario puede ejecutar cualquier comando con sudo.",
|
||||
"sshSudoModeCommands": "Comandos",
|
||||
"sshSudoModeCommandsDescription": "El usuario sólo puede ejecutar los comandos especificados con sudo.",
|
||||
"sshSudo": "Permitir sudo",
|
||||
"sshSudoCommands": "Comandos Sudo",
|
||||
"sshSudoCommandsDescription": "Lista separada por comas de comandos que el usuario puede ejecutar con sudo.",
|
||||
"sshCreateHomeDir": "Crear directorio principal",
|
||||
"sshUnixGroups": "Grupos Unix",
|
||||
"sshUnixGroupsDescription": "Grupos Unix separados por comas para agregar el usuario en el host de destino.",
|
||||
"retryAttempts": "Intentos de Reintento",
|
||||
"expectedResponseCodes": "Códigos de respuesta esperados",
|
||||
"expectedResponseCodesDescription": "Código de estado HTTP que indica un estado saludable. Si se deja en blanco, se considera saludable de 200 a 300.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Nodo de Salida",
|
||||
"country": "País",
|
||||
"rulesMatchCountry": "Actualmente basado en IP de origen",
|
||||
"region": "Región",
|
||||
"selectRegion": "Seleccionar región",
|
||||
"searchRegions": "Buscar regiones...",
|
||||
"noRegionFound": "Región no encontrada.",
|
||||
"rulesMatchRegion": "Seleccione una agrupación regional de países",
|
||||
"rulesErrorInvalidRegion": "Región no válida",
|
||||
"rulesErrorInvalidRegionDescription": "Por favor, seleccione una región válida.",
|
||||
"regionAfrica": "Africa",
|
||||
"regionNorthernAfrica": "África septentrional",
|
||||
"regionEasternAfrica": "África oriental",
|
||||
"regionMiddleAfrica": "África central",
|
||||
"regionSouthernAfrica": "África del Sur",
|
||||
"regionWesternAfrica": "África Occidental",
|
||||
"regionAmericas": "Américas",
|
||||
"regionCaribbean": "Caribe",
|
||||
"regionCentralAmerica": "América Central",
|
||||
"regionSouthAmerica": "América del Sur",
|
||||
"regionNorthernAmerica": "América del Norte",
|
||||
"regionAsia": "Asia",
|
||||
"regionCentralAsia": "Asia Central",
|
||||
"regionEasternAsia": "Asia oriental",
|
||||
"regionSouthEasternAsia": "Asia sudoriental",
|
||||
"regionSouthernAsia": "Asia meridional",
|
||||
"regionWesternAsia": "Asia Occidental",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Europa del Este",
|
||||
"regionNorthernEurope": "Europa septentrional",
|
||||
"regionSouthernEurope": "Europa meridional",
|
||||
"regionWesternEurope": "Europa Occidental",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australia y Nueva Zelanda",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Autogestionado",
|
||||
"description": "Servidor Pangolin autoalojado más fiable y de bajo mantenimiento con campanas y silbidos extra",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Valor inválido",
|
||||
"idpTypeLabel": "Tipo de proveedor de identidad",
|
||||
"roleMappingExpressionPlaceholder": "e.g., contiene(grupos, 'administrador') && 'administrador' || 'miembro'",
|
||||
"roleMappingModeFixedRoles": "Roles fijos",
|
||||
"roleMappingModeMappingBuilder": "Constructor de mapeo",
|
||||
"roleMappingModeRawExpression": "Expresión sin procesar",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Seleccione uno o más roles",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Nombre de rol de tipo (coincidencia exacta por organización)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Asignar el mismo rol establecido a cada usuario auto-provisionado.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Para las políticas predeterminadas, escriba nombres de roles que existen en cada organización donde los usuarios son proporcionados. Los nombres deben coincidir exactamente.",
|
||||
"roleMappingClaimPath": "Reclamar ruta",
|
||||
"roleMappingClaimPathPlaceholder": "grupos",
|
||||
"roleMappingClaimPathDescription": "Ruta en el payload del token que contiene valores de origen (por ejemplo, grupos).",
|
||||
"roleMappingMatchValue": "Valor de partida",
|
||||
"roleMappingAssignRoles": "Asignar roles",
|
||||
"roleMappingAddMappingRule": "Añadir regla de mapeo",
|
||||
"roleMappingRawExpressionResultDescription": "La expresión debe evaluar a un array de cadenas o cadenas.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "La expresión debe evaluar una cadena (un solo nombre de rol).",
|
||||
"roleMappingMatchValuePlaceholder": "Valor coincidente (por ejemplo: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Escriba nombres de rol (exacto por org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Los nombres de rol deben coincidir con un rol en cada organización objetivo.",
|
||||
"roleMappingRemoveRule": "Eliminar",
|
||||
"idpGoogleConfiguration": "Configuración de Google",
|
||||
"idpGoogleConfigurationDescription": "Configurar las credenciales de Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "¿Está seguro de que desea eliminar la marca de las páginas de autenticación?",
|
||||
"authPageBrandingDeleteConfirm": "Confirmar eliminación de la marca",
|
||||
"brandingLogoURL": "URL del logotipo",
|
||||
"brandingLogoURLOrPath": "URL o ruta de Logo",
|
||||
"brandingLogoPathDescription": "Introduzca una URL o una ruta local.",
|
||||
"brandingLogoURLDescription": "Introduzca una URL de acceso público a su imagen de logotipo.",
|
||||
"brandingPrimaryColor": "Color primario",
|
||||
"brandingLogoWidth": "Ancho (px)",
|
||||
"brandingLogoHeight": "Altura (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Volver a iniciar sesión estándar",
|
||||
"orgAuthNoAccount": "¿No tienes una cuenta?",
|
||||
"subscriptionRequiredToUse": "Se requiere una suscripción para utilizar esta función.",
|
||||
"mustUpgradeToUse": "Debes actualizar tu suscripción para usar esta función.",
|
||||
"subscriptionRequiredTierToUse": "Esta función requiere <tierLink>{tier}</tierLink> o superior.",
|
||||
"upgradeToTierToUse": "Actualiza a <tierLink>{tier}</tierLink> o superior para usar esta función.",
|
||||
"subscriptionTierTier1": "Inicio",
|
||||
"subscriptionTierTier2": "Equipo",
|
||||
"subscriptionTierTier3": "Negocio",
|
||||
"subscriptionTierEnterprise": "Empresa",
|
||||
"idpDisabled": "Los proveedores de identidad están deshabilitados.",
|
||||
"orgAuthPageDisabled": "La página de autenticación de la organización está deshabilitada.",
|
||||
"domainRestartedDescription": "Verificación de dominio reiniciada con éxito",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Obtener una licencia",
|
||||
"description": "Elige un plan y dinos cómo planeas usar Pangolin.",
|
||||
"chooseTier": "Elige tu plan",
|
||||
"viewPricingLink": "Ver precios, características y límites",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Interruptor",
|
||||
"description": "Características de la empresa, 25 usuarios, 25 sitios y soporte comunitario."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Escala",
|
||||
"description": "Características de la empresa, 50 usuarios, 50 sitios y soporte prioritario."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Solo uso personal (licencia gratuita, sin pago)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Continuar con el pago"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Error de pago",
|
||||
"description": "No se pudo iniciar el pago. Por favor, inténtelo de nuevo."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Prioridad",
|
||||
"priorityDescription": "Las rutas de prioridad más alta son evaluadas primero. Prioridad = 100 significa orden automático (decisiones del sistema). Utilice otro número para hacer cumplir la prioridad manual.",
|
||||
"instanceName": "Nombre de instancia",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Cuánto tiempo retener los registros de acceso",
|
||||
"logRetentionActionLabel": "Retención de registro de acción",
|
||||
"logRetentionActionDescription": "Cuánto tiempo retener los registros de acción",
|
||||
"logRetentionConnectionLabel": "Retención de Registro de Conexión",
|
||||
"logRetentionConnectionDescription": "Cuánto tiempo conservar los registros de conexión",
|
||||
"logRetentionDisabled": "Deshabilitado",
|
||||
"logRetention3Days": "3 días",
|
||||
"logRetention7Days": "7 días",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Fin del año siguiente",
|
||||
"actionLogsDescription": "Ver un historial de acciones realizadas en esta organización",
|
||||
"accessLogsDescription": "Ver solicitudes de acceso a los recursos de esta organización",
|
||||
"licenseRequiredToUse": "Se requiere una licencia Enterprise para utilizar esta función.",
|
||||
"connectionLogs": "Registros de conexión",
|
||||
"connectionLogsDescription": "Ver registros de conexión para túneles en esta organización",
|
||||
"sidebarLogsConnection": "Registros de conexión",
|
||||
"sidebarLogsStreaming": "Transmisión",
|
||||
"sourceAddress": "Dirección de origen",
|
||||
"destinationAddress": "Dirección de destino",
|
||||
"duration": "Duración",
|
||||
"licenseRequiredToUse": "Se requiere una licencia <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> o <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> para usar esta función. <bookADemoLink>Reserve una demostración o prueba POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "La <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> es necesaria para utilizar esta función. Esta función también está disponible en <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Reserva una demostración o prueba POC</bookADemoLink>.",
|
||||
"certResolver": "Resolver certificado",
|
||||
"certResolverDescription": "Seleccione la resolución de certificados a utilizar para este recurso.",
|
||||
"selectCertResolver": "Seleccionar Resolver Certificado",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Control de acceso",
|
||||
"editInternalResourceDialogAccessControlDescription": "Controla qué roles, usuarios y clientes de máquinas tienen acceso a este recurso cuando están conectados. Los administradores siempre tienen acceso.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "El rango de puertos debe ser \"*\" para todos los puertos, o una lista separada por comas de puertos y rangos (por ejemplo, \"80,443,8000-9000\"). Los puertos deben estar entre 1 y 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "Ubicación del demonio de autenticación SSSH",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Elija dónde se ejecuta el daemon de autenticación SSH: en el sitio (Newt) o en un host remoto.",
|
||||
"internalResourceAuthDaemonDescription": "El daemon de autenticación SSSH maneja la firma de claves SSH y autenticación PAM para este recurso. Elija si se ejecuta en el sitio (Newt) o en un host remoto separado. Vea <docsLink>la documentación</docsLink> para más.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Seleccionar estrategia",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Ubicación",
|
||||
"internalResourceAuthDaemonSite": "En el sitio",
|
||||
"internalResourceAuthDaemonSiteDescription": "Auth daemon corre en el sitio (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Host remoto",
|
||||
"internalResourceAuthDaemonRemoteDescription": "El daemon Auth corre en un host que no es el sitio.",
|
||||
"internalResourceAuthDaemonPort": "Puerto de demonio (opcional)",
|
||||
"orgAuthWhatsThis": "¿Dónde puedo encontrar el ID de mi organización?",
|
||||
"learnMore": "Más información",
|
||||
"backToHome": "Volver a inicio",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Cortafuegos activado",
|
||||
"autoUpdatesEnabled": "Actualizaciones automáticas habilitadas",
|
||||
"tpmAvailable": "TPM disponible",
|
||||
"windowsAntivirusEnabled": "Antivirus activado",
|
||||
"macosSipEnabled": "Protección de integridad del sistema (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Modo Sigilo Firewall",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Habilitar aprobaciones de dispositivo",
|
||||
"approvalsEmptyStateStep2Description": "Editar un rol y habilitar la opción 'Requerir aprobaciones de dispositivos'. Los usuarios con este rol necesitarán la aprobación del administrador para nuevos dispositivos.",
|
||||
"approvalsEmptyStatePreviewDescription": "Vista previa: Cuando está habilitado, las solicitudes de dispositivo pendientes aparecerán aquí para su revisión",
|
||||
"approvalsEmptyStateButtonText": "Administrar roles"
|
||||
"approvalsEmptyStateButtonText": "Administrar roles",
|
||||
"domainErrorTitle": "Estamos teniendo problemas para verificar su dominio",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configure el mapeo de roles y las políticas de organización en la pestaña <policiesTabLink>Configuración de provisión automática</policiesTabLink>.",
|
||||
"streamingTitle": "Transmisión de Eventos",
|
||||
"streamingDescription": "Transmita eventos desde su organización a destinos externos en tiempo real.",
|
||||
"streamingUnnamedDestination": "Destino sin nombre",
|
||||
"streamingNoUrlConfigured": "No hay URL configurada",
|
||||
"streamingAddDestination": "Añadir destino",
|
||||
"streamingHttpWebhookTitle": "Webhook HTTP",
|
||||
"streamingHttpWebhookDescription": "Enviar eventos a cualquier extremo HTTP con autenticación flexible y plantilla.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Transmite eventos a un bucket de almacenamiento de objetos compatible con S3. Próximamente.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Reenviar eventos directamente a tu cuenta de Datadog. Próximamente.",
|
||||
"streamingTypePickerDescription": "Elija un tipo de destino para empezar.",
|
||||
"streamingFailedToLoad": "Error al cargar destinos",
|
||||
"streamingUnexpectedError": "Se ha producido un error inesperado.",
|
||||
"streamingFailedToUpdate": "Error al actualizar destino",
|
||||
"streamingDeletedSuccess": "Destino eliminado correctamente",
|
||||
"streamingFailedToDelete": "Error al eliminar destino",
|
||||
"streamingDeleteTitle": "Eliminar destino",
|
||||
"streamingDeleteButtonText": "Eliminar destino",
|
||||
"streamingDeleteDialogAreYouSure": "¿Está seguro que desea eliminar",
|
||||
"streamingDeleteDialogThisDestination": "este destino",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Toda la configuración se eliminará permanentemente.",
|
||||
"httpDestEditTitle": "Editar destino",
|
||||
"httpDestAddTitle": "Añadir destino HTTP",
|
||||
"httpDestEditDescription": "Actualizar la configuración para este destino de transmisión de eventos HTTP.",
|
||||
"httpDestAddDescription": "Configure un nuevo extremo HTTP para recibir los eventos de su organización.",
|
||||
"httpDestTabSettings": "Ajustes",
|
||||
"httpDestTabHeaders": "Encabezados",
|
||||
"httpDestTabBody": "Cuerpo",
|
||||
"httpDestTabLogs": "Registros",
|
||||
"httpDestNamePlaceholder": "Mi destino HTTP",
|
||||
"httpDestUrlLabel": "URL de destino",
|
||||
"httpDestUrlErrorHttpRequired": "URL debe usar http o https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS es necesario en implementaciones en la nube",
|
||||
"httpDestUrlErrorInvalid": "Introduzca una URL válida (ej. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Autenticación",
|
||||
"httpDestAuthDescription": "Elija cómo están autenticadas las solicitudes en su punto final.",
|
||||
"httpDestAuthNoneTitle": "Sin autenticación",
|
||||
"httpDestAuthNoneDescription": "Envía solicitudes sin un encabezado de autorización.",
|
||||
"httpDestAuthBearerTitle": "Tóken de portador",
|
||||
"httpDestAuthBearerDescription": "Añade una autorización: portador <token> encabezado a cada solicitud.",
|
||||
"httpDestAuthBearerPlaceholder": "Tu clave o token API",
|
||||
"httpDestAuthBasicTitle": "Auth Básica",
|
||||
"httpDestAuthBasicDescription": "Añade una Autorización: encabezado básico <credentials> . Proporcione credenciales como nombre de usuario: contraseña.",
|
||||
"httpDestAuthBasicPlaceholder": "usuario:contraseña",
|
||||
"httpDestAuthCustomTitle": "Cabecera personalizada",
|
||||
"httpDestAuthCustomDescription": "Especifique un nombre de cabecera HTTP personalizado y un valor para la autenticación (por ejemplo, X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Nombre de cabecera (ej. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Valor de cabecera",
|
||||
"httpDestCustomHeadersTitle": "Cabeceras HTTP personalizadas",
|
||||
"httpDestCustomHeadersDescription": "Añadir cabeceras personalizadas a cada petición saliente. Útil para tokens estáticos o un tipo de contenido personalizado. De forma predeterminada, Content Type: application/json es enviado.",
|
||||
"httpDestNoHeadersConfigured": "No hay cabeceras personalizadas. Haga clic en \"Añadir cabecera\" para añadir una.",
|
||||
"httpDestHeaderNamePlaceholder": "Nombre de cabecera",
|
||||
"httpDestHeaderValuePlaceholder": "Valor",
|
||||
"httpDestAddHeader": "Añadir cabecera",
|
||||
"httpDestBodyTemplateTitle": "Plantilla de cuerpo personalizada",
|
||||
"httpDestBodyTemplateDescription": "Controla la estructura de carga de JSON enviada a tu punto final. Si está desactivado, se envía un objeto JSON por defecto para cada evento.",
|
||||
"httpDestEnableBodyTemplate": "Activar plantilla de cuerpo personalizado",
|
||||
"httpDestBodyTemplateLabel": "Plantilla de cuerpo (JSON)",
|
||||
"httpDestBodyTemplateHint": "Utilice variables de plantilla para referenciar los campos del evento en su carga útil.",
|
||||
"httpDestPayloadFormatTitle": "Formato de carga",
|
||||
"httpDestPayloadFormatDescription": "Cómo se serializan los eventos en cada cuerpo de solicitud.",
|
||||
"httpDestFormatJsonArrayTitle": "Matriz JSON",
|
||||
"httpDestFormatJsonArrayDescription": "Una petición por lote, cuerpo es una matriz JSON. Compatible con la mayoría de los webhooks y Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Una petición por lote, el cuerpo es JSON delimitado por línea — un objeto por línea, sin arrays externos. Requerido por Splunk HEC, Elastic / OpenSearch, y Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Un evento por solicitud",
|
||||
"httpDestFormatSingleDescription": "Envía un HTTP POST separado para cada evento individual. Úsalo sólo para los extremos que no pueden manejar lotes.",
|
||||
"httpDestLogTypesTitle": "Tipos de Log",
|
||||
"httpDestLogTypesDescription": "Elija qué tipos de registro son reenviados a este destino. Sólo los tipos de registro habilitados serán transmitidos.",
|
||||
"httpDestAccessLogsTitle": "Registros de acceso",
|
||||
"httpDestAccessLogsDescription": "Intentos de acceso a recursos, incluyendo solicitudes autenticadas y denegadas.",
|
||||
"httpDestActionLogsTitle": "Registros de acción",
|
||||
"httpDestActionLogsDescription": "Acciones administrativas realizadas por los usuarios dentro de la organización.",
|
||||
"httpDestConnectionLogsTitle": "Registros de conexión",
|
||||
"httpDestConnectionLogsDescription": "Eventos de conexión de sitios y túneles, incluyendo conexiones y desconexiones.",
|
||||
"httpDestRequestLogsTitle": "Registros de Solicitud",
|
||||
"httpDestRequestLogsDescription": "Registros de peticiones HTTP para recursos proxyficados, incluyendo método, ruta y código de respuesta.",
|
||||
"httpDestSaveChanges": "Guardar Cambios",
|
||||
"httpDestCreateDestination": "Crear destino",
|
||||
"httpDestUpdatedSuccess": "Destino actualizado correctamente",
|
||||
"httpDestCreatedSuccess": "Destino creado correctamente",
|
||||
"httpDestUpdateFailed": "Error al actualizar destino",
|
||||
"httpDestCreateFailed": "Error al crear el destino"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Vous {count, plural, =0 {n'} other {} }êtes membre {count, plural, =0 {d'aucune organisation} one {d'une organisation} other {de # organisations}}.",
|
||||
"componentsInvalidKey": "Clés de licence invalides ou expirées détectées. Veuillez respecter les conditions de licence pour continuer à utiliser toutes les fonctionnalités.",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Merci de soutenir Pangolin en tant que {tier}!",
|
||||
"inviteErrorNotValid": "Nous sommes désolés, mais il semble que l'invitation à laquelle vous essayez d'accéder n'ait pas été acceptée ou ne soit plus valide.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Créer un lien",
|
||||
"resourcesNotFound": "Aucune ressource trouvée",
|
||||
"resourceSearch": "Rechercher des ressources",
|
||||
"machineSearch": "Rechercher des machines",
|
||||
"machinesSearch": "Rechercher des clients de la machine...",
|
||||
"machineNotFound": "Aucune machine trouvée",
|
||||
"userDeviceSearch": "Rechercher des périphériques utilisateur",
|
||||
"userDevicesSearch": "Rechercher des appareils utilisateurs...",
|
||||
"openMenu": "Ouvrir le menu",
|
||||
"resource": "Ressource",
|
||||
"title": "Titre de la page",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy les demandes sur HTTPS en utilisant un nom de domaine entièrement qualifié.",
|
||||
"resourceRaw": "Ressource TCP/UDP brute",
|
||||
"resourceRawDescription": "Proxy les demandes sur TCP/UDP brut en utilisant un numéro de port.",
|
||||
"resourceRawDescriptionCloud": "Requêtes de proxy sur TCP/UDP brute en utilisant un numéro de port. Nécessite des sites pour se connecter à un noeud distant.",
|
||||
"resourceCreate": "Créer une ressource",
|
||||
"resourceCreateDescription": "Suivez les étapes ci-dessous pour créer une nouvelle ressource",
|
||||
"resourceSeeAll": "Voir toutes les ressources",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Choisir un protocole",
|
||||
"resourcePortNumber": "Numéro de port",
|
||||
"resourcePortNumberDescription": "Le numéro de port externe pour les requêtes de proxy.",
|
||||
"back": "Précédent",
|
||||
"cancel": "Abandonner",
|
||||
"resourceConfig": "Snippets de configuration",
|
||||
"resourceConfigDescription": "Copiez et collez ces extraits de configuration pour configurer la ressource TCP/UDP",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Une erreur s'est produite lors de la suppression de l'organisation.",
|
||||
"orgDeleted": "Organisation supprimée",
|
||||
"orgDeletedMessage": "L'organisation et ses données ont été supprimées.",
|
||||
"deleteAccount": "Supprimer le compte",
|
||||
"deleteAccountDescription": "Supprimer définitivement votre compte, toutes les organisations que vous possédez et toutes les données au sein de ces organisations. Cela ne peut pas être annulé.",
|
||||
"deleteAccountButton": "Supprimer le compte",
|
||||
"deleteAccountConfirmTitle": "Supprimer le compte",
|
||||
"deleteAccountConfirmMessage": "Cela effacera définitivement votre compte, toutes les organisations que vous possédez et toutes les données au sein de ces organisations. Cela ne peut pas être annulé.",
|
||||
"deleteAccountConfirmString": "supprimer le compte",
|
||||
"deleteAccountSuccess": "Compte supprimé",
|
||||
"deleteAccountSuccessMessage": "Votre compte a été supprimé.",
|
||||
"deleteAccountError": "Échec de la suppression du compte",
|
||||
"deleteAccountPreviewAccount": "Votre Compte",
|
||||
"deleteAccountPreviewOrgs": "Organisations que vous possédez (et toutes leurs données)",
|
||||
"orgMissing": "ID d'organisation manquant",
|
||||
"orgMissingMessage": "Impossible de régénérer l'invitation sans un ID d'organisation.",
|
||||
"accessUsersManage": "Gérer les utilisateurs",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Supprimer la clé d'API",
|
||||
"apiKeysManage": "Gérer les clés d'API",
|
||||
"apiKeysDescription": "Les clés d'API sont utilisées pour s'authentifier avec l'API d'intégration",
|
||||
"provisioningKeysTitle": "Clé de provisioning",
|
||||
"provisioningKeysManage": "Gérer les clés de provisioning",
|
||||
"provisioningKeysDescription": "Les clés de provisioning sont utilisées pour authentifier la fourniture automatique de sites pour votre organisation.",
|
||||
"provisioningManage": "Mise en place",
|
||||
"provisioningDescription": "Gérer les clés de provisioning et examiner les sites en attente d'approbation.",
|
||||
"pendingSites": "Sites en attente",
|
||||
"siteApproveSuccess": "Site approuvé avec succès",
|
||||
"siteApproveError": "Erreur lors de l'approbation du site",
|
||||
"provisioningKeys": "Clés de provisionnement",
|
||||
"searchProvisioningKeys": "Recherche des clés de provision...",
|
||||
"provisioningKeysAdd": "Générer une clé de provisioning",
|
||||
"provisioningKeysErrorDelete": "Erreur lors de la suppression de la clé de provisioning",
|
||||
"provisioningKeysErrorDeleteMessage": "Erreur lors de la suppression de la clé de provisioning",
|
||||
"provisioningKeysQuestionRemove": "Êtes-vous sûr de vouloir supprimer cette clé de provisioning de l'organisation ?",
|
||||
"provisioningKeysMessageRemove": "Une fois supprimée, la clé ne peut plus être utilisée pour le provisionnement du site.",
|
||||
"provisioningKeysDeleteConfirm": "Confirmer la suppression de la clé de provisioning",
|
||||
"provisioningKeysDelete": "Supprimer la clé de provisioning",
|
||||
"provisioningKeysCreate": "Générer une clé de provisioning",
|
||||
"provisioningKeysCreateDescription": "Générer une nouvelle clé de provisioning pour l'organisation",
|
||||
"provisioningKeysSeeAll": "Voir toutes les clés de provisioning",
|
||||
"provisioningKeysSave": "Enregistrer la clé de provisioning",
|
||||
"provisioningKeysSaveDescription": "Vous ne pourrez voir cela qu'une seule fois. Copiez-le dans un endroit sécurisé.",
|
||||
"provisioningKeysErrorCreate": "Erreur lors de la création de la clé de provisioning",
|
||||
"provisioningKeysList": "Nouvelle clé de provisioning",
|
||||
"provisioningKeysMaxBatchSize": "Taille maximale du lot",
|
||||
"provisioningKeysUnlimitedBatchSize": "Taille de lot illimitée (sans limite)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Illimité",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Entrez une taille de lot maximale valide (1–1 000 000).",
|
||||
"provisioningKeysValidUntil": "Valable jusqu'au",
|
||||
"provisioningKeysValidUntilHint": "Laisser vide pour ne pas expirer.",
|
||||
"provisioningKeysValidUntilInvalid": "Entrez une date et une heure valides.",
|
||||
"provisioningKeysNumUsed": "Nombre de fois utilisées",
|
||||
"provisioningKeysLastUsed": "Dernière utilisation",
|
||||
"provisioningKeysNoExpiry": "Pas d'expiration",
|
||||
"provisioningKeysNeverUsed": "Jamais",
|
||||
"provisioningKeysEdit": "Modifier la clé de provisioning",
|
||||
"provisioningKeysEditDescription": "Mettre à jour la taille maximale du lot et la durée d'expiration de cette clé.",
|
||||
"provisioningKeysApproveNewSites": "Approuver les nouveaux sites",
|
||||
"provisioningKeysApproveNewSitesDescription": "Approuver automatiquement les sites qui s'inscrivent avec cette clé.",
|
||||
"provisioningKeysUpdateError": "Erreur lors de la mise à jour de la clé de provisioning",
|
||||
"provisioningKeysUpdated": "Clé de provisioning mise à jour",
|
||||
"provisioningKeysUpdatedDescription": "Vos modifications ont été enregistrées.",
|
||||
"provisioningKeysBannerTitle": "Clés de provisioning du site",
|
||||
"provisioningKeysBannerDescription": "Générez une clé de provisioning et utilisez-la avec le connecteur Newt pour créer automatiquement des sites au premier démarrage — pas besoin de configurer des identifiants distincts pour chaque site.",
|
||||
"provisioningKeysBannerButtonText": "En savoir plus",
|
||||
"pendingSitesBannerTitle": "Sites en attente",
|
||||
"pendingSitesBannerDescription": "Les sites qui se connectent à l'aide d'une clé de provisioning apparaissent ici pour être revus. Approuver chaque site avant qu'il ne devienne actif et qu'il accède à vos ressources.",
|
||||
"pendingSitesBannerButtonText": "En savoir plus",
|
||||
"apiKeysSettings": "Paramètres de {apiKeyName}",
|
||||
"userTitle": "Gérer tous les utilisateurs",
|
||||
"userDescription": "Voir et gérer tous les utilisateurs du système",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtrer par État d'Approbation",
|
||||
"approvalListEmpty": "Aucune approbation",
|
||||
"approvalState": "État d'approbation",
|
||||
"approvalLoadMore": "Charger plus",
|
||||
"loadingApprovals": "Chargement des approbations",
|
||||
"approve": "Approuver",
|
||||
"approved": "Approuvé",
|
||||
"denied": "Refusé",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Utilisateur enregistré",
|
||||
"userSavedDescription": "L'utilisateur a été mis à jour.",
|
||||
"autoProvisioned": "Auto-provisionné",
|
||||
"autoProvisionSettings": "Paramètres de la fourniture automatique",
|
||||
"autoProvisionedDescription": "Permettre à cet utilisateur d'être géré automatiquement par le fournisseur d'identité",
|
||||
"accessControlsDescription": "Gérer ce que cet utilisateur peut accéder et faire dans l'organisation",
|
||||
"accessControlsSubmit": "Enregistrer les contrôles d'accès",
|
||||
"singleRolePerUserPlanNotice": "Votre plan ne prend en charge qu'un seul rôle par utilisateur.",
|
||||
"singleRolePerUserEditionNotice": "Cette édition ne prend en charge qu'un rôle par utilisateur.",
|
||||
"roles": "Rôles",
|
||||
"accessUsersRoles": "Gérer les utilisateurs et les rôles",
|
||||
"accessUsersRolesDescription": "Invitez des utilisateurs et ajoutez-les aux rôles pour gérer l'accès à l'organisation",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Échec de la bascule de la ressource",
|
||||
"resourcesErrorUpdateDescription": "Une erreur s'est produite lors de la mise à jour de la ressource",
|
||||
"access": "Accès",
|
||||
"accessControl": "Contrôle d'accès",
|
||||
"shareLink": "Lien de partage {resource}",
|
||||
"resourceSelect": "Sélectionner une ressource",
|
||||
"shareLinks": "Liens de partage",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rôle supprimé",
|
||||
"accessRoleRemovedDescription": "Le rôle a été supprimé avec succès.",
|
||||
"accessRoleRequiredRemove": "Avant de supprimer ce rôle, veuillez sélectionner un nouveau rôle pour transférer les membres existants.",
|
||||
"network": "Réseau",
|
||||
"manage": "Gérer",
|
||||
"sitesNotFound": "Aucun site trouvé.",
|
||||
"pangolinServerAdmin": "Admin Serveur - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Augmenter le nombre de sites",
|
||||
"idpManage": "Gérer les fournisseurs d'identité",
|
||||
"idpManageDescription": "Voir et gérer les fournisseurs d'identité dans le système",
|
||||
"idpGlobalModeBanner": "Les fournisseurs d'identité (IdPs) par organisation sont désactivés sur ce serveur. Il utilise des IdPs globaux (partagés entre toutes les organisations). Gérez les IdPs globaux dans le panneau d'administration <adminPanelLink></adminPanelLink>. Pour activer les IdPs par organisation, éditez la configuration du serveur et réglez le mode IdP sur org. <configDocsLink>Voir la documentation</configDocsLink>. Si vous voulez continuer à utiliser les IdPs globaux et faire disparaître cela des paramètres de l'organisation, définissez explicitement le mode à global dans la configuration.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Les fournisseurs d'identité (IdPs) par organisation sont désactivés sur ce serveur. Il utilise des IdPs globaux (partagés entre toutes les organisations). Gérer les IdPs globaux dans le panneau d'administration <adminPanelLink></adminPanelLink>. Pour utiliser les fournisseurs d'identité par organisation, vous devez passer à l'édition Entreprise.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Les fournisseurs d'identité (IdPs) par organisation sont désactivés sur ce serveur. Il utilise des IdPs globaux (partagés entre toutes les organisations). Gérer les IdPs globaux dans le panneau d'administration <adminPanelLink></adminPanelLink>. Pour utiliser les fournisseurs d'identité par organisation, une licence d'entreprise est requise.",
|
||||
"idpDeletedDescription": "Fournisseur d'identité supprimé avec succès",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Êtes-vous sûr de vouloir supprimer définitivement le fournisseur d'identité?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Configuration - Pangolin",
|
||||
"orgNameRequired": "Le nom de l'organisation est requis",
|
||||
"orgIdRequired": "L'ID de l'organisation est requis",
|
||||
"orgIdMaxLength": "L'identifiant de l'organisation doit comporter au plus 32 caractères",
|
||||
"orgErrorCreate": "Une erreur s'est produite lors de la création de l'organisation",
|
||||
"pageNotFound": "Page non trouvée",
|
||||
"pageNotFoundDescription": "Oups! La page que vous recherchez n'existe pas.",
|
||||
"overview": "Vue d'ensemble",
|
||||
"home": "Accueil",
|
||||
"accessControl": "Contrôle d'accès",
|
||||
"settings": "Paramètres",
|
||||
"usersAll": "Tous les utilisateurs",
|
||||
"license": "Licence",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Obtenir l'utilisateur",
|
||||
"actionGetOrgUser": "Obtenir l'utilisateur de l'organisation",
|
||||
"actionListOrgDomains": "Lister les domaines de l'organisation",
|
||||
"actionGetDomain": "Obtenir un domaine",
|
||||
"actionCreateOrgDomain": "Créer un domaine",
|
||||
"actionUpdateOrgDomain": "Mettre à jour le domaine",
|
||||
"actionDeleteOrgDomain": "Supprimer le domaine",
|
||||
"actionGetDNSRecords": "Récupérer les enregistrements DNS",
|
||||
"actionRestartOrgDomain": "Redémarrer le domaine",
|
||||
"actionCreateSite": "Créer un site",
|
||||
"actionDeleteSite": "Supprimer un site",
|
||||
"actionGetSite": "Obtenir un site",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Entrez le jeton de configuration depuis la console du serveur.",
|
||||
"setupTokenRequired": "Le jeton de configuration est requis.",
|
||||
"actionUpdateSite": "Mettre à jour un site",
|
||||
"actionResetSiteBandwidth": "Réinitialiser la bande passante de l'organisation",
|
||||
"actionListSiteRoles": "Lister les rôles autorisés du site",
|
||||
"actionCreateResource": "Créer une ressource",
|
||||
"actionDeleteResource": "Supprimer une ressource",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Supprimer un utilisateur",
|
||||
"actionListUsers": "Lister les utilisateurs",
|
||||
"actionAddUserRole": "Ajouter un rôle utilisateur",
|
||||
"actionSetUserOrgRoles": "Définir les rôles de l'utilisateur",
|
||||
"actionGenerateAccessToken": "Générer un jeton d'accès",
|
||||
"actionDeleteAccessToken": "Supprimer un jeton d'accès",
|
||||
"actionListAccessTokens": "Lister les jetons d'accès",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Voir les logs",
|
||||
"noneSelected": "Aucune sélection",
|
||||
"orgNotFound2": "Aucune organisation trouvée.",
|
||||
"searchProgress": "Rechercher...",
|
||||
"searchPlaceholder": "Recherche...",
|
||||
"emptySearchOptions": "Aucune option trouvée",
|
||||
"create": "Créer",
|
||||
"orgs": "Organisations",
|
||||
"loginError": "Une erreur inattendue s'est produite. Veuillez réessayer.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privé",
|
||||
"sidebarAccessControl": "Contrôle d'accès",
|
||||
"sidebarLogsAndAnalytics": "Journaux & Analytiques",
|
||||
"sidebarTeam": "Equipe",
|
||||
"sidebarUsers": "Utilisateurs",
|
||||
"sidebarAdmin": "Administrateur",
|
||||
"sidebarInvitations": "Invitations",
|
||||
"sidebarRoles": "Rôles",
|
||||
"sidebarShareableLinks": "Liens",
|
||||
"sidebarApiKeys": "Clés API",
|
||||
"sidebarProvisioning": "Mise en place",
|
||||
"sidebarSettings": "Réglages",
|
||||
"sidebarAllUsers": "Tous les utilisateurs",
|
||||
"sidebarIdentityProviders": "Fournisseurs d'identité",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Journaux & Analytiques",
|
||||
"sidebarBluePrints": "Configs",
|
||||
"sidebarOrganization": "Organisation",
|
||||
"sidebarManagement": "Gestion",
|
||||
"sidebarBillingAndLicenses": "Facturation & Licences",
|
||||
"sidebarLogsAnalytics": "Analyses",
|
||||
"blueprints": "Configs",
|
||||
"blueprintsDescription": "Appliquer les configurations déclaratives et afficher les exécutions précédentes",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Contenu analysé (lecture seule)",
|
||||
"enableDockerSocket": "Activer la Config Docker",
|
||||
"enableDockerSocketDescription": "Activer le ramassage d'étiquettes de socket Docker pour les étiquettes de plan. Le chemin de socket doit être fourni à Newt.",
|
||||
"enableDockerSocketLink": "En savoir plus",
|
||||
"viewDockerContainers": "Voir les conteneurs Docker",
|
||||
"containersIn": "Conteneurs en {siteName}",
|
||||
"selectContainerDescription": "Sélectionnez n'importe quel conteneur à utiliser comme nom d'hôte pour cette cible. Cliquez sur un port pour utiliser un port.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Espace de noms : {namespace}",
|
||||
"domainPickerShowMore": "Afficher plus",
|
||||
"regionSelectorTitle": "Sélectionner Région",
|
||||
"domainPickerRemoteExitNodeWarning": "Les domaines fournis ne sont pas pris en charge lorsque les sites se connectent à des nœuds de sortie distants. Pour que les ressources soient disponibles sur des nœuds distants, utilisez un domaine personnalisé à la place.",
|
||||
"regionSelectorInfo": "Sélectionner une région nous aide à offrir de meilleures performances pour votre localisation. Vous n'avez pas besoin d'être dans la même région que votre serveur.",
|
||||
"regionSelectorPlaceholder": "Choisissez une région",
|
||||
"regionSelectorComingSoon": "Bientôt disponible",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Vue d'ensemble des limites d'utilisation",
|
||||
"billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@pangolin.net.",
|
||||
"billingDataUsage": "Utilisation des données",
|
||||
"billingOnlineTime": "Temps en ligne du site",
|
||||
"billingUsers": "Utilisateurs actifs",
|
||||
"billingDomains": "Domaines actifs",
|
||||
"billingRemoteExitNodes": "Nœuds auto-hébergés actifs",
|
||||
"billingSites": "Nœuds",
|
||||
"billingUsers": "Utilisateurs",
|
||||
"billingDomains": "Domaines",
|
||||
"billingOrganizations": "Organes",
|
||||
"billingRemoteExitNodes": "Nœuds distants",
|
||||
"billingNoLimitConfigured": "Aucune limite configurée",
|
||||
"billingEstimatedPeriod": "Période de facturation estimée",
|
||||
"billingIncludedUsage": "Utilisation incluse",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Échec pour obtenir l'URL du portail",
|
||||
"billingPortalError": "Erreur du portail",
|
||||
"billingDataUsageInfo": "Vous êtes facturé pour toutes les données transférées via vos tunnels sécurisés lorsque vous êtes connecté au cloud. Cela inclut le trafic entrant et sortant sur tous vos sites. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre plan ou réduisiez l'utilisation. Les données ne sont pas facturées lors de l'utilisation de nœuds.",
|
||||
"billingOnlineTimeInfo": "Vous êtes facturé en fonction de la durée de connexion de vos sites au cloud. Par exemple, 44 640 minutes équivaut à un site fonctionnant 24/7 pendant un mois complet. Lorsque vous atteignez votre limite, vos sites se déconnecteront jusqu'à ce que vous mettiez à niveau votre forfait ou réduisiez votre consommation. Le temps n'est pas facturé lors de l'utilisation de nœuds.",
|
||||
"billingUsersInfo": "Vous êtes facturé pour chaque utilisateur de l'organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes d'utilisateurs actifs dans votre organisation.",
|
||||
"billingDomainInfo": "Vous êtes facturé pour chaque domaine de l'organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes de domaine actifs dans votre organisation.",
|
||||
"billingRemoteExitNodesInfo": "Vous êtes facturé pour chaque noeud géré dans l'organisation. La facturation est calculée quotidiennement en fonction du nombre de nœuds gérés actifs dans votre organisation.",
|
||||
"billingSInfo": "Combien de sites vous pouvez utiliser",
|
||||
"billingUsersInfo": "Combien d'utilisateurs vous pouvez utiliser",
|
||||
"billingDomainInfo": "Combien de domaines vous pouvez utiliser",
|
||||
"billingRemoteExitNodesInfo": "Combien de nœuds distants vous pouvez utiliser",
|
||||
"billingLicenseKeys": "Clés de licence",
|
||||
"billingLicenseKeysDescription": "Gérer vos abonnements à la clé de licence",
|
||||
"billingLicenseSubscription": "Abonnement à la licence",
|
||||
"billingInactive": "Inactif",
|
||||
"billingLicenseItem": "Article de la licence",
|
||||
"billingQuantity": "Quantité",
|
||||
"billingTotal": "total",
|
||||
"billingModifyLicenses": "Modifier l'abonnement à la licence",
|
||||
"domainNotFound": "Domaine introuvable",
|
||||
"domainNotFoundDescription": "Cette ressource est désactivée car le domaine n'existe plus dans notre système. Veuillez définir un nouveau domaine pour cette ressource.",
|
||||
"failed": "Échec",
|
||||
"createNewOrgDescription": "Créer une nouvelle organisation",
|
||||
"organization": "Organisation",
|
||||
"primary": "Primaire",
|
||||
"port": "Port",
|
||||
"securityKeyManage": "Gérer les clés de sécurité",
|
||||
"securityKeyDescription": "Ajouter ou supprimer des clés de sécurité pour l'authentification sans mot de passe",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Le numéro de port est requis pour les ressources non-HTTP",
|
||||
"resourcePortNotAllowed": "Le numéro de port ne doit pas être défini pour les ressources HTTP",
|
||||
"billingPricingCalculatorLink": "Calculateur de prix",
|
||||
"billingYourPlan": "Votre plan",
|
||||
"billingViewOrModifyPlan": "Voir ou modifier votre forfait actuel",
|
||||
"billingViewPlanDetails": "Voir les détails du plan",
|
||||
"billingUsageAndLimits": "Utilisation et limites",
|
||||
"billingViewUsageAndLimits": "Voir les limites de votre plan et l'utilisation actuelle",
|
||||
"billingCurrentUsage": "Utilisation actuelle",
|
||||
"billingMaximumLimits": "Limites maximum",
|
||||
"billingRemoteNodes": "Nœuds distants",
|
||||
"billingUnlimited": "Illimité",
|
||||
"billingPaidLicenseKeys": "Clés de licence payantes",
|
||||
"billingManageLicenseSubscription": "Gérer votre abonnement pour les clés de licence auto-hébergées payantes",
|
||||
"billingCurrentKeys": "Clés actuelles",
|
||||
"billingModifyCurrentPlan": "Modifier le plan actuel",
|
||||
"billingConfirmUpgrade": "Confirmer la mise à niveau",
|
||||
"billingConfirmDowngrade": "Confirmer la rétrogradation",
|
||||
"billingConfirmUpgradeDescription": "Vous êtes sur le point de mettre à niveau votre offre. Examinez les nouvelles limites et les nouveaux prix ci-dessous.",
|
||||
"billingConfirmDowngradeDescription": "Vous êtes sur le point de rétrograder votre forfait. Examinez les nouvelles limites et les prix ci-dessous.",
|
||||
"billingPlanIncludes": "Le forfait comprend",
|
||||
"billingProcessing": "Traitement en cours...",
|
||||
"billingConfirmUpgradeButton": "Confirmer la mise à niveau",
|
||||
"billingConfirmDowngradeButton": "Confirmer la rétrogradation",
|
||||
"billingLimitViolationWarning": "Utilisation dépassée les nouvelles limites de plan",
|
||||
"billingLimitViolationDescription": "Votre utilisation actuelle dépasse les limites de ce plan. Après rétrogradation, toutes les actions seront désactivées jusqu'à ce que vous réduisiez l'utilisation dans les nouvelles limites. Veuillez consulter les fonctionnalités ci-dessous qui dépassent actuellement les limites. Limites en violation :",
|
||||
"billingFeatureLossWarning": "Avis de disponibilité des fonctionnalités",
|
||||
"billingFeatureLossDescription": "En rétrogradant, les fonctionnalités non disponibles dans le nouveau plan seront automatiquement désactivées. Certains paramètres et configurations peuvent être perdus. Veuillez consulter la matrice de prix pour comprendre quelles fonctionnalités ne seront plus disponibles.",
|
||||
"billingUsageExceedsLimit": "L'utilisation actuelle ({current}) dépasse la limite ({limit})",
|
||||
"billingPastDueTitle": "Paiement en retard",
|
||||
"billingPastDueDescription": "Votre paiement est échu. Veuillez mettre à jour votre méthode de paiement pour continuer à utiliser les fonctionnalités de votre plan actuel. Si non résolu, votre abonnement sera annulé et vous serez remis au niveau gratuit.",
|
||||
"billingUnpaidTitle": "Abonnement impayé",
|
||||
"billingUnpaidDescription": "Votre abonnement est impayé et vous avez été reversé au niveau gratuit. Veuillez mettre à jour votre méthode de paiement pour restaurer votre abonnement.",
|
||||
"billingIncompleteTitle": "Paiement incomplet",
|
||||
"billingIncompleteDescription": "Votre paiement est incomplet. Veuillez compléter le processus de paiement pour activer votre abonnement.",
|
||||
"billingIncompleteExpiredTitle": "Paiement expiré",
|
||||
"billingIncompleteExpiredDescription": "Votre paiement n'a jamais été complété et a expiré. Vous avez été restauré au niveau gratuit. Veuillez vous abonner à nouveau pour restaurer l'accès aux fonctionnalités payantes.",
|
||||
"billingManageSubscription": "Gérer votre abonnement",
|
||||
"billingResolvePaymentIssue": "Veuillez résoudre votre problème de paiement avant de procéder à la mise à niveau ou à la rétrogradation",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Je suis d'accord avec",
|
||||
"termsOfService": "les conditions d'utilisation",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Le temps est exprimé en secondes",
|
||||
"requireDeviceApproval": "Exiger les autorisations de l'appareil",
|
||||
"requireDeviceApprovalDescription": "Les utilisateurs ayant ce rôle ont besoin de nouveaux périphériques approuvés par un administrateur avant de pouvoir se connecter et accéder aux ressources.",
|
||||
"sshAccess": "Accès SSH",
|
||||
"roleAllowSsh": "Autoriser SSH",
|
||||
"roleAllowSshAllow": "Autoriser",
|
||||
"roleAllowSshDisallow": "Interdire",
|
||||
"roleAllowSshDescription": "Autoriser les utilisateurs avec ce rôle à se connecter aux ressources via SSH. Lorsque désactivé, le rôle ne peut pas utiliser les accès SSH.",
|
||||
"sshSudoMode": "Accès Sudo",
|
||||
"sshSudoModeNone": "Aucun",
|
||||
"sshSudoModeNoneDescription": "L'utilisateur ne peut pas exécuter de commandes avec sudo.",
|
||||
"sshSudoModeFull": "Sudo complet",
|
||||
"sshSudoModeFullDescription": "L'utilisateur peut exécuter n'importe quelle commande avec sudo.",
|
||||
"sshSudoModeCommands": "Commandes",
|
||||
"sshSudoModeCommandsDescription": "L'utilisateur ne peut exécuter que les commandes spécifiées avec sudo.",
|
||||
"sshSudo": "Autoriser sudo",
|
||||
"sshSudoCommands": "Commandes Sudo",
|
||||
"sshSudoCommandsDescription": "Liste des commandes séparées par des virgules que l'utilisateur est autorisé à exécuter avec sudo.",
|
||||
"sshCreateHomeDir": "Créer un répertoire personnel",
|
||||
"sshUnixGroups": "Groupes Unix",
|
||||
"sshUnixGroupsDescription": "Groupes Unix séparés par des virgules pour ajouter l'utilisateur sur l'hôte cible.",
|
||||
"retryAttempts": "Tentatives de réessai",
|
||||
"expectedResponseCodes": "Codes de réponse attendus",
|
||||
"expectedResponseCodesDescription": "Code de statut HTTP indiquant un état de santé satisfaisant. Si non renseigné, 200-300 est considéré comme satisfaisant.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Nœud de sortie",
|
||||
"country": "Pays",
|
||||
"rulesMatchCountry": "Actuellement basé sur l'IP source",
|
||||
"region": "Région",
|
||||
"selectRegion": "Sélectionner une région",
|
||||
"searchRegions": "Rechercher des régions...",
|
||||
"noRegionFound": "Aucune région trouvée.",
|
||||
"rulesMatchRegion": "Sélectionnez un groupement régional de pays",
|
||||
"rulesErrorInvalidRegion": "Région invalide",
|
||||
"rulesErrorInvalidRegionDescription": "Veuillez sélectionner une région valide.",
|
||||
"regionAfrica": "L'Afrique",
|
||||
"regionNorthernAfrica": "Afrique du Nord",
|
||||
"regionEasternAfrica": "Afrique de l'Est",
|
||||
"regionMiddleAfrica": "Afrique Moyenne",
|
||||
"regionSouthernAfrica": "Afrique australe",
|
||||
"regionWesternAfrica": "Afrique de l'Ouest",
|
||||
"regionAmericas": "Amériques",
|
||||
"regionCaribbean": "Caraïbes",
|
||||
"regionCentralAmerica": "Amérique centrale",
|
||||
"regionSouthAmerica": "Amérique du Sud",
|
||||
"regionNorthernAmerica": "Amérique du Nord",
|
||||
"regionAsia": "L'Asie",
|
||||
"regionCentralAsia": "Asie centrale",
|
||||
"regionEasternAsia": "Asie de l'Est",
|
||||
"regionSouthEasternAsia": "Asie du Sud-Est",
|
||||
"regionSouthernAsia": "Asie du Sud",
|
||||
"regionWesternAsia": "Asie de l'Ouest",
|
||||
"regionEurope": "L’Europe",
|
||||
"regionEasternEurope": "Europe de l'Est",
|
||||
"regionNorthernEurope": "Europe du Nord",
|
||||
"regionSouthernEurope": "Europe du Sud",
|
||||
"regionWesternEurope": "Europe occidentale",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australie et Nouvelle-Zélande",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Gestion autonome",
|
||||
"description": "Serveur Pangolin auto-hébergé avec des cloches et des sifflets supplémentaires",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Valeur non valide",
|
||||
"idpTypeLabel": "Type de fournisseur d'identité",
|
||||
"roleMappingExpressionPlaceholder": "ex: contenu(groupes) && 'admin' || 'membre'",
|
||||
"roleMappingModeFixedRoles": "Rôles fixes",
|
||||
"roleMappingModeMappingBuilder": "Constructeur de cartographie",
|
||||
"roleMappingModeRawExpression": "Expression brute",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Sélectionnez un ou plusieurs rôles",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Tapez les noms des rôles (correspondance exacte par organisation)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Assigner le même jeu de rôles à chaque utilisateur auto-provisionné.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Pour les politiques par défaut, les noms de rôles de type qui existent dans chaque organisation où les utilisateurs sont fournis. Les noms doivent correspondre exactement.",
|
||||
"roleMappingClaimPath": "Chemin de revendication",
|
||||
"roleMappingClaimPathPlaceholder": "Groupes",
|
||||
"roleMappingClaimPathDescription": "Chemin dans le bloc de jeton qui contient les valeurs source (par exemple, les groupes).",
|
||||
"roleMappingMatchValue": "Valeur de la correspondance",
|
||||
"roleMappingAssignRoles": "Assigner des rôles",
|
||||
"roleMappingAddMappingRule": "Ajouter une règle de mappage",
|
||||
"roleMappingRawExpressionResultDescription": "L'expression doit être évaluée à une chaîne ou un tableau de chaînes.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "L'expression doit être évaluée à une chaîne (un seul nom de rôle).",
|
||||
"roleMappingMatchValuePlaceholder": "Valeur de la correspondance (par exemple: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Tapez les noms des rôles (exact par org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Les noms de rôle doivent correspondre à un rôle dans chaque organisation cible.",
|
||||
"roleMappingRemoveRule": "Supprimer",
|
||||
"idpGoogleConfiguration": "Configuration Google",
|
||||
"idpGoogleConfigurationDescription": "Configurer les identifiants Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Êtes-vous sûr de vouloir supprimer la marque des pages d'authentification ?",
|
||||
"authPageBrandingDeleteConfirm": "Confirmer la suppression de la marque",
|
||||
"brandingLogoURL": "URL du logo",
|
||||
"brandingLogoURLOrPath": "URL du logo ou du chemin d'accès",
|
||||
"brandingLogoPathDescription": "Entrez une URL ou un chemin local.",
|
||||
"brandingLogoURLDescription": "Entrez une URL accessible au public à votre image de logo.",
|
||||
"brandingPrimaryColor": "Couleur principale",
|
||||
"brandingLogoWidth": "Largeur (px)",
|
||||
"brandingLogoHeight": "Hauteur (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Retour à la connexion standard",
|
||||
"orgAuthNoAccount": "Vous n'avez pas de compte ?",
|
||||
"subscriptionRequiredToUse": "Un abonnement est requis pour utiliser cette fonctionnalité.",
|
||||
"mustUpgradeToUse": "Vous devez mettre à jour votre abonnement pour utiliser cette fonctionnalité.",
|
||||
"subscriptionRequiredTierToUse": "Cette fonctionnalité nécessite <tierLink>{tier}</tierLink> ou supérieur.",
|
||||
"upgradeToTierToUse": "Passez à <tierLink>{tier}</tierLink> ou plus pour utiliser cette fonctionnalité.",
|
||||
"subscriptionTierTier1": "Domicile",
|
||||
"subscriptionTierTier2": "Equipe",
|
||||
"subscriptionTierTier3": "Entreprise",
|
||||
"subscriptionTierEnterprise": "Entreprise",
|
||||
"idpDisabled": "Les fournisseurs d'identité sont désactivés.",
|
||||
"orgAuthPageDisabled": "La page d'authentification de l'organisation est désactivée.",
|
||||
"domainRestartedDescription": "La vérification du domaine a été redémarrée avec succès",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Obtenir une licence",
|
||||
"description": "Choisissez un plan et dites-nous comment vous comptez utiliser Pangolin.",
|
||||
"chooseTier": "Choisissez votre forfait",
|
||||
"viewPricingLink": "Voir les prix, les fonctionnalités et les limites",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Démarrage",
|
||||
"description": "Fonctionnalités d'entreprise, 25 utilisateurs, 25 sites et un support communautaire."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Échelle",
|
||||
"description": "Fonctionnalités d'entreprise, 50 utilisateurs, 50 sites et une prise en charge prioritaire."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Utilisation personnelle uniquement (licence gratuite — sans checkout)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Continuer vers le paiement"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Erreur de paiement",
|
||||
"description": "Impossible de commencer la commande. Veuillez réessayer."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Priorité",
|
||||
"priorityDescription": "Les routes de haute priorité sont évaluées en premier. La priorité = 100 signifie l'ordre automatique (décision du système). Utilisez un autre nombre pour imposer la priorité manuelle.",
|
||||
"instanceName": "Nom de l'instance",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Durée de conservation des journaux d'accès",
|
||||
"logRetentionActionLabel": "Retention du journal des actions",
|
||||
"logRetentionActionDescription": "Durée de conservation du journal des actions",
|
||||
"logRetentionConnectionLabel": "Rétention du journal de connexion",
|
||||
"logRetentionConnectionDescription": "Durée de conservation des logs de connexion",
|
||||
"logRetentionDisabled": "Désactivé",
|
||||
"logRetention3Days": "3 jours",
|
||||
"logRetention7Days": "7 jours",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Fin de l'année suivante",
|
||||
"actionLogsDescription": "Voir l'historique des actions effectuées dans cette organisation",
|
||||
"accessLogsDescription": "Voir les demandes d'authentification d'accès aux ressources de cette organisation",
|
||||
"licenseRequiredToUse": "Une licence Entreprise est nécessaire pour utiliser cette fonctionnalité.",
|
||||
"connectionLogs": "Journaux de connexion",
|
||||
"connectionLogsDescription": "Voir les journaux de connexion pour les tunnels de cette organisation",
|
||||
"sidebarLogsConnection": "Journaux de connexion",
|
||||
"sidebarLogsStreaming": "Streaming en cours",
|
||||
"sourceAddress": "Adresse source",
|
||||
"destinationAddress": "Adresse de destination",
|
||||
"duration": "Durée",
|
||||
"licenseRequiredToUse": "Une <enterpriseLicenseLink>licence Enterprise Edition</enterpriseLicenseLink> ou <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> est requise pour utiliser cette fonctionnalité. <bookADemoLink>Réservez une démonstration ou une évaluation de POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "La version <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> est requise pour utiliser cette fonctionnalité. Cette fonctionnalité est également disponible dans <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Réservez une démo ou un essai POC</bookADemoLink>.",
|
||||
"certResolver": "Résolveur de certificat",
|
||||
"certResolverDescription": "Sélectionnez le solveur de certificat à utiliser pour cette ressource.",
|
||||
"selectCertResolver": "Sélectionnez le résolveur de certificat",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Contrôle d'accès",
|
||||
"editInternalResourceDialogAccessControlDescription": "Contrôlez quels rôles, utilisateurs et clients de machine ont accès à cette ressource lorsqu'ils sont connectés. Les administrateurs ont toujours accès.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "La plage de ports doit être \"*\" pour tous les ports, ou une liste de ports et de plages séparés par des virgules (par exemple, \"80,443,8000-9000\"). Les ports doivent être compris entre 1 et 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "Emplacement du démon d'authentification SSH",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Choisissez où le démon d'authentification SSH s'exécute : sur le site (Newt) ou sur un hôte distant.",
|
||||
"internalResourceAuthDaemonDescription": "Le démon d'authentification SSH gère la signature des clés SSH et l'authentification PAM pour cette ressource. Choisissez s'il fonctionne sur le site (Newt) ou sur un hôte distant séparé. Voir <docsLink>la documentation</docsLink> pour plus d'informations.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Choisir une stratégie",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Localisation",
|
||||
"internalResourceAuthDaemonSite": "Sur le site",
|
||||
"internalResourceAuthDaemonSiteDescription": "Le démon Auth fonctionne sur le site (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Hôte distant",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Le démon Auth fonctionne sur un hôte qui n'est pas le site.",
|
||||
"internalResourceAuthDaemonPort": "Port du démon (optionnel)",
|
||||
"orgAuthWhatsThis": "Où puis-je trouver mon identifiant d'organisation ?",
|
||||
"learnMore": "En savoir plus",
|
||||
"backToHome": "Retour à l'accueil",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Pare-feu activé",
|
||||
"autoUpdatesEnabled": "Mises à jour automatiques activées",
|
||||
"tpmAvailable": "TPM disponible",
|
||||
"windowsAntivirusEnabled": "Antivirus activé",
|
||||
"macosSipEnabled": "Protection contre l'intégrité du système (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Mode furtif du pare-feu",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Activer les autorisations de l'appareil",
|
||||
"approvalsEmptyStateStep2Description": "Modifier un rôle et activer l'option 'Exiger les autorisations de l'appareil'. Les utilisateurs avec ce rôle auront besoin de l'approbation de l'administrateur pour les nouveaux appareils.",
|
||||
"approvalsEmptyStatePreviewDescription": "Aperçu: Lorsque cette option est activée, les demandes de périphérique en attente apparaîtront ici pour vérification",
|
||||
"approvalsEmptyStateButtonText": "Gérer les rôles"
|
||||
"approvalsEmptyStateButtonText": "Gérer les rôles",
|
||||
"domainErrorTitle": "Nous avons des difficultés à vérifier votre domaine",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configurer les politiques de mappage des rôles et de l'organisation dans l'onglet <policiesTabLink>Paramètres de la fourniture automatique</policiesTabLink>.",
|
||||
"streamingTitle": "Streaming d'événements",
|
||||
"streamingDescription": "Diffusez en temps réel des événements de votre organisation vers des destinations externes.",
|
||||
"streamingUnnamedDestination": "Destination sans nom",
|
||||
"streamingNoUrlConfigured": "Aucune URL configurée",
|
||||
"streamingAddDestination": "Ajouter une destination",
|
||||
"streamingHttpWebhookTitle": "Webhook HTTP",
|
||||
"streamingHttpWebhookDescription": "Envoyez des événements à n'importe quel point de terminaison HTTP avec une authentification flexible et un template.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Flux d'événements vers un compartiment de stockage d'objet compatible S3. Bientôt.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Transférer des événements directement sur votre compte Datadog. Prochainement.",
|
||||
"streamingTypePickerDescription": "Choisissez un type de destination pour commencer.",
|
||||
"streamingFailedToLoad": "Impossible de charger les destinations",
|
||||
"streamingUnexpectedError": "Une erreur inattendue s'est produite.",
|
||||
"streamingFailedToUpdate": "Impossible de mettre à jour la destination",
|
||||
"streamingDeletedSuccess": "Destination supprimée avec succès",
|
||||
"streamingFailedToDelete": "Impossible de supprimer la destination",
|
||||
"streamingDeleteTitle": "Supprimer la destination",
|
||||
"streamingDeleteButtonText": "Supprimer la destination",
|
||||
"streamingDeleteDialogAreYouSure": "Êtes-vous sûr de vouloir supprimer",
|
||||
"streamingDeleteDialogThisDestination": "cette destination",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Toutes les configurations seront définitivement supprimées.",
|
||||
"httpDestEditTitle": "Modifier la destination",
|
||||
"httpDestAddTitle": "Ajouter une destination HTTP",
|
||||
"httpDestEditDescription": "Mettre à jour la configuration pour cette destination de streaming d'événements HTTP.",
|
||||
"httpDestAddDescription": "Configurez un nouveau point de terminaison HTTP pour recevoir les événements de votre organisation.",
|
||||
"httpDestTabSettings": "Réglages",
|
||||
"httpDestTabHeaders": "En-têtes",
|
||||
"httpDestTabBody": "Corps",
|
||||
"httpDestTabLogs": "Journaux",
|
||||
"httpDestNamePlaceholder": "Ma destination HTTP",
|
||||
"httpDestUrlLabel": "URL de destination",
|
||||
"httpDestUrlErrorHttpRequired": "L'URL doit utiliser http ou https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS est requis pour les déploiements du cloud",
|
||||
"httpDestUrlErrorInvalid": "Entrez une URL valide (par exemple https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Authentification",
|
||||
"httpDestAuthDescription": "Choisissez comment les requêtes à votre terminaison sont authentifiées.",
|
||||
"httpDestAuthNoneTitle": "Aucune authentification",
|
||||
"httpDestAuthNoneDescription": "Envoie des requêtes sans en-tête d'autorisation.",
|
||||
"httpDestAuthBearerTitle": "Jeton de Porteur",
|
||||
"httpDestAuthBearerDescription": "Ajoute un en-tête Authorization: Bearer <token> à chaque requête.",
|
||||
"httpDestAuthBearerPlaceholder": "Votre clé API ou votre jeton",
|
||||
"httpDestAuthBasicTitle": "Authentification basique",
|
||||
"httpDestAuthBasicDescription": "Ajoute une autorisation : en-tête de base <credentials> . Fournissez des informations d'identification comme nom d'utilisateur:mot de passe.",
|
||||
"httpDestAuthBasicPlaceholder": "nom d'utilisateur:mot de passe",
|
||||
"httpDestAuthCustomTitle": "En-tête personnalisé",
|
||||
"httpDestAuthCustomDescription": "Spécifiez un nom d'en-tête HTTP personnalisé et une valeur pour l'authentification (par exemple X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Nom de l'en-tête (par exemple X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Valeur de l'en-tête",
|
||||
"httpDestCustomHeadersTitle": "En-têtes HTTP personnalisés",
|
||||
"httpDestCustomHeadersDescription": "Ajouter des en-têtes personnalisés à chaque requête sortante. Utile pour les jetons statiques ou un type de contenu personnalisé. Par défaut, Content-Type: application/json est envoyé.",
|
||||
"httpDestNoHeadersConfigured": "Aucun en-tête personnalisé configuré. Cliquez sur \"Ajouter un en-tête\" pour en ajouter un.",
|
||||
"httpDestHeaderNamePlaceholder": "Nom de l'en-tête",
|
||||
"httpDestHeaderValuePlaceholder": "Valeur",
|
||||
"httpDestAddHeader": "Ajouter un en-tête",
|
||||
"httpDestBodyTemplateTitle": "Modèle de corps personnalisé",
|
||||
"httpDestBodyTemplateDescription": "Contrôle la structure de charge utile JSON envoyée à votre terminal. Si désactivé, un objet JSON par défaut est envoyé pour chaque événement.",
|
||||
"httpDestEnableBodyTemplate": "Activer le modèle de corps personnalisé",
|
||||
"httpDestBodyTemplateLabel": "Modèle de corps (JSON)",
|
||||
"httpDestBodyTemplateHint": "Utilisez les variables de modèle pour référencer les champs d'événement dans votre charge utile.",
|
||||
"httpDestPayloadFormatTitle": "Format de la charge utile",
|
||||
"httpDestPayloadFormatDescription": "Comment les événements sont sérialisés dans chaque corps de requête.",
|
||||
"httpDestFormatJsonArrayTitle": "Tableau JSON",
|
||||
"httpDestFormatJsonArrayDescription": "Une requête par lot, le corps est un tableau JSON. Compatible avec la plupart des webhooks génériques et des datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Une requête par lot, body est un JSON délimité par une nouvelle ligne — un objet par ligne, pas de tableau extérieur. Requis par Splunk HEC, Elastic / OpenSearch, et Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Un événement par demande",
|
||||
"httpDestFormatSingleDescription": "Envoie un POST HTTP séparé pour chaque événement individuel. Utilisé uniquement pour les terminaux qui ne peuvent pas gérer des lots.",
|
||||
"httpDestLogTypesTitle": "Types de logs",
|
||||
"httpDestLogTypesDescription": "Choisissez quels types de journaux sont envoyés à cette destination. Seuls les types de journaux activés seront diffusés.",
|
||||
"httpDestAccessLogsTitle": "Journaux d'accès",
|
||||
"httpDestAccessLogsDescription": "Tentatives d'accès aux ressources, y compris les demandes authentifiées et refusées.",
|
||||
"httpDestActionLogsTitle": "Journaux des actions",
|
||||
"httpDestActionLogsDescription": "Actions administratives effectuées par les utilisateurs au sein de l'organisation.",
|
||||
"httpDestConnectionLogsTitle": "Journaux de connexion",
|
||||
"httpDestConnectionLogsDescription": "Événements de connexion du site et du tunnel, y compris les connexions et les déconnexions.",
|
||||
"httpDestRequestLogsTitle": "Journal des requêtes",
|
||||
"httpDestRequestLogsDescription": "Journaux des requêtes HTTP pour les ressources proxiées, y compris la méthode, le chemin et le code de réponse.",
|
||||
"httpDestSaveChanges": "Enregistrer les modifications",
|
||||
"httpDestCreateDestination": "Créer une destination",
|
||||
"httpDestUpdatedSuccess": "Destination mise à jour avec succès",
|
||||
"httpDestCreatedSuccess": "Destination créée avec succès",
|
||||
"httpDestUpdateFailed": "Impossible de mettre à jour la destination",
|
||||
"httpDestCreateFailed": "Impossible de créer la destination"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Sei un membro di {count, plural, =0 {nessuna organizzazione} one {un'organizzazione} other {# organizzazioni}}.",
|
||||
"componentsInvalidKey": "Rilevata chiave di licenza non valida o scaduta. Segui i termini di licenza per continuare a utilizzare tutte le funzionalità.",
|
||||
"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.",
|
||||
"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à.",
|
||||
"componentsSupporterMessage": "Grazie per aver supportato Pangolin come {tier}!",
|
||||
"inviteErrorNotValid": "Siamo spiacenti, ma sembra che l'invito che stai cercando di accedere non sia stato accettato o non sia più valido.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Crea Collegamento",
|
||||
"resourcesNotFound": "Nessuna risorsa trovata",
|
||||
"resourceSearch": "Cerca risorse",
|
||||
"machineSearch": "Ricerca macchine",
|
||||
"machinesSearch": "Cerca client macchina...",
|
||||
"machineNotFound": "Nessuna macchina trovata",
|
||||
"userDeviceSearch": "Cerca dispositivi utente",
|
||||
"userDevicesSearch": "Cerca dispositivi utente...",
|
||||
"openMenu": "Apri menu",
|
||||
"resource": "Risorsa",
|
||||
"title": "Titolo",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Richieste proxy su HTTPS usando un nome di dominio completo.",
|
||||
"resourceRaw": "Risorsa Raw TCP/UDP",
|
||||
"resourceRawDescription": "Richieste proxy su TCP/UDP grezzo utilizzando un numero di porta.",
|
||||
"resourceRawDescriptionCloud": "Richiesta proxy su TCP/UDP grezzo utilizzando un numero di porta. Richiede siti per connettersi a un nodo remoto.",
|
||||
"resourceCreate": "Crea Risorsa",
|
||||
"resourceCreateDescription": "Segui i passaggi seguenti per creare una nuova risorsa",
|
||||
"resourceSeeAll": "Vedi Tutte Le Risorse",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Seleziona un protocollo",
|
||||
"resourcePortNumber": "Numero Porta",
|
||||
"resourcePortNumberDescription": "Il numero di porta esterna per le richieste di proxy.",
|
||||
"back": "Indietro",
|
||||
"cancel": "Annulla",
|
||||
"resourceConfig": "Snippet Di Configurazione",
|
||||
"resourceConfigDescription": "Copia e incolla questi snippet di configurazione per configurare la risorsa TCP/UDP",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Si è verificato un errore durante l'eliminazione dell'organizzazione.",
|
||||
"orgDeleted": "Organizzazione eliminata",
|
||||
"orgDeletedMessage": "L'organizzazione e i suoi dati sono stati eliminati.",
|
||||
"deleteAccount": "Elimina Account",
|
||||
"deleteAccountDescription": "Elimina definitivamente il tuo account, tutte le organizzazioni che possiedi e tutti i dati all'interno di tali organizzazioni. Questo non può essere annullato.",
|
||||
"deleteAccountButton": "Elimina Account",
|
||||
"deleteAccountConfirmTitle": "Elimina Account",
|
||||
"deleteAccountConfirmMessage": "Questo cancellerà definitivamente il tuo account, tutte le organizzazioni che possiedi e tutti i dati all'interno di tali organizzazioni. Questo non può essere annullato.",
|
||||
"deleteAccountConfirmString": "elimina account",
|
||||
"deleteAccountSuccess": "Account Eliminato",
|
||||
"deleteAccountSuccessMessage": "Il tuo account è stato eliminato.",
|
||||
"deleteAccountError": "Impossibile eliminare l'account",
|
||||
"deleteAccountPreviewAccount": "Il Tuo Account",
|
||||
"deleteAccountPreviewOrgs": "Organizzazioni che possiedi (e tutti i loro dati)",
|
||||
"orgMissing": "ID Organizzazione Mancante",
|
||||
"orgMissingMessage": "Impossibile rigenerare l'invito senza un ID organizzazione.",
|
||||
"accessUsersManage": "Gestisci Utenti",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Elimina Chiave API",
|
||||
"apiKeysManage": "Gestisci Chiavi API",
|
||||
"apiKeysDescription": "Le chiavi API sono utilizzate per autenticarsi con l'API di integrazione",
|
||||
"provisioningKeysTitle": "Chiave Di Provvedimento",
|
||||
"provisioningKeysManage": "Gestisci Chiavi Di Provvedimento",
|
||||
"provisioningKeysDescription": "Le chiavi di provisioning vengono utilizzate per autenticare il provisioning automatico del sito per la tua organizzazione.",
|
||||
"provisioningManage": "Accantonamento",
|
||||
"provisioningDescription": "Gestire le chiavi di provisioning e rivedere i siti in attesa di approvazione.",
|
||||
"pendingSites": "Siti In Attesa",
|
||||
"siteApproveSuccess": "Sito approvato con successo",
|
||||
"siteApproveError": "Errore nell'approvazione del sito",
|
||||
"provisioningKeys": "Chiavi Di Provvedimento",
|
||||
"searchProvisioningKeys": "Cerca i tasti di provisioning ...",
|
||||
"provisioningKeysAdd": "Genera Chiave Di Provvedimento",
|
||||
"provisioningKeysErrorDelete": "Errore nell'eliminare la chiave di provisioning",
|
||||
"provisioningKeysErrorDeleteMessage": "Errore nell'eliminare la chiave di provisioning",
|
||||
"provisioningKeysQuestionRemove": "Sei sicuro di voler rimuovere questa chiave di provisioning dall'organizzazione?",
|
||||
"provisioningKeysMessageRemove": "Una volta rimossa, la chiave non può più essere utilizzata per il provisioning.",
|
||||
"provisioningKeysDeleteConfirm": "Conferma Elimina Chiave Provvisoria",
|
||||
"provisioningKeysDelete": "Elimina chiave di provisioning",
|
||||
"provisioningKeysCreate": "Genera Chiave Di Provvedimento",
|
||||
"provisioningKeysCreateDescription": "Genera una nuova chiave di provisioning per l'organizzazione",
|
||||
"provisioningKeysSeeAll": "Vedi tutte le chiavi di provisioning",
|
||||
"provisioningKeysSave": "Salva la chiave di provisioning",
|
||||
"provisioningKeysSaveDescription": "Sarai in grado di vedere solo una volta. Copiarlo in un posto sicuro.",
|
||||
"provisioningKeysErrorCreate": "Errore nella creazione della chiave di provisioning",
|
||||
"provisioningKeysList": "Nuova chiave di provisioning",
|
||||
"provisioningKeysMaxBatchSize": "Dimensione massima lotto",
|
||||
"provisioningKeysUnlimitedBatchSize": "Dimensione illimitata del lotto (nessun limite)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Illimitato",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Inserisci un lotto massimo valido (1–1.000.000).",
|
||||
"provisioningKeysValidUntil": "Valido fino al",
|
||||
"provisioningKeysValidUntilHint": "Lasciare vuoto per nessuna scadenza.",
|
||||
"provisioningKeysValidUntilInvalid": "Inserisci una data e ora valide.",
|
||||
"provisioningKeysNumUsed": "Volte usate",
|
||||
"provisioningKeysLastUsed": "Ultimo utilizzo",
|
||||
"provisioningKeysNoExpiry": "Nessuna scadenza",
|
||||
"provisioningKeysNeverUsed": "Mai",
|
||||
"provisioningKeysEdit": "Modifica Chiave Di Provvedimento",
|
||||
"provisioningKeysEditDescription": "Aggiorna la dimensione massima del lotto e il tempo di scadenza per questa chiave.",
|
||||
"provisioningKeysApproveNewSites": "Approva nuovi siti",
|
||||
"provisioningKeysApproveNewSitesDescription": "Approvare automaticamente i siti che si registrano con questa chiave.",
|
||||
"provisioningKeysUpdateError": "Errore nell'aggiornamento della chiave di provisioning",
|
||||
"provisioningKeysUpdated": "Chiave di accantonamento aggiornata",
|
||||
"provisioningKeysUpdatedDescription": "Le tue modifiche sono state salvate.",
|
||||
"provisioningKeysBannerTitle": "Chiavi Di Provvedimento Sito",
|
||||
"provisioningKeysBannerDescription": "Generare una chiave di provisioning e usarla con il connettore Newt per creare automaticamente siti al primo avvio — non è necessario impostare credenziali separate per ogni sito.",
|
||||
"provisioningKeysBannerButtonText": "Scopri di più",
|
||||
"pendingSitesBannerTitle": "Siti In Attesa",
|
||||
"pendingSitesBannerDescription": "I siti che si connettono utilizzando una chiave di provisioning appaiono qui per la revisione. Approva ogni sito prima che diventi attivo e ottenga l'accesso alle tue risorse.",
|
||||
"pendingSitesBannerButtonText": "Scopri di più",
|
||||
"apiKeysSettings": "Impostazioni {apiKeyName}",
|
||||
"userTitle": "Gestisci Tutti Gli Utenti",
|
||||
"userDescription": "Visualizza e gestisci tutti gli utenti del sistema",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtra Per Stato Di Approvazione",
|
||||
"approvalListEmpty": "Nessuna approvazione",
|
||||
"approvalState": "Stato Di Approvazione",
|
||||
"approvalLoadMore": "Carica altro",
|
||||
"loadingApprovals": "Caricamento Approvazioni",
|
||||
"approve": "Approva",
|
||||
"approved": "Approvato",
|
||||
"denied": "Negato",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Utente salvato",
|
||||
"userSavedDescription": "L'utente è stato aggiornato.",
|
||||
"autoProvisioned": "Auto Provisioned",
|
||||
"autoProvisionSettings": "Impostazioni Automatiche Di Fornitura",
|
||||
"autoProvisionedDescription": "Permetti a questo utente di essere gestito automaticamente dal provider di identità",
|
||||
"accessControlsDescription": "Gestisci cosa questo utente può accedere e fare nell'organizzazione",
|
||||
"accessControlsSubmit": "Salva Controlli di Accesso",
|
||||
"singleRolePerUserPlanNotice": "Il tuo piano supporta solo un ruolo per utente.",
|
||||
"singleRolePerUserEditionNotice": "Questa edizione supporta solo un ruolo per utente.",
|
||||
"roles": "Ruoli",
|
||||
"accessUsersRoles": "Gestisci Utenti e Ruoli",
|
||||
"accessUsersRolesDescription": "Invita gli utenti e aggiungili ai ruoli per gestire l'accesso all'organizzazione",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Impossibile attivare/disattivare la risorsa",
|
||||
"resourcesErrorUpdateDescription": "Si è verificato un errore durante l'aggiornamento della risorsa",
|
||||
"access": "Accesso",
|
||||
"accessControl": "Controllo Accessi",
|
||||
"shareLink": "Link di Condivisione {resource}",
|
||||
"resourceSelect": "Seleziona risorsa",
|
||||
"shareLinks": "Link di Condivisione",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Ruolo rimosso",
|
||||
"accessRoleRemovedDescription": "Il ruolo è stato rimosso con successo.",
|
||||
"accessRoleRequiredRemove": "Prima di eliminare questo ruolo, seleziona un nuovo ruolo a cui trasferire i membri esistenti.",
|
||||
"network": "Rete",
|
||||
"manage": "Gestisci",
|
||||
"sitesNotFound": "Nessun sito trovato.",
|
||||
"pangolinServerAdmin": "Server Admin - Pangolina",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Aumenta conteggio siti",
|
||||
"idpManage": "Gestisci Provider di Identità",
|
||||
"idpManageDescription": "Visualizza e gestisci i provider di identità nel sistema",
|
||||
"idpGlobalModeBanner": "I provider di identità (IdP) per organizzazione sono disabilitati su questo server. Sta utilizzando IdP globali (condivisi in tutte le organizzazioni). Gestisci IdP globali nel pannello di amministrazione <adminPanelLink></adminPanelLink>. Per abilitare IdP per organizzazione, modificare la configurazione del server e impostare la modalità IdP su org. <configDocsLink>Vedere i documenti</configDocsLink>. Se si desidera continuare a utilizzare IdP globali e far sparire questo dalle impostazioni dell'organizzazione, impostare esplicitamente la modalità globale nella configurazione.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "I provider di identità (IdP) per organizzazione sono disabilitati su questo server. Utilizza IdP globali (condivisi tra tutte le organizzazioni). Gestisci gli IdP globali nel pannello di amministrazione <adminPanelLink></adminPanelLink>. Per utilizzare i provider di identità per organizzazione, è necessario aggiornare all'edizione Enterprise.",
|
||||
"idpGlobalModeBannerLicenseRequired": "I provider di identità (IdP) per organizzazione sono disabilitati su questo server. Utilizza IdP globali (condivisi tra tutte le organizzazioni). Gestisci IdP globali nel pannello di amministrazione <adminPanelLink></adminPanelLink>. Per utilizzare provider di identità per organizzazione, è richiesta una licenza Enterprise.",
|
||||
"idpDeletedDescription": "Provider di identità eliminato con successo",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Sei sicuro di voler eliminare definitivamente il provider di identità?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Configurazione - Pangolin",
|
||||
"orgNameRequired": "Il nome dell'organizzazione è obbligatorio",
|
||||
"orgIdRequired": "L'ID dell'organizzazione è obbligatorio",
|
||||
"orgIdMaxLength": "L'ID dell'organizzazione deve contenere al massimo 32 caratteri",
|
||||
"orgErrorCreate": "Si è verificato un errore durante la creazione dell'organizzazione",
|
||||
"pageNotFound": "Pagina Non Trovata",
|
||||
"pageNotFoundDescription": "Oops! La pagina che stai cercando non esiste.",
|
||||
"overview": "Panoramica",
|
||||
"home": "Home",
|
||||
"accessControl": "Controllo Accessi",
|
||||
"settings": "Impostazioni",
|
||||
"usersAll": "Tutti Gli Utenti",
|
||||
"license": "Licenza",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Ottieni Utente",
|
||||
"actionGetOrgUser": "Ottieni Utente Organizzazione",
|
||||
"actionListOrgDomains": "Elenca Domini Organizzazione",
|
||||
"actionGetDomain": "Ottieni Dominio",
|
||||
"actionCreateOrgDomain": "Crea Dominio",
|
||||
"actionUpdateOrgDomain": "Aggiorna Dominio",
|
||||
"actionDeleteOrgDomain": "Elimina Dominio",
|
||||
"actionGetDNSRecords": "Ottieni Record DNS",
|
||||
"actionRestartOrgDomain": "Riavvia Dominio",
|
||||
"actionCreateSite": "Crea Sito",
|
||||
"actionDeleteSite": "Elimina Sito",
|
||||
"actionGetSite": "Ottieni Sito",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Inserisci il token di configurazione dalla console del server.",
|
||||
"setupTokenRequired": "Il token di configurazione è richiesto",
|
||||
"actionUpdateSite": "Aggiorna Sito",
|
||||
"actionResetSiteBandwidth": "Reimposta Larghezza Banda Dell'Organizzazione",
|
||||
"actionListSiteRoles": "Elenca Ruoli Sito Consentiti",
|
||||
"actionCreateResource": "Crea Risorsa",
|
||||
"actionDeleteResource": "Elimina Risorsa",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Rimuovi Utente",
|
||||
"actionListUsers": "Elenca Utenti",
|
||||
"actionAddUserRole": "Aggiungi Ruolo Utente",
|
||||
"actionSetUserOrgRoles": "Imposta Ruoli Utente",
|
||||
"actionGenerateAccessToken": "Genera Token di Accesso",
|
||||
"actionDeleteAccessToken": "Elimina Token di Accesso",
|
||||
"actionListAccessTokens": "Elenca Token di Accesso",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Visualizza Log",
|
||||
"noneSelected": "Nessuna selezione",
|
||||
"orgNotFound2": "Nessuna organizzazione trovata.",
|
||||
"searchProgress": "Ricerca...",
|
||||
"searchPlaceholder": "Cerca...",
|
||||
"emptySearchOptions": "Nessuna opzione trovata",
|
||||
"create": "Crea",
|
||||
"orgs": "Organizzazioni",
|
||||
"loginError": "Si è verificato un errore imprevisto. Riprova.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privato",
|
||||
"sidebarAccessControl": "Controllo Accesso",
|
||||
"sidebarLogsAndAnalytics": "Registri E Analisi",
|
||||
"sidebarTeam": "Squadra",
|
||||
"sidebarUsers": "Utenti",
|
||||
"sidebarAdmin": "Amministratore",
|
||||
"sidebarInvitations": "Inviti",
|
||||
"sidebarRoles": "Ruoli",
|
||||
"sidebarShareableLinks": "Collegamenti",
|
||||
"sidebarApiKeys": "Chiavi API",
|
||||
"sidebarProvisioning": "Accantonamento",
|
||||
"sidebarSettings": "Impostazioni",
|
||||
"sidebarAllUsers": "Tutti Gli Utenti",
|
||||
"sidebarIdentityProviders": "Fornitori Di Identità",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Log & Analytics",
|
||||
"sidebarBluePrints": "Progetti",
|
||||
"sidebarOrganization": "Organizzazione",
|
||||
"sidebarManagement": "Gestione",
|
||||
"sidebarBillingAndLicenses": "Fatturazione E Licenze",
|
||||
"sidebarLogsAnalytics": "Analisi",
|
||||
"blueprints": "Progetti",
|
||||
"blueprintsDescription": "Applica le configurazioni dichiarative e visualizza le partite precedenti",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Sommario Analizzato (Solo Lettura)",
|
||||
"enableDockerSocket": "Abilita Progetto Docker",
|
||||
"enableDockerSocketDescription": "Abilita la raschiatura dell'etichetta Docker Socket per le etichette dei progetti. Il percorso del socket deve essere fornito a Newt.",
|
||||
"enableDockerSocketLink": "Scopri di più",
|
||||
"viewDockerContainers": "Visualizza Contenitori Docker",
|
||||
"containersIn": "Contenitori in {siteName}",
|
||||
"selectContainerDescription": "Seleziona qualsiasi contenitore da usare come hostname per questo obiettivo. Fai clic su una porta per usare una porta.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Namespace: {namespace}",
|
||||
"domainPickerShowMore": "Mostra Altro",
|
||||
"regionSelectorTitle": "Seleziona regione",
|
||||
"domainPickerRemoteExitNodeWarning": "I domini forniti non sono supportati quando i siti si connettono a nodi di uscita remoti. Affinché le risorse siano disponibili su nodi remoti, utilizza invece un dominio personalizzato.",
|
||||
"regionSelectorInfo": "Selezionare una regione ci aiuta a fornire migliori performance per la tua posizione. Non devi necessariamente essere nella stessa regione del tuo server.",
|
||||
"regionSelectorPlaceholder": "Scegli una regione",
|
||||
"regionSelectorComingSoon": "Prossimamente",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Panoramica dei Limiti di Utilizzo",
|
||||
"billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@pangolin.net.",
|
||||
"billingDataUsage": "Utilizzo dei Dati",
|
||||
"billingOnlineTime": "Tempo Online del Sito",
|
||||
"billingUsers": "Utenti Attivi",
|
||||
"billingDomains": "Domini Attivi",
|
||||
"billingRemoteExitNodes": "Nodi Self-hosted Attivi",
|
||||
"billingSites": "Siti",
|
||||
"billingUsers": "Utenti",
|
||||
"billingDomains": "Domini",
|
||||
"billingOrganizations": "Organi",
|
||||
"billingRemoteExitNodes": "Nodi Remoti",
|
||||
"billingNoLimitConfigured": "Nessun limite configurato",
|
||||
"billingEstimatedPeriod": "Periodo di Fatturazione Stimato",
|
||||
"billingIncludedUsage": "Utilizzo Incluso",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Errore durante l'ottenimento dell'URL del portale",
|
||||
"billingPortalError": "Errore del Portale",
|
||||
"billingDataUsageInfo": "Hai addebitato tutti i dati trasferiti attraverso i tunnel sicuri quando sei connesso al cloud. Questo include sia il traffico in entrata e in uscita attraverso tutti i siti. Quando si raggiunge il limite, i siti si disconnetteranno fino a quando non si aggiorna il piano o si riduce l'utilizzo. I dati non vengono caricati quando si utilizzano nodi.",
|
||||
"billingOnlineTimeInfo": "Ti viene addebitato in base al tempo in cui i tuoi siti rimangono connessi al cloud. Ad esempio, 44,640 minuti è uguale a un sito in esecuzione 24/7 per un mese intero. Quando raggiungi il tuo limite, i tuoi siti si disconnetteranno fino a quando non aggiorni il tuo piano o riduci l'utilizzo. Il tempo non viene caricato quando si usano i nodi.",
|
||||
"billingUsersInfo": "Sei addebitato per ogni utente nell'organizzazione. La fatturazione viene calcolata quotidianamente in base al numero di account utente attivi nel tuo org.",
|
||||
"billingDomainInfo": "Sei addebitato per ogni dominio nell'organizzazione. La fatturazione viene calcolata quotidianamente in base al numero di account di dominio attivi nel tuo org.",
|
||||
"billingRemoteExitNodesInfo": "Sei addebitato per ogni nodo gestito nell'organizzazione. La fatturazione viene calcolata quotidianamente in base al numero di nodi gestiti attivi nel tuo org.",
|
||||
"billingSInfo": "Quanti siti puoi usare",
|
||||
"billingUsersInfo": "Quanti utenti puoi usare",
|
||||
"billingDomainInfo": "Quanti domini puoi usare",
|
||||
"billingRemoteExitNodesInfo": "Quanti nodi remoti puoi usare",
|
||||
"billingLicenseKeys": "Chiavi di Licenza",
|
||||
"billingLicenseKeysDescription": "Gestisci le sottoscrizioni alla chiave di licenza",
|
||||
"billingLicenseSubscription": "Abbonamento Licenza",
|
||||
"billingInactive": "Inattivo",
|
||||
"billingLicenseItem": "Elemento Licenza",
|
||||
"billingQuantity": "Quantità",
|
||||
"billingTotal": "totale",
|
||||
"billingModifyLicenses": "Modifica Abbonamento Licenza",
|
||||
"domainNotFound": "Domini Non Trovati",
|
||||
"domainNotFoundDescription": "Questa risorsa è disabilitata perché il dominio non esiste più nel nostro sistema. Si prega di impostare un nuovo dominio per questa risorsa.",
|
||||
"failed": "Fallito",
|
||||
"createNewOrgDescription": "Crea una nuova organizzazione",
|
||||
"organization": "Organizzazione",
|
||||
"primary": "Principale",
|
||||
"port": "Porta",
|
||||
"securityKeyManage": "Gestisci chiavi di sicurezza",
|
||||
"securityKeyDescription": "Aggiungi o rimuovi chiavi di sicurezza per l'autenticazione senza password",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Numero di porta richiesto per risorse non-HTTP",
|
||||
"resourcePortNotAllowed": "Il numero di porta non deve essere impostato per risorse HTTP",
|
||||
"billingPricingCalculatorLink": "Calcolatore di Prezzi",
|
||||
"billingYourPlan": "Il Tuo Piano",
|
||||
"billingViewOrModifyPlan": "Visualizza o modifica il tuo piano corrente",
|
||||
"billingViewPlanDetails": "Visualizza Dettagli Piano",
|
||||
"billingUsageAndLimits": "Utilizzo e limiti",
|
||||
"billingViewUsageAndLimits": "Visualizza i limiti del tuo piano e l'utilizzo corrente",
|
||||
"billingCurrentUsage": "Utilizzo Corrente",
|
||||
"billingMaximumLimits": "Limiti Massimi",
|
||||
"billingRemoteNodes": "Nodi Remoti",
|
||||
"billingUnlimited": "Illimitato",
|
||||
"billingPaidLicenseKeys": "Chiavi Di Licenza Pagate",
|
||||
"billingManageLicenseSubscription": "Gestisci il tuo abbonamento per le chiavi di licenza self-hosted a pagamento",
|
||||
"billingCurrentKeys": "Tasti Attuali",
|
||||
"billingModifyCurrentPlan": "Modifica Il Piano Corrente",
|
||||
"billingConfirmUpgrade": "Conferma Aggiornamento",
|
||||
"billingConfirmDowngrade": "Conferma Downgrade",
|
||||
"billingConfirmUpgradeDescription": "Stai per aggiornare il tuo piano. Controlla i nuovi limiti e prezzi qui sotto.",
|
||||
"billingConfirmDowngradeDescription": "Stai per effettuare il downgrade del tuo piano. Controlla i nuovi limiti e i prezzi qui sotto.",
|
||||
"billingPlanIncludes": "Piano Include",
|
||||
"billingProcessing": "Elaborazione...",
|
||||
"billingConfirmUpgradeButton": "Conferma Aggiornamento",
|
||||
"billingConfirmDowngradeButton": "Conferma Downgrade",
|
||||
"billingLimitViolationWarning": "Utilizzo Supera I Nuovi Limiti Del Piano",
|
||||
"billingLimitViolationDescription": "Il tuo utilizzo attuale supera i limiti di questo piano. Dopo il downgrading, tutte le azioni saranno disabilitate fino a ridurre l'utilizzo entro i nuovi limiti. Si prega di rivedere le caratteristiche qui sotto che sono attualmente oltre i limiti. Limiti di violazione:",
|
||||
"billingFeatureLossWarning": "Avviso Di Disponibilità Caratteristica",
|
||||
"billingFeatureLossDescription": "Con il downgrading, le funzioni non disponibili nel nuovo piano saranno disattivate automaticamente. Alcune impostazioni e configurazioni potrebbero andare perse. Controlla la matrice dei prezzi per capire quali funzioni non saranno più disponibili.",
|
||||
"billingUsageExceedsLimit": "L'utilizzo corrente ({current}) supera il limite ({limit})",
|
||||
"billingPastDueTitle": "Pagamento Scaduto",
|
||||
"billingPastDueDescription": "Il pagamento è scaduto. Si prega di aggiornare il metodo di pagamento per continuare a utilizzare le funzioni del piano corrente. Se non risolto, il tuo abbonamento verrà annullato e verrai ripristinato al livello gratuito.",
|
||||
"billingUnpaidTitle": "Abbonamento Non Pagato",
|
||||
"billingUnpaidDescription": "Il tuo abbonamento non è pagato e sei stato restituito al livello gratuito. Per favore aggiorna il metodo di pagamento per ripristinare l'abbonamento.",
|
||||
"billingIncompleteTitle": "Pagamento Incompleto",
|
||||
"billingIncompleteDescription": "Il pagamento è incompleto. Si prega di completare il processo di pagamento per attivare il tuo abbonamento.",
|
||||
"billingIncompleteExpiredTitle": "Pagamento Scaduto",
|
||||
"billingIncompleteExpiredDescription": "Il tuo pagamento non è mai stato completato ed è scaduto. Sei stato ripristinato al livello gratuito. Si prega di iscriversi nuovamente per ripristinare l'accesso alle funzionalità a pagamento.",
|
||||
"billingManageSubscription": "Gestisci il tuo abbonamento",
|
||||
"billingResolvePaymentIssue": "Si prega di risolvere il problema di pagamento prima di aggiornare o declassare",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Accetto i",
|
||||
"termsOfService": "termini di servizio",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Il tempo è in secondi",
|
||||
"requireDeviceApproval": "Richiede Approvazioni Dispositivo",
|
||||
"requireDeviceApprovalDescription": "Gli utenti con questo ruolo hanno bisogno di nuovi dispositivi approvati da un amministratore prima di poter connettersi e accedere alle risorse.",
|
||||
"sshAccess": "Accesso SSH",
|
||||
"roleAllowSsh": "Consenti SSH",
|
||||
"roleAllowSshAllow": "Consenti",
|
||||
"roleAllowSshDisallow": "Non Consentire",
|
||||
"roleAllowSshDescription": "Consenti agli utenti con questo ruolo di connettersi alle risorse tramite SSH. Quando disabilitato, il ruolo non può utilizzare l'accesso SSH.",
|
||||
"sshSudoMode": "Accesso Sudo",
|
||||
"sshSudoModeNone": "Nessuno",
|
||||
"sshSudoModeNoneDescription": "L'utente non può eseguire comandi con sudo.",
|
||||
"sshSudoModeFull": "Sudo Completo",
|
||||
"sshSudoModeFullDescription": "L'utente può eseguire qualsiasi comando con sudo.",
|
||||
"sshSudoModeCommands": "Comandi",
|
||||
"sshSudoModeCommandsDescription": "L'utente può eseguire solo i comandi specificati con sudo.",
|
||||
"sshSudo": "Consenti sudo",
|
||||
"sshSudoCommands": "Comandi Sudo",
|
||||
"sshSudoCommandsDescription": "Elenco di comandi separati da virgole che l'utente può eseguire con sudo.",
|
||||
"sshCreateHomeDir": "Crea Cartella Home",
|
||||
"sshUnixGroups": "Gruppi Unix",
|
||||
"sshUnixGroupsDescription": "Gruppi Unix separati da virgole per aggiungere l'utente sull'host di destinazione.",
|
||||
"retryAttempts": "Tentativi di Riprova",
|
||||
"expectedResponseCodes": "Codici di Risposta Attesi",
|
||||
"expectedResponseCodesDescription": "Codice di stato HTTP che indica lo stato di salute. Se lasciato vuoto, considerato sano è compreso tra 200-300.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Nodo di Uscita",
|
||||
"country": "Paese",
|
||||
"rulesMatchCountry": "Attualmente basato sull'IP di origine",
|
||||
"region": "Regione",
|
||||
"selectRegion": "Seleziona regione",
|
||||
"searchRegions": "Cerca regioni...",
|
||||
"noRegionFound": "Nessuna regione trovata.",
|
||||
"rulesMatchRegion": "Seleziona un raggruppamento regionale di paesi",
|
||||
"rulesErrorInvalidRegion": "Regione non valida",
|
||||
"rulesErrorInvalidRegionDescription": "Seleziona una regione valida.",
|
||||
"regionAfrica": "Africa",
|
||||
"regionNorthernAfrica": "Africa Settentrionale",
|
||||
"regionEasternAfrica": "Africa Orientale",
|
||||
"regionMiddleAfrica": "Africa Centrale",
|
||||
"regionSouthernAfrica": "Africa Meridionale",
|
||||
"regionWesternAfrica": "Africa Occidentale",
|
||||
"regionAmericas": "Americhe",
|
||||
"regionCaribbean": "Caraibi",
|
||||
"regionCentralAmerica": "America Centrale",
|
||||
"regionSouthAmerica": "America Del Sud",
|
||||
"regionNorthernAmerica": "America Del Nord",
|
||||
"regionAsia": "Asia",
|
||||
"regionCentralAsia": "Asia Centrale",
|
||||
"regionEasternAsia": "Asia Orientale",
|
||||
"regionSouthEasternAsia": "Asia Sudorientale",
|
||||
"regionSouthernAsia": "Asia Meridionale",
|
||||
"regionWesternAsia": "Asia Occidentale",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Europa Orientale",
|
||||
"regionNorthernEurope": "Europa Settentrionale",
|
||||
"regionSouthernEurope": "Europa Meridionale",
|
||||
"regionWesternEurope": "Europa Occidentale",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australia e Nuova Zelanda",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Gestito Auto-Ospitato",
|
||||
"description": "Server Pangolin self-hosted più affidabile e a bassa manutenzione con campanelli e fischietti extra",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Valore non valido",
|
||||
"idpTypeLabel": "Tipo Provider Identità",
|
||||
"roleMappingExpressionPlaceholder": "es. contiene(gruppi, 'admin') && 'Admin' <unk> <unk> 'Membro'",
|
||||
"roleMappingModeFixedRoles": "Ruoli Fissi",
|
||||
"roleMappingModeMappingBuilder": "Mapping Builder",
|
||||
"roleMappingModeRawExpression": "Espressione Raw",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Seleziona uno o più ruoli",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Digita nomi dei ruoli (corrispondenza esatta per organizzazione)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Assegna lo stesso ruolo impostato a ogni utente auto-provisioned.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Per i criteri predefiniti, digita i nomi dei ruoli che esistono in ogni organizzazione in cui gli utenti sono forniti. I nomi devono corrispondere esattamente.",
|
||||
"roleMappingClaimPath": "Richiedi Percorso",
|
||||
"roleMappingClaimPathPlaceholder": "gruppi",
|
||||
"roleMappingClaimPathDescription": "Percorso nel payload del token che contiene valori sorgente (ad esempio, gruppi).",
|
||||
"roleMappingMatchValue": "Valore Della Partita",
|
||||
"roleMappingAssignRoles": "Assegna Ruoli",
|
||||
"roleMappingAddMappingRule": "Aggiungi Regola Mappatura",
|
||||
"roleMappingRawExpressionResultDescription": "Espressione deve essere valutata in una stringa o array di stringhe.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Espressione deve valutare in una stringa (un singolo nome ruolo).",
|
||||
"roleMappingMatchValuePlaceholder": "Valore della corrispondenza (per esempio: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Digita i nomi dei ruoli (esatto per org)",
|
||||
"roleMappingBuilderFreeformRowHint": "I nomi dei ruoli devono corrispondere a un ruolo in ogni organizzazione di destinazione.",
|
||||
"roleMappingRemoveRule": "Rimuovi",
|
||||
"idpGoogleConfiguration": "Configurazione Google",
|
||||
"idpGoogleConfigurationDescription": "Configura le credenziali di Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Sei sicuro di voler rimuovere il branding per le pagine di autenticazione?",
|
||||
"authPageBrandingDeleteConfirm": "Conferma Eliminazione Branding",
|
||||
"brandingLogoURL": "URL Logo",
|
||||
"brandingLogoURLOrPath": "URL o percorso del logo",
|
||||
"brandingLogoPathDescription": "Inserisci un URL o un percorso locale.",
|
||||
"brandingLogoURLDescription": "Inserisci un URL accessibile al pubblico per la tua immagine del logo.",
|
||||
"brandingPrimaryColor": "Colore Primario",
|
||||
"brandingLogoWidth": "Larghezza (px)",
|
||||
"brandingLogoHeight": "Altezza (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Torna alla modalità di accesso standard",
|
||||
"orgAuthNoAccount": "Non hai un account?",
|
||||
"subscriptionRequiredToUse": "Per utilizzare questa funzionalità è necessario un abbonamento.",
|
||||
"mustUpgradeToUse": "Devi aggiornare il tuo abbonamento per utilizzare questa funzionalità.",
|
||||
"subscriptionRequiredTierToUse": "Questa funzione richiede <tierLink>{tier}</tierLink> o superiore.",
|
||||
"upgradeToTierToUse": "Aggiorna ad <tierLink>{tier}</tierLink> o superiore per utilizzare questa funzionalità.",
|
||||
"subscriptionTierTier1": "Home",
|
||||
"subscriptionTierTier2": "Squadra",
|
||||
"subscriptionTierTier3": "Business",
|
||||
"subscriptionTierEnterprise": "Impresa",
|
||||
"idpDisabled": "I provider di identità sono disabilitati.",
|
||||
"orgAuthPageDisabled": "La pagina di autenticazione dell'organizzazione è disabilitata.",
|
||||
"domainRestartedDescription": "Verifica del dominio riavviata con successo",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Ottieni una licenza",
|
||||
"description": "Scegli un piano e ci dica come intendi usare Pangolin.",
|
||||
"chooseTier": "Scegli il tuo piano",
|
||||
"viewPricingLink": "Vedi prezzi, funzionalità e limiti",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Avviatore",
|
||||
"description": "Caratteristiche aziendali, 25 utenti, 25 siti e supporto alla comunità."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Scala",
|
||||
"description": "Funzionalità aziendali, 50 utenti, 50 siti e supporto prioritario."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Solo uso personale (licenza gratuita — nessun checkout)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Continua al Checkout"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Errore di pagamento",
|
||||
"description": "Impossibile avviare il checkout. Per favore riprova."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Priorità",
|
||||
"priorityDescription": "I percorsi prioritari più alti sono valutati prima. Priorità = 100 significa ordinamento automatico (decidi di sistema). Usa un altro numero per applicare la priorità manuale.",
|
||||
"instanceName": "Nome Istanza",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Per quanto tempo conservare i log di accesso",
|
||||
"logRetentionActionLabel": "Ritenzione Registro Azioni",
|
||||
"logRetentionActionDescription": "Per quanto tempo conservare i log delle azioni",
|
||||
"logRetentionConnectionLabel": "Ritenzione Registro Di Connessione",
|
||||
"logRetentionConnectionDescription": "Per quanto tempo conservare i log di connessione",
|
||||
"logRetentionDisabled": "Disabilitato",
|
||||
"logRetention3Days": "3 giorni",
|
||||
"logRetention7Days": "7 giorni",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Fine dell'anno successivo",
|
||||
"actionLogsDescription": "Visualizza una cronologia delle azioni eseguite in questa organizzazione",
|
||||
"accessLogsDescription": "Visualizza le richieste di autenticazione di accesso per le risorse in questa organizzazione",
|
||||
"licenseRequiredToUse": "Per utilizzare questa funzione è necessaria una licenza Enterprise.",
|
||||
"connectionLogs": "Log Di Connessione",
|
||||
"connectionLogsDescription": "Visualizza i log di connessione per i tunnel in questa organizzazione",
|
||||
"sidebarLogsConnection": "Log Di Connessione",
|
||||
"sidebarLogsStreaming": "Streaming",
|
||||
"sourceAddress": "Indirizzo Di Origine",
|
||||
"destinationAddress": "Indirizzo Di Destinazione",
|
||||
"duration": "Durata",
|
||||
"licenseRequiredToUse": "Per utilizzare questa funzione è necessaria una licenza <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> o <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> . <bookADemoLink>Prenota una demo o una prova POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "L' <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> è necessaria per utilizzare questa funzione. Questa funzione è disponibile anche in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Prenota una demo o una prova POC</bookADemoLink>.",
|
||||
"certResolver": "Risolutore Di Certificato",
|
||||
"certResolverDescription": "Selezionare il risolutore di certificati da usare per questa risorsa.",
|
||||
"selectCertResolver": "Seleziona Risolutore Di Certificato",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Controllo Accesso",
|
||||
"editInternalResourceDialogAccessControlDescription": "Controlla quali ruoli, utenti e client macchina hanno accesso a questa risorsa quando connessi. Gli amministratori hanno sempre accesso.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Il range delle porte deve essere \"*\" per tutte le porte, o un elenco di porte e intervalli separato da virgole (ad es. \"80,443,8000-9000\"). Le porte devono essere tra 1 e 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "Posizione Demone Autenticazione SSH",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Scegli dove funziona il demone di autenticazione SSH: sul sito (Newt) o su un host remoto.",
|
||||
"internalResourceAuthDaemonDescription": "Il demone di autenticazione SSH gestisce la firma della chiave SSH e l'autenticazione PAM per questa risorsa. Scegli se viene eseguito sul sito (Newt) o su un host remoto separato. Vedi <docsLink>la documentazione</docsLink> per ulteriori informazioni.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Seleziona Strategia",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Posizione",
|
||||
"internalResourceAuthDaemonSite": "Sul Sito",
|
||||
"internalResourceAuthDaemonSiteDescription": "Il demone Auth viene eseguito sul sito (Nuovo).",
|
||||
"internalResourceAuthDaemonRemote": "Host Remoto",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Il demone di autenticazione viene eseguito su un host che non è il sito.",
|
||||
"internalResourceAuthDaemonPort": "Porta Demone (facoltativa)",
|
||||
"orgAuthWhatsThis": "Dove posso trovare l'ID della mia organizzazione?",
|
||||
"learnMore": "Scopri di più",
|
||||
"backToHome": "Torna alla home",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Firewall Abilitato",
|
||||
"autoUpdatesEnabled": "Aggiornamenti Automatici Abilitati",
|
||||
"tpmAvailable": "TPM Disponibile",
|
||||
"windowsAntivirusEnabled": "Antivirus Abilitato",
|
||||
"macosSipEnabled": "Protezione Dell'Integrità Del Sistema (Sip)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Modo Furtivo Del Firewall",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Abilita Approvazioni Dispositivo",
|
||||
"approvalsEmptyStateStep2Description": "Modifica un ruolo e abilita l'opzione 'Richiedi l'approvazione del dispositivo'. Gli utenti con questo ruolo avranno bisogno dell'approvazione dell'amministratore per i nuovi dispositivi.",
|
||||
"approvalsEmptyStatePreviewDescription": "Anteprima: quando abilitato, le richieste di dispositivo in attesa appariranno qui per la revisione",
|
||||
"approvalsEmptyStateButtonText": "Gestisci Ruoli"
|
||||
"approvalsEmptyStateButtonText": "Gestisci Ruoli",
|
||||
"domainErrorTitle": "Stiamo avendo problemi a verificare il tuo dominio",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configura la mappatura dei ruoli e le politiche di organizzazione nella scheda <policiesTabLink>Auto Provision Settings</policiesTabLink>.",
|
||||
"streamingTitle": "Streaming Eventi",
|
||||
"streamingDescription": "Trasmetti eventi dalla tua organizzazione a destinazioni esterne in tempo reale.",
|
||||
"streamingUnnamedDestination": "Destinazione senza nome",
|
||||
"streamingNoUrlConfigured": "Nessun URL configurato",
|
||||
"streamingAddDestination": "Aggiungi Destinazione",
|
||||
"streamingHttpWebhookTitle": "Webhook HTTP",
|
||||
"streamingHttpWebhookDescription": "Invia eventi a qualsiasi endpoint HTTP con autenticazione e template flessibili.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Trasmetti eventi su un contenitore di archiviazione per oggetti compatibile con S3. Presto in arrivo.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Inoltra gli eventi direttamente al tuo account Datadog. In arrivo.",
|
||||
"streamingTypePickerDescription": "Scegli un tipo di destinazione per iniziare.",
|
||||
"streamingFailedToLoad": "Impossibile caricare le destinazioni",
|
||||
"streamingUnexpectedError": "Si è verificato un errore imprevisto.",
|
||||
"streamingFailedToUpdate": "Impossibile aggiornare la destinazione",
|
||||
"streamingDeletedSuccess": "Destinazione eliminata con successo",
|
||||
"streamingFailedToDelete": "Impossibile eliminare la destinazione",
|
||||
"streamingDeleteTitle": "Elimina Destinazione",
|
||||
"streamingDeleteButtonText": "Elimina Destinazione",
|
||||
"streamingDeleteDialogAreYouSure": "Sei sicuro di voler eliminare",
|
||||
"streamingDeleteDialogThisDestination": "questa destinazione",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Tutta la configurazione verrà definitivamente rimossa.",
|
||||
"httpDestEditTitle": "Modifica Destinazione",
|
||||
"httpDestAddTitle": "Aggiungi Destinazione HTTP",
|
||||
"httpDestEditDescription": "Aggiorna la configurazione per questa destinazione di streaming di eventi HTTP.",
|
||||
"httpDestAddDescription": "Configura un nuovo endpoint HTTP per ricevere gli eventi della tua organizzazione.",
|
||||
"httpDestTabSettings": "Impostazioni",
|
||||
"httpDestTabHeaders": "Intestazioni",
|
||||
"httpDestTabBody": "Corpo",
|
||||
"httpDestTabLogs": "Registri",
|
||||
"httpDestNamePlaceholder": "La mia destinazione HTTP",
|
||||
"httpDestUrlLabel": "Url Di Destinazione",
|
||||
"httpDestUrlErrorHttpRequired": "L'URL deve usare http o https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS è richiesto sulle distribuzioni cloud",
|
||||
"httpDestUrlErrorInvalid": "Inserisci un URL valido (es. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Autenticazione",
|
||||
"httpDestAuthDescription": "Scegli come vengono autenticate le richieste al tuo endpoint.",
|
||||
"httpDestAuthNoneTitle": "Nessuna Autenticazione",
|
||||
"httpDestAuthNoneDescription": "Invia richieste senza intestazione autorizzazione.",
|
||||
"httpDestAuthBearerTitle": "Token Del Portatore",
|
||||
"httpDestAuthBearerDescription": "Aggiunge un'intestazione Autorizzazione: Bearer <token> ad ogni richiesta.",
|
||||
"httpDestAuthBearerPlaceholder": "La tua chiave API o token",
|
||||
"httpDestAuthBasicTitle": "Autenticazione Base",
|
||||
"httpDestAuthBasicDescription": "Aggiunge un'autorizzazione: intestazione di base <credentials> . Fornisce le credenziali come username:password.",
|
||||
"httpDestAuthBasicPlaceholder": "username:password",
|
||||
"httpDestAuthCustomTitle": "Intestazione Personalizzata",
|
||||
"httpDestAuthCustomDescription": "Specifica un nome e un valore di intestazione HTTP personalizzati per l'autenticazione (ad esempio X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Nome intestazione (es. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Valore intestazione",
|
||||
"httpDestCustomHeadersTitle": "Intestazioni Http Personalizzate",
|
||||
"httpDestCustomHeadersDescription": "Aggiungi intestazioni personalizzate ad ogni richiesta in uscita. Utile per token statici o un tipo di contenuto personalizzato. Come impostazione predefinita, viene inviato il tipo di contenuto/json.",
|
||||
"httpDestNoHeadersConfigured": "Nessuna intestazione personalizzata configurata. Fare clic su \"Aggiungi intestazione\" per aggiungerne una.",
|
||||
"httpDestHeaderNamePlaceholder": "Nome intestazione",
|
||||
"httpDestHeaderValuePlaceholder": "Valore",
|
||||
"httpDestAddHeader": "Aggiungi Intestazione",
|
||||
"httpDestBodyTemplateTitle": "Modello Corpo Personalizzato",
|
||||
"httpDestBodyTemplateDescription": "Controlla la struttura JSON payload inviata al tuo endpoint. Se disabilitata, viene inviato un oggetto JSON predefinito per ogni evento.",
|
||||
"httpDestEnableBodyTemplate": "Abilita modello corpo personalizzato",
|
||||
"httpDestBodyTemplateLabel": "Modello Corpo (JSON)",
|
||||
"httpDestBodyTemplateHint": "Usa le variabili del modello per fare riferimento ai campi dell'evento nel tuo payload.",
|
||||
"httpDestPayloadFormatTitle": "Formato Payload",
|
||||
"httpDestPayloadFormatDescription": "Come gli eventi sono serializzati in ogni organismo di richiesta.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON Array",
|
||||
"httpDestFormatJsonArrayDescription": "Una richiesta per lotto, corpo è un array JSON. Compatibile con la maggior parte dei webhooks generici e Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Una richiesta per lotto, corpo è newline-delimited JSON — un oggetto per linea, nessun array esterno. Richiesto da Splunk HEC, Elastic / OpenSearch, e Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Un Evento Per Richiesta",
|
||||
"httpDestFormatSingleDescription": "Invia un HTTP POST separato per ogni singolo evento. Usa solo per gli endpoint che non possono gestire i batch.",
|
||||
"httpDestLogTypesTitle": "Tipi Di Log",
|
||||
"httpDestLogTypesDescription": "Scegli quali tipi di log vengono inoltrati a questa destinazione. Verranno trasmessi solo i tipi di log abilitati.",
|
||||
"httpDestAccessLogsTitle": "Log Accesso",
|
||||
"httpDestAccessLogsDescription": "Tentativi di accesso alle risorse, comprese le richieste autenticate e negate.",
|
||||
"httpDestActionLogsTitle": "Log Azioni",
|
||||
"httpDestActionLogsDescription": "Azioni amministrative eseguite dagli utenti all'interno dell'organizzazione.",
|
||||
"httpDestConnectionLogsTitle": "Log Di Connessione",
|
||||
"httpDestConnectionLogsDescription": "Eventi di connessione al sito e al tunnel, inclusi collegamenti e disconnessioni.",
|
||||
"httpDestRequestLogsTitle": "Log Richiesta",
|
||||
"httpDestRequestLogsDescription": "Registri di richiesta HTTP per le risorse proxy, inclusi metodo, percorso e codice di risposta.",
|
||||
"httpDestSaveChanges": "Salva Modifiche",
|
||||
"httpDestCreateDestination": "Crea Destinazione",
|
||||
"httpDestUpdatedSuccess": "Destinazione aggiornata con successo",
|
||||
"httpDestCreatedSuccess": "Destinazione creata con successo",
|
||||
"httpDestUpdateFailed": "Impossibile aggiornare la destinazione",
|
||||
"httpDestCreateFailed": "Impossibile creare la destinazione"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "당신은 {count, plural, =0 {조직이 없습니다} one {하나의 조직} other {# 개의 조직}}의 구성원입니다.",
|
||||
"componentsInvalidKey": "유효하지 않거나 만료된 라이센스 키가 감지되었습니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.",
|
||||
"dismiss": "해제",
|
||||
"subscriptionViolationMessage": "현재 계획의 한계를 초과했습니다. 사이트, 사용자 또는 기타 리소스를 제거하여 계획 내에 머물도록 해결하세요.",
|
||||
"subscriptionViolationViewBilling": "청구 보기",
|
||||
"componentsLicenseViolation": "라이센스 위반: 이 서버는 {usedSites} 사이트를 사용하고 있으며, 이는 {maxSites} 사이트의 라이센스 한도를 초과합니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.",
|
||||
"componentsSupporterMessage": "{tier}로 판골린을 지원해 주셔서 감사합니다!",
|
||||
"inviteErrorNotValid": "죄송하지만, 접근하려는 초대가 수락되지 않았거나 더 이상 유효하지 않은 것 같습니다.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "링크 생성",
|
||||
"resourcesNotFound": "리소스가 발견되지 않았습니다.",
|
||||
"resourceSearch": "리소스 검색",
|
||||
"machineSearch": "기계 검색",
|
||||
"machinesSearch": "기계 클라이언트 검색...",
|
||||
"machineNotFound": "기계를 찾을 수 없습니다",
|
||||
"userDeviceSearch": "사용자 장치 검색",
|
||||
"userDevicesSearch": "사용자 장치 검색...",
|
||||
"openMenu": "메뉴 열기",
|
||||
"resource": "리소스",
|
||||
"title": "제목",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "완전한 도메인 이름을 사용해 RAW 또는 HTTPS로 프록시 요청을 수행합니다.",
|
||||
"resourceRaw": "원시 TCP/UDP 리소스",
|
||||
"resourceRawDescription": "포트 번호를 사용하여 RAW TCP/UDP로 요청을 프록시합니다.",
|
||||
"resourceRawDescriptionCloud": "포트 번호를 사용하여 원격 노드에 연결해야 합니다. 원격 노드에서 리소스를 사용하려면 사용자 지정 도메인을 사용하십시오.",
|
||||
"resourceCreate": "리소스 생성",
|
||||
"resourceCreateDescription": "아래 단계를 따라 새 리소스를 생성하세요.",
|
||||
"resourceSeeAll": "모든 리소스 보기",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "프로토콜 선택",
|
||||
"resourcePortNumber": "포트 번호",
|
||||
"resourcePortNumberDescription": "요청을 프록시하기 위한 외부 포트 번호입니다.",
|
||||
"back": "뒤로",
|
||||
"cancel": "취소",
|
||||
"resourceConfig": "구성 스니펫",
|
||||
"resourceConfigDescription": "TCP/UDP 리소스를 설정하기 위해 이 구성 스니펫을 복사하여 붙여넣습니다.",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "조직을 삭제하는 중 오류가 발생했습니다.",
|
||||
"orgDeleted": "조직이 삭제되었습니다.",
|
||||
"orgDeletedMessage": "조직과 그 데이터가 삭제되었습니다.",
|
||||
"deleteAccount": "계정 삭제",
|
||||
"deleteAccountDescription": "계정, 소유한 모든 조직 및 조직 내의 모든 데이터를 영구적으로 삭제합니다. 이 작업은 되돌릴 수 없습니다.",
|
||||
"deleteAccountButton": "계정 삭제",
|
||||
"deleteAccountConfirmTitle": "계정 삭제",
|
||||
"deleteAccountConfirmMessage": "이 작업은 귀하의 계정, 소유한 모든 조직 및 조직 내 모든 데이터를 영구적으로 삭제합니다. 이 작업은 되돌릴 수 없습니다.",
|
||||
"deleteAccountConfirmString": "계정 삭제",
|
||||
"deleteAccountSuccess": "계정 삭제됨",
|
||||
"deleteAccountSuccessMessage": "계정이 삭제되었습니다.",
|
||||
"deleteAccountError": "계정 삭제 실패",
|
||||
"deleteAccountPreviewAccount": "귀하의 계정",
|
||||
"deleteAccountPreviewOrgs": "귀하가 소유한 조직(포함된 모든 데이터)",
|
||||
"orgMissing": "조직 ID가 누락되었습니다",
|
||||
"orgMissingMessage": "조직 ID 없이 초대장을 재생성할 수 없습니다.",
|
||||
"accessUsersManage": "사용자 관리",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "API 키 삭제",
|
||||
"apiKeysManage": "API 키 관리",
|
||||
"apiKeysDescription": "API 키는 통합 API와 인증하는 데 사용됩니다.",
|
||||
"provisioningKeysTitle": "프로비저닝 키",
|
||||
"provisioningKeysManage": "프로비저닝 키 관리",
|
||||
"provisioningKeysDescription": "프로비저닝 키는 조직의 자동 사이트 프로비저닝 인증에 사용됩니다.",
|
||||
"provisioningManage": "프로비저닝",
|
||||
"provisioningDescription": "프로비저닝 키를 관리하고 승인을 기다리는 사이트를 검토합니다.",
|
||||
"pendingSites": "대기중인 사이트",
|
||||
"siteApproveSuccess": "사이트가 성공적으로 승인되었습니다",
|
||||
"siteApproveError": "사이트 승인 오류",
|
||||
"provisioningKeys": "프로비저닝 키",
|
||||
"searchProvisioningKeys": "프로비저닝 키 검색...",
|
||||
"provisioningKeysAdd": "프로비저닝 키 생성",
|
||||
"provisioningKeysErrorDelete": "프로비저닝 키 삭제 오류",
|
||||
"provisioningKeysErrorDeleteMessage": "프로비저닝 키 삭제 오류",
|
||||
"provisioningKeysQuestionRemove": "이 프로비저닝 키를 조직에서 제거하시겠습니까?",
|
||||
"provisioningKeysMessageRemove": "제거 후에는 이 키를 사이트 프로비저닝에 사용할 수 없습니다.",
|
||||
"provisioningKeysDeleteConfirm": "프로비저닝 키 삭제 확인",
|
||||
"provisioningKeysDelete": "프로비저닝 키 삭제",
|
||||
"provisioningKeysCreate": "프로비저닝 키 생성",
|
||||
"provisioningKeysCreateDescription": "조직을 위한 새로운 프로비저닝 키 생성",
|
||||
"provisioningKeysSeeAll": "모든 프로비저닝 키 보기",
|
||||
"provisioningKeysSave": "프로비저닝 키 저장",
|
||||
"provisioningKeysSaveDescription": "이것은 한 번만 볼 수 있습니다. 안전한 장소에 복사해 두세요.",
|
||||
"provisioningKeysErrorCreate": "프로비저닝 키 생성 오류",
|
||||
"provisioningKeysList": "새 프로비저닝 키",
|
||||
"provisioningKeysMaxBatchSize": "최대 배치 크기",
|
||||
"provisioningKeysUnlimitedBatchSize": "무제한 배치 크기 (제한 없음)",
|
||||
"provisioningKeysMaxBatchUnlimited": "무제한",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "유효한 최대 배치 크기를 입력하세요 (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "유효 기간",
|
||||
"provisioningKeysValidUntilHint": "만료 날짜를 설정하지 않을 경우 빈칸으로 남겨 두세요.",
|
||||
"provisioningKeysValidUntilInvalid": "유효한 날짜와 시간을 입력하세요.",
|
||||
"provisioningKeysNumUsed": "사용 횟수",
|
||||
"provisioningKeysLastUsed": "마지막 사용",
|
||||
"provisioningKeysNoExpiry": "만료 없음",
|
||||
"provisioningKeysNeverUsed": "절대",
|
||||
"provisioningKeysEdit": "프로비저닝 키 수정",
|
||||
"provisioningKeysEditDescription": "이 키의 최대 배치 크기 및 만료 시간을 업데이트하세요.",
|
||||
"provisioningKeysApproveNewSites": "새로운 사이트 승인",
|
||||
"provisioningKeysApproveNewSitesDescription": "이 키를 등록하는 사이트를 자동으로 승인합니다.",
|
||||
"provisioningKeysUpdateError": "프로비저닝 키 업데이트 오류",
|
||||
"provisioningKeysUpdated": "프로비저닝 키가 업데이트되었습니다",
|
||||
"provisioningKeysUpdatedDescription": "변경 사항이 저장되었습니다.",
|
||||
"provisioningKeysBannerTitle": "사이트 프로비저닝 키",
|
||||
"provisioningKeysBannerDescription": "프로비저닝 키를 생성하여 Newt 커넥터와 함께 사용해 첫 실행 시 자동으로 사이트를 생성하세요 — 각 사이트마다 별도의 인증을 설정할 필요가 없습니다.",
|
||||
"provisioningKeysBannerButtonText": "자세히 알아보기",
|
||||
"pendingSitesBannerTitle": "대기중인 사이트",
|
||||
"pendingSitesBannerDescription": "프로비저닝 키를 사용하여 연결하는 사이트는 검토 대기 중입니다. 사이트가 활성화되어 리소스에 액세스하기 전에 각 사이트를 승인하세요.",
|
||||
"pendingSitesBannerButtonText": "자세히 알아보기",
|
||||
"apiKeysSettings": "{apiKeyName} 설정",
|
||||
"userTitle": "모든 사용자 관리",
|
||||
"userDescription": "시스템의 모든 사용자를 보고 관리합니다",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "승인 상태로 필터링",
|
||||
"approvalListEmpty": "승인이 없습니다.",
|
||||
"approvalState": "승인 상태",
|
||||
"approvalLoadMore": "더 불러오기",
|
||||
"loadingApprovals": "승인 불러오는 중",
|
||||
"approve": "승인",
|
||||
"approved": "승인됨",
|
||||
"denied": "거부됨",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "사용자 저장됨",
|
||||
"userSavedDescription": "사용자가 업데이트되었습니다.",
|
||||
"autoProvisioned": "자동 프로비저닝됨",
|
||||
"autoProvisionSettings": "자동 프로비저닝 설정",
|
||||
"autoProvisionedDescription": "이 사용자가 ID 공급자에 의해 자동으로 관리될 수 있도록 허용합니다",
|
||||
"accessControlsDescription": "이 사용자가 조직에서 접근하고 수행할 수 있는 작업을 관리하세요",
|
||||
"accessControlsSubmit": "접근 제어 저장",
|
||||
"singleRolePerUserPlanNotice": "계획에는 사용자당 한 가지 역할만 지원됩니다.",
|
||||
"singleRolePerUserEditionNotice": "이 판에는 사용자당 한 가지 역할만 지원됩니다.",
|
||||
"roles": "역할",
|
||||
"accessUsersRoles": "사용자 및 역할 관리",
|
||||
"accessUsersRolesDescription": "사용자를 초대하고 역할에 추가하여 조직에 대한 접근을 관리하세요",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "리소스를 전환하는 데 실패했습니다.",
|
||||
"resourcesErrorUpdateDescription": "리소스를 업데이트하는 동안 오류가 발생했습니다.",
|
||||
"access": "접속",
|
||||
"accessControl": "액세스 제어",
|
||||
"shareLink": "{resource} 공유 링크",
|
||||
"resourceSelect": "리소스 선택",
|
||||
"shareLinks": "공유 링크",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "역할이 제거되었습니다",
|
||||
"accessRoleRemovedDescription": "역할이 성공적으로 제거되었습니다.",
|
||||
"accessRoleRequiredRemove": "이 역할을 삭제하기 전에 기존 구성원을 전송할 새 역할을 선택하세요.",
|
||||
"network": "네트워크",
|
||||
"manage": "관리",
|
||||
"sitesNotFound": "사이트를 찾을 수 없습니다.",
|
||||
"pangolinServerAdmin": "서버 관리자 - 판골린",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "사이트 수 증가",
|
||||
"idpManage": "아이덴티티 공급자 관리",
|
||||
"idpManageDescription": "시스템에서 ID 제공자를 보고 관리합니다",
|
||||
"idpGlobalModeBanner": "조직별 신원 제공자(IdP)는 이 서버에서 비활성화되었습니다. 이 서버는 모든 조직에 걸쳐 공유된 글로벌 IdP를 사용 중입니다. <adminPanelLink>관리자 패널</adminPanelLink>에서 글로벌 IdP를 관리하십시오. 조직별 IdP를 활성화하려면 서버 설정을 편집하고 IdP 모드를 조직으로 설정하십시오. <configDocsLink>문서 보기</configDocsLink>. 글로벌 IdP 사용을 계속하고 조직 설정에서 이 항목을 제거하려면 설정에서 모드를 글로벌로 명시적으로 설정하십시오.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "조직별 신원 제공자(IdP)는 이 서버에서 비활성화되었습니다. 이 서버는 모든 조직에 걸쳐 공유된 글로벌 IdP를 사용 중입니다. <adminPanelLink>관리자 패널</adminPanelLink>에서 글로벌 IdP를 관리하십시오. 조직별 신원 제공자를 사용하려면 Enterprise 에디션으로 업그레이드해야 합니다.",
|
||||
"idpGlobalModeBannerLicenseRequired": "조직별 신원 제공자(IdP)는 이 서버에서 비활성화되었습니다. 이 서버는 모든 조직에 걸쳐 공유된 글로벌 IdP를 사용 중입니다. <adminPanelLink>관리자 패널</adminPanelLink>에서 글로벌 IdP를 관리하십시오. 조직별 신원 제공자를 사용하려면 엔터프라이즈 라이선스가 필요합니다.",
|
||||
"idpDeletedDescription": "신원 공급자가 성공적으로 삭제되었습니다",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "아이덴티티 공급자를 영구적으로 삭제하시겠습니까?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "설정 - 판골린",
|
||||
"orgNameRequired": "조직 이름은 필수입니다.",
|
||||
"orgIdRequired": "조직 ID가 필요합니다",
|
||||
"orgIdMaxLength": "조직 ID는 최대 32자 이내여야 합니다",
|
||||
"orgErrorCreate": "조직 생성 중 오류가 발생했습니다.",
|
||||
"pageNotFound": "페이지를 찾을 수 없습니다",
|
||||
"pageNotFoundDescription": "앗! 찾고 있는 페이지가 존재하지 않습니다.",
|
||||
"overview": "개요",
|
||||
"home": "홈",
|
||||
"accessControl": "액세스 제어",
|
||||
"settings": "설정",
|
||||
"usersAll": "모든 사용자",
|
||||
"license": "라이선스",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "사용자 조회",
|
||||
"actionGetOrgUser": "조직 사용자 가져오기",
|
||||
"actionListOrgDomains": "조직 도메인 목록",
|
||||
"actionGetDomain": "도메인 가져오기",
|
||||
"actionCreateOrgDomain": "도메인 생성",
|
||||
"actionUpdateOrgDomain": "도메인 업데이트",
|
||||
"actionDeleteOrgDomain": "도메인 삭제",
|
||||
"actionGetDNSRecords": "DNS 레코드 가져오기",
|
||||
"actionRestartOrgDomain": "도메인 재시작",
|
||||
"actionCreateSite": "사이트 생성",
|
||||
"actionDeleteSite": "사이트 삭제",
|
||||
"actionGetSite": "사이트 가져오기",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "서버 콘솔에서 설정 토큰 입력.",
|
||||
"setupTokenRequired": "설정 토큰이 필요합니다",
|
||||
"actionUpdateSite": "사이트 업데이트",
|
||||
"actionResetSiteBandwidth": "조직 대역폭 재설정",
|
||||
"actionListSiteRoles": "허용된 사이트 역할 목록",
|
||||
"actionCreateResource": "리소스 생성",
|
||||
"actionDeleteResource": "리소스 삭제",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "사용자 제거",
|
||||
"actionListUsers": "사용자 목록",
|
||||
"actionAddUserRole": "사용자 역할 추가",
|
||||
"actionSetUserOrgRoles": "사용자 역할 설정",
|
||||
"actionGenerateAccessToken": "액세스 토큰 생성",
|
||||
"actionDeleteAccessToken": "액세스 토큰 삭제",
|
||||
"actionListAccessTokens": "액세스 토큰 목록",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "로그 보기",
|
||||
"noneSelected": "선택된 항목 없음",
|
||||
"orgNotFound2": "조직이 없습니다.",
|
||||
"searchProgress": "검색...",
|
||||
"searchPlaceholder": "검색...",
|
||||
"emptySearchOptions": "옵션이 없습니다",
|
||||
"create": "생성",
|
||||
"orgs": "조직",
|
||||
"loginError": "예기치 않은 오류가 발생했습니다. 다시 시도해주세요.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "비공개",
|
||||
"sidebarAccessControl": "액세스 제어",
|
||||
"sidebarLogsAndAnalytics": "로그 및 분석",
|
||||
"sidebarTeam": "팀",
|
||||
"sidebarUsers": "사용자",
|
||||
"sidebarAdmin": "관리자",
|
||||
"sidebarInvitations": "초대",
|
||||
"sidebarRoles": "역할",
|
||||
"sidebarShareableLinks": "링크",
|
||||
"sidebarApiKeys": "API 키",
|
||||
"sidebarProvisioning": "프로비저닝",
|
||||
"sidebarSettings": "설정",
|
||||
"sidebarAllUsers": "모든 사용자",
|
||||
"sidebarIdentityProviders": "신원 공급자",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "로그 & 통계",
|
||||
"sidebarBluePrints": "청사진",
|
||||
"sidebarOrganization": "조직",
|
||||
"sidebarManagement": "관리",
|
||||
"sidebarBillingAndLicenses": "결제 및 라이선스",
|
||||
"sidebarLogsAnalytics": "분석",
|
||||
"blueprints": "청사진",
|
||||
"blueprintsDescription": "선언적 구성을 적용하고 이전 실행을 봅니다",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "구문 분석된 콘텐츠 (읽기 전용)",
|
||||
"enableDockerSocket": "Docker 청사진 활성화",
|
||||
"enableDockerSocketDescription": "블루프린트 레이블을 위한 Docker 소켓 레이블 수집을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
|
||||
"enableDockerSocketLink": "자세히 알아보기",
|
||||
"viewDockerContainers": "도커 컨테이너 보기",
|
||||
"containersIn": "{siteName}의 컨테이너",
|
||||
"selectContainerDescription": "이 대상을 위한 호스트 이름으로 사용할 컨테이너를 선택하세요. 포트를 사용하려면 포트를 클릭하세요.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "이름 공간: {namespace}",
|
||||
"domainPickerShowMore": "더보기",
|
||||
"regionSelectorTitle": "지역 선택",
|
||||
"domainPickerRemoteExitNodeWarning": "제공된 도메인은 원격 종료 노드에 연결된 사이트에서 지원되지 않습니다. 원격 노드에서 리소스를 사용하려면 사용자 지정 도메인을 사용하십시오.",
|
||||
"regionSelectorInfo": "지역을 선택하면 위치에 따라 더 나은 성능이 제공됩니다. 서버와 같은 지역에 있을 필요는 없습니다.",
|
||||
"regionSelectorPlaceholder": "지역 선택",
|
||||
"regionSelectorComingSoon": "곧 출시 예정",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "사용 한도 개요",
|
||||
"billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@pangolin.net로 연락하십시오.",
|
||||
"billingDataUsage": "데이터 사용량",
|
||||
"billingOnlineTime": "사이트 온라인 시간",
|
||||
"billingUsers": "활성 사용자",
|
||||
"billingDomains": "활성 도메인",
|
||||
"billingRemoteExitNodes": "활성 자체 호스팅 노드",
|
||||
"billingSites": "사이트",
|
||||
"billingUsers": "사용자",
|
||||
"billingDomains": "도메인",
|
||||
"billingOrganizations": "조직",
|
||||
"billingRemoteExitNodes": "원격 노드",
|
||||
"billingNoLimitConfigured": "구성된 한도가 없습니다.",
|
||||
"billingEstimatedPeriod": "예상 청구 기간",
|
||||
"billingIncludedUsage": "포함 사용량",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "포털 URL을 가져오는 데 실패했습니다.",
|
||||
"billingPortalError": "포털 오류",
|
||||
"billingDataUsageInfo": "클라우드에 연결할 때 보안 터널을 통해 전송된 모든 데이터에 대해 비용이 청구됩니다. 여기에는 모든 사이트의 들어오고 나가는 트래픽이 포함됩니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용하는 경우 데이터는 요금이 청구되지 않습니다.",
|
||||
"billingOnlineTimeInfo": "사이트가 클라우드에 연결된 시간에 따라 요금이 청구됩니다. 예를 들어, 44,640분은 사이트가 한 달 내내 24시간 작동하는 것과 같습니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용할 때 시간은 요금이 청구되지 않습니다.",
|
||||
"billingUsersInfo": "조직의 사용자마다 요금이 청구됩니다. 청구는 조직의 활성 사용자 계정 수에 따라 매일 계산됩니다.",
|
||||
"billingDomainInfo": "조직의 도메인마다 요금이 청구됩니다. 청구는 조직의 활성 도메인 계정 수에 따라 매일 계산됩니다.",
|
||||
"billingRemoteExitNodesInfo": "조직의 관리 노드마다 요금이 청구됩니다. 청구는 조직의 활성 관리 노드 수에 따라 매일 계산됩니다.",
|
||||
"billingSInfo": "사용할 수 있는 사이트 수",
|
||||
"billingUsersInfo": "사용할 수 있는 사용자 수",
|
||||
"billingDomainInfo": "사용할 수 있는 도메인 수",
|
||||
"billingRemoteExitNodesInfo": "사용할 수 있는 원격 노드 수",
|
||||
"billingLicenseKeys": "라이센스 키",
|
||||
"billingLicenseKeysDescription": "라이센스 키 구독을 관리하세요",
|
||||
"billingLicenseSubscription": "라이센스 구독",
|
||||
"billingInactive": "비활성화됨",
|
||||
"billingLicenseItem": "라이센스 항목",
|
||||
"billingQuantity": "수량",
|
||||
"billingTotal": "총계",
|
||||
"billingModifyLicenses": "라이센스 구독 수정",
|
||||
"domainNotFound": "도메인을 찾을 수 없습니다",
|
||||
"domainNotFoundDescription": "이 리소스는 도메인이 더 이상 시스템에 존재하지 않아 비활성화되었습니다. 이 리소스에 대한 새 도메인을 설정하세요.",
|
||||
"failed": "실패",
|
||||
"createNewOrgDescription": "새 조직 생성",
|
||||
"organization": "조직",
|
||||
"primary": "기본",
|
||||
"port": "포트",
|
||||
"securityKeyManage": "보안 키 관리",
|
||||
"securityKeyDescription": "비밀번호 없는 인증을 위해 보안 키를 추가하거나 제거합니다.",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "HTTP 리소스가 아닌 경우 포트 번호가 필요합니다",
|
||||
"resourcePortNotAllowed": "HTTP 리소스에 대해 포트 번호를 설정하지 마세요",
|
||||
"billingPricingCalculatorLink": "가격 계산기",
|
||||
"billingYourPlan": "귀하의 계획",
|
||||
"billingViewOrModifyPlan": "현재 계획 보기 또는 수정",
|
||||
"billingViewPlanDetails": "계획 세부정보 보기",
|
||||
"billingUsageAndLimits": "사용량 및 제한",
|
||||
"billingViewUsageAndLimits": "계획의 제한 및 현재 사용량 보기",
|
||||
"billingCurrentUsage": "현재 사용량",
|
||||
"billingMaximumLimits": "최대 제한",
|
||||
"billingRemoteNodes": "원격 노드",
|
||||
"billingUnlimited": "무제한",
|
||||
"billingPaidLicenseKeys": "유료 라이센스 키",
|
||||
"billingManageLicenseSubscription": "유료 독립 호스트 라이센스 키를 위한 구독 관리",
|
||||
"billingCurrentKeys": "현재 키",
|
||||
"billingModifyCurrentPlan": "현재 계획 수정",
|
||||
"billingConfirmUpgrade": "업그레이드 확인",
|
||||
"billingConfirmDowngrade": "다운그레이드 확인",
|
||||
"billingConfirmUpgradeDescription": "계획을 업그레이드하려고 합니다. 아래의 새로운 제한 및 가격을 검토하세요.",
|
||||
"billingConfirmDowngradeDescription": "계획을 다운그레이드하려고 합니다. 아래의 새로운 제한 및 가격을 검토하세요.",
|
||||
"billingPlanIncludes": "계획 포함",
|
||||
"billingProcessing": "처리 중...",
|
||||
"billingConfirmUpgradeButton": "업그레이드 확인",
|
||||
"billingConfirmDowngradeButton": "다운그레이드 확인",
|
||||
"billingLimitViolationWarning": "사용량이 새 계획의 제한을 초과합니다.",
|
||||
"billingLimitViolationDescription": "현재 사용량이 이 계획의 제한을 초과합니다. 다운그레이드 후 모든 작업은 새로운 제한 내로 사용량을 줄일 때까지 비활성화됩니다. 현재 초과된 제한 특징들을 검토하세요. 위반된 제한:",
|
||||
"billingFeatureLossWarning": "기능 가용성 알림",
|
||||
"billingFeatureLossDescription": "다운그레이드함으로써 새 계획에서 사용할 수 없는 기능은 자동으로 비활성화됩니다. 일부 설정 및 구성은 손실될 수 있습니다. 어떤 기능들이 더 이상 사용 불가능한지 이해하기 위해 가격표를 검토하세요.",
|
||||
"billingUsageExceedsLimit": "현재 사용량 ({current})이 제한 ({limit})을 초과합니다",
|
||||
"billingPastDueTitle": "연체된 결제",
|
||||
"billingPastDueDescription": "결제가 연체되었습니다. 현재 이용 중인 플랜 기능을 계속 사용하기 위해 결제 수단을 업데이트해 주세요. 해결되지 않으면 구독이 취소되고 무료 요금제로 전환됩니다.",
|
||||
"billingUnpaidTitle": "결제되지 않은 구독",
|
||||
"billingUnpaidDescription": "구독 결제가 완료되지 않아 무료 요금제로 전환되었습니다. 구독을 복원하려면 결제 수단을 업데이트해 주세요.",
|
||||
"billingIncompleteTitle": "불완전한 결제",
|
||||
"billingIncompleteDescription": "결제가 불완전합니다. 구독을 활성화하기 위해 결제 과정을 완료해 주세요.",
|
||||
"billingIncompleteExpiredTitle": "만료된 결제",
|
||||
"billingIncompleteExpiredDescription": "결제가 완료되지 않아 만료되었습니다. 무료 요금제로 전환되었습니다. 유료 기능에 대한 액세스를 복원하려면 다시 구독해 주세요.",
|
||||
"billingManageSubscription": "구독을 관리하십시오",
|
||||
"billingResolvePaymentIssue": "업그레이드 또는 다운그레이드하기 전에 결제 문제를 해결해 주세요.",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "동의합니다",
|
||||
"termsOfService": "서비스 약관",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "시간은 초 단위입니다",
|
||||
"requireDeviceApproval": "장치 승인 요구",
|
||||
"requireDeviceApprovalDescription": "이 역할을 가진 사용자는 장치가 연결되기 전에 관리자의 승인이 필요합니다.",
|
||||
"sshAccess": "SSH 접속",
|
||||
"roleAllowSsh": "SSH 허용",
|
||||
"roleAllowSshAllow": "허용",
|
||||
"roleAllowSshDisallow": "허용 안 함",
|
||||
"roleAllowSshDescription": "이 역할을 가진 사용자가 SSH를 통해 리소스에 연결할 수 있도록 허용합니다. 비활성화되면 역할은 SSH 접속을 사용할 수 없습니다.",
|
||||
"sshSudoMode": "Sudo 접속",
|
||||
"sshSudoModeNone": "없음",
|
||||
"sshSudoModeNoneDescription": "사용자는 sudo로 명령을 실행할 수 없습니다.",
|
||||
"sshSudoModeFull": "전체 Sudo",
|
||||
"sshSudoModeFullDescription": "사용자는 모든 명령을 sudo로 실행할 수 있습니다.",
|
||||
"sshSudoModeCommands": "명령",
|
||||
"sshSudoModeCommandsDescription": "사용자는 sudo로 지정된 명령만 실행할 수 있습니다.",
|
||||
"sshSudo": "Sudo 허용",
|
||||
"sshSudoCommands": "Sudo 명령",
|
||||
"sshSudoCommandsDescription": "사용자가 sudo로 실행할 수 있는 명령어의 쉼표로 구분된 목록입니다.",
|
||||
"sshCreateHomeDir": "홈 디렉터리 생성",
|
||||
"sshUnixGroups": "유닉스 그룹",
|
||||
"sshUnixGroupsDescription": "대상 호스트에서 사용자에게 추가할 유닉스 그룹의 쉼표로 구분된 목록입니다.",
|
||||
"retryAttempts": "재시도 횟수",
|
||||
"expectedResponseCodes": "예상 응답 코드",
|
||||
"expectedResponseCodesDescription": "정상 상태를 나타내는 HTTP 상태 코드입니다. 비워 두면 200-300이 정상으로 간주됩니다.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "종단 노드",
|
||||
"country": "국가",
|
||||
"rulesMatchCountry": "현재 소스 IP를 기반으로 합니다",
|
||||
"region": "지역",
|
||||
"selectRegion": "지역 선택",
|
||||
"searchRegions": "지역 검색...",
|
||||
"noRegionFound": "지역을 찾을 수 없습니다.",
|
||||
"rulesMatchRegion": "국가의 지역 구성을 선택합니다",
|
||||
"rulesErrorInvalidRegion": "잘못된 지역",
|
||||
"rulesErrorInvalidRegionDescription": "유효한 지역을 선택하세요.",
|
||||
"regionAfrica": "아프리카",
|
||||
"regionNorthernAfrica": "북부 아프리카",
|
||||
"regionEasternAfrica": "동부 아프리카",
|
||||
"regionMiddleAfrica": "중부 아프리카",
|
||||
"regionSouthernAfrica": "남부 아프리카",
|
||||
"regionWesternAfrica": "서부 아프리카",
|
||||
"regionAmericas": "아메리카",
|
||||
"regionCaribbean": "카리브",
|
||||
"regionCentralAmerica": "중앙 아메리카",
|
||||
"regionSouthAmerica": "남아메리카",
|
||||
"regionNorthernAmerica": "북미",
|
||||
"regionAsia": "아시아",
|
||||
"regionCentralAsia": "중앙 아시아",
|
||||
"regionEasternAsia": "동아시아",
|
||||
"regionSouthEasternAsia": "동남아시아",
|
||||
"regionSouthernAsia": "남아시아",
|
||||
"regionWesternAsia": "서아시아",
|
||||
"regionEurope": "유럽",
|
||||
"regionEasternEurope": "동부 유럽",
|
||||
"regionNorthernEurope": "북부 유럽",
|
||||
"regionSouthernEurope": "남부 유럽",
|
||||
"regionWesternEurope": "서부 유럽",
|
||||
"regionOceania": "오세아니아",
|
||||
"regionAustraliaAndNewZealand": "호주와 뉴질랜드",
|
||||
"regionMelanesia": "멜라네시아",
|
||||
"regionMicronesia": "미크로네시아",
|
||||
"regionPolynesia": "폴리네시아",
|
||||
"managedSelfHosted": {
|
||||
"title": "관리 자체 호스팅",
|
||||
"description": "더 신뢰할 수 있고 낮은 유지보수의 자체 호스팅 팡골린 서버, 추가 기능 포함",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "잘못된 값",
|
||||
"idpTypeLabel": "신원 공급자 유형",
|
||||
"roleMappingExpressionPlaceholder": "예: contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||
"roleMappingModeFixedRoles": "고정 역할",
|
||||
"roleMappingModeMappingBuilder": "매핑 빌더",
|
||||
"roleMappingModeRawExpression": "원시 표현식",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "하나 이상의 역할을 선택하세요",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "역할 이름 입력 (조직마다 정확히 일치)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "모든 자동 프로비전 사용자에게 동일한 역할 세트를 할당합니다.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "기본 정책의 경우 사용자가 프로비저닝된 조직의 역할 이름을 입력하세요. 이름은 정확히 일치해야 합니다.",
|
||||
"roleMappingClaimPath": "클레임 경로",
|
||||
"roleMappingClaimPathPlaceholder": "그룹",
|
||||
"roleMappingClaimPathDescription": "토큰 페이로드에서 소스 값을 포함하는 경로 (예: 그룹).",
|
||||
"roleMappingMatchValue": "매치 값",
|
||||
"roleMappingAssignRoles": "역할 할당",
|
||||
"roleMappingAddMappingRule": "매핑 규칙 추가",
|
||||
"roleMappingRawExpressionResultDescription": "표현식은 문자열 또는 문자열 배열로 평가되어야 합니다.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "표현식은 문자열 (단일 역할 이름)로 평가되어야 합니다.",
|
||||
"roleMappingMatchValuePlaceholder": "매치 값 (예: 관리자)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "역할 이름 입력 (조직마다 정확히)",
|
||||
"roleMappingBuilderFreeformRowHint": "역할 이름은 각 대상 조직의 역할과 일치해야 합니다.",
|
||||
"roleMappingRemoveRule": "제거",
|
||||
"idpGoogleConfiguration": "Google 구성",
|
||||
"idpGoogleConfigurationDescription": "Google OAuth2 자격 증명을 구성합니다.",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 클라이언트 ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "인증 페이지의 브랜딩을 제거하시겠습니까?",
|
||||
"authPageBrandingDeleteConfirm": "브랜딩 삭제 확인",
|
||||
"brandingLogoURL": "로고 URL",
|
||||
"brandingLogoURLOrPath": "로고 URL 또는 경로",
|
||||
"brandingLogoPathDescription": "URL 또는 로컬 경로를 입력하세요.",
|
||||
"brandingLogoURLDescription": "로고 이미지에 대한 공용 URL을 입력하십시오.",
|
||||
"brandingPrimaryColor": "기본 색상",
|
||||
"brandingLogoWidth": "너비(px)",
|
||||
"brandingLogoHeight": "높이(px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "표준 로그인을 통해 돌아가기",
|
||||
"orgAuthNoAccount": "계정이 없으신가요?",
|
||||
"subscriptionRequiredToUse": "이 기능을 사용하려면 구독이 필요합니다.",
|
||||
"mustUpgradeToUse": "이 기능을 사용하려면 구독을 업그레이드해야 합니다.",
|
||||
"subscriptionRequiredTierToUse": "이 기능을 사용하려면 <tierLink>{tier}</tierLink> 이상의 등급이 필요합니다.",
|
||||
"upgradeToTierToUse": "이 기능을 사용하려면 <tierLink>{tier}</tierLink> 이상으로 업그레이드하세요.",
|
||||
"subscriptionTierTier1": "홈",
|
||||
"subscriptionTierTier2": "팀",
|
||||
"subscriptionTierTier3": "비즈니스",
|
||||
"subscriptionTierEnterprise": "기업",
|
||||
"idpDisabled": "신원 공급자가 비활성화되었습니다.",
|
||||
"orgAuthPageDisabled": "조직 인증 페이지가 비활성화되었습니다.",
|
||||
"domainRestartedDescription": "도메인 인증이 성공적으로 재시작되었습니다.",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "라이센스 가져오기",
|
||||
"description": "계획을 선택하고 Pangolin을 어떻게 사용할지 알려주세요.",
|
||||
"chooseTier": "계획 선택",
|
||||
"viewPricingLink": "가격, 기능 및 제한 보기",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "스타터",
|
||||
"description": "기업 기능, 25명의 사용자, 25개의 사이트, 커뮤니티 지원."
|
||||
},
|
||||
"scale": {
|
||||
"title": "스케일",
|
||||
"description": "기업 기능, 50명의 사용자, 50개의 사이트, 우선 지원."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "개인 사용 전용 (무료 라이센스 — 체크아웃 없음)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "결제로 진행"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "체크아웃 오류",
|
||||
"description": "체크아웃을 시작할 수 없습니다. 다시 시도하세요."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "우선순위",
|
||||
"priorityDescription": "우선 순위가 높은 경로가 먼저 평가됩니다. 우선 순위 = 100은 자동 정렬(시스템 결정)이 의미합니다. 수동 우선 순위를 적용하려면 다른 숫자를 사용하세요.",
|
||||
"instanceName": "인스턴스 이름",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "접근 로그를 얼마나 오래 보관할지",
|
||||
"logRetentionActionLabel": "작업 로그 보관",
|
||||
"logRetentionActionDescription": "작업 로그를 얼마나 오래 보관할지",
|
||||
"logRetentionConnectionLabel": "연결 로그 보유 기간",
|
||||
"logRetentionConnectionDescription": "연결 로그를 얼마나 오래 보유할지",
|
||||
"logRetentionDisabled": "비활성화됨",
|
||||
"logRetention3Days": "3 일",
|
||||
"logRetention7Days": "7 일",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "다음 연도 말",
|
||||
"actionLogsDescription": "이 조직에서 수행된 작업의 기록을 봅니다",
|
||||
"accessLogsDescription": "이 조직의 자원에 대한 접근 인증 요청을 확인합니다",
|
||||
"licenseRequiredToUse": "이 기능을 사용하려면 Enterprise 라이선스가 필요합니다.",
|
||||
"connectionLogs": "연결 로그",
|
||||
"connectionLogsDescription": "이 조직의 터널 연결 로그 보기",
|
||||
"sidebarLogsConnection": "연결 로그",
|
||||
"sidebarLogsStreaming": "스트리밍",
|
||||
"sourceAddress": "소스 주소",
|
||||
"destinationAddress": "대상 주소",
|
||||
"duration": "지속 시간",
|
||||
"licenseRequiredToUse": "이 기능을 사용하려면 <enterpriseLicenseLink>엔터프라이즈 에디션</enterpriseLicenseLink> 라이선스가 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다. <bookADemoLink>데모 또는 POC 체험을 예약하세요</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "이 기능을 사용하려면 <enterpriseEditionLink>엔터프라이즈 에디션</enterpriseEditionLink>이(가) 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다. <bookADemoLink>데모 또는 POC 체험을 예약하세요</bookADemoLink>.",
|
||||
"certResolver": "인증서 해결사",
|
||||
"certResolverDescription": "이 리소스에 사용할 인증서 해결사를 선택하세요.",
|
||||
"selectCertResolver": "인증서 해결사 선택",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "액세스 제어",
|
||||
"editInternalResourceDialogAccessControlDescription": "연결 시 이 리소스에 대한 액세스 권한을 가지는 역할, 사용자, 그리고 머신 클라이언트를 제어합니다. 관리자는 항상 접근할 수 있습니다.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "모든 포트에 대해서는 \"*\"로, 아니면 쉼표로 구분된 포트 및 범위 목록(예: \"80,443,8000-9000\")을 설정해야 합니다. 포트는 1에서 65535 사이여야 합니다.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH 인증 데몬 위치",
|
||||
"internalResourceAuthDaemonStrategyDescription": "SSH 인증 데몬이 작동하는 위치를 선택하세요: 사이트(Newt)에서 또는 원격 호스트에서.",
|
||||
"internalResourceAuthDaemonDescription": "SSH 인증 데몬은 이 리소스를 위한 SSH 키 서명과 PAM 인증을 처리합니다. 사이트(Newt)에서 나 별도의 원격 호스트에서 실행할 것인지를 선택하세요. 자세한 내용은 <docsLink>문서</docsLink>를 참조하세요.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "전략 선택",
|
||||
"internalResourceAuthDaemonStrategyLabel": "위치",
|
||||
"internalResourceAuthDaemonSite": "사이트에서 인증 데몬이 실행됩니다(Newt).",
|
||||
"internalResourceAuthDaemonSiteDescription": "인증 데몬이 사이트(Newt)에서 실행됩니다.",
|
||||
"internalResourceAuthDaemonRemote": "원격 호스트",
|
||||
"internalResourceAuthDaemonRemoteDescription": "인증 데몬이 사이트가 아닌 다른 호스트에서 실행됩니다.",
|
||||
"internalResourceAuthDaemonPort": "데몬 포트 (선택 사항)",
|
||||
"orgAuthWhatsThis": "조직 ID를 어디에서 찾을 수 있습니까?",
|
||||
"learnMore": "자세히 알아보기",
|
||||
"backToHome": "홈으로 돌아가기",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "방화벽 활성화",
|
||||
"autoUpdatesEnabled": "자동 업데이트 활성화",
|
||||
"tpmAvailable": "TPM 사용 가능",
|
||||
"windowsAntivirusEnabled": "안티바이러스 활성화됨",
|
||||
"macosSipEnabled": "시스템 무결성 보호 (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "방화벽 스텔스 모드",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "장치 승인 활성화",
|
||||
"approvalsEmptyStateStep2Description": "역할을 편집하고 '장치 승인 요구' 옵션을 활성화하세요. 이 역할을 가진 사용자는 새 장치에 대해 관리자의 승인이 필요합니다.",
|
||||
"approvalsEmptyStatePreviewDescription": "미리 보기: 활성화된 경우, 승인 대기 중인 장치 요청이 검토용으로 여기에 표시됩니다.",
|
||||
"approvalsEmptyStateButtonText": "역할 관리"
|
||||
"approvalsEmptyStateButtonText": "역할 관리",
|
||||
"domainErrorTitle": "도메인 확인에 문제가 발생했습니다.",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "<policiesTabLink>자동 프로비저닝 설정</policiesTabLink> 탭에서 역할 매핑 및 조직 정책을 구성합니다.",
|
||||
"streamingTitle": "이벤트 스트리밍",
|
||||
"streamingDescription": "조직의 이벤트를 외부 목적지로 실시간 전송합니다.",
|
||||
"streamingUnnamedDestination": "이름이 없는 대상지",
|
||||
"streamingNoUrlConfigured": "설정된 URL이 없습니다",
|
||||
"streamingAddDestination": "대상지 추가",
|
||||
"streamingHttpWebhookTitle": "HTTP 웹훅",
|
||||
"streamingHttpWebhookDescription": "유연한 인증 및 템플릿 작성 기능을 갖춘 HTTP 엔드포인트에 이벤트를 전송합니다.",
|
||||
"streamingS3Title": "아마존 S3",
|
||||
"streamingS3Description": "S3 호환 객체 스토리지 버킷에 이벤트를 스트리밍합니다. 곧 제공됩니다.",
|
||||
"streamingDatadogTitle": "데이터독",
|
||||
"streamingDatadogDescription": "이벤트를 직접 Datadog 계정으로 전달합니다. 곧 제공됩니다.",
|
||||
"streamingTypePickerDescription": "목표 유형을 선택하여 시작합니다.",
|
||||
"streamingFailedToLoad": "대상 로드에 실패했습니다",
|
||||
"streamingUnexpectedError": "예기치 않은 오류가 발생했습니다.",
|
||||
"streamingFailedToUpdate": "대상지를 업데이트하는 데 실패했습니다",
|
||||
"streamingDeletedSuccess": "대상지가 성공적으로 삭제되었습니다",
|
||||
"streamingFailedToDelete": "대상지 삭제 실패",
|
||||
"streamingDeleteTitle": "대상지 삭제",
|
||||
"streamingDeleteButtonText": "대상지 삭제",
|
||||
"streamingDeleteDialogAreYouSure": "삭제하시겠습니까",
|
||||
"streamingDeleteDialogThisDestination": "이 대상지",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? 모든 구성은 영구적으로 제거됩니다.",
|
||||
"httpDestEditTitle": "대상지 수정",
|
||||
"httpDestAddTitle": "HTTP 대상지 추가",
|
||||
"httpDestEditDescription": "이 HTTP 이벤트 스트리밍 대상지의 구성을 업데이트하세요.",
|
||||
"httpDestAddDescription": "조직의 이벤트 수신을 위한 새로운 HTTP 엔드포인트를 구성하세요.",
|
||||
"httpDestTabSettings": "설정",
|
||||
"httpDestTabHeaders": "헤더",
|
||||
"httpDestTabBody": "본문",
|
||||
"httpDestTabLogs": "로그",
|
||||
"httpDestNamePlaceholder": "내 HTTP 대상",
|
||||
"httpDestUrlLabel": "대상 URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL은 http 또는 https를 사용해야 합니다",
|
||||
"httpDestUrlErrorHttpsRequired": "클라우드 배포에는 HTTPS가 필요합니다",
|
||||
"httpDestUrlErrorInvalid": "유효한 URL을 입력하세요 (예: https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "인증",
|
||||
"httpDestAuthDescription": "엔드포인트에 대한 요청 인증 방법을 선택하세요.",
|
||||
"httpDestAuthNoneTitle": "인증 없음",
|
||||
"httpDestAuthNoneDescription": "Authorization 헤더 없이 요청을 보냅니다.",
|
||||
"httpDestAuthBearerTitle": "Bearer 토큰",
|
||||
"httpDestAuthBearerDescription": "모든 요청에 Authorization: Bearer <token> 헤더를 추가합니다.",
|
||||
"httpDestAuthBearerPlaceholder": "API 키 또는 토큰",
|
||||
"httpDestAuthBasicTitle": "기본 인증",
|
||||
"httpDestAuthBasicDescription": "Authorization: Basic <credentials> 헤더를 추가합니다. 자격 증명은 username:password 형식으로 제공하세요.",
|
||||
"httpDestAuthBasicPlaceholder": "사용자 이름:비밀번호",
|
||||
"httpDestAuthCustomTitle": "사용자 정의 헤더",
|
||||
"httpDestAuthCustomDescription": "인증을 위한 사용자 정의 HTTP 헤더 이름 및 값을 지정하세요 (예: X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "헤더 이름 (예: X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "헤더 값",
|
||||
"httpDestCustomHeadersTitle": "사용자 정의 HTTP 헤더",
|
||||
"httpDestCustomHeadersDescription": "모든 발신 요청에 사용자 정의 헤더를 추가합니다. 정적 토큰 또는 사용자 정의 Content-Type에 유용합니다. 기본적으로 Content-Type: application/json이 전송됩니다.",
|
||||
"httpDestNoHeadersConfigured": "구성된 사용자 정의 헤더가 없습니다. \"헤더 추가\"를 클릭하여 추가하세요.",
|
||||
"httpDestHeaderNamePlaceholder": "헤더 이름",
|
||||
"httpDestHeaderValuePlaceholder": "값",
|
||||
"httpDestAddHeader": "헤더 추가",
|
||||
"httpDestBodyTemplateTitle": "사용자 정의 본문 템플릿",
|
||||
"httpDestBodyTemplateDescription": "엔드포인트에 전송되는 JSON 페이로드 구조를 제어합니다. 비활성화된 경우 각 이벤트에 대해 기본 JSON 객체가 전송됩니다.",
|
||||
"httpDestEnableBodyTemplate": "사용자 정의 본문 템플릿 활성화",
|
||||
"httpDestBodyTemplateLabel": "본문 템플릿 (JSON)",
|
||||
"httpDestBodyTemplateHint": "템플릿 변수를 사용하여 페이로드에서 이벤트 필드를 참조하세요.",
|
||||
"httpDestPayloadFormatTitle": "페이로드 형식",
|
||||
"httpDestPayloadFormatDescription": "각 요청 본문에 이벤트가 시리얼라이즈되는 방식입니다.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON 배열",
|
||||
"httpDestFormatJsonArrayDescription": "각 배치마다 요청 하나씩, 본문은 JSON 배열입니다. 대부분의 일반 웹훅 및 Datadog과 호환됩니다.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "각 배치마다 요청 하나씩, 본문은 줄 구분 JSON — 한 라인에 하나의 객체가 있으며 외부 배열이 없습니다. Splunk HEC, Elastic / OpenSearch, Grafana Loki에 필요합니다.",
|
||||
"httpDestFormatSingleTitle": "각 요청 당 하나의 이벤트",
|
||||
"httpDestFormatSingleDescription": "각 개별 이벤트에 대해 별도의 HTTP POST를 전송합니다. 배치를 처리할 수 없는 엔드포인트에만 사용하세요.",
|
||||
"httpDestLogTypesTitle": "로그 유형",
|
||||
"httpDestLogTypesDescription": "이 대상지에 전달될 로그 유형을 선택하세요. 활성화된 로그 유형만 스트리밍 됩니다.",
|
||||
"httpDestAccessLogsTitle": "접근 로그",
|
||||
"httpDestAccessLogsDescription": "인증 및 거부된 요청을 포함한 리소스 접근 시도.",
|
||||
"httpDestActionLogsTitle": "작업 로그",
|
||||
"httpDestActionLogsDescription": "조직 내에서 사용자가 수행한 관리 작업.",
|
||||
"httpDestConnectionLogsTitle": "연결 로그",
|
||||
"httpDestConnectionLogsDescription": "사이트 및 터널 연결 이벤트, 연결 및 연결 끊기를 포함합니다.",
|
||||
"httpDestRequestLogsTitle": "요청 로그",
|
||||
"httpDestRequestLogsDescription": "프록시된 리소스에 대한 HTTP 요청 로그, 메서드, 경로 및 응답 코드를 포함합니다.",
|
||||
"httpDestSaveChanges": "변경 사항 저장",
|
||||
"httpDestCreateDestination": "대상지 생성",
|
||||
"httpDestUpdatedSuccess": "대상지가 성공적으로 업데이트되었습니다",
|
||||
"httpDestCreatedSuccess": "대상지가 성공적으로 생성되었습니다",
|
||||
"httpDestUpdateFailed": "대상지를 업데이트하는 데 실패했습니다",
|
||||
"httpDestCreateFailed": "대상지를 생성하는 데 실패했습니다"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Du er {count, plural, =0 {ikke medlem av noen organisasjoner} one {medlem av en organisasjon} other {medlem av # organisasjoner}}.",
|
||||
"componentsInvalidKey": "Ugyldig eller utgått lisensnøkkel oppdaget. Følg lisensvilkårene for å fortsette å kunne bruke alle funksjonene.",
|
||||
"dismiss": "Avvis",
|
||||
"subscriptionViolationMessage": "Du er utenfor grensen for gjeldende plan. Rett problemet ved å fjerne nettsteder, brukere eller andre ressurser for å bli innenfor planen din.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Takk for at du støtter Pangolin som en {tier}!",
|
||||
"inviteErrorNotValid": "Beklager, men det ser ut som invitasjonen du prøver å bruke ikke har blitt akseptert eller ikke er gyldig lenger.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Opprett lenke",
|
||||
"resourcesNotFound": "Ingen ressurser funnet",
|
||||
"resourceSearch": "Søk i ressurser",
|
||||
"machineSearch": "Søk etter maskiner",
|
||||
"machinesSearch": "Søk etter maskinklienter...",
|
||||
"machineNotFound": "Ingen maskiner funnet",
|
||||
"userDeviceSearch": "Søk etter brukerenheter",
|
||||
"userDevicesSearch": "Søk etter brukerenheter...",
|
||||
"openMenu": "Åpne meny",
|
||||
"resource": "Ressurs",
|
||||
"title": "Tittel",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy forespørsler over HTTPS ved å bruke et fullstendig kvalifisert domenenavn.",
|
||||
"resourceRaw": "Rå TCP/UDP-ressurs",
|
||||
"resourceRawDescription": "Proxy forespørsler over rå TCP/UDP ved å bruke et portnummer.",
|
||||
"resourceRawDescriptionCloud": "Proxy forespørsler om rå TCP/UDP ved hjelp av et portnummer. Krever sider for å koble til en ekstern node.",
|
||||
"resourceCreate": "Opprett ressurs",
|
||||
"resourceCreateDescription": "Følg trinnene nedenfor for å opprette en ny ressurs",
|
||||
"resourceSeeAll": "Se alle ressurser",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Velg en protokoll",
|
||||
"resourcePortNumber": "Portnummer",
|
||||
"resourcePortNumberDescription": "Det eksterne portnummeret for proxy forespørsler.",
|
||||
"back": "Tilbake",
|
||||
"cancel": "Avbryt",
|
||||
"resourceConfig": "Konfigurasjonsutdrag",
|
||||
"resourceConfigDescription": "Kopier og lim inn disse konfigurasjons-øyeblikkene for å sette opp TCP/UDP ressursen",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Det oppsto en feil under sletting av organisasjonen.",
|
||||
"orgDeleted": "Organisasjon slettet",
|
||||
"orgDeletedMessage": "Organisasjonen og tilhørende data er slettet.",
|
||||
"deleteAccount": "Slett konto",
|
||||
"deleteAccountDescription": "Slett kontoen din permanent, alle organisasjoner du eier, og alle data i disse organisasjonene. Dette kan ikke angres.",
|
||||
"deleteAccountButton": "Slett konto",
|
||||
"deleteAccountConfirmTitle": "Slett konto",
|
||||
"deleteAccountConfirmMessage": "Dette vil slette kontoen din, alle organisasjoner du eier og alle data i disse organisasjonene. Dette kan ikke gjøres om.",
|
||||
"deleteAccountConfirmString": "Slett konto",
|
||||
"deleteAccountSuccess": "Kontoen er slettet",
|
||||
"deleteAccountSuccessMessage": "Kontoen din er slettet.",
|
||||
"deleteAccountError": "Kunne ikke slette konto",
|
||||
"deleteAccountPreviewAccount": "Din konto",
|
||||
"deleteAccountPreviewOrgs": "Organisasjoner du eier (og alle deres data)",
|
||||
"orgMissing": "Organisasjons-ID Mangler",
|
||||
"orgMissingMessage": "Kan ikke regenerere invitasjon uten en organisasjons-ID.",
|
||||
"accessUsersManage": "Administrer brukere",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Slett API-nøkkel",
|
||||
"apiKeysManage": "Administrer API-nøkler",
|
||||
"apiKeysDescription": "API-nøkler brukes for å autentisere med integrasjons-API",
|
||||
"provisioningKeysTitle": "Foreløpig nøkkel",
|
||||
"provisioningKeysManage": "Behandle bestemmende nøkler",
|
||||
"provisioningKeysDescription": "Bestemmelsesnøkler brukes til å godkjenne automatisert nettstedsløsning for din organisasjon.",
|
||||
"provisioningManage": "Levering",
|
||||
"provisioningDescription": "Administrer foreløpig nøkler og gjennomgå ventende nettsteder som venter på godkjenning.",
|
||||
"pendingSites": "Ventende nettsteder",
|
||||
"siteApproveSuccess": "Vellykket godkjenning av nettsted",
|
||||
"siteApproveError": "Feil ved godkjenning av side",
|
||||
"provisioningKeys": "Foreløpig nøkler",
|
||||
"searchProvisioningKeys": "Søk varer i lagrings nøkler...",
|
||||
"provisioningKeysAdd": "Generer fremvisende nøkkel",
|
||||
"provisioningKeysErrorDelete": "Feil under sletting av foreløpig nøkkel",
|
||||
"provisioningKeysErrorDeleteMessage": "Feil under sletting av foreløpig nøkkel",
|
||||
"provisioningKeysQuestionRemove": "Er du sikker på at du vil fjerne denne midlertidig nøkkelen fra organisasjonen?",
|
||||
"provisioningKeysMessageRemove": "Når nøkkelen er fjernet, kan den ikke lenger brukes til anleggsavsetning.",
|
||||
"provisioningKeysDeleteConfirm": "Bekreft sletting av bestemmelsesnøkkel",
|
||||
"provisioningKeysDelete": "Slett bestemmelsesnøkkel",
|
||||
"provisioningKeysCreate": "Generer fremvisende nøkkel",
|
||||
"provisioningKeysCreateDescription": "Generer en ny foreløpig nøkkel til organisasjonen",
|
||||
"provisioningKeysSeeAll": "Se alle foreløpig nøkler",
|
||||
"provisioningKeysSave": "Lagre den midlertidig nøkkelen",
|
||||
"provisioningKeysSaveDescription": "Du kan bare se denne én gang. Kopier det til et sikkert sted.",
|
||||
"provisioningKeysErrorCreate": "Feil under oppretting av foreløpig nøkkel",
|
||||
"provisioningKeysList": "Ny provisorisk nøkkel",
|
||||
"provisioningKeysMaxBatchSize": "Maks størrelse på bunt",
|
||||
"provisioningKeysUnlimitedBatchSize": "Ubegrenset mengde bunt (ingen begrensning)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Ubegrenset",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Angi en gyldig sjakkstørrelse (1–1 000.000).",
|
||||
"provisioningKeysValidUntil": "Gyldig til",
|
||||
"provisioningKeysValidUntilHint": "La stå tomt for ingen utløp.",
|
||||
"provisioningKeysValidUntilInvalid": "Angi en gyldig dato og klokkeslett.",
|
||||
"provisioningKeysNumUsed": "Antall ganger brukt",
|
||||
"provisioningKeysLastUsed": "Sist brukt",
|
||||
"provisioningKeysNoExpiry": "Ingen utløpsdato",
|
||||
"provisioningKeysNeverUsed": "Aldri",
|
||||
"provisioningKeysEdit": "Rediger bestemmelsesnøkkel",
|
||||
"provisioningKeysEditDescription": "Oppdater maksimal størrelse for bunt og utløpstid for denne nøkkelen.",
|
||||
"provisioningKeysApproveNewSites": "Godkjenn nye nettsteder",
|
||||
"provisioningKeysApproveNewSitesDescription": "Godkjenn automatisk nettsteder som registrerer deg med denne nøkkelen.",
|
||||
"provisioningKeysUpdateError": "Feil under oppdatering av foreløpig nøkkel",
|
||||
"provisioningKeysUpdated": "Foreslå nøkkel oppdatert",
|
||||
"provisioningKeysUpdatedDescription": "Dine endringer er lagret.",
|
||||
"provisioningKeysBannerTitle": "Sidens bestemmende nøkler",
|
||||
"provisioningKeysBannerDescription": "Generer en foreløpig nøkkel og bruk den med Nyhetskontakten for å automatisk opprette sider ved første oppstart — trenger ikke å sette opp separat innloggingsinformasjon for hver side.",
|
||||
"provisioningKeysBannerButtonText": "Lær mer",
|
||||
"pendingSitesBannerTitle": "Ventende nettsteder",
|
||||
"pendingSitesBannerDescription": "Nettsteder som kobler deg til ved hjelp av en bestemmelsestekst, vises her for gjennomgang. Godkjenn hvert nettsted før det blir aktivt og får tilgang til ressursene dine.",
|
||||
"pendingSitesBannerButtonText": "Lær mer",
|
||||
"apiKeysSettings": "{apiKeyName} Innstillinger",
|
||||
"userTitle": "Administrer alle brukere",
|
||||
"userDescription": "Vis og administrer alle brukere i systemet",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtrer etter godkjenningsstatus",
|
||||
"approvalListEmpty": "Ingen godkjenninger",
|
||||
"approvalState": "Godkjennings tilstand",
|
||||
"approvalLoadMore": "Last mer",
|
||||
"loadingApprovals": "Laster inn godkjenninger",
|
||||
"approve": "Godkjenn",
|
||||
"approved": "Godkjent",
|
||||
"denied": "Avvist",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Bruker lagret",
|
||||
"userSavedDescription": "Brukeren har blitt oppdatert.",
|
||||
"autoProvisioned": "Auto avlyst",
|
||||
"autoProvisionSettings": "Auto leveringsinnstillinger",
|
||||
"autoProvisionedDescription": "Tillat denne brukeren å bli automatisk administrert av en identitetsleverandør",
|
||||
"accessControlsDescription": "Administrer hva denne brukeren kan få tilgang til og gjøre i organisasjonen",
|
||||
"accessControlsSubmit": "Lagre tilgangskontroller",
|
||||
"singleRolePerUserPlanNotice": "Din plan støtter bare én rolle per bruker.",
|
||||
"singleRolePerUserEditionNotice": "Denne utgaven støtter bare én rolle per bruker.",
|
||||
"roles": "Roller",
|
||||
"accessUsersRoles": "Administrer brukere og roller",
|
||||
"accessUsersRolesDescription": "Inviter brukere og legg dem til roller for å administrere tilgang til organisasjonen",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Feilet å slå av/på ressurs",
|
||||
"resourcesErrorUpdateDescription": "En feil oppstod under oppdatering av ressursen",
|
||||
"access": "Tilgang",
|
||||
"accessControl": "Tilgangskontroll",
|
||||
"shareLink": "{resource} Del Lenke",
|
||||
"resourceSelect": "Velg ressurs",
|
||||
"shareLinks": "Del lenker",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rolle fjernet",
|
||||
"accessRoleRemovedDescription": "Rollen er vellykket fjernet.",
|
||||
"accessRoleRequiredRemove": "Før du sletter denne rollen, vennligst velg en ny rolle å overføre eksisterende medlemmer til.",
|
||||
"network": "Nettverk",
|
||||
"manage": "Administrer",
|
||||
"sitesNotFound": "Ingen områder funnet.",
|
||||
"pangolinServerAdmin": "Server Admin - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Øk antall områder",
|
||||
"idpManage": "Administrer Identitetsleverandører",
|
||||
"idpManageDescription": "Vis og administrer identitetsleverandører i systemet",
|
||||
"idpGlobalModeBanner": "Identitetsleverandører (IdPs) per organisasjon er deaktivert på denne serveren. Den bruker globale IdP (delt over alle organisasjoner). Administrer globale IdP'er i <adminPanelLink>admin-panelet</adminPanelLink>. For å aktivere IdP per organisasjon, rediger serverkonfigurasjonen og sett IdP-modus til org. <configDocsLink>Se dokumentasjonen</configDocsLink>. Hvis du vil fortsette å bruke globale IdPs og få denne til å forsvinne fra organisasjonens innstillinger, satt eksplisitt modusen til global i konfigurasjonen.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Identitetsleverandører (IdPs) per organisasjon er deaktivert på denne serveren. Den bruker globale IdPs (delt på tvers av alle organisasjoner). Administrer globale IdPs i <adminPanelLink>administrasjons-panelet</adminPanelLink>. For å bruke identitetsleverandører per organisasjon, må du oppgradere til Enterprise-utgaven.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Identitetsleverandører (IdPs) per organisasjon er deaktivert på denne serveren. Den bruker globale IdPs (delt på tvers av alle organisasjoner). Administrer globale IdPs i <adminPanelLink>administrasjons-panelet</adminPanelLink>. For å bruke identitetsleverandører per organisasjon, kreves en Enterprise-lisens.",
|
||||
"idpDeletedDescription": "Identitetsleverandør slettet vellykket",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Er du sikker på at du vil slette identitetsleverandøren permanent?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Oppsett - Pangolin",
|
||||
"orgNameRequired": "Organisasjonsnavn er påkrevd",
|
||||
"orgIdRequired": "Organisasjons-ID er påkrevd",
|
||||
"orgIdMaxLength": "Organisasjons-ID må maksimalt være 32 tegn",
|
||||
"orgErrorCreate": "En feil oppstod under oppretting av organisasjon",
|
||||
"pageNotFound": "Siden ble ikke funnet",
|
||||
"pageNotFoundDescription": "Oops! Siden du leter etter finnes ikke.",
|
||||
"overview": "Oversikt",
|
||||
"home": "Hjem",
|
||||
"accessControl": "Tilgangskontroll",
|
||||
"settings": "Innstillinger",
|
||||
"usersAll": "Alle brukere",
|
||||
"license": "Lisens",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Hent bruker",
|
||||
"actionGetOrgUser": "Hent organisasjonsbruker",
|
||||
"actionListOrgDomains": "List opp organisasjonsdomener",
|
||||
"actionGetDomain": "Få Domene",
|
||||
"actionCreateOrgDomain": "Opprett domene",
|
||||
"actionUpdateOrgDomain": "Oppdater domene",
|
||||
"actionDeleteOrgDomain": "Slett domene",
|
||||
"actionGetDNSRecords": "Hent DNS-oppføringer",
|
||||
"actionRestartOrgDomain": "Omstart Domene",
|
||||
"actionCreateSite": "Opprett område",
|
||||
"actionDeleteSite": "Slett område",
|
||||
"actionGetSite": "Hent område",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Skriv inn oppsetttoken fra serverkonsollen.",
|
||||
"setupTokenRequired": "Oppsetttoken er nødvendig",
|
||||
"actionUpdateSite": "Oppdater område",
|
||||
"actionResetSiteBandwidth": "Tilbakestill organisasjons-båndbredde",
|
||||
"actionListSiteRoles": "List opp tillatte områderoller",
|
||||
"actionCreateResource": "Opprett ressurs",
|
||||
"actionDeleteResource": "Slett ressurs",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Fjern bruker",
|
||||
"actionListUsers": "List opp brukere",
|
||||
"actionAddUserRole": "Legg til brukerrolle",
|
||||
"actionSetUserOrgRoles": "Angi brukerroller",
|
||||
"actionGenerateAccessToken": "Generer tilgangstoken",
|
||||
"actionDeleteAccessToken": "Slett tilgangstoken",
|
||||
"actionListAccessTokens": "List opp tilgangstokener",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Vis logger",
|
||||
"noneSelected": "Ingen valgt",
|
||||
"orgNotFound2": "Ingen organisasjoner funnet.",
|
||||
"searchProgress": "Søker...",
|
||||
"searchPlaceholder": "Søk...",
|
||||
"emptySearchOptions": "Ingen valg funnet",
|
||||
"create": "Opprett",
|
||||
"orgs": "Organisasjoner",
|
||||
"loginError": "En uventet feil oppstod. Vennligst prøv igjen.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privat",
|
||||
"sidebarAccessControl": "Tilgangskontroll",
|
||||
"sidebarLogsAndAnalytics": "Logger og analyser",
|
||||
"sidebarTeam": "Lag",
|
||||
"sidebarUsers": "Brukere",
|
||||
"sidebarAdmin": "Administrator",
|
||||
"sidebarInvitations": "Invitasjoner",
|
||||
"sidebarRoles": "Roller",
|
||||
"sidebarShareableLinks": "Lenker",
|
||||
"sidebarApiKeys": "API-nøkler",
|
||||
"sidebarProvisioning": "Levering",
|
||||
"sidebarSettings": "Innstillinger",
|
||||
"sidebarAllUsers": "Alle brukere",
|
||||
"sidebarIdentityProviders": "Identitetsleverandører",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Logg og analyser",
|
||||
"sidebarBluePrints": "Tegninger",
|
||||
"sidebarOrganization": "Organisasjon",
|
||||
"sidebarManagement": "Administrasjon",
|
||||
"sidebarBillingAndLicenses": "Fakturering & lisenser",
|
||||
"sidebarLogsAnalytics": "Analyser",
|
||||
"blueprints": "Tegninger",
|
||||
"blueprintsDescription": "Bruk deklarative konfigurasjoner og vis tidligere kjøringer",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Parastinnhold (kun lese)",
|
||||
"enableDockerSocket": "Aktiver Docker blåkopi",
|
||||
"enableDockerSocketDescription": "Aktiver skraping av Docker Socket for blueprint Etiketter. Socket bane må brukes for nye.",
|
||||
"enableDockerSocketLink": "Lær mer",
|
||||
"viewDockerContainers": "Vis Docker-containere",
|
||||
"containersIn": "Containere i {siteName}",
|
||||
"selectContainerDescription": "Velg en hvilken som helst container for å bruke som vertsnavn for dette målet. Klikk på en port for å bruke en port.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Navnerom: {namespace}",
|
||||
"domainPickerShowMore": "Vis mer",
|
||||
"regionSelectorTitle": "Velg Region",
|
||||
"domainPickerRemoteExitNodeWarning": "Tilbudte domener støttes ikke når sider kobles til eksterne avkjøringsnoder. For ressurser som skal være tilgjengelige på eksterne noder, brukes et egendefinert domene i stedet.",
|
||||
"regionSelectorInfo": "Å velge en region hjelper oss med å gi bedre ytelse for din lokasjon. Du trenger ikke være i samme region som serveren.",
|
||||
"regionSelectorPlaceholder": "Velg en region",
|
||||
"regionSelectorComingSoon": "Kommer snart",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Oversikt over bruksgrenser",
|
||||
"billingMonitorUsage": "Overvåk bruken din i forhold til konfigurerte grenser. Hvis du trenger økte grenser, vennligst kontakt support@pangolin.net.",
|
||||
"billingDataUsage": "Databruk",
|
||||
"billingOnlineTime": "Online tid for nettsteder",
|
||||
"billingUsers": "Aktive brukere",
|
||||
"billingDomains": "Aktive domener",
|
||||
"billingRemoteExitNodes": "Aktive selvstyrte noder",
|
||||
"billingSites": "Områder",
|
||||
"billingUsers": "Brukere",
|
||||
"billingDomains": "Domener",
|
||||
"billingOrganizations": "Orger",
|
||||
"billingRemoteExitNodes": "Eksterne Noder",
|
||||
"billingNoLimitConfigured": "Ingen grense konfigurert",
|
||||
"billingEstimatedPeriod": "Estimert faktureringsperiode",
|
||||
"billingIncludedUsage": "Inkludert Bruk",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Mislyktes å hente portal URL",
|
||||
"billingPortalError": "Portalfeil",
|
||||
"billingDataUsageInfo": "Du er ladet for all data som overføres gjennom dine sikre tunneler når du er koblet til skyen. Dette inkluderer både innkommende og utgående trafikk på alle dine nettsteder. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Data belastes ikke ved bruk av EK-grupper.",
|
||||
"billingOnlineTimeInfo": "Du er ladet på hvor lenge sidene dine forblir koblet til skyen. For eksempel tilsvarer 44,640 minutter ett nettsted som går 24/7 i en hel måned. Når du når grensen din, vil sidene koble fra til du oppgraderer planen eller reduserer bruken. Tid belastes ikke når du bruker noder.",
|
||||
"billingUsersInfo": "Du lades for hver bruker i organisasjonen. Fakturering beregnes daglig basert på antall aktive brukerkontoer i dine org.",
|
||||
"billingDomainInfo": "Du lades for hvert domene i organisasjonen. Fakturering beregnes daglig basert på antallet aktive domenekontoer i din org.",
|
||||
"billingRemoteExitNodesInfo": "Du lades for hver håndterte node i organisasjonen. Fakturering beregnes daglig basert på antallet aktive håndterte noder i dine org.",
|
||||
"billingSInfo": "Hvor mange nettsteder du kan bruke",
|
||||
"billingUsersInfo": "Hvor mange brukere du kan bruke",
|
||||
"billingDomainInfo": "Hvor mange domener du kan bruke",
|
||||
"billingRemoteExitNodesInfo": "Hvor mange fjernnoder du kan bruke",
|
||||
"billingLicenseKeys": "Lisensnøkler",
|
||||
"billingLicenseKeysDescription": "Administrer dine lisensnøkkelabonnementer",
|
||||
"billingLicenseSubscription": "Lisens abonnement",
|
||||
"billingInactive": "Inaktiv",
|
||||
"billingLicenseItem": "Lisens artikkel",
|
||||
"billingQuantity": "Antall",
|
||||
"billingTotal": "totalt",
|
||||
"billingModifyLicenses": "Endre lisensabonnement",
|
||||
"domainNotFound": "Domene ikke funnet",
|
||||
"domainNotFoundDescription": "Denne ressursen er deaktivert fordi domenet ikke lenger eksisterer i systemet vårt. Vennligst angi et nytt domene for denne ressursen.",
|
||||
"failed": "Mislyktes",
|
||||
"createNewOrgDescription": "Opprett en ny organisasjon",
|
||||
"organization": "Organisasjon",
|
||||
"primary": "Primær",
|
||||
"port": "Port",
|
||||
"securityKeyManage": "Administrer sikkerhetsnøkler",
|
||||
"securityKeyDescription": "Legg til eller fjern sikkerhetsnøkler for passordløs autentisering",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Portnummer er påkrevd for ikke-HTTP-ressurser",
|
||||
"resourcePortNotAllowed": "Portnummer skal ikke angis for HTTP-ressurser",
|
||||
"billingPricingCalculatorLink": "Pris Kalkulator",
|
||||
"billingYourPlan": "Din funksjonsplan",
|
||||
"billingViewOrModifyPlan": "Vis eller endre gjeldende abonnement",
|
||||
"billingViewPlanDetails": "Se Planleggings detaljer",
|
||||
"billingUsageAndLimits": "Bruk og grenser",
|
||||
"billingViewUsageAndLimits": "Se planets grenser og gjeldende bruk",
|
||||
"billingCurrentUsage": "Gjeldende bruk",
|
||||
"billingMaximumLimits": "Maks antall grenser",
|
||||
"billingRemoteNodes": "Eksterne Noder",
|
||||
"billingUnlimited": "Ubegrenset",
|
||||
"billingPaidLicenseKeys": "Betalt lisensnøkler",
|
||||
"billingManageLicenseSubscription": "Administrer abonnementet for betalte lisensnøkler selv hostet",
|
||||
"billingCurrentKeys": "Nåværende nøkler",
|
||||
"billingModifyCurrentPlan": "Endre gjeldende plan",
|
||||
"billingConfirmUpgrade": "Bekreft oppgradering",
|
||||
"billingConfirmDowngrade": "Bekreft nedgradering",
|
||||
"billingConfirmUpgradeDescription": "Du er i ferd med å oppgradere abonnementet ditt. Gå gjennom de nye grensene og pris nedenfor.",
|
||||
"billingConfirmDowngradeDescription": "Du er i ferd med å nedgradere planen din. Gå gjennom de nye grensene og pris nedenfor.",
|
||||
"billingPlanIncludes": "Plan Inkluderer",
|
||||
"billingProcessing": "Behandler...",
|
||||
"billingConfirmUpgradeButton": "Bekreft oppgradering",
|
||||
"billingConfirmDowngradeButton": "Bekreft nedgradering",
|
||||
"billingLimitViolationWarning": "Bruk overbelastede grenser for ny plan",
|
||||
"billingLimitViolationDescription": "Gjeldende bruk overskrider grensene for denne planen. Etter nedgradering vil alle handlinger deaktiveres inntil du reduserer bruken innenfor de nye grensene. Vennligst se igjennom funksjonene under som er i øyeblikket over grensene. Begrensninger i vold:",
|
||||
"billingFeatureLossWarning": "Fremhev tilgjengelig varsel",
|
||||
"billingFeatureLossDescription": "Ved å nedgradere vil funksjoner som ikke er tilgjengelige i den nye planen automatisk bli deaktivert. Noen innstillinger og konfigurasjoner kan gå tapt. Vennligst gjennomgå prismatrisen for å forstå hvilke funksjoner som ikke lenger vil være tilgjengelige.",
|
||||
"billingUsageExceedsLimit": "Gjeldende bruk ({current}) overskrider grensen ({limit})",
|
||||
"billingPastDueTitle": "Betalingen har forfalt",
|
||||
"billingPastDueDescription": "Betalingen er forfalt. Vennligst oppdater betalingsmetoden din for å fortsette å bruke den gjeldende funksjonsplanen din. Hvis du ikke har løst deg, vil abonnementet ditt avbrytes, og du vil bli tilbakestilt til gratistiden.",
|
||||
"billingUnpaidTitle": "Abonnement ubetalt",
|
||||
"billingUnpaidDescription": "Ditt abonnement er ubetalt og du har blitt tilbakestilt til gratis kasse. Vennligst oppdater din betalingsmetode for å gjenopprette abonnementet.",
|
||||
"billingIncompleteTitle": "Betaling ufullstendig",
|
||||
"billingIncompleteDescription": "Betalingen er ufullstendig. Vennligst fullfør betalingsprosessen for å aktivere abonnementet.",
|
||||
"billingIncompleteExpiredTitle": "Betaling utløpt",
|
||||
"billingIncompleteExpiredDescription": "Din betaling ble aldri fullført, og har utløpt. Du har blitt tilbakestilt til gratis dekk. Vennligst abonner på nytt for å gjenopprette tilgangen til betalte funksjoner.",
|
||||
"billingManageSubscription": "Administrere ditt abonnement",
|
||||
"billingResolvePaymentIssue": "Vennligst løs ditt betalingsproblem før du oppgraderer eller nedgraderer betalingen",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Jeg godtar",
|
||||
"termsOfService": "brukervilkårene",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Tid er i sekunder",
|
||||
"requireDeviceApproval": "Krev enhetsgodkjenning",
|
||||
"requireDeviceApprovalDescription": "Brukere med denne rollen trenger nye enheter godkjent av en admin før de kan koble seg og få tilgang til ressurser.",
|
||||
"sshAccess": "SSH tilgang",
|
||||
"roleAllowSsh": "Tillat SSH",
|
||||
"roleAllowSshAllow": "Tillat",
|
||||
"roleAllowSshDisallow": "Forby",
|
||||
"roleAllowSshDescription": "Tillat brukere med denne rollen å koble til ressurser via SSH. Når deaktivert får rollen ikke tilgang til SSH.",
|
||||
"sshSudoMode": "Sudo tilgang",
|
||||
"sshSudoModeNone": "Ingen",
|
||||
"sshSudoModeNoneDescription": "Brukeren kan ikke kjøre kommandoer med sudo.",
|
||||
"sshSudoModeFull": "Full Sudo",
|
||||
"sshSudoModeFullDescription": "Brukeren kan kjøre hvilken som helst kommando med sudo.",
|
||||
"sshSudoModeCommands": "Kommandoer",
|
||||
"sshSudoModeCommandsDescription": "Brukeren kan bare kjøre de angitte kommandoene med sudo.",
|
||||
"sshSudo": "Tillat sudo",
|
||||
"sshSudoCommands": "Sudo kommandoer",
|
||||
"sshSudoCommandsDescription": "Kommaseparert liste med kommandoer brukeren kan kjøre med sudo.",
|
||||
"sshCreateHomeDir": "Opprett hjemmappe",
|
||||
"sshUnixGroups": "Unix grupper",
|
||||
"sshUnixGroupsDescription": "Kommaseparerte Unix grupper for å legge brukeren til på mål-verten.",
|
||||
"retryAttempts": "Forsøk på nytt",
|
||||
"expectedResponseCodes": "Forventede svarkoder",
|
||||
"expectedResponseCodesDescription": "HTTP-statuskode som indikerer sunn status. Hvis den blir stående tom, regnes 200-300 som sunn.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Utgangsnode",
|
||||
"country": "Land",
|
||||
"rulesMatchCountry": "For tiden basert på kilde IP",
|
||||
"region": "Fylke",
|
||||
"selectRegion": "Velg region",
|
||||
"searchRegions": "Søk etter områder...",
|
||||
"noRegionFound": "Ingen region funnet.",
|
||||
"rulesMatchRegion": "Velg en regional gruppering av land",
|
||||
"rulesErrorInvalidRegion": "Ugyldig område",
|
||||
"rulesErrorInvalidRegionDescription": "Vennligst velg et gyldig område.",
|
||||
"regionAfrica": "Afrika",
|
||||
"regionNorthernAfrica": "[country name] Nord-Afrika",
|
||||
"regionEasternAfrica": "Øst-Afrika",
|
||||
"regionMiddleAfrica": "Middle Africa",
|
||||
"regionSouthernAfrica": "Sør-Afrika",
|
||||
"regionWesternAfrica": "[country name] Vest-Afrika",
|
||||
"regionAmericas": "Amerika",
|
||||
"regionCaribbean": "Karibia",
|
||||
"regionCentralAmerica": "Sentral-Amerika",
|
||||
"regionSouthAmerica": "Sør-Amerika",
|
||||
"regionNorthernAmerica": "Nord-Amerika",
|
||||
"regionAsia": "Asia",
|
||||
"regionCentralAsia": "Sentral-Asia",
|
||||
"regionEasternAsia": "Øst-Asia",
|
||||
"regionSouthEasternAsia": "Sørøst-Asia",
|
||||
"regionSouthernAsia": "Sørlige Asia",
|
||||
"regionWesternAsia": "Vest-Asia",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Øst-Europa",
|
||||
"regionNorthernEurope": "Nord-Europa",
|
||||
"regionSouthernEurope": "Sørlige Europa",
|
||||
"regionWesternEurope": "Vest-Europa",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australia og New Zealand",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Administrert selv-hostet",
|
||||
"description": "Sikre og lavvedlikeholdsservere, selvbetjente Pangolin med ekstra klokker, og understell",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Ugyldig verdi",
|
||||
"idpTypeLabel": "Identitet leverandør type",
|
||||
"roleMappingExpressionPlaceholder": "F.eks. inneholder(grupper, 'admin') && 'Admin' ⋅'Medlem'",
|
||||
"roleMappingModeFixedRoles": "Fast roller",
|
||||
"roleMappingModeMappingBuilder": "Kartlegger bygger",
|
||||
"roleMappingModeRawExpression": "Rå uttrykk",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Velg en eller flere roller",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Skriv inn rollenavn (eksakt treff per organisasjon)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Tilordne den samme rollen som er satt til hver automatisk midlertidig bruker.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "For standard policyer, type rollenavn som eksisterer i hver organisasjon der brukerne tilbys. Navn må stemmer nøyaktig.",
|
||||
"roleMappingClaimPath": "Krev sti",
|
||||
"roleMappingClaimPathPlaceholder": "grupper",
|
||||
"roleMappingClaimPathDescription": "Sti i i token nyttelast som inneholder kildeverdier (for eksempel grupper).",
|
||||
"roleMappingMatchValue": "Treff verdi",
|
||||
"roleMappingAssignRoles": "Tilordne roller",
|
||||
"roleMappingAddMappingRule": "Legg til tilordningsregel",
|
||||
"roleMappingRawExpressionResultDescription": "Uttrykk skal vurderes til en streng eller en tekststreng.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Uttrykk må evaluere til en streng (en rollenavn).",
|
||||
"roleMappingMatchValuePlaceholder": "Match verdi (for eksempel: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Angi rollenavn (eksakt per org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Rollenavn må samsvare med en rolle i hver målorganisasjon.",
|
||||
"roleMappingRemoveRule": "Fjern",
|
||||
"idpGoogleConfiguration": "Google Konfigurasjon",
|
||||
"idpGoogleConfigurationDescription": "Konfigurer Google OAuth2 legitimasjonen",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Er du sikker på at du vil fjerne merkevarebyggingen for autentiseringssider?",
|
||||
"authPageBrandingDeleteConfirm": "Bekreft sletting av merkevarebygging",
|
||||
"brandingLogoURL": "Logo URL",
|
||||
"brandingLogoURLOrPath": "Logoen URL eller sti",
|
||||
"brandingLogoPathDescription": "Skriv inn en URL eller en lokal bane.",
|
||||
"brandingLogoURLDescription": "Skriv inn en offentlig tilgjengelig nettadresse til din logobilde.",
|
||||
"brandingPrimaryColor": "Primærfarge",
|
||||
"brandingLogoWidth": "Bredde (px)",
|
||||
"brandingLogoHeight": "Høyde (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Tilbake til standard innlogging",
|
||||
"orgAuthNoAccount": "Har du ikke konto?",
|
||||
"subscriptionRequiredToUse": "Et abonnement er påkrevd for å bruke denne funksjonen.",
|
||||
"mustUpgradeToUse": "Du må oppgradere ditt abonnement for å bruke denne funksjonen.",
|
||||
"subscriptionRequiredTierToUse": "Denne funksjonen krever <tierLink>{tier}</tierLink> eller høyere.",
|
||||
"upgradeToTierToUse": "Oppgrader til <tierLink>{tier}</tierLink> eller høyere for å bruke denne funksjonen.",
|
||||
"subscriptionTierTier1": "Hjem",
|
||||
"subscriptionTierTier2": "Lag",
|
||||
"subscriptionTierTier3": "Forretninger",
|
||||
"subscriptionTierEnterprise": "Bedrift",
|
||||
"idpDisabled": "Identitetsleverandører er deaktivert.",
|
||||
"orgAuthPageDisabled": "Informasjons-siden for organisasjon er deaktivert.",
|
||||
"domainRestartedDescription": "Domene-verifiseringen ble startet på nytt",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Få en lisens",
|
||||
"description": "Velg en plan og fortell oss hvordan du planlegger å bruke Pangolin.",
|
||||
"chooseTier": "Velg din funksjonsplan",
|
||||
"viewPricingLink": "Se prising, egenskaper og grenser",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Begynner",
|
||||
"description": "Enterprise features, 25 brukere, 25 sitater og støtte fra fellesskapet."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Skala",
|
||||
"description": "Enterprise features, 50 brukere, 50 nettsteder og prioritetsstøtte."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Kun personlig bruk (gratis lisens - ingen utsjekking)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Fortsett til kassen"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Feil ved utsjekk",
|
||||
"description": "Kan ikke starte kassen. Prøv på nytt."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Prioritet",
|
||||
"priorityDescription": "Høyere prioriterte ruter evalueres først. Prioritet = 100 betyr automatisk bestilling (systembeslutninger). Bruk et annet nummer til å håndheve manuell prioritet.",
|
||||
"instanceName": "Forekomst navn",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Hvor lenge du vil beholde adgangslogger",
|
||||
"logRetentionActionLabel": "Handlings logg nytt",
|
||||
"logRetentionActionDescription": "Hvor lenge handlingen skal lagres",
|
||||
"logRetentionConnectionLabel": "Logg nyhet",
|
||||
"logRetentionConnectionDescription": "Hvor lenge du vil beholde tilkoblingslogger",
|
||||
"logRetentionDisabled": "Deaktivert",
|
||||
"logRetention3Days": "3 dager",
|
||||
"logRetention7Days": "7 dager",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Slutt på neste år",
|
||||
"actionLogsDescription": "Vis historikk for handlinger som er utført i denne organisasjonen",
|
||||
"accessLogsDescription": "Vis autoriseringsforespørsler for ressurser i denne organisasjonen",
|
||||
"licenseRequiredToUse": "En Enterprise lisens er påkrevd for å bruke denne funksjonen.",
|
||||
"connectionLogs": "Loggfiler for tilkobling",
|
||||
"connectionLogsDescription": "Vis tilkoblingslogger for tunneler i denne organisasjonen",
|
||||
"sidebarLogsConnection": "Loggfiler for tilkobling",
|
||||
"sidebarLogsStreaming": "Strømming",
|
||||
"sourceAddress": "Kilde adresse",
|
||||
"destinationAddress": "Måladresse (Automatic Translation)",
|
||||
"duration": "Varighet",
|
||||
"licenseRequiredToUse": "En <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisens eller <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> er påkrevd for å bruke denne funksjonen. <bookADemoLink>Bestill en demo eller POC prøveversjon</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> er nødvendig for å bruke denne funksjonen. Denne funksjonen er også tilgjengelig i <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Bestill en demo eller POC studie</bookADemoLink>.",
|
||||
"certResolver": "Sertifikat løser",
|
||||
"certResolverDescription": "Velg sertifikatløser som skal brukes for denne ressursen.",
|
||||
"selectCertResolver": "Velg sertifikatløser",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Tilgangskontroll",
|
||||
"editInternalResourceDialogAccessControlDescription": "Kontroller hvilke roller, brukere og maskinklienter som har tilgang til denne ressursen når den er koblet til. Administratorer har alltid tilgang.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Portsjiktet må være \"*\" for alle porter, eller en kommaseparert liste med porter og sjikt (f.eks. \"80,443,8000-9000\"). Porter må være mellom 1 og 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon Sted",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Velg hvor SSH-autentisering daemon kjører: på nettstedet (Newt) eller på en ekstern vert.",
|
||||
"internalResourceAuthDaemonDescription": "SSH-godkjenning daemon håndterer SSH-nøkkel signering og PAM autentisering for denne ressursen. Velg om den kjører på nettstedet (Newt) eller på en separat ekstern vert. Se <docsLink>dokumentasjonen</docsLink> for mer.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Velg strategi",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Sted",
|
||||
"internalResourceAuthDaemonSite": "På nettsted",
|
||||
"internalResourceAuthDaemonSiteDescription": "Autentiser daemon kjører på nettstedet (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Ekstern vert",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Autentiser daemon kjører på en vert som ikke er nettstedet.",
|
||||
"internalResourceAuthDaemonPort": "Daemon Port (valgfritt)",
|
||||
"orgAuthWhatsThis": "Hvor kan jeg finne min organisasjons-ID?",
|
||||
"learnMore": "Lær mer",
|
||||
"backToHome": "Gå tilbake til start",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Brannmur aktivert",
|
||||
"autoUpdatesEnabled": "Automatiske oppdateringer aktivert",
|
||||
"tpmAvailable": "TPM tilgjengelig",
|
||||
"windowsAntivirusEnabled": "Antivirus aktivert",
|
||||
"macosSipEnabled": "System Integritetsbeskyttelse (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Brannmur Usynlig Modus",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Aktiver enhetsgodkjenninger",
|
||||
"approvalsEmptyStateStep2Description": "Rediger en rolle og aktiver alternativet 'Kreve enhetsgodkjenninger'. Brukere med denne rollen vil trenge administratorgodkjenning for nye enheter.",
|
||||
"approvalsEmptyStatePreviewDescription": "Forhåndsvisning: Når aktivert, ventende enhets forespørsler vil vises her for vurdering",
|
||||
"approvalsEmptyStateButtonText": "Administrer Roller"
|
||||
"approvalsEmptyStateButtonText": "Administrer Roller",
|
||||
"domainErrorTitle": "Vi har problemer med å verifisere domenet ditt",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Konfigurer rollegartlegging og organisasjonspolicyer på <policiesTabLink>Auto leveringsinnstillinger</policiesTabLink> fanen.",
|
||||
"streamingTitle": "Hendelse Strømming",
|
||||
"streamingDescription": "Stream hendelser fra din organisasjon til eksterne destinasjoner i sanntid.",
|
||||
"streamingUnnamedDestination": "Plassering uten navn",
|
||||
"streamingNoUrlConfigured": "Ingen URL konfigurert",
|
||||
"streamingAddDestination": "Legg til mål",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookDescription": "Send hendelser til alle HTTP-endepunkter med fleksibel autentisering og maling.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Strøm hendelser til en S3-kompatibel objektlagringskjøt. Kommer snart.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Videresend arrangementer direkte til din Datadog-konto. Kommer snart.",
|
||||
"streamingTypePickerDescription": "Velg en måltype for å komme i gang.",
|
||||
"streamingFailedToLoad": "Kan ikke laste inn destinasjoner",
|
||||
"streamingUnexpectedError": "En uventet feil oppstod.",
|
||||
"streamingFailedToUpdate": "Kunne ikke oppdatere destinasjon",
|
||||
"streamingDeletedSuccess": "Målet ble slettet",
|
||||
"streamingFailedToDelete": "Kunne ikke slette destinasjon",
|
||||
"streamingDeleteTitle": "Slett mål",
|
||||
"streamingDeleteButtonText": "Slett mål",
|
||||
"streamingDeleteDialogAreYouSure": "Er du sikker på at du vil slette",
|
||||
"streamingDeleteDialogThisDestination": "denne destinasjonen",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Alle konfigurasjoner vil bli slettet permanent.",
|
||||
"httpDestEditTitle": "Rediger mål",
|
||||
"httpDestAddTitle": "Legg til HTTP-destinasjon",
|
||||
"httpDestEditDescription": "Oppdater konfigurasjonen for denne HTTP-hendelsesstrømmedestinasjonen.",
|
||||
"httpDestAddDescription": "Konfigurer et nytt HTTP endepunkt for å motta organisasjonens hendelser.",
|
||||
"httpDestTabSettings": "Innstillinger",
|
||||
"httpDestTabHeaders": "Overskrifter",
|
||||
"httpDestTabBody": "Innhold",
|
||||
"httpDestTabLogs": "Logger",
|
||||
"httpDestNamePlaceholder": "Min HTTP destinasjon",
|
||||
"httpDestUrlLabel": "Destinasjons URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL-adressen må bruke httpp eller https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS er nødvendig for distribusjon av sky",
|
||||
"httpDestUrlErrorInvalid": "Skriv inn en gyldig nettadresse (f.eks. https://eksempel.com/webhook)",
|
||||
"httpDestAuthTitle": "Autentisering",
|
||||
"httpDestAuthDescription": "Velg hvordan ønsker til sluttpunktet ditt er autentisert.",
|
||||
"httpDestAuthNoneTitle": "Ingen godkjenning",
|
||||
"httpDestAuthNoneDescription": "Sender forespørsler uten autorisasjonsoverskrift.",
|
||||
"httpDestAuthBearerTitle": "Bærer Symbol",
|
||||
"httpDestAuthBearerDescription": "Legger til en autorisasjon: Bearer <token> header til hver forespørsel.",
|
||||
"httpDestAuthBearerPlaceholder": "Din API-nøkkel eller token",
|
||||
"httpDestAuthBasicTitle": "Standard Auth",
|
||||
"httpDestAuthBasicDescription": "Legger til en godkjenning: Grunnleggende <credentials> overskrift. Angi legitimasjon som brukernavn:passord.",
|
||||
"httpDestAuthBasicPlaceholder": "brukernavn:passord",
|
||||
"httpDestAuthCustomTitle": "Egendefinert topptekst",
|
||||
"httpDestAuthCustomDescription": "Angi et egendefinert HTTP headers navn og verdi for autentisering (f.eks X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Topptekst navn (f.eks X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Header verdi",
|
||||
"httpDestCustomHeadersTitle": "Egendefinerte HTTP-overskrifter",
|
||||
"httpDestCustomHeadersDescription": "Legg til egendefinerte overskrifter til hver utgående forespørsel. Nyttig for statisk tokens eller en egendefinert innholdstype. Som standard blir innholdstype: applikasjon/json sendt.",
|
||||
"httpDestNoHeadersConfigured": "Ingen egendefinerte overskrifter konfigurert. Klikk \"Legg til topptekst\" for å legge til en.",
|
||||
"httpDestHeaderNamePlaceholder": "Navn på topptekst",
|
||||
"httpDestHeaderValuePlaceholder": "Verdi",
|
||||
"httpDestAddHeader": "Legg til topptekst",
|
||||
"httpDestBodyTemplateTitle": "Egendefinert hovedmal",
|
||||
"httpDestBodyTemplateDescription": "Kontroller JSON nyttelaststrukturen sendt til ditt endepunkt. Hvis deaktivert, sendes et standard JSON-objekt for hver hendelse.",
|
||||
"httpDestEnableBodyTemplate": "Aktiver egendefinert meldingsmal",
|
||||
"httpDestBodyTemplateLabel": "Kroppsmal (JSON)",
|
||||
"httpDestBodyTemplateHint": "Bruk designmal variabler for å referere til eventfelt i din betaling.",
|
||||
"httpDestPayloadFormatTitle": "Mål format",
|
||||
"httpDestPayloadFormatDescription": "Hvordan blir hendelser serialisert inn i hver forespørselsorgan.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON liste",
|
||||
"httpDestFormatJsonArrayDescription": "Én forespørsel per batch, innholdet er en JSON-liste. Kompatibel med de mest generiske webhooks og Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Én forespørsel per sats, innholdet er nytt avgrenset JSON — et objekt per linje, ingen ytterarray. Kreves av Splunk HEC, Elastisk/OpenSearch, og Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "En hendelse per forespørsel",
|
||||
"httpDestFormatSingleDescription": "Sender en separat HTTP POST for hver enkelt hendelse. Bruk bare for endepunkter som ikke kan håndtere batcher.",
|
||||
"httpDestLogTypesTitle": "Logg typer",
|
||||
"httpDestLogTypesDescription": "Velg hvilke loggtyper som blir videresendt til dette målet. Bare aktiverte loggtyper vil bli strømmet.",
|
||||
"httpDestAccessLogsTitle": "Tilgangslogger (Automatic Translation)",
|
||||
"httpDestAccessLogsDescription": "Adgangsforsøk for ressurser, inkludert godkjente og nektet forespørsler.",
|
||||
"httpDestActionLogsTitle": "Handlingslogger",
|
||||
"httpDestActionLogsDescription": "Administrative tiltak som utføres av brukere innenfor organisasjonen.",
|
||||
"httpDestConnectionLogsTitle": "Loggfiler for tilkobling",
|
||||
"httpDestConnectionLogsDescription": "Utstyrs- og tunneltilkoblingshendelser, inkludert forbindelser og frakobling.",
|
||||
"httpDestRequestLogsTitle": "Forespørselslogger (Automatic Translation)",
|
||||
"httpDestRequestLogsDescription": "HTTP-forespørsel logger for bekreftede ressurser, inkludert metode, bane og responskode.",
|
||||
"httpDestSaveChanges": "Lagre endringer",
|
||||
"httpDestCreateDestination": "Opprett mål",
|
||||
"httpDestUpdatedSuccess": "Målet er oppdatert",
|
||||
"httpDestCreatedSuccess": "Målet er opprettet",
|
||||
"httpDestUpdateFailed": "Kunne ikke oppdatere destinasjon",
|
||||
"httpDestCreateFailed": "Kan ikke opprette mål"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} one {één organisatie} other {# organisaties}}.",
|
||||
"componentsInvalidKey": "Ongeldige of verlopen licentiesleutels gedetecteerd. Volg de licentievoorwaarden om alle functies te blijven gebruiken.",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Bedankt voor het ondersteunen van Pangolin als {tier}!",
|
||||
"inviteErrorNotValid": "Het spijt ons, maar de uitnodiging die je probeert te bezoeken is niet geaccepteerd of is niet meer geldig.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Koppeling aanmaken",
|
||||
"resourcesNotFound": "Geen bronnen gevonden",
|
||||
"resourceSearch": "Zoek bronnen",
|
||||
"machineSearch": "Zoek machines",
|
||||
"machinesSearch": "Zoek machine-clients...",
|
||||
"machineNotFound": "Geen machines gevonden",
|
||||
"userDeviceSearch": "Gebruikersapparaten zoeken",
|
||||
"userDevicesSearch": "Gebruikersapparaten zoeken...",
|
||||
"openMenu": "Menu openen",
|
||||
"resource": "Bron",
|
||||
"title": "Aanspreektitel",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxyverzoeken via HTTPS met een volledig gekwalificeerde domeinnaam.",
|
||||
"resourceRaw": "TCP/UDP bron",
|
||||
"resourceRawDescription": "Proxyverzoeken via ruwe TCP/UDP met een poortnummer.",
|
||||
"resourceRawDescriptionCloud": "Proxy verzoeken over rauwe TCP/UDP met behulp van een poortnummer. Vereist sites om verbinding te maken met een remote node.",
|
||||
"resourceCreate": "Bron maken",
|
||||
"resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken",
|
||||
"resourceSeeAll": "Alle bronnen bekijken",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Selecteer een protocol",
|
||||
"resourcePortNumber": "Nummer van poort",
|
||||
"resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.",
|
||||
"back": "Achterzijde",
|
||||
"cancel": "Annuleren",
|
||||
"resourceConfig": "Configuratie tekstbouwstenen",
|
||||
"resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om de TCP/UDP-bron in te stellen",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Er is een fout opgetreden tijdens het verwijderen van de organisatie.",
|
||||
"orgDeleted": "Organisatie verwijderd",
|
||||
"orgDeletedMessage": "De organisatie en haar gegevens zijn verwijderd.",
|
||||
"deleteAccount": "Verwijder account",
|
||||
"deleteAccountDescription": "Verwijdert permanent uw account, alle organisaties die u bezit, en alle gegevens binnen deze organisaties. Dit kan niet ongedaan worden gemaakt.",
|
||||
"deleteAccountButton": "Verwijder account",
|
||||
"deleteAccountConfirmTitle": "Verwijder account",
|
||||
"deleteAccountConfirmMessage": "Dit zal uw account permanent wissen, alle organisaties die u bezit, en alle gegevens binnen deze organisaties. Dit kan niet ongedaan worden gemaakt.",
|
||||
"deleteAccountConfirmString": "verwijder account",
|
||||
"deleteAccountSuccess": "Account verwijderd",
|
||||
"deleteAccountSuccessMessage": "Uw account is verwijderd.",
|
||||
"deleteAccountError": "Kan account niet verwijderen",
|
||||
"deleteAccountPreviewAccount": "Uw account",
|
||||
"deleteAccountPreviewOrgs": "Organisaties die je bezit (en al hun gegevens)",
|
||||
"orgMissing": "Organisatie-ID ontbreekt",
|
||||
"orgMissingMessage": "Niet in staat om de uitnodiging te regenereren zonder organisatie-ID.",
|
||||
"accessUsersManage": "Gebruikers beheren",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "API-sleutel verwijderen",
|
||||
"apiKeysManage": "API-sleutels beheren",
|
||||
"apiKeysDescription": "API-sleutels worden gebruikt om te verifiëren met de integratie-API",
|
||||
"provisioningKeysTitle": "Vertrekkende sleutel",
|
||||
"provisioningKeysManage": "Beheren van Provisioning Sleutels",
|
||||
"provisioningKeysDescription": "Provisionerende sleutels worden gebruikt om geautomatiseerde sitebepaling voor uw organisatie te verifiëren.",
|
||||
"provisioningManage": "Provisie",
|
||||
"provisioningDescription": "Voorzieningssleutels beheren en sites beoordelen in afwachting van goedkeuring.",
|
||||
"pendingSites": "Openstaande sites",
|
||||
"siteApproveSuccess": "Site succesvol goedgekeurd",
|
||||
"siteApproveError": "Fout bij goedkeuren website",
|
||||
"provisioningKeys": "Verhelderende sleutels",
|
||||
"searchProvisioningKeys": "Zoek provisioningsleutels ...",
|
||||
"provisioningKeysAdd": "Genereer Provisioning Sleutel",
|
||||
"provisioningKeysErrorDelete": "Fout bij verwijderen provisioning sleutel",
|
||||
"provisioningKeysErrorDeleteMessage": "Fout bij verwijderen provisioning sleutel",
|
||||
"provisioningKeysQuestionRemove": "Weet u zeker dat u deze proefsleutel van de organisatie wilt verwijderen?",
|
||||
"provisioningKeysMessageRemove": "Eenmaal verwijderd, kan de sleutel niet meer worden gebruikt voor site-instructie.",
|
||||
"provisioningKeysDeleteConfirm": "Bevestig Verwijderen Provisione-sleutel",
|
||||
"provisioningKeysDelete": "Provisione-sleutel verwijderen",
|
||||
"provisioningKeysCreate": "Genereer Provisioning Sleutel",
|
||||
"provisioningKeysCreateDescription": "Een nieuwe provisioningsleutel voor de organisatie genereren",
|
||||
"provisioningKeysSeeAll": "Bekijk alle provisioning sleutels",
|
||||
"provisioningKeysSave": "Sla de provisioning sleutel op",
|
||||
"provisioningKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plaats.",
|
||||
"provisioningKeysErrorCreate": "Fout bij aanmaken provisioning sleutel",
|
||||
"provisioningKeysList": "Nieuwe provisioning sleutel",
|
||||
"provisioningKeysMaxBatchSize": "Maximale batchgrootte",
|
||||
"provisioningKeysUnlimitedBatchSize": "Onbeperkte batchgrootte (geen limiet)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Onbeperkt",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Voer een geldige maximale batchgrootte in (1–1.000,000).",
|
||||
"provisioningKeysValidUntil": "Geldig tot",
|
||||
"provisioningKeysValidUntilHint": "Laat leeg voor geen vervaldatum.",
|
||||
"provisioningKeysValidUntilInvalid": "Voer een geldige datum en tijd in.",
|
||||
"provisioningKeysNumUsed": "Aantal keer gebruikt",
|
||||
"provisioningKeysLastUsed": "Laatst gebruikt",
|
||||
"provisioningKeysNoExpiry": "Geen vervaldatum",
|
||||
"provisioningKeysNeverUsed": "Nooit",
|
||||
"provisioningKeysEdit": "Wijzig Provisioning Sleutel",
|
||||
"provisioningKeysEditDescription": "Werk de maximale batchgrootte en verlooptijd voor deze sleutel bij.",
|
||||
"provisioningKeysApproveNewSites": "Goedkeuren van nieuwe sites",
|
||||
"provisioningKeysApproveNewSitesDescription": "Automatisch sites goedkeuren die zich registreren met deze sleutel.",
|
||||
"provisioningKeysUpdateError": "Fout tijdens bijwerken provisioning sleutel",
|
||||
"provisioningKeysUpdated": "Provisie sleutel bijgewerkt",
|
||||
"provisioningKeysUpdatedDescription": "Uw wijzigingen zijn opgeslagen.",
|
||||
"provisioningKeysBannerTitle": "Bewerkingssleutels voor websites",
|
||||
"provisioningKeysBannerDescription": "Genereer een provisioning-sleutel en gebruik deze met de Newt-connector om automatisch sites aan te maken bij het opstarten van de eerste opstart- het is niet nodig om afzonderlijke inloggegevens in te stellen voor elke site.",
|
||||
"provisioningKeysBannerButtonText": "Meer informatie",
|
||||
"pendingSitesBannerTitle": "Openstaande sites",
|
||||
"pendingSitesBannerDescription": "Sites die met elkaar verbinden met behulp van een provisioning-sleutel verschijnen hier voor beoordeling. Accepteer elke site voordat deze actief wordt en krijgt toegang tot uw bronnen.",
|
||||
"pendingSitesBannerButtonText": "Meer informatie",
|
||||
"apiKeysSettings": "{apiKeyName} instellingen",
|
||||
"userTitle": "Alle gebruikers beheren",
|
||||
"userDescription": "Bekijk en beheer alle gebruikers in het systeem",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filter op goedkeuringsstatus",
|
||||
"approvalListEmpty": "Geen goedkeuringen",
|
||||
"approvalState": "Goedkeuring status",
|
||||
"approvalLoadMore": "Meer laden",
|
||||
"loadingApprovals": "Goedkeuringen laden",
|
||||
"approve": "Goedkeuren",
|
||||
"approved": "Goedgekeurd",
|
||||
"denied": "Geweigerd",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Gebruiker opgeslagen",
|
||||
"userSavedDescription": "De gebruiker is bijgewerkt.",
|
||||
"autoProvisioned": "Automatisch bevestigen",
|
||||
"autoProvisionSettings": "Auto Provisie Instellingen",
|
||||
"autoProvisionedDescription": "Toestaan dat deze gebruiker automatisch wordt beheerd door een identiteitsprovider",
|
||||
"accessControlsDescription": "Beheer wat deze gebruiker toegang heeft tot en doet in de organisatie",
|
||||
"accessControlsSubmit": "Bewaar Toegangsbesturing",
|
||||
"singleRolePerUserPlanNotice": "Uw plan ondersteunt slechts één rol per gebruiker.",
|
||||
"singleRolePerUserEditionNotice": "Deze editie ondersteunt slechts één rol per gebruiker.",
|
||||
"roles": "Rollen",
|
||||
"accessUsersRoles": "Beheer Gebruikers & Rollen",
|
||||
"accessUsersRolesDescription": "Nodig gebruikers uit en voeg ze toe aan de rollen om toegang tot de organisatie te beheren",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Bron wisselen mislukt",
|
||||
"resourcesErrorUpdateDescription": "Er is een fout opgetreden tijdens het bijwerken van het document",
|
||||
"access": "Toegangsrechten",
|
||||
"accessControl": "Toegangs controle",
|
||||
"shareLink": "{resource} Share link",
|
||||
"resourceSelect": "Selecteer resource",
|
||||
"shareLinks": "Links delen",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rol verwijderd",
|
||||
"accessRoleRemovedDescription": "De rol is succesvol verwijderd.",
|
||||
"accessRoleRequiredRemove": "Voordat u deze rol verwijdert, selecteer een nieuwe rol om bestaande leden aan te dragen.",
|
||||
"network": "Netwerk",
|
||||
"manage": "Beheren",
|
||||
"sitesNotFound": "Geen sites gevonden.",
|
||||
"pangolinServerAdmin": "Serverbeheer - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Toename van site vergroten",
|
||||
"idpManage": "Identiteitsaanbieders beheren",
|
||||
"idpManageDescription": "Identiteitsaanbieders in het systeem bekijken en beheren",
|
||||
"idpGlobalModeBanner": "Identiteitsaanbieders (IdPs) per organisatie zijn uitgeschakeld op deze server. Het gebruikt globale IdPs (gedeeld tussen alle organisaties). Beheer globale IdPs in het <adminPanelLink>beheerderspaneel</adminPanelLink>. Om IdPs per organisatie in te schakelen, bewerk de server configuratie en zet IdP modus op org. <configDocsLink>Zie de documenten</configDocsLink>. Als je globale IdPs wilt blijven gebruiken en dit uit de organisatie-instellingen wilt laten verdwijnen, zet dan expliciet de modus naar globaal in de config.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Identity providers (IdPs) per organisatie zijn uitgeschakeld op deze server. Het gebruikt globale IdPs (gedeeld in alle organisaties) Beheer globale IdPs in het <adminPanelLink>beheerderspaneel</adminPanelLink>. Om identiteitsproviders per organisatie te gebruiken, moet u upgraden naar de Enterprise editie.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Identity providers (IdPs) per organisatie zijn uitgeschakeld op deze server. Het gebruikt globale IdPs (gedeeld in alle organisaties) Beheer globale IdPs in het <adminPanelLink>beheerderspaneel</adminPanelLink>. Om identiteitsaanbieders per organisatie te gebruiken, is een Enterprise-licentie vereist.",
|
||||
"idpDeletedDescription": "Identity provider succesvol verwijderd",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider permanent wilt verwijderen?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Instellen - Pangolin",
|
||||
"orgNameRequired": "Organisatienaam is vereist",
|
||||
"orgIdRequired": "Organisatie-ID is vereist",
|
||||
"orgIdMaxLength": "Organisatie-ID mag maximaal 32 tekens lang zijn",
|
||||
"orgErrorCreate": "Fout opgetreden tijdens het aanmaken org",
|
||||
"pageNotFound": "Pagina niet gevonden",
|
||||
"pageNotFoundDescription": "Oeps! De pagina die je zoekt bestaat niet.",
|
||||
"overview": "Overzicht.",
|
||||
"home": "Startpagina",
|
||||
"accessControl": "Toegangs controle",
|
||||
"settings": "Instellingen",
|
||||
"usersAll": "Alle gebruikers",
|
||||
"license": "Licentie",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Gebruiker ophalen",
|
||||
"actionGetOrgUser": "Krijg organisatie-gebruiker",
|
||||
"actionListOrgDomains": "Lijst organisatie domeinen",
|
||||
"actionGetDomain": "Domein verkrijgen",
|
||||
"actionCreateOrgDomain": "Domein aanmaken",
|
||||
"actionUpdateOrgDomain": "Domein bijwerken",
|
||||
"actionDeleteOrgDomain": "Domein verwijderen",
|
||||
"actionGetDNSRecords": "Krijg DNS Records",
|
||||
"actionRestartOrgDomain": "Domein opnieuw starten",
|
||||
"actionCreateSite": "Site aanmaken",
|
||||
"actionDeleteSite": "Site verwijderen",
|
||||
"actionGetSite": "Site ophalen",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Voer het setup-token in vanaf de serverconsole.",
|
||||
"setupTokenRequired": "Setup-token is vereist",
|
||||
"actionUpdateSite": "Site bijwerken",
|
||||
"actionResetSiteBandwidth": "Reset organisatieschandbreedte",
|
||||
"actionListSiteRoles": "Toon toegestane sitenollen",
|
||||
"actionCreateResource": "Bron maken",
|
||||
"actionDeleteResource": "Document verwijderen",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Gebruiker verwijderen",
|
||||
"actionListUsers": "Gebruikers weergeven",
|
||||
"actionAddUserRole": "Gebruikersrol toevoegen",
|
||||
"actionSetUserOrgRoles": "Stel gebruikersrollen in",
|
||||
"actionGenerateAccessToken": "Genereer Toegangstoken",
|
||||
"actionDeleteAccessToken": "Verwijder toegangstoken",
|
||||
"actionListAccessTokens": "Lijst toegangstokens",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Logboeken bekijken",
|
||||
"noneSelected": "Niet geselecteerd",
|
||||
"orgNotFound2": "Geen organisaties gevonden.",
|
||||
"searchProgress": "Zoeken...",
|
||||
"searchPlaceholder": "Zoeken...",
|
||||
"emptySearchOptions": "Geen opties gevonden",
|
||||
"create": "Aanmaken",
|
||||
"orgs": "Organisaties",
|
||||
"loginError": "Er is een onverwachte fout opgetreden. Probeer het opnieuw.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privé",
|
||||
"sidebarAccessControl": "Toegangs controle",
|
||||
"sidebarLogsAndAnalytics": "Logs & Analytics",
|
||||
"sidebarTeam": "Team",
|
||||
"sidebarUsers": "Gebruikers",
|
||||
"sidebarAdmin": "Beheerder",
|
||||
"sidebarInvitations": "Uitnodigingen",
|
||||
"sidebarRoles": "Rollen",
|
||||
"sidebarShareableLinks": "Koppelingen",
|
||||
"sidebarApiKeys": "API sleutels",
|
||||
"sidebarProvisioning": "Provisie",
|
||||
"sidebarSettings": "Instellingen",
|
||||
"sidebarAllUsers": "Alle gebruikers",
|
||||
"sidebarIdentityProviders": "Identiteit aanbieders",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Log & Analytics",
|
||||
"sidebarBluePrints": "Blauwdrukken",
|
||||
"sidebarOrganization": "Organisatie",
|
||||
"sidebarManagement": "Beheer",
|
||||
"sidebarBillingAndLicenses": "Facturatie & Licenties",
|
||||
"sidebarLogsAnalytics": "Analyses",
|
||||
"blueprints": "Blauwdrukken",
|
||||
"blueprintsDescription": "Gebruik declaratieve configuraties en bekijk vorige uitvoeringen.",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Geparseerde inhoud (alleen lezen)",
|
||||
"enableDockerSocket": "Schakel Docker Blauwdruk in",
|
||||
"enableDockerSocketDescription": "Schakel Docker Socket label in voor blauwdruk labels. Pad naar Nieuw.",
|
||||
"enableDockerSocketLink": "Meer informatie",
|
||||
"viewDockerContainers": "Bekijk Docker containers",
|
||||
"containersIn": "Containers in {siteName}",
|
||||
"selectContainerDescription": "Selecteer een container om als hostnaam voor dit doel te gebruiken. Klik op een poort om een poort te gebruiken.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Naamruimte: {namespace}",
|
||||
"domainPickerShowMore": "Meer weergeven",
|
||||
"regionSelectorTitle": "Selecteer Regio",
|
||||
"domainPickerRemoteExitNodeWarning": "Opgegeven domeinen worden niet ondersteund wanneer websites verbinding maken met externe sluitnodes. Gebruik in plaats daarvan een aangepast domein. Om bronnen beschikbaar te maken op externe nodes.",
|
||||
"regionSelectorInfo": "Het selecteren van een regio helpt ons om betere prestaties te leveren voor uw locatie. U hoeft niet in dezelfde regio als uw server te zijn.",
|
||||
"regionSelectorPlaceholder": "Kies een regio",
|
||||
"regionSelectorComingSoon": "Komt binnenkort",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Overzicht gebruikslimieten",
|
||||
"billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@pangolin.net.",
|
||||
"billingDataUsage": "Gegevensgebruik",
|
||||
"billingOnlineTime": "Site Online Tijd",
|
||||
"billingUsers": "Actieve Gebruikers",
|
||||
"billingDomains": "Actieve Domeinen",
|
||||
"billingRemoteExitNodes": "Actieve Zelfgehoste Nodes",
|
||||
"billingSites": "Sites",
|
||||
"billingUsers": "Gebruikers",
|
||||
"billingDomains": "Domeinen",
|
||||
"billingOrganizations": "Ordenen",
|
||||
"billingRemoteExitNodes": "Externe knooppunten",
|
||||
"billingNoLimitConfigured": "Geen limiet ingesteld",
|
||||
"billingEstimatedPeriod": "Geschatte Facturatie Periode",
|
||||
"billingIncludedUsage": "Opgenomen Gebruik",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Niet gelukt om portal URL te krijgen",
|
||||
"billingPortalError": "Portal Fout",
|
||||
"billingDataUsageInfo": "U bent in rekening gebracht voor alle gegevens die via uw beveiligde tunnels via de cloud worden verzonden. Dit omvat zowel inkomende als uitgaande verkeer over al uw sites. Wanneer u uw limiet bereikt zullen uw sites de verbinding verbreken totdat u uw abonnement upgradet of het gebruik vermindert. Gegevens worden niet in rekening gebracht bij het gebruik van knooppunten.",
|
||||
"billingOnlineTimeInfo": "U wordt in rekening gebracht op basis van hoe lang uw sites verbonden blijven met de cloud. Bijvoorbeeld 44,640 minuten is gelijk aan één site met 24/7 voor een volledige maand. Wanneer u uw limiet bereikt, zal de verbinding tussen uw sites worden verbroken totdat u een upgrade van uw abonnement uitvoert of het gebruik vermindert. Tijd wordt niet belast bij het gebruik van knooppunten.",
|
||||
"billingUsersInfo": "U bent in rekening gebracht voor elke gebruiker in de organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve gebruikersaccounts in uw org.",
|
||||
"billingDomainInfo": "U wordt voor elk domein in de organisatie in rekening gebracht. Facturering wordt dagelijks berekend op basis van het aantal actieve domeinaccounts in uw org.",
|
||||
"billingRemoteExitNodesInfo": "U bent belast voor elke beheerde node in de organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve beheerde knooppunten in uw org.",
|
||||
"billingSInfo": "Hoeveel sites u kunt gebruiken",
|
||||
"billingUsersInfo": "Hoeveel gebruikers je kan gebruiken",
|
||||
"billingDomainInfo": "Hoeveel domeinen je kunt gebruiken",
|
||||
"billingRemoteExitNodesInfo": "Hoeveel externe nodes je kunt gebruiken",
|
||||
"billingLicenseKeys": "Licentie Sleutels",
|
||||
"billingLicenseKeysDescription": "Beheer uw licentiesleutelabonnementen",
|
||||
"billingLicenseSubscription": "Licentie abonnement",
|
||||
"billingInactive": "Inactief",
|
||||
"billingLicenseItem": "Licentie artikel",
|
||||
"billingQuantity": "Aantal",
|
||||
"billingTotal": "totaal",
|
||||
"billingModifyLicenses": "Licentieabonnement wijzigen",
|
||||
"domainNotFound": "Domein niet gevonden",
|
||||
"domainNotFoundDescription": "Deze bron is uitgeschakeld omdat het domein niet langer in ons systeem bestaat. Stel een nieuw domein in voor deze bron.",
|
||||
"failed": "Mislukt",
|
||||
"createNewOrgDescription": "Maak een nieuwe organisatie",
|
||||
"organization": "Organisatie",
|
||||
"primary": "Primair",
|
||||
"port": "Poort",
|
||||
"securityKeyManage": "Beveiligingssleutels beheren",
|
||||
"securityKeyDescription": "Voeg beveiligingssleutels toe of verwijder ze voor wachtwoordloze authenticatie",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Poortnummer is vereist voor niet-HTTP-bronnen",
|
||||
"resourcePortNotAllowed": "Poortnummer mag niet worden ingesteld voor HTTP-bronnen",
|
||||
"billingPricingCalculatorLink": "Prijs Calculator",
|
||||
"billingYourPlan": "Uw abonnement",
|
||||
"billingViewOrModifyPlan": "Bekijk of wijzig uw huidige abonnement",
|
||||
"billingViewPlanDetails": "Abonnementsdetails bekijken",
|
||||
"billingUsageAndLimits": "Gebruik en limieten",
|
||||
"billingViewUsageAndLimits": "Limiet van je abonnement en huidig gebruik bekijken",
|
||||
"billingCurrentUsage": "Huidig gebruik",
|
||||
"billingMaximumLimits": "Maximaal aantal limieten",
|
||||
"billingRemoteNodes": "Externe knooppunten",
|
||||
"billingUnlimited": "Onbeperkt",
|
||||
"billingPaidLicenseKeys": "Betaalde licentiesleutels",
|
||||
"billingManageLicenseSubscription": "Beheer je abonnement voor betaalde zelf gehoste licentiesleutels",
|
||||
"billingCurrentKeys": "Huidige toetsen",
|
||||
"billingModifyCurrentPlan": "Huidig plan wijzigen",
|
||||
"billingConfirmUpgrade": "Bevestig Upgrade",
|
||||
"billingConfirmDowngrade": "Downgraden bevestigen",
|
||||
"billingConfirmUpgradeDescription": "U staat op het punt uw abonnement te upgraden. Controleer de nieuwe limieten en prijzen hieronder.",
|
||||
"billingConfirmDowngradeDescription": "U staat op het punt om uw abonnement te downgraden. Controleer de nieuwe limieten en prijzen hieronder.",
|
||||
"billingPlanIncludes": "Abonnement bevat",
|
||||
"billingProcessing": "Verwerken...",
|
||||
"billingConfirmUpgradeButton": "Bevestig Upgrade",
|
||||
"billingConfirmDowngradeButton": "Downgraden bevestigen",
|
||||
"billingLimitViolationWarning": "Gebruik Overschrijdt nieuwe Plan Limieten",
|
||||
"billingLimitViolationDescription": "Uw huidige verbruik overschrijdt de limieten van dit plan. Na het downgraden worden alle acties uitgeschakeld totdat u het verbruik vermindert binnen de nieuwe grenzen. Controleer de onderstaande functies die de limieten overschrijden. Beperkingen in overtreding:",
|
||||
"billingFeatureLossWarning": "Kennisgeving beschikbaarheid",
|
||||
"billingFeatureLossDescription": "Door downgraden worden functies die niet beschikbaar zijn in het nieuwe abonnement automatisch uitgeschakeld. Sommige instellingen en configuraties kunnen verloren gaan. Raadpleeg de prijsmatrix om te begrijpen welke functies niet langer beschikbaar zijn.",
|
||||
"billingUsageExceedsLimit": "Huidig gebruik ({current}) overschrijdt limiet ({limit})",
|
||||
"billingPastDueTitle": "Vervaldatum betaling",
|
||||
"billingPastDueDescription": "Uw betaling is verlopen. Werk uw betaalmethode bij om uw huidige abonnementsfuncties te blijven gebruiken. Als dit niet is opgelost, zal je abonnement worden geannuleerd en zal je worden teruggezet naar de vrije rang.",
|
||||
"billingUnpaidTitle": "Abonnement Onbetaald",
|
||||
"billingUnpaidDescription": "Uw abonnement is niet betaald en u bent teruggekeerd naar het gratis niveau. Update uw betalingsmethode om uw abonnement te herstellen.",
|
||||
"billingIncompleteTitle": "Betaling onvolledig",
|
||||
"billingIncompleteDescription": "Uw betaling is onvolledig. Voltooi alstublieft het betalingsproces om uw abonnement te activeren.",
|
||||
"billingIncompleteExpiredTitle": "Betaling verlopen",
|
||||
"billingIncompleteExpiredDescription": "Uw betaling is nooit voltooid en verlopen. U bent teruggekeerd naar de gratis niveaus. Abonneer u opnieuw om de toegang tot betaalde functies te herstellen.",
|
||||
"billingManageSubscription": "Beheer uw abonnement",
|
||||
"billingResolvePaymentIssue": "Gelieve uw betalingsprobleem op te lossen voor het upgraden of downgraden",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Ik ga akkoord met de",
|
||||
"termsOfService": "servicevoorwaarden",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Tijd is in seconden",
|
||||
"requireDeviceApproval": "Vereist goedkeuring van apparaat",
|
||||
"requireDeviceApprovalDescription": "Gebruikers met deze rol hebben nieuwe apparaten nodig die door een beheerder zijn goedgekeurd voordat ze verbinding kunnen maken met bronnen en deze kunnen gebruiken.",
|
||||
"sshAccess": "SSH toegang",
|
||||
"roleAllowSsh": "SSH toestaan",
|
||||
"roleAllowSshAllow": "Toestaan",
|
||||
"roleAllowSshDisallow": "Weigeren",
|
||||
"roleAllowSshDescription": "Sta gebruikers met deze rol toe om verbinding te maken met bronnen via SSH. Indien uitgeschakeld kan de rol geen gebruik maken van SSH toegang.",
|
||||
"sshSudoMode": "Sudo toegang",
|
||||
"sshSudoModeNone": "geen",
|
||||
"sshSudoModeNoneDescription": "Gebruiker kan geen commando's uitvoeren met sudo.",
|
||||
"sshSudoModeFull": "Volledige Sudo",
|
||||
"sshSudoModeFullDescription": "Gebruiker kan elk commando uitvoeren met een sudo.",
|
||||
"sshSudoModeCommands": "Opdrachten",
|
||||
"sshSudoModeCommandsDescription": "Gebruiker kan alleen de opgegeven commando's uitvoeren met de sudo.",
|
||||
"sshSudo": "sudo toestaan",
|
||||
"sshSudoCommands": "Sudo Commando's",
|
||||
"sshSudoCommandsDescription": "Komma's gescheiden lijst van commando's waar de gebruiker een sudo mee mag uitvoeren.",
|
||||
"sshCreateHomeDir": "Maak Home Directory",
|
||||
"sshUnixGroups": "Unix groepen",
|
||||
"sshUnixGroupsDescription": "Door komma's gescheiden Unix-groepen om de gebruiker toe te voegen aan de doelhost.",
|
||||
"retryAttempts": "Herhaal Pogingen",
|
||||
"expectedResponseCodes": "Verwachte Reactiecodes",
|
||||
"expectedResponseCodesDescription": "HTTP-statuscode die gezonde status aangeeft. Indien leeg wordt 200-300 als gezond beschouwd.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Exit Node",
|
||||
"country": "Land",
|
||||
"rulesMatchCountry": "Momenteel gebaseerd op bron IP",
|
||||
"region": "Regio",
|
||||
"selectRegion": "Selecteer regio",
|
||||
"searchRegions": "Zoek regio's...",
|
||||
"noRegionFound": "Geen regio gevonden.",
|
||||
"rulesMatchRegion": "Selecteer een regionale groepering van landen",
|
||||
"rulesErrorInvalidRegion": "Ongeldige regio",
|
||||
"rulesErrorInvalidRegionDescription": "Selecteer een geldige regio.",
|
||||
"regionAfrica": "Afrika",
|
||||
"regionNorthernAfrica": "Noord-Afrika",
|
||||
"regionEasternAfrica": "Oost Afrika",
|
||||
"regionMiddleAfrica": "Midden Afrika",
|
||||
"regionSouthernAfrica": "Zuidelijk Afrika",
|
||||
"regionWesternAfrica": "Westelijk Afrika",
|
||||
"regionAmericas": "Amerika's",
|
||||
"regionCaribbean": "Caraïben",
|
||||
"regionCentralAmerica": "Midden-Amerika",
|
||||
"regionSouthAmerica": "Zuid Amerika",
|
||||
"regionNorthernAmerica": "Noord-Amerika",
|
||||
"regionAsia": "Azië",
|
||||
"regionCentralAsia": "Centraal-Azië",
|
||||
"regionEasternAsia": "Oost-Azië",
|
||||
"regionSouthEasternAsia": "Zuid-Oost-Azië",
|
||||
"regionSouthernAsia": "Zuid-Azië",
|
||||
"regionWesternAsia": "Westelijk Azië",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Oost-Europa",
|
||||
"regionNorthernEurope": "Noord-Europa",
|
||||
"regionSouthernEurope": "Zuid-Europa",
|
||||
"regionWesternEurope": "West-Europa",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australië en Nieuw-Zeeland",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Beheerde Self-Hosted",
|
||||
"description": "betrouwbaardere en slecht onderhouden Pangolin server met extra klokken en klokkenluiders",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Ongeldige waarde",
|
||||
"idpTypeLabel": "Identiteit provider type",
|
||||
"roleMappingExpressionPlaceholder": "bijvoorbeeld bevat (groepen, 'admin') && 'Admin' ½ 'Member'",
|
||||
"roleMappingModeFixedRoles": "Vaste rollen",
|
||||
"roleMappingModeMappingBuilder": "Toewijzing Bouwer",
|
||||
"roleMappingModeRawExpression": "Ruwe expressie",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Selecteer één of meer rollen",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Typ rolnamen (exacte overeenkomst per organisatie)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Wijs dezelfde rolset toe aan elke auto-provisioned gebruiker.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Voor standaardbeleid, typ rolnamen die bestaan in elke organisatie waar gebruikers worden opgegeven. Namen moeten exact overeenkomen.",
|
||||
"roleMappingClaimPath": "Claim pad",
|
||||
"roleMappingClaimPathPlaceholder": "Groepen",
|
||||
"roleMappingClaimPathDescription": "Pad in de token payload die bronwaarden bevat (bijvoorbeeld groepen).",
|
||||
"roleMappingMatchValue": "Kies een waarde",
|
||||
"roleMappingAssignRoles": "Rollen toewijzen",
|
||||
"roleMappingAddMappingRule": "Toewijzingsregel toevoegen",
|
||||
"roleMappingRawExpressionResultDescription": "Expressie moet een tekenreeks of tekenreeks evalueren.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Expressie moet evalueren naar een tekenreeks (een naam met één rol).",
|
||||
"roleMappingMatchValuePlaceholder": "Overeenkomende waarde (bijvoorbeeld: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Typ rolnamen (exact per org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Rol namen moeten overeenkomen met een rol in elke doelorganisatie.",
|
||||
"roleMappingRemoveRule": "Verwijderen",
|
||||
"idpGoogleConfiguration": "Google Configuratie",
|
||||
"idpGoogleConfigurationDescription": "Configureer de Google OAuth2-referenties",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Weet u zeker dat u de branding voor Auth-pagina's wilt verwijderen?",
|
||||
"authPageBrandingDeleteConfirm": "Bevestig verwijder Branding",
|
||||
"brandingLogoURL": "Het logo-URL",
|
||||
"brandingLogoURLOrPath": "Logo URL of pad",
|
||||
"brandingLogoPathDescription": "Voer een URL of een lokaal pad in.",
|
||||
"brandingLogoURLDescription": "Voer een openbaar toegankelijke URL in voor uw logo afbeelding.",
|
||||
"brandingPrimaryColor": "Primaire kleur",
|
||||
"brandingLogoWidth": "Breedte (px)",
|
||||
"brandingLogoHeight": "Hoogte (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Terug naar standaard aanmelden",
|
||||
"orgAuthNoAccount": "Nog geen account?",
|
||||
"subscriptionRequiredToUse": "Een abonnement is vereist om deze functie te gebruiken.",
|
||||
"mustUpgradeToUse": "U moet uw abonnement upgraden om deze functie te gebruiken.",
|
||||
"subscriptionRequiredTierToUse": "Deze functie vereist <tierLink>{tier}</tierLink> of hoger.",
|
||||
"upgradeToTierToUse": "Upgrade naar <tierLink>{tier}</tierLink> of hoger om deze functie te gebruiken.",
|
||||
"subscriptionTierTier1": "Startpagina",
|
||||
"subscriptionTierTier2": "Team",
|
||||
"subscriptionTierTier3": "Bedrijfsleven",
|
||||
"subscriptionTierEnterprise": "Onderneming",
|
||||
"idpDisabled": "Identiteitsaanbieders zijn uitgeschakeld.",
|
||||
"orgAuthPageDisabled": "Pagina voor organisatie-authenticatie is uitgeschakeld.",
|
||||
"domainRestartedDescription": "Domeinverificatie met succes opnieuw gestart",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Krijg een licentie",
|
||||
"description": "Kies een plan en vertel ons hoe u Pangolin wilt gebruiken.",
|
||||
"chooseTier": "Kies uw abonnement",
|
||||
"viewPricingLink": "Zie prijzen, functies en limieten",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Beginner",
|
||||
"description": "Enterprise functies, 25 gebruikers, 25 sites en community ondersteuning."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Schaal",
|
||||
"description": "Enterprise functies, 50 gebruikers, 50 sites en prioriteit ondersteuning."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Alleen persoonlijk gebruik (gratis licentie - geen afrekenen)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Doorgaan naar afrekenen"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Fout bij afrekenen",
|
||||
"description": "Kan de afhandeling niet starten. Probeer het opnieuw."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Prioriteit",
|
||||
"priorityDescription": "routes met hogere prioriteit worden eerst geëvalueerd. Prioriteit = 100 betekent automatisch bestellen (systeem beslist de). Gebruik een ander nummer om handmatige prioriteit af te dwingen.",
|
||||
"instanceName": "Naam instantie",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Hoe lang de toegangslogboeken behouden blijven",
|
||||
"logRetentionActionLabel": "Actie log bewaring",
|
||||
"logRetentionActionDescription": "Hoe lang de action logs behouden moeten blijven",
|
||||
"logRetentionConnectionLabel": "Connectie log bewaring",
|
||||
"logRetentionConnectionDescription": "Hoe lang de verbindingslogs onderhouden",
|
||||
"logRetentionDisabled": "Uitgeschakeld",
|
||||
"logRetention3Days": "3 dagen",
|
||||
"logRetention7Days": "7 dagen",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Einde van volgend jaar",
|
||||
"actionLogsDescription": "Bekijk een geschiedenis van acties die worden uitgevoerd in deze organisatie",
|
||||
"accessLogsDescription": "Toegangsverificatieverzoeken voor resources in deze organisatie bekijken",
|
||||
"licenseRequiredToUse": "Een Enterprise-licentie is vereist om deze functie te gebruiken.",
|
||||
"connectionLogs": "Connectie Logs",
|
||||
"connectionLogsDescription": "Toon verbindingslogs voor tunnels in deze organisatie",
|
||||
"sidebarLogsConnection": "Connectie Logs",
|
||||
"sidebarLogsStreaming": "Streamen",
|
||||
"sourceAddress": "Bron adres",
|
||||
"destinationAddress": "Adres bestemming",
|
||||
"duration": "Duur",
|
||||
"licenseRequiredToUse": "Een <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> licentie of <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is vereist om deze functie te gebruiken. <bookADemoLink>Boek een demo of POC trial</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "De <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is vereist om deze functie te gebruiken. Deze functie is ook beschikbaar in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Boek een demo of POC trial</bookADemoLink>.",
|
||||
"certResolver": "Certificaat Resolver",
|
||||
"certResolverDescription": "Selecteer de certificaat resolver die moet worden gebruikt voor deze resource.",
|
||||
"selectCertResolver": "Certificaat Resolver selecteren",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Toegangs controle",
|
||||
"editInternalResourceDialogAccessControlDescription": "Beheer welke rollen, gebruikers en machineclients toegang hebben tot deze bron wanneer ze zijn verbonden. Beheerders hebben altijd toegang.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Poortbereik moet \"*\" zijn voor alle poorten, of een komma-gescheiden lijst van poorten en bereiken (bijv. \"80,443,8000-9000\"). Poorten moeten tussen 1 en 65535 zijn.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon locatie",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Kies waar de SSH authenticatie daemon wordt uitgevoerd: op de website (Newt) of op een externe host.",
|
||||
"internalResourceAuthDaemonDescription": "De SSH authenticatie daemon zorgt voor SSH sleutelondertekening en PAM authenticatie voor deze resource. Kies of het wordt uitgevoerd op de website (Nieuw) of op een afzonderlijke externe host. Zie <docsLink>de documentatie</docsLink> voor meer.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Selecteer strategie",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Locatie",
|
||||
"internalResourceAuthDaemonSite": "In de site",
|
||||
"internalResourceAuthDaemonSiteDescription": "Auth daemon draait op de site (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Externe host",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Authenticatiedaemon draait op een host die niet de site is.",
|
||||
"internalResourceAuthDaemonPort": "Daemon poort (optioneel)",
|
||||
"orgAuthWhatsThis": "Waar kan ik mijn organisatie-ID vinden?",
|
||||
"learnMore": "Meer informatie",
|
||||
"backToHome": "Ga terug naar startpagina",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Firewall ingeschakeld",
|
||||
"autoUpdatesEnabled": "Auto Updates Ingeschakeld",
|
||||
"tpmAvailable": "TPM beschikbaar",
|
||||
"windowsAntivirusEnabled": "Antivirus ingeschakeld",
|
||||
"macosSipEnabled": "Systeemintegriteitsbescherming (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Firewall Verberg Modus",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Toestel goedkeuringen inschakelen",
|
||||
"approvalsEmptyStateStep2Description": "Bewerk een rol en schakel de optie 'Vereist Apparaat Goedkeuringen' in. Gebruikers met deze rol hebben admin goedkeuring nodig voor nieuwe apparaten.",
|
||||
"approvalsEmptyStatePreviewDescription": "Voorbeeld: Indien ingeschakeld, zullen in afwachting van apparaatverzoeken hier verschijnen om te beoordelen",
|
||||
"approvalsEmptyStateButtonText": "Rollen beheren"
|
||||
"approvalsEmptyStateButtonText": "Rollen beheren",
|
||||
"domainErrorTitle": "We ondervinden problemen bij het controleren van uw domein",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configureer rolverrekening en organisatie beleid in het <policiesTabLink>Auto Provision Settings</policiesTabLink> tab.",
|
||||
"streamingTitle": "Event streaming",
|
||||
"streamingDescription": "Stream events van uw organisatie naar externe bestemmingen in realtime.",
|
||||
"streamingUnnamedDestination": "Naamloze bestemming",
|
||||
"streamingNoUrlConfigured": "Geen URL ingesteld",
|
||||
"streamingAddDestination": "Bestemming toevoegen",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookDescription": "Stuur gebeurtenissen naar elk HTTP eindpunt met flexibele authenticatie en template.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Stream events naar een S3-compatibele object-opslagemmer. Binnenkort beschikbaar.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Stuur gebeurtenissen rechtstreeks door naar je Datadog account. Binnenkort beschikbaar.",
|
||||
"streamingTypePickerDescription": "Kies een bestemmingstype om te beginnen.",
|
||||
"streamingFailedToLoad": "Laden van bestemmingen mislukt",
|
||||
"streamingUnexpectedError": "Er is een onverwachte fout opgetreden.",
|
||||
"streamingFailedToUpdate": "Bijwerken bestemming mislukt",
|
||||
"streamingDeletedSuccess": "Bestemming succesvol verwijderd",
|
||||
"streamingFailedToDelete": "Verwijderen van bestemming mislukt",
|
||||
"streamingDeleteTitle": "Verwijder bestemming",
|
||||
"streamingDeleteButtonText": "Verwijder bestemming",
|
||||
"streamingDeleteDialogAreYouSure": "Weet u zeker dat u wilt verwijderen",
|
||||
"streamingDeleteDialogThisDestination": "deze bestemming",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Alle configuratie zal permanent worden verwijderd.",
|
||||
"httpDestEditTitle": "Bewerk bestemming",
|
||||
"httpDestAddTitle": "Voeg HTTP bestemming toe",
|
||||
"httpDestEditDescription": "Werk de configuratie voor deze HTTP-event streaming bestemming bij.",
|
||||
"httpDestAddDescription": "Configureer een nieuw HTTP-eindpunt om de gebeurtenissen van uw organisatie te ontvangen.",
|
||||
"httpDestTabSettings": "Instellingen",
|
||||
"httpDestTabHeaders": "Kopteksten",
|
||||
"httpDestTabBody": "Lichaam",
|
||||
"httpDestTabLogs": "Logboeken",
|
||||
"httpDestNamePlaceholder": "Mijn HTTP-bestemming",
|
||||
"httpDestUrlLabel": "Bestemming URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL moet http of https gebruiken",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS is vereist op cloud implementaties",
|
||||
"httpDestUrlErrorInvalid": "Voer een geldige URL in (bijv. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Authenticatie",
|
||||
"httpDestAuthDescription": "Kies hoe verzoeken voor uw eindpunt zijn geverifieerd.",
|
||||
"httpDestAuthNoneTitle": "Geen authenticatie",
|
||||
"httpDestAuthNoneDescription": "Stuurt verzoeken zonder toestemmingskop.",
|
||||
"httpDestAuthBearerTitle": "Betere Token",
|
||||
"httpDestAuthBearerDescription": "Voegt een machtiging toe: Drager <token> header aan elke aanvraag.",
|
||||
"httpDestAuthBearerPlaceholder": "Uw API-sleutel of -token",
|
||||
"httpDestAuthBasicTitle": "Basis authenticatie",
|
||||
"httpDestAuthBasicDescription": "Voegt een Authorizatie toe: Basis <credentials> kop. Geef inloggegevens op als gebruikersnaam:wachtwoord.",
|
||||
"httpDestAuthBasicPlaceholder": "Gebruikersnaam:wachtwoord",
|
||||
"httpDestAuthCustomTitle": "Aangepaste koptekst",
|
||||
"httpDestAuthCustomDescription": "Specificeer een aangepaste HTTP header naam en waarde voor authenticatie (bijv. X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Header naam (bijv. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Header waarde",
|
||||
"httpDestCustomHeadersTitle": "Aangepaste HTTP Headers",
|
||||
"httpDestCustomHeadersDescription": "Voeg aangepaste headers toe aan elk uitgaande verzoek. Handig voor statische tokens of een aangepast Content-Type. Standaard Content-Type: application/json wordt verzonden.",
|
||||
"httpDestNoHeadersConfigured": "Geen aangepaste headers geconfigureerd. Klik op \"Header\" om er een toe te voegen.",
|
||||
"httpDestHeaderNamePlaceholder": "Naam koptekst",
|
||||
"httpDestHeaderValuePlaceholder": "Waarde",
|
||||
"httpDestAddHeader": "Koptekst toevoegen",
|
||||
"httpDestBodyTemplateTitle": "Aangepaste Body Sjabloon",
|
||||
"httpDestBodyTemplateDescription": "Bestuur de JSON payload structuur verzonden naar uw eindpunt. Indien uitgeschakeld, wordt een standaard JSON object verzonden voor elke event.",
|
||||
"httpDestEnableBodyTemplate": "Aangepaste lichaam sjabloon inschakelen",
|
||||
"httpDestBodyTemplateLabel": "Body sjabloon (JSON)",
|
||||
"httpDestBodyTemplateHint": "Gebruik sjabloonvariabelen om te verwijzen naar gebeurtenisvelden in uw payload.",
|
||||
"httpDestPayloadFormatTitle": "Payload formaat",
|
||||
"httpDestPayloadFormatDescription": "Hoe evenementen worden geserialiseerd in elk verzoeklichaam.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON matrix",
|
||||
"httpDestFormatJsonArrayDescription": "Eén verzoek per batch, lichaam is een JSON-array. Compatibel met de meeste algemene webhooks en Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Eén aanvraag per batch, lichaam is nieuwe JSON gescheiden - één object per regel, geen buitenste array. Vereist door Splunk HEC, Elastic / OpenSearch, en Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Eén afspraak per verzoek",
|
||||
"httpDestFormatSingleDescription": "Stuurt een aparte HTTP POST voor elk individueel event. Gebruik alleen voor eindpunten die geen batches kunnen verwerken.",
|
||||
"httpDestLogTypesTitle": "Log soorten",
|
||||
"httpDestLogTypesDescription": "Kies welke log types doorgestuurd worden naar deze bestemming. Alleen ingeschakelde log types worden gestreden.",
|
||||
"httpDestAccessLogsTitle": "Toegang tot logboek",
|
||||
"httpDestAccessLogsDescription": "Hulpbrontoegangspogingen, inclusief geauthenticeerde en weigerde aanvragen.",
|
||||
"httpDestActionLogsTitle": "Actie logs",
|
||||
"httpDestActionLogsDescription": "Administratieve acties uitgevoerd door gebruikers binnen de organisatie.",
|
||||
"httpDestConnectionLogsTitle": "Connectie Logs",
|
||||
"httpDestConnectionLogsDescription": "Verbinding met de Site en tunnel maken verbroken, inclusief verbindingen en verbindingen.",
|
||||
"httpDestRequestLogsTitle": "Logboeken aanvragen",
|
||||
"httpDestRequestLogsDescription": "HTTP request logs voor proxied hulpmiddelen, waaronder methode, pad en response code.",
|
||||
"httpDestSaveChanges": "Wijzigingen opslaan",
|
||||
"httpDestCreateDestination": "Maak bestemming aan",
|
||||
"httpDestUpdatedSuccess": "Bestemming succesvol bijgewerkt",
|
||||
"httpDestCreatedSuccess": "Bestemming succesvol aangemaakt",
|
||||
"httpDestUpdateFailed": "Bijwerken bestemming mislukt",
|
||||
"httpDestCreateFailed": "Aanmaken bestemming mislukt"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Jesteś członkiem {count, plural, =0 {żadna organizacja} one {jedna organizacja} few {# organizacje} many {# organizacji} other {# organizacji}}.",
|
||||
"componentsInvalidKey": "Wykryto nieprawidłowe lub wygasłe klucze licencyjne. Postępuj zgodnie z warunkami licencji, aby kontynuować korzystanie ze wszystkich funkcji.",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Dziękujemy za wsparcie Pangolina jako {tier}!",
|
||||
"inviteErrorNotValid": "Przykro nam, ale wygląda na to, że zaproszenie, do którego próbujesz uzyskać dostęp, nie zostało zaakceptowane lub jest już nieważne.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Utwórz link",
|
||||
"resourcesNotFound": "Nie znaleziono zasobów",
|
||||
"resourceSearch": "Szukaj zasobów",
|
||||
"machineSearch": "Wyszukiwarki",
|
||||
"machinesSearch": "Szukaj klientów maszyn...",
|
||||
"machineNotFound": "Nie znaleziono maszyn",
|
||||
"userDeviceSearch": "Szukaj urządzeń użytkownika",
|
||||
"userDevicesSearch": "Szukaj urządzeń użytkownika...",
|
||||
"openMenu": "Otwórz menu",
|
||||
"resource": "Zasoby",
|
||||
"title": "Tytuł",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxy zapytań przez HTTPS przy użyciu w pełni kwalifikowanej nazwy domeny.",
|
||||
"resourceRaw": "Surowy zasób TCP/UDP",
|
||||
"resourceRawDescription": "Proxy zapytań przez surowe TCP/UDP przy użyciu numeru portu.",
|
||||
"resourceRawDescriptionCloud": "Żądania proxy nad surowym TCP/UDP przy użyciu numeru portu. Wymaga stron aby połączyć się ze zdalnym węzłem.",
|
||||
"resourceCreate": "Utwórz zasób",
|
||||
"resourceCreateDescription": "Wykonaj poniższe kroki, aby utworzyć nowy zasób",
|
||||
"resourceSeeAll": "Zobacz wszystkie zasoby",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Wybierz protokół",
|
||||
"resourcePortNumber": "Numer portu",
|
||||
"resourcePortNumberDescription": "Numer portu zewnętrznego do żądań proxy.",
|
||||
"back": "Powrót",
|
||||
"cancel": "Anuluj",
|
||||
"resourceConfig": "Snippety konfiguracji",
|
||||
"resourceConfigDescription": "Skopiuj i wklej te fragmenty konfiguracji, aby skonfigurować zasób TCP/UDP",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Wystąpił błąd podczas usuwania organizacji.",
|
||||
"orgDeleted": "Organizacja usunięta",
|
||||
"orgDeletedMessage": "Organizacja i jej dane zostały usunięte.",
|
||||
"deleteAccount": "Usuń konto",
|
||||
"deleteAccountDescription": "Trwale usuń swoje konto, wszystkie organizacje, które posiadasz, oraz wszystkie dane w ramach tych organizacji. Tej operacji nie można cofnąć.",
|
||||
"deleteAccountButton": "Usuń konto",
|
||||
"deleteAccountConfirmTitle": "Usuń konto",
|
||||
"deleteAccountConfirmMessage": "Spowoduje to trwałe usunięcie konta, wszystkich organizacji, które posiadasz, oraz wszystkich danych w tych organizacjach. Tej operacji nie można cofnąć.",
|
||||
"deleteAccountConfirmString": "usuń konto",
|
||||
"deleteAccountSuccess": "Konto usunięte",
|
||||
"deleteAccountSuccessMessage": "Twoje konto zostało usunięte.",
|
||||
"deleteAccountError": "Nie udało się usunąć konta",
|
||||
"deleteAccountPreviewAccount": "Twoje konto",
|
||||
"deleteAccountPreviewOrgs": "Organizacje, które jesteś właścicielem (i wszystkie ich dane)",
|
||||
"orgMissing": "Brak ID organizacji",
|
||||
"orgMissingMessage": "Nie można ponownie wygenerować zaproszenia bez ID organizacji.",
|
||||
"accessUsersManage": "Zarządzaj użytkownikami",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Usuń klucz API",
|
||||
"apiKeysManage": "Zarządzaj kluczami API",
|
||||
"apiKeysDescription": "Klucze API służą do uwierzytelniania z API integracji",
|
||||
"provisioningKeysTitle": "Klucz Zaopatrzenia",
|
||||
"provisioningKeysManage": "Zarządzaj kluczami zaopatrzenia",
|
||||
"provisioningKeysDescription": "Klucze zaopatrzenia są używane do uwierzytelniania zautomatyzowanego zaopatrzenia twojej organizacji.",
|
||||
"provisioningManage": "Dostarczanie",
|
||||
"provisioningDescription": "Zarządzaj kluczami rezerwacji i sprawdzaj oczekujące strony oczekujące na zatwierdzenie.",
|
||||
"pendingSites": "Witryny oczekujące",
|
||||
"siteApproveSuccess": "Witryna została pomyślnie zatwierdzona",
|
||||
"siteApproveError": "Błąd zatwierdzania witryny",
|
||||
"provisioningKeys": "Klucze Zaopatrzenia",
|
||||
"searchProvisioningKeys": "Szukaj kluczy zaopatrzenia...",
|
||||
"provisioningKeysAdd": "Wygeneruj klucz zaopatrzenia",
|
||||
"provisioningKeysErrorDelete": "Błąd podczas usuwania klucza zaopatrzenia",
|
||||
"provisioningKeysErrorDeleteMessage": "Błąd podczas usuwania klucza zaopatrzenia",
|
||||
"provisioningKeysQuestionRemove": "Czy na pewno chcesz usunąć ten klucz rezerwacji z organizacji?",
|
||||
"provisioningKeysMessageRemove": "Po usunięciu, klucz nie może być już używany do tworzenia witryny.",
|
||||
"provisioningKeysDeleteConfirm": "Potwierdź usunięcie klucza zaopatrzenia",
|
||||
"provisioningKeysDelete": "Usuń klucz zaopatrzenia",
|
||||
"provisioningKeysCreate": "Wygeneruj klucz zaopatrzenia",
|
||||
"provisioningKeysCreateDescription": "Wygeneruj nowy klucz tworzenia rezerw dla organizacji",
|
||||
"provisioningKeysSeeAll": "Zobacz wszystkie klucze rezerwacji",
|
||||
"provisioningKeysSave": "Zapisz klucz zaopatrzenia",
|
||||
"provisioningKeysSaveDescription": "Możesz to zobaczyć tylko raz. Skopiuj je do bezpiecznego miejsca.",
|
||||
"provisioningKeysErrorCreate": "Błąd podczas tworzenia klucza zaopatrzenia",
|
||||
"provisioningKeysList": "Nowy klucz rezerwacji",
|
||||
"provisioningKeysMaxBatchSize": "Maksymalny rozmiar partii",
|
||||
"provisioningKeysUnlimitedBatchSize": "Nieograniczony rozmiar partii (bez limitu)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Nieograniczona",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Wprowadź poprawny maksymalny rozmiar partii (1–1 000,000).",
|
||||
"provisioningKeysValidUntil": "Ważny do",
|
||||
"provisioningKeysValidUntilHint": "Pozostaw puste, aby nie wygasnąć.",
|
||||
"provisioningKeysValidUntilInvalid": "Wprowadź prawidłową datę i godzinę.",
|
||||
"provisioningKeysNumUsed": "Używane czasy",
|
||||
"provisioningKeysLastUsed": "Ostatnio używane",
|
||||
"provisioningKeysNoExpiry": "Brak wygaśnięcia",
|
||||
"provisioningKeysNeverUsed": "Nigdy",
|
||||
"provisioningKeysEdit": "Edytuj klucz zaopatrzenia",
|
||||
"provisioningKeysEditDescription": "Zaktualizuj maksymalny rozmiar partii i czas wygaśnięcia dla tego klucza.",
|
||||
"provisioningKeysApproveNewSites": "Zatwierdź nowe witryny",
|
||||
"provisioningKeysApproveNewSitesDescription": "Automatycznie zatwierdzaj witryny, które rejestrują się za pomocą tego klucza.",
|
||||
"provisioningKeysUpdateError": "Błąd podczas aktualizacji klucza zaopatrzenia",
|
||||
"provisioningKeysUpdated": "Klucz zaopatrzenia zaktualizowany",
|
||||
"provisioningKeysUpdatedDescription": "Twoje zmiany zostały zapisane.",
|
||||
"provisioningKeysBannerTitle": "Klucze Zaopatrzenia witryny",
|
||||
"provisioningKeysBannerDescription": "Wygeneruj klucz tworzenia rezerw i użyj go z konektorem Newt do automatycznego tworzenia witryn przy pierwszym uruchomieniu — nie ma potrzeby ustawiania oddzielnych poświadczeń dla każdej witryny.",
|
||||
"provisioningKeysBannerButtonText": "Dowiedz się więcej",
|
||||
"pendingSitesBannerTitle": "Witryny oczekujące",
|
||||
"pendingSitesBannerDescription": "Witryny, które łączą się przy użyciu klucza zaopatrzenia, pojawiają się tutaj, aby przejrzeć. Zatwierdź każdą witrynę, zanim stanie się aktywna i uzyska dostęp do twoich zasobów.",
|
||||
"pendingSitesBannerButtonText": "Dowiedz się więcej",
|
||||
"apiKeysSettings": "Ustawienia {apiKeyName}",
|
||||
"userTitle": "Zarządzaj wszystkimi użytkownikami",
|
||||
"userDescription": "Zobacz i zarządzaj wszystkimi użytkownikami w systemie",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtruj według państwa zatwierdzenia",
|
||||
"approvalListEmpty": "Brak zatwierdzeń",
|
||||
"approvalState": "Państwo zatwierdzające",
|
||||
"approvalLoadMore": "Załaduj więcej",
|
||||
"loadingApprovals": "Wczytywanie zatwierdzeń",
|
||||
"approve": "Zatwierdź",
|
||||
"approved": "Zatwierdzone",
|
||||
"denied": "Odmowa",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Użytkownik zapisany",
|
||||
"userSavedDescription": "Użytkownik został zaktualizowany.",
|
||||
"autoProvisioned": "Przesłane automatycznie",
|
||||
"autoProvisionSettings": "Ustawienia automatycznego dostarczania",
|
||||
"autoProvisionedDescription": "Pozwól temu użytkownikowi na automatyczne zarządzanie przez dostawcę tożsamości",
|
||||
"accessControlsDescription": "Zarządzaj tym, do czego użytkownik ma dostęp i co może robić w organizacji",
|
||||
"accessControlsSubmit": "Zapisz kontrole dostępu",
|
||||
"singleRolePerUserPlanNotice": "Twój plan obsługuje tylko jedną rolę na użytkownika.",
|
||||
"singleRolePerUserEditionNotice": "Ta edycja obsługuje tylko jedną rolę na użytkownika.",
|
||||
"roles": "Role",
|
||||
"accessUsersRoles": "Zarządzaj użytkownikami i rolami",
|
||||
"accessUsersRolesDescription": "Zaproś użytkowników i dodaj je do ról do zarządzania dostępem do organizacji",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Nie udało się przełączyć zasobu",
|
||||
"resourcesErrorUpdateDescription": "Wystąpił błąd podczas aktualizacji zasobu",
|
||||
"access": "Dostęp",
|
||||
"accessControl": "Kontrola dostępu",
|
||||
"shareLink": "Link udostępniania {resource}",
|
||||
"resourceSelect": "Wybierz zasób",
|
||||
"shareLinks": "Linki udostępniania",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rola usunięta",
|
||||
"accessRoleRemovedDescription": "Rola została pomyślnie usunięta.",
|
||||
"accessRoleRequiredRemove": "Przed usunięciem tej roli, wybierz nową rolę do której zostaną przeniesieni obecni członkowie.",
|
||||
"network": "Sieć",
|
||||
"manage": "Zarządzaj",
|
||||
"sitesNotFound": "Nie znaleziono witryn.",
|
||||
"pangolinServerAdmin": "Administrator serwera - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Zwiększ liczbę witryn",
|
||||
"idpManage": "Zarządzaj dostawcami tożsamości",
|
||||
"idpManageDescription": "Wyświetl i zarządzaj dostawcami tożsamości w systemie",
|
||||
"idpGlobalModeBanner": "Dostawcy tożsamości (IdPs) na organizację są wyłączeni na tym serwerze. Używa globalnych IdP (współdzielonych ze wszystkimi organizacjami). Zarządzaj globalnymi IdP w panelu administracyjnym <adminPanelLink></adminPanelLink>. Aby włączyć IdP na organizację, edytuj konfigurację serwera i ustaw tryb IdP na org. <configDocsLink>Zobacz dokumentację</configDocsLink>. Jeśli chcesz nadal używać globalnych IdP i sprawić, że zniknie to z ustawień organizacji, wyraźnie ustaw tryb globalny w konfiguracji.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Dostawcy tożsamości (IdPs) na organizację są wyłączeni na tym serwerze. Używają globalnych IdP (współdzielonych między wszystkimi organizacjami). Zarządzaj globalnymi IdP w panelu administracyjnym <adminPanelLink></adminPanelLink>. Aby korzystać z dostawców tożsamości na organizację, musisz zaktualizować do edycji Enterprise.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Dostawcy tożsamości (IdPs) na organizację są wyłączeni na tym serwerze. Używają globalnych IdP (współdzielonych między wszystkimi organizacjami). Zarządzaj globalnymi IdP w panelu administracyjnym <adminPanelLink></adminPanelLink>. Aby korzystać z dostawców tożsamości na organizację, wymagana jest licencja Enterprise.",
|
||||
"idpDeletedDescription": "Dostawca tożsamości został pomyślnie usunięty",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Czy na pewno chcesz trwale usunąć dostawcę tożsamości?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Konfiguracja - Pangolin",
|
||||
"orgNameRequired": "Nazwa organizacji jest wymagana",
|
||||
"orgIdRequired": "ID organizacji jest wymagane",
|
||||
"orgIdMaxLength": "Identyfikator organizacji musi mieć co najwyżej 32 znaki",
|
||||
"orgErrorCreate": "Wystąpił błąd podczas tworzenia organizacji",
|
||||
"pageNotFound": "Nie znaleziono strony",
|
||||
"pageNotFoundDescription": "Ups! Strona, której szukasz, nie istnieje.",
|
||||
"overview": "Przegląd",
|
||||
"home": "Strona główna",
|
||||
"accessControl": "Kontrola dostępu",
|
||||
"settings": "Ustawienia",
|
||||
"usersAll": "Wszyscy użytkownicy",
|
||||
"license": "Licencja",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Pobierz użytkownika",
|
||||
"actionGetOrgUser": "Pobierz użytkownika organizacji",
|
||||
"actionListOrgDomains": "Lista domen organizacji",
|
||||
"actionGetDomain": "Pobierz domenę",
|
||||
"actionCreateOrgDomain": "Utwórz domenę",
|
||||
"actionUpdateOrgDomain": "Aktualizuj domenę",
|
||||
"actionDeleteOrgDomain": "Usuń domenę",
|
||||
"actionGetDNSRecords": "Pobierz rekordy DNS",
|
||||
"actionRestartOrgDomain": "Zrestartuj domenę",
|
||||
"actionCreateSite": "Utwórz witrynę",
|
||||
"actionDeleteSite": "Usuń witrynę",
|
||||
"actionGetSite": "Pobierz witrynę",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Wprowadź token konfiguracji z konsoli serwera.",
|
||||
"setupTokenRequired": "Wymagany jest token konfiguracji",
|
||||
"actionUpdateSite": "Aktualizuj witrynę",
|
||||
"actionResetSiteBandwidth": "Zresetuj przepustowość organizacji",
|
||||
"actionListSiteRoles": "Lista dozwolonych ról witryny",
|
||||
"actionCreateResource": "Utwórz zasób",
|
||||
"actionDeleteResource": "Usuń zasób",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Usuń użytkownika",
|
||||
"actionListUsers": "Lista użytkowników",
|
||||
"actionAddUserRole": "Dodaj rolę użytkownika",
|
||||
"actionSetUserOrgRoles": "Ustaw role użytkownika",
|
||||
"actionGenerateAccessToken": "Wygeneruj token dostępu",
|
||||
"actionDeleteAccessToken": "Usuń token dostępu",
|
||||
"actionListAccessTokens": "Lista tokenów dostępu",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Zobacz dzienniki",
|
||||
"noneSelected": "Nie wybrano",
|
||||
"orgNotFound2": "Nie znaleziono organizacji.",
|
||||
"searchProgress": "Szukaj...",
|
||||
"searchPlaceholder": "Szukaj...",
|
||||
"emptySearchOptions": "Nie znaleziono opcji",
|
||||
"create": "Utwórz",
|
||||
"orgs": "Organizacje",
|
||||
"loginError": "Wystąpił nieoczekiwany błąd. Spróbuj ponownie.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Prywatny",
|
||||
"sidebarAccessControl": "Kontrola dostępu",
|
||||
"sidebarLogsAndAnalytics": "Logi i Analityki",
|
||||
"sidebarTeam": "Drużyna",
|
||||
"sidebarUsers": "Użytkownicy",
|
||||
"sidebarAdmin": "Administrator",
|
||||
"sidebarInvitations": "Zaproszenia",
|
||||
"sidebarRoles": "Role",
|
||||
"sidebarShareableLinks": "Linki",
|
||||
"sidebarApiKeys": "Klucze API",
|
||||
"sidebarProvisioning": "Dostarczanie",
|
||||
"sidebarSettings": "Ustawienia",
|
||||
"sidebarAllUsers": "Wszyscy użytkownicy",
|
||||
"sidebarIdentityProviders": "Dostawcy tożsamości",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Dziennik & Analityka",
|
||||
"sidebarBluePrints": "Schematy",
|
||||
"sidebarOrganization": "Organizacja",
|
||||
"sidebarManagement": "Zarządzanie",
|
||||
"sidebarBillingAndLicenses": "Płatność i licencje",
|
||||
"sidebarLogsAnalytics": "Analityka",
|
||||
"blueprints": "Schematy",
|
||||
"blueprintsDescription": "Zastosuj konfiguracje deklaracyjne i wyświetl poprzednie operacje",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Przetworzona zawartość (tylko do odczytu)",
|
||||
"enableDockerSocket": "Włącz schemat dokera",
|
||||
"enableDockerSocketDescription": "Włącz etykietowanie kieszeni dokującej dla etykiet schematów. Ścieżka do gniazda musi być dostarczona do Newt.",
|
||||
"enableDockerSocketLink": "Dowiedz się więcej",
|
||||
"viewDockerContainers": "Zobacz kontenery dokujące",
|
||||
"containersIn": "Pojemniki w {siteName}",
|
||||
"selectContainerDescription": "Wybierz dowolny kontener do użycia jako nazwa hosta dla tego celu. Kliknij port, aby użyć portu.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Przestrzeń nazw: {namespace}",
|
||||
"domainPickerShowMore": "Pokaż więcej",
|
||||
"regionSelectorTitle": "Wybierz region",
|
||||
"domainPickerRemoteExitNodeWarning": "Podane domeny nie są obsługiwane, gdy witryny łączą się ze zdalnymi węzłami wyjścia. Aby zasoby były dostępne w węzłach zdalnych, użyj domeny niestandardowej.",
|
||||
"regionSelectorInfo": "Wybór regionu pomaga nam zapewnić lepszą wydajność dla Twojej lokalizacji. Nie musisz być w tym samym regionie co Twój serwer.",
|
||||
"regionSelectorPlaceholder": "Wybierz region",
|
||||
"regionSelectorComingSoon": "Wkrótce dostępne",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Przegląd Limitów Użytkowania",
|
||||
"billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@pangolin.net.",
|
||||
"billingDataUsage": "Użycie danych",
|
||||
"billingOnlineTime": "Czas Online Strony",
|
||||
"billingUsers": "Aktywni użytkownicy",
|
||||
"billingDomains": "Aktywne domeny",
|
||||
"billingRemoteExitNodes": "Aktywne samodzielnie-hostowane węzły",
|
||||
"billingSites": "Witryny",
|
||||
"billingUsers": "Użytkownicy",
|
||||
"billingDomains": "Domeny",
|
||||
"billingOrganizations": "O masie całkowitej pojazdu przekraczającej 5 ton, ale nieprzekraczającej 5 ton",
|
||||
"billingRemoteExitNodes": "Zdalne węzły",
|
||||
"billingNoLimitConfigured": "Nie skonfigurowano limitu",
|
||||
"billingEstimatedPeriod": "Szacowany Okres Rozliczeniowy",
|
||||
"billingIncludedUsage": "Zawarte użycie",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Nie udało się uzyskać adresu URL portalu",
|
||||
"billingPortalError": "Błąd Portalu",
|
||||
"billingDataUsageInfo": "Jesteś obciążony za wszystkie dane przesyłane przez bezpieczne tunele, gdy jesteś podłączony do chmury. Obejmuje to zarówno ruch przychodzący, jak i wychodzący we wszystkich Twoich witrynach. Gdy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie ograniczysz użycia. Dane nie będą naliczane przy użyciu węzłów.",
|
||||
"billingOnlineTimeInfo": "Opłata zależy od tego, jak długo twoje strony pozostają połączone z chmurą. Na przykład 44,640 minut oznacza jedną stronę działającą 24/7 przez cały miesiąc. Kiedy osiągniesz swój limit, twoje strony zostaną rozłączone, dopóki nie zaktualizujesz planu lub nie zmniejsz jego wykorzystania. Czas nie będzie naliczany przy użyciu węzłów.",
|
||||
"billingUsersInfo": "Opłata za każdego użytkownika w organizacji. Płatność jest obliczana codziennie na podstawie liczby aktywnych kont użytkowników w Twojej organizacji.",
|
||||
"billingDomainInfo": "Opłata za każdą domenę w organizacji. Płatność jest obliczana codziennie na podstawie liczby aktywnych kont domen w Twojej organizacji.",
|
||||
"billingRemoteExitNodesInfo": "Opłata za każdy zarządzany węzeł w organizacji. Płatność jest obliczana codziennie na podstawie liczby aktywnych zarządzanych węzłów w Twojej organizacji.",
|
||||
"billingSInfo": "Ile stron możesz użyć",
|
||||
"billingUsersInfo": "Ile użytkowników możesz użyć",
|
||||
"billingDomainInfo": "Ile domen możesz użyć",
|
||||
"billingRemoteExitNodesInfo": "Ile zdalnych węzłów możesz użyć",
|
||||
"billingLicenseKeys": "Klucze licencyjne",
|
||||
"billingLicenseKeysDescription": "Zarządzaj subskrypcjami kluczy licencyjnych",
|
||||
"billingLicenseSubscription": "Subskrypcja licencji",
|
||||
"billingInactive": "Nieaktywny",
|
||||
"billingLicenseItem": "Element licencji",
|
||||
"billingQuantity": "Ilość",
|
||||
"billingTotal": "łącznie",
|
||||
"billingModifyLicenses": "Modyfikuj subskrypcję licencji",
|
||||
"domainNotFound": "Nie znaleziono domeny",
|
||||
"domainNotFoundDescription": "Zasób jest wyłączony, ponieważ domena nie istnieje już w naszym systemie. Proszę ustawić nową domenę dla tego zasobu.",
|
||||
"failed": "Niepowodzenie",
|
||||
"createNewOrgDescription": "Utwórz nową organizację",
|
||||
"organization": "Organizacja",
|
||||
"primary": "Podstawowy",
|
||||
"port": "Port",
|
||||
"securityKeyManage": "Zarządzaj kluczami bezpieczeństwa",
|
||||
"securityKeyDescription": "Dodaj lub usuń klucze bezpieczeństwa do uwierzytelniania bez hasła",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Numer portu jest wymagany dla zasobów non-HTTP",
|
||||
"resourcePortNotAllowed": "Numer portu nie powinien być ustawiony dla zasobów HTTP",
|
||||
"billingPricingCalculatorLink": "Kalkulator Cen",
|
||||
"billingYourPlan": "Twój plan",
|
||||
"billingViewOrModifyPlan": "Wyświetl lub zmodyfikuj swój aktualny plan",
|
||||
"billingViewPlanDetails": "Zobacz szczegóły planu",
|
||||
"billingUsageAndLimits": "Stosowanie i ograniczenia",
|
||||
"billingViewUsageAndLimits": "Zobacz limity swojego planu i bieżące użycie",
|
||||
"billingCurrentUsage": "Bieżące użycie",
|
||||
"billingMaximumLimits": "Maksymalne limity",
|
||||
"billingRemoteNodes": "Zdalne węzły",
|
||||
"billingUnlimited": "Nieograniczona",
|
||||
"billingPaidLicenseKeys": "Płatne klucze licencyjne",
|
||||
"billingManageLicenseSubscription": "Zarządzaj subskrypcją płatnych własnych kluczy licencyjnych",
|
||||
"billingCurrentKeys": "Bieżące klucze",
|
||||
"billingModifyCurrentPlan": "Modyfikuj bieżący plan",
|
||||
"billingConfirmUpgrade": "Potwierdź aktualizację",
|
||||
"billingConfirmDowngrade": "Potwierdź obniżenie",
|
||||
"billingConfirmUpgradeDescription": "Zamierzasz ulepszyć swój plan. Przejrzyj nowe limity i ceny poniżej.",
|
||||
"billingConfirmDowngradeDescription": "Zamierzasz obniżyć swój plan. Przejrzyj nowe limity i ceny poniżej.",
|
||||
"billingPlanIncludes": "Plan zawiera",
|
||||
"billingProcessing": "Przetwarzanie...",
|
||||
"billingConfirmUpgradeButton": "Potwierdź aktualizację",
|
||||
"billingConfirmDowngradeButton": "Potwierdź obniżenie",
|
||||
"billingLimitViolationWarning": "Użycie przekracza nowe limity planu",
|
||||
"billingLimitViolationDescription": "Bieżące użycie przekracza limity tego planu. Po obniżeniu, wszystkie działania zostaną wyłączone, dopóki nie zmniejsz zużycia w ramach nowych limitów. Zapoznaj się z poniższymi funkcjami, które obecnie przekraczają limity. Limity naruszenia:",
|
||||
"billingFeatureLossWarning": "Powiadomienie o dostępności funkcji",
|
||||
"billingFeatureLossDescription": "Po obniżeniu wartości funkcje niedostępne w nowym planie zostaną automatycznie wyłączone. Niektóre ustawienia i konfiguracje mogą zostać utracone. Zapoznaj się z matrycą cenową, aby zrozumieć, które funkcje nie będą już dostępne.",
|
||||
"billingUsageExceedsLimit": "Bieżące użycie ({current}) przekracza limit ({limit})",
|
||||
"billingPastDueTitle": "Płatność w przeszłości",
|
||||
"billingPastDueDescription": "Twoja płatność jest zaległa. Zaktualizuj metodę płatności, aby kontynuować korzystanie z funkcji aktualnego planu. Jeśli nie zostanie rozwiązana, Twoja subskrypcja zostanie anulowana i zostaniesz przywrócony do darmowego poziomu.",
|
||||
"billingUnpaidTitle": "Subskrypcja niezapłacona",
|
||||
"billingUnpaidDescription": "Twoja subskrypcja jest niezapłacona i została przywrócona do darmowego poziomu. Zaktualizuj swoją metodę płatności, aby przywrócić subskrypcję.",
|
||||
"billingIncompleteTitle": "Płatność niezakończona",
|
||||
"billingIncompleteDescription": "Twoja płatność jest niekompletna. Ukończ proces płatności, aby aktywować subskrypcję.",
|
||||
"billingIncompleteExpiredTitle": "Płatność wygasła",
|
||||
"billingIncompleteExpiredDescription": "Twoja płatność nigdy nie została zakończona i wygasła. Zostałeś przywrócony do darmowego poziomu. Zapisz się ponownie, aby przywrócić dostęp do płatnych funkcji.",
|
||||
"billingManageSubscription": "Zarządzaj subskrypcją",
|
||||
"billingResolvePaymentIssue": "Rozwiąż problem z płatnościami przed aktualizacją lub obniżeniem oceny",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Zgadzam się z",
|
||||
"termsOfService": "warunkami usługi",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Czas w sekundach",
|
||||
"requireDeviceApproval": "Wymagaj zatwierdzenia urządzenia",
|
||||
"requireDeviceApprovalDescription": "Użytkownicy o tej roli potrzebują nowych urządzeń zatwierdzonych przez administratora, zanim będą mogli połączyć się i uzyskać dostęp do zasobów.",
|
||||
"sshAccess": "Dostęp SSH",
|
||||
"roleAllowSsh": "Zezwalaj na SSH",
|
||||
"roleAllowSshAllow": "Zezwól",
|
||||
"roleAllowSshDisallow": "Nie zezwalaj",
|
||||
"roleAllowSshDescription": "Zezwalaj użytkownikom z tej roli na łączenie się z zasobami za pomocą SSH. Gdy wyłączone, rola nie może korzystać z dostępu SSH.",
|
||||
"sshSudoMode": "Dostęp Sudo",
|
||||
"sshSudoModeNone": "Brak",
|
||||
"sshSudoModeNoneDescription": "Użytkownik nie może uruchamiać poleceń z sudo.",
|
||||
"sshSudoModeFull": "Pełne Sudo",
|
||||
"sshSudoModeFullDescription": "Użytkownik może uruchomić dowolne polecenie z sudo.",
|
||||
"sshSudoModeCommands": "Polecenia",
|
||||
"sshSudoModeCommandsDescription": "Użytkownik może uruchamiać tylko określone polecenia z sudo.",
|
||||
"sshSudo": "Zezwól na sudo",
|
||||
"sshSudoCommands": "Komendy Sudo",
|
||||
"sshSudoCommandsDescription": "Lista poleceń oddzielonych przecinkami, które użytkownik może uruchamiać z sudo.",
|
||||
"sshCreateHomeDir": "Utwórz katalog domowy",
|
||||
"sshUnixGroups": "Grupy Unix",
|
||||
"sshUnixGroupsDescription": "Oddzielone przecinkami grupy Unix, aby dodać użytkownika do docelowego hosta.",
|
||||
"retryAttempts": "Próby Ponowienia",
|
||||
"expectedResponseCodes": "Oczekiwane Kody Odpowiedzi",
|
||||
"expectedResponseCodesDescription": "Kod statusu HTTP, który wskazuje zdrowy status. Jeśli pozostanie pusty, uznaje się 200-300 za zdrowy.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Węzeł Wyjściowy",
|
||||
"country": "Kraj",
|
||||
"rulesMatchCountry": "Obecnie bazuje na adresie IP źródła",
|
||||
"region": "Region",
|
||||
"selectRegion": "Wybierz region",
|
||||
"searchRegions": "Szukaj regionów...",
|
||||
"noRegionFound": "Nie znaleziono regionu.",
|
||||
"rulesMatchRegion": "Wybierz regionalną grupę krajów",
|
||||
"rulesErrorInvalidRegion": "Nieprawidłowy region",
|
||||
"rulesErrorInvalidRegionDescription": "Proszę wybrać prawidłowy region.",
|
||||
"regionAfrica": "Afryka",
|
||||
"regionNorthernAfrica": "Afryka Północna",
|
||||
"regionEasternAfrica": "Afryka Wschodnia",
|
||||
"regionMiddleAfrica": "Afryka Środkowa",
|
||||
"regionSouthernAfrica": "Afryka Południowa",
|
||||
"regionWesternAfrica": "Afryka Zachodnia",
|
||||
"regionAmericas": "Ameryka",
|
||||
"regionCaribbean": "Karaiby",
|
||||
"regionCentralAmerica": "Ameryka Środkowa",
|
||||
"regionSouthAmerica": "Ameryka Południowej",
|
||||
"regionNorthernAmerica": "Ameryka Północna",
|
||||
"regionAsia": "Akwakultura",
|
||||
"regionCentralAsia": "Azja Środkowa",
|
||||
"regionEasternAsia": "Azja Wschodnia",
|
||||
"regionSouthEasternAsia": "Azja Południowo-Wschodnia",
|
||||
"regionSouthernAsia": "Azja Południowa",
|
||||
"regionWesternAsia": "Azja Zachodnia",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Europa Wschodnia",
|
||||
"regionNorthernEurope": "Europa Północna",
|
||||
"regionSouthernEurope": "Europa Południowa",
|
||||
"regionWesternEurope": "Europa Zachodnia",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Australia i Nowa Zelandia",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Zarządzane Samodzielnie-Hostingowane",
|
||||
"description": "Większa niezawodność i niska konserwacja serwera Pangolin z dodatkowymi dzwonkami i sygnałami",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Nieprawidłowa wartość",
|
||||
"idpTypeLabel": "Typ dostawcy tożsamości",
|
||||
"roleMappingExpressionPlaceholder": "np. zawiera(grupy, 'admin') && 'Admin' || 'Członek'",
|
||||
"roleMappingModeFixedRoles": "Stałe role",
|
||||
"roleMappingModeMappingBuilder": "Konstruktor mapowania",
|
||||
"roleMappingModeRawExpression": "Surowe wyrażenie",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Wybierz jedną lub więcej ról",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Wpisz nazwy ról (dopasowanie na organizację)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Przypisz tę samą rolę do każdego automatycznie udostępnionego użytkownika.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "W przypadku domyślnych zasad nazwy ról typu które istnieją w każdej organizacji, gdzie użytkownicy są zapisywani. Nazwy muszą się dokładnie zgadzać.",
|
||||
"roleMappingClaimPath": "Ścieżka przejęcia",
|
||||
"roleMappingClaimPathPlaceholder": "grupy",
|
||||
"roleMappingClaimPathDescription": "Ścieżka w payloadzie tokenów, która zawiera wartości źródłowe (np. grupy).",
|
||||
"roleMappingMatchValue": "Wartość dopasowania",
|
||||
"roleMappingAssignRoles": "Przypisz role",
|
||||
"roleMappingAddMappingRule": "Dodaj regułę mapowania",
|
||||
"roleMappingRawExpressionResultDescription": "Wyrażenie musi ocenić do tablicy ciągów lub ciągów.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Wyrażenie musi oceniać ciąg znaków (pojedyncza nazwa).",
|
||||
"roleMappingMatchValuePlaceholder": "Wartość dopasowania (na przykład: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Wpisz nazwy ról (aktywizacja na org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Nazwy ról muszą pasować do roli w każdej organizacji docelowej.",
|
||||
"roleMappingRemoveRule": "Usuń",
|
||||
"idpGoogleConfiguration": "Konfiguracja Google",
|
||||
"idpGoogleConfigurationDescription": "Skonfiguruj dane logowania Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Czy na pewno chcesz usunąć branding dla stron uwierzytelniania?",
|
||||
"authPageBrandingDeleteConfirm": "Potwierdź usunięcie brandingu",
|
||||
"brandingLogoURL": "URL logo",
|
||||
"brandingLogoURLOrPath": "Adres URL logo lub ścieżka",
|
||||
"brandingLogoPathDescription": "Wprowadź adres URL lub ścieżkę lokalną.",
|
||||
"brandingLogoURLDescription": "Wprowadź publicznie dostępny adres URL do obrazu logo.",
|
||||
"brandingPrimaryColor": "Główny kolor",
|
||||
"brandingLogoWidth": "Szerokość (piksele)",
|
||||
"brandingLogoHeight": "Wysokość (piksele)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Powrót do standardowego logowania",
|
||||
"orgAuthNoAccount": "Nie masz konta?",
|
||||
"subscriptionRequiredToUse": "Do korzystania z tej funkcji wymagana jest subskrypcja.",
|
||||
"mustUpgradeToUse": "Musisz uaktualnić subskrypcję, aby korzystać z tej funkcji.",
|
||||
"subscriptionRequiredTierToUse": "Ta funkcja wymaga funkcji <tierLink>{tier}</tierLink> lub wyższej.",
|
||||
"upgradeToTierToUse": "Aby skorzystać z tej funkcji, przejdź na <tierLink>{tier}</tierLink> lub wyższy pakiet.",
|
||||
"subscriptionTierTier1": "Strona główna",
|
||||
"subscriptionTierTier2": "Drużyna",
|
||||
"subscriptionTierTier3": "Biznes",
|
||||
"subscriptionTierEnterprise": "Przedsiębiorstwo",
|
||||
"idpDisabled": "Dostawcy tożsamości są wyłączeni",
|
||||
"orgAuthPageDisabled": "Strona autoryzacji organizacji jest wyłączona.",
|
||||
"domainRestartedDescription": "Weryfikacja domeny zrestartowana pomyślnie",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Uzyskaj licencję",
|
||||
"description": "Wybierz plan i powiedz nam, jak planujesz korzystać z Pangolin.",
|
||||
"chooseTier": "Wybierz swój plan",
|
||||
"viewPricingLink": "Zobacz cenniki, funkcje i limity",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Rozpocznij",
|
||||
"description": "Środki te przeznaczone są na pokrycie wydatków na personel i wydatków administracyjnych Agencji (tytuły 1 i 2) oraz jej wydatków operacyjnych (tytuł 3)."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Skala",
|
||||
"description": "Cechy przedsiębiorstw, 50 użytkowników, 50 obiektów i wsparcie priorytetowe."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Wyłącznie do użytku osobistego (bezpłatna licencja – brak zamówień)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Przejdź do zamówienia"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Błąd zamówienia",
|
||||
"description": "Nie można uruchomić zamówienia. Spróbuj ponownie."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Priorytet",
|
||||
"priorityDescription": "Najpierw oceniane są trasy priorytetowe. Priorytet = 100 oznacza automatyczne zamawianie (decyzje systemowe). Użyj innego numeru, aby wyegzekwować ręczny priorytet.",
|
||||
"instanceName": "Nazwa instancji",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Jak długo zachować dzienniki dostępu",
|
||||
"logRetentionActionLabel": "Zachowanie dziennika akcji",
|
||||
"logRetentionActionDescription": "Jak długo zachować dzienniki akcji",
|
||||
"logRetentionConnectionLabel": "Zachowanie dziennika połączeń",
|
||||
"logRetentionConnectionDescription": "Jak długo zachować dzienniki połączeń",
|
||||
"logRetentionDisabled": "Wyłączone",
|
||||
"logRetention3Days": "3 dni",
|
||||
"logRetention7Days": "7 dni",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Koniec następnego roku",
|
||||
"actionLogsDescription": "Zobacz historię działań wykonywanych w tej organizacji",
|
||||
"accessLogsDescription": "Wyświetl prośby o autoryzację dostępu do zasobów w tej organizacji",
|
||||
"licenseRequiredToUse": "Licencja Enterprise jest wymagana do korzystania z tej funkcji.",
|
||||
"connectionLogs": "Dzienniki połączeń",
|
||||
"connectionLogsDescription": "Wyświetl dzienniki połączeń dla tuneli w tej organizacji",
|
||||
"sidebarLogsConnection": "Dzienniki połączeń",
|
||||
"sidebarLogsStreaming": "Strumieniowanie",
|
||||
"sourceAddress": "Adres źródłowy",
|
||||
"destinationAddress": "Adres docelowy",
|
||||
"duration": "Czas trwania",
|
||||
"licenseRequiredToUse": "Do korzystania z tej funkcji wymagana jest licencja <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lub <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> . <bookADemoLink>Zarezerwuj wersję demonstracyjną lub wersję próbną POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> jest wymagany do korzystania z tej funkcji. Ta funkcja jest również dostępna w <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Zarezerwuj demo lub okres próbny POC</bookADemoLink>.",
|
||||
"certResolver": "Rozwiązywanie certyfikatów",
|
||||
"certResolverDescription": "Wybierz resolver certyfikatów do użycia dla tego zasobu.",
|
||||
"selectCertResolver": "Wybierz Resolver certyfikatów",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Kontrola dostępu",
|
||||
"editInternalResourceDialogAccessControlDescription": "Kontroluj, które role, użytkownicy i klienci maszyn mają dostęp do tego zasobu po połączeniu. Administratorzy zawsze mają dostęp.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Zakres portów musi być \"*\" dla wszystkich portów lub listą portów i zakresów oddzielonych przecinkami (np. \"80,443,8000-9000\"). Porty muszą znajdować się w przedziale od 1 do 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Auth Daemon Lokalizacja",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Wybierz, gdzie działa demon uwierzytelniania SSH: na stronie (Newt) lub na zdalnym serwerze.",
|
||||
"internalResourceAuthDaemonDescription": "Uwierzytelnianie SSH obsługuje podpisywanie klucza SSH i uwierzytelnianie PAM dla tego zasobu. Wybierz, czy działa na stronie (Newt), czy na oddzielnym serwerze zdalnym. Zobacz <docsLink>dokumentację</docsLink> dla więcej.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Wybierz strategię",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Lokalizacja",
|
||||
"internalResourceAuthDaemonSite": "Na stronie",
|
||||
"internalResourceAuthDaemonSiteDescription": "Demon Auth działa na stronie (nowy).",
|
||||
"internalResourceAuthDaemonRemote": "Zdalny host",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Demon Auth działa na serwerze, który nie jest stroną.",
|
||||
"internalResourceAuthDaemonPort": "Port Daemon (opcjonalnie)",
|
||||
"orgAuthWhatsThis": "Gdzie mogę znaleźć swój identyfikator organizacji?",
|
||||
"learnMore": "Dowiedz się więcej",
|
||||
"backToHome": "Wróć do strony głównej",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Zapora włączona",
|
||||
"autoUpdatesEnabled": "Automatyczne aktualizacje włączone",
|
||||
"tpmAvailable": "TPM dostępne",
|
||||
"windowsAntivirusEnabled": "Antywirus włączony",
|
||||
"macosSipEnabled": "Ochrona integralności systemu (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Tryb Stealth zapory",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Włącz zatwierdzanie urządzenia",
|
||||
"approvalsEmptyStateStep2Description": "Edytuj rolę i włącz opcję \"Wymagaj zatwierdzenia urządzenia\". Użytkownicy z tą rolą będą potrzebowali zatwierdzenia administratora dla nowych urządzeń.",
|
||||
"approvalsEmptyStatePreviewDescription": "Podgląd: Gdy włączone, oczekujące prośby o sprawdzenie pojawią się tutaj",
|
||||
"approvalsEmptyStateButtonText": "Zarządzaj rolami"
|
||||
"approvalsEmptyStateButtonText": "Zarządzaj rolami",
|
||||
"domainErrorTitle": "Mamy problem z weryfikacją Twojej domeny",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Skonfiguruj mapowanie ról i zasady organizacji na karcie <policiesTabLink>Auto Provivision Settings</policiesTabLink>.",
|
||||
"streamingTitle": "Strumieniowanie wydarzeń",
|
||||
"streamingDescription": "Wydarzenia strumieniowe z Twojej organizacji do zewnętrznych miejsc przeznaczenia w czasie rzeczywistym.",
|
||||
"streamingUnnamedDestination": "Miejsce przeznaczenia bez nazwy",
|
||||
"streamingNoUrlConfigured": "Brak skonfigurowanego adresu URL",
|
||||
"streamingAddDestination": "Dodaj cel",
|
||||
"streamingHttpWebhookTitle": "Webhook HTTP",
|
||||
"streamingHttpWebhookDescription": "Wyślij zdarzenia do dowolnego punktu końcowego HTTP z elastycznym uwierzytelnianiem i szablonem.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Zdarzenia strumieniowe do magazynu obiektów kompatybilnych z S3. Już wkrótce.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Przekaż wydarzenia bezpośrednio do Twojego konta Datadog. Już wkrótce.",
|
||||
"streamingTypePickerDescription": "Wybierz typ docelowy, aby rozpocząć.",
|
||||
"streamingFailedToLoad": "Nie udało się załadować miejsc docelowych",
|
||||
"streamingUnexpectedError": "Wystąpił nieoczekiwany błąd.",
|
||||
"streamingFailedToUpdate": "Nie udało się zaktualizować miejsca docelowego",
|
||||
"streamingDeletedSuccess": "Cel usunięty pomyślnie",
|
||||
"streamingFailedToDelete": "Nie udało się usunąć miejsca docelowego",
|
||||
"streamingDeleteTitle": "Usuń cel",
|
||||
"streamingDeleteButtonText": "Usuń cel",
|
||||
"streamingDeleteDialogAreYouSure": "Czy na pewno chcesz usunąć",
|
||||
"streamingDeleteDialogThisDestination": "ten cel",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Wszystkie konfiguracje zostaną trwale usunięte.",
|
||||
"httpDestEditTitle": "Edytuj cel",
|
||||
"httpDestAddTitle": "Dodaj cel HTTP",
|
||||
"httpDestEditDescription": "Aktualizuj konfigurację dla tego celu przesyłania strumieniowego zdarzeń HTTP.",
|
||||
"httpDestAddDescription": "Skonfiguruj nowy punkt końcowy HTTP, aby otrzymywać wydarzenia organizacji.",
|
||||
"httpDestTabSettings": "Ustawienia",
|
||||
"httpDestTabHeaders": "Nagłówki",
|
||||
"httpDestTabBody": "Ciało",
|
||||
"httpDestTabLogs": "Logi",
|
||||
"httpDestNamePlaceholder": "Mój cel HTTP",
|
||||
"httpDestUrlLabel": "Adres docelowy",
|
||||
"httpDestUrlErrorHttpRequired": "Adres URL musi używać http lub https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS jest wymagany dla wdrożenia w chmurze",
|
||||
"httpDestUrlErrorInvalid": "Wprowadź poprawny adres URL (np. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Uwierzytelnianie",
|
||||
"httpDestAuthDescription": "Wybierz sposób uwierzytelniania żądań do Twojego punktu końcowego.",
|
||||
"httpDestAuthNoneTitle": "Brak uwierzytelniania",
|
||||
"httpDestAuthNoneDescription": "Wysyła żądania bez nagłówka autoryzacji.",
|
||||
"httpDestAuthBearerTitle": "Token Bearer",
|
||||
"httpDestAuthBearerDescription": "Dodaje autoryzację: nagłówek Bearer <token> do każdego żądania.",
|
||||
"httpDestAuthBearerPlaceholder": "Twój klucz API lub token",
|
||||
"httpDestAuthBasicTitle": "Podstawowa Autoryzacja",
|
||||
"httpDestAuthBasicDescription": "Dodaje Autoryzacja: Nagłówek Basic <credentials> . Podaj poświadczenia jako nazwę użytkownika: hasło.",
|
||||
"httpDestAuthBasicPlaceholder": "Nazwa użytkownika:hasło",
|
||||
"httpDestAuthCustomTitle": "Niestandardowy nagłówek",
|
||||
"httpDestAuthCustomDescription": "Określ niestandardową nazwę nagłówka HTTP i wartość dla uwierzytelniania (np. X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Nazwa nagłówka (np. klucz X-API)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Wartość nagłówka",
|
||||
"httpDestCustomHeadersTitle": "Niestandardowe nagłówki HTTP",
|
||||
"httpDestCustomHeadersDescription": "Dodaj własne nagłówki do każdego wychodzącego żądania. Przydatne dla tokenów statycznych lub niestandardowego typu zawartości. Domyślnie Content-Type: aplikacja/json jest wysyłane.",
|
||||
"httpDestNoHeadersConfigured": "Nie skonfigurowano nagłówków niestandardowych. Kliknij \"Dodaj nagłówek\", aby go dodać.",
|
||||
"httpDestHeaderNamePlaceholder": "Nazwa nagłówka",
|
||||
"httpDestHeaderValuePlaceholder": "Wartość",
|
||||
"httpDestAddHeader": "Dodaj nagłówek",
|
||||
"httpDestBodyTemplateTitle": "Własny szablon ciała",
|
||||
"httpDestBodyTemplateDescription": "Kontroluj strukturę JSON wysyłaną do Twojego punktu końcowego. Jeśli wyłączone, dla każdego zdarzenia wysyłany jest domyślny obiekt JSON.",
|
||||
"httpDestEnableBodyTemplate": "Włącz niestandardowy szablon ciała",
|
||||
"httpDestBodyTemplateLabel": "Szablon ciała (JSON)",
|
||||
"httpDestBodyTemplateHint": "Użyj zmiennych szablonu do odniesienia pól zdarzeń w twoim payloadzie.",
|
||||
"httpDestPayloadFormatTitle": "Format obciążenia",
|
||||
"httpDestPayloadFormatDescription": "Jak zdarzenia są serializowane w każdym organie żądania.",
|
||||
"httpDestFormatJsonArrayTitle": "Tablica JSON",
|
||||
"httpDestFormatJsonArrayDescription": "Jedna prośba na partię, treść jest tablicą JSON. Kompatybilna z najbardziej ogólnymi webhookami i Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Jedno żądanie na partię, ciałem jest plik JSON rozdzielony na newline-delimited — jeden obiekt na wiersz, bez tablicy zewnętrznej. Wymagane przez Splunk HEC, Elastic / OpenSesearch i Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Jedno wydarzenie na żądanie",
|
||||
"httpDestFormatSingleDescription": "Wysyła oddzielny POST HTTP dla każdego zdarzenia. Użyj tylko dla punktów końcowych, które nie mogą obsługiwać partii.",
|
||||
"httpDestLogTypesTitle": "Typy logów",
|
||||
"httpDestLogTypesDescription": "Wybierz, które typy logów są przekazywane do tego miejsca docelowego. Tylko włączone typy logów będą strumieniowane.",
|
||||
"httpDestAccessLogsTitle": "Logi dostępu",
|
||||
"httpDestAccessLogsDescription": "Próby dostępu do zasobów, w tym uwierzytelnione i odrzucone żądania.",
|
||||
"httpDestActionLogsTitle": "Dzienniki działań",
|
||||
"httpDestActionLogsDescription": "Działania administracyjne wykonywane przez użytkowników w organizacji.",
|
||||
"httpDestConnectionLogsTitle": "Dzienniki połączeń",
|
||||
"httpDestConnectionLogsDescription": "Zdarzenia związane z miejscem i tunelem, w tym połączenia i rozłączenia.",
|
||||
"httpDestRequestLogsTitle": "Dzienniki żądań",
|
||||
"httpDestRequestLogsDescription": "Logi żądań HTTP dla zasobów proxy, w tym metody, ścieżki i kodu odpowiedzi.",
|
||||
"httpDestSaveChanges": "Zapisz zmiany",
|
||||
"httpDestCreateDestination": "Utwórz cel",
|
||||
"httpDestUpdatedSuccess": "Cel został pomyślnie zaktualizowany",
|
||||
"httpDestCreatedSuccess": "Cel został utworzony pomyślnie",
|
||||
"httpDestUpdateFailed": "Nie udało się zaktualizować miejsca docelowego",
|
||||
"httpDestCreateFailed": "Nie udało się utworzyć miejsca docelowego"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "É membro de {count, plural, =0 {nenhuma organização} one {uma organização} other {# organizações}}.",
|
||||
"componentsInvalidKey": "Chaves de licença inválidas ou expiradas detectadas. Siga os termos da licença para continuar usando todos os recursos.",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Obrigado por apoiar o Pangolin como um {tier}!",
|
||||
"inviteErrorNotValid": "Desculpe, mas parece que o convite que está a tentar aceder não foi aceito ou não é mais válido.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Criar Link",
|
||||
"resourcesNotFound": "Nenhum recurso encontrado",
|
||||
"resourceSearch": "Recursos de pesquisa",
|
||||
"machineSearch": "Procurar máquinas",
|
||||
"machinesSearch": "Pesquisar clientes de máquina...",
|
||||
"machineNotFound": "Nenhuma máquina encontrada",
|
||||
"userDeviceSearch": "Procurar dispositivos do usuário",
|
||||
"userDevicesSearch": "Pesquisar dispositivos do usuário...",
|
||||
"openMenu": "Abrir menu",
|
||||
"resource": "Recurso",
|
||||
"title": "Título",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Proxies requests sobre HTTPS usando um nome de domínio totalmente qualificado.",
|
||||
"resourceRaw": "Recurso TCP/UDP bruto",
|
||||
"resourceRawDescription": "Proxies solicitações sobre TCP/UDP bruto usando um número de porta.",
|
||||
"resourceRawDescriptionCloud": "Proxy solicita por TCP/UDP bruto usando um número de porta. Requer que sites se conectem a um nó remoto.",
|
||||
"resourceCreate": "Criar Recurso",
|
||||
"resourceCreateDescription": "Siga os passos abaixo para criar um novo recurso",
|
||||
"resourceSeeAll": "Ver todos os recursos",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Selecione um protocolo",
|
||||
"resourcePortNumber": "Número da Porta",
|
||||
"resourcePortNumberDescription": "O número da porta externa para requisições de proxy.",
|
||||
"back": "Anterior",
|
||||
"cancel": "cancelar",
|
||||
"resourceConfig": "Snippets de Configuração",
|
||||
"resourceConfigDescription": "Copie e cole estes snippets de configuração para configurar o recurso TCP/UDP",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Ocorreu um erro ao apagar a organização.",
|
||||
"orgDeleted": "Organização excluída",
|
||||
"orgDeletedMessage": "A organização e seus dados foram excluídos.",
|
||||
"deleteAccount": "Excluir Conta",
|
||||
"deleteAccountDescription": "Exclua permanentemente sua conta, todas as organizações que você possui e todos os dados nessas organizações. Isso não pode ser desfeito.",
|
||||
"deleteAccountButton": "Excluir Conta",
|
||||
"deleteAccountConfirmTitle": "Excluir Conta",
|
||||
"deleteAccountConfirmMessage": "Isto limpará permanentemente sua conta, todas as organizações que você possui e todos os dados dentro dessas organizações. Isso não pode ser desfeito.",
|
||||
"deleteAccountConfirmString": "excluir conta",
|
||||
"deleteAccountSuccess": "Conta excluída",
|
||||
"deleteAccountSuccessMessage": "Sua conta foi excluída.",
|
||||
"deleteAccountError": "Falha ao excluir conta",
|
||||
"deleteAccountPreviewAccount": "Sua conta",
|
||||
"deleteAccountPreviewOrgs": "Organizações que você possui (e todos os dados deles)",
|
||||
"orgMissing": "ID da Organização Ausente",
|
||||
"orgMissingMessage": "Não é possível regenerar o convite sem um ID de organização.",
|
||||
"accessUsersManage": "Gerir Utilizadores",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Excluir Chave API",
|
||||
"apiKeysManage": "Gerir Chaves API",
|
||||
"apiKeysDescription": "As chaves API são usadas para autenticar com a API de integração",
|
||||
"provisioningKeysTitle": "Chave de provisionamento",
|
||||
"provisioningKeysManage": "Gerenciar chaves de provisionamento",
|
||||
"provisioningKeysDescription": "Chaves de provisionamento são usadas para autenticar o provisionamento automatizado do site para sua organização.",
|
||||
"provisioningManage": "Provisionamento",
|
||||
"provisioningDescription": "Gerenciar chaves de provisionamento e revisar sites pendentes aguardando aprovação.",
|
||||
"pendingSites": "Sites pendentes",
|
||||
"siteApproveSuccess": "Site aprovado com sucesso",
|
||||
"siteApproveError": "Erro ao aprovar site",
|
||||
"provisioningKeys": "Posicionando chaves",
|
||||
"searchProvisioningKeys": "Pesquisar chaves de provisionamento...",
|
||||
"provisioningKeysAdd": "Gerar chave de provisionamento",
|
||||
"provisioningKeysErrorDelete": "Erro ao excluir chave de provisionamento",
|
||||
"provisioningKeysErrorDeleteMessage": "Erro ao excluir chave de provisionamento",
|
||||
"provisioningKeysQuestionRemove": "Tem certeza de que deseja remover esta chave de provisionamento da organização?",
|
||||
"provisioningKeysMessageRemove": "Uma vez removida, a chave não pode mais ser usada para o provisionamento do site.",
|
||||
"provisioningKeysDeleteConfirm": "Confirmar chave de exclusão",
|
||||
"provisioningKeysDelete": "Apagar chave de provisionamento",
|
||||
"provisioningKeysCreate": "Gerar chave de provisionamento",
|
||||
"provisioningKeysCreateDescription": "Gerar uma nova chave de provisionamento para a organização",
|
||||
"provisioningKeysSeeAll": "Ver todas as chaves provisionadas",
|
||||
"provisioningKeysSave": "Salvar a chave de provisionamento",
|
||||
"provisioningKeysSaveDescription": "Você só será capaz de ver esta vez. Copiá-lo para um lugar seguro.",
|
||||
"provisioningKeysErrorCreate": "Erro ao criar chave de provisionamento",
|
||||
"provisioningKeysList": "Nova chave de aprovisionamento",
|
||||
"provisioningKeysMaxBatchSize": "Tamanho máximo do lote",
|
||||
"provisioningKeysUnlimitedBatchSize": "Tamanho ilimitado em lote (sem limite)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Ilimitado",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Informe um tamanho máximo válido em lote (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Valido ate",
|
||||
"provisioningKeysValidUntilHint": "Deixe em branco para nenhuma expiração.",
|
||||
"provisioningKeysValidUntilInvalid": "Informe uma data e hora válidas.",
|
||||
"provisioningKeysNumUsed": "Use percentual",
|
||||
"provisioningKeysLastUsed": "Última utilização",
|
||||
"provisioningKeysNoExpiry": "Sem vencimento",
|
||||
"provisioningKeysNeverUsed": "nunca",
|
||||
"provisioningKeysEdit": "Editar chave de provisionamento",
|
||||
"provisioningKeysEditDescription": "Atualizar o tamanho máximo do lote e tempo de expiração para esta chave.",
|
||||
"provisioningKeysApproveNewSites": "Aprovar novos sites",
|
||||
"provisioningKeysApproveNewSitesDescription": "Aprovar automaticamente sites que se registram com esta chave.",
|
||||
"provisioningKeysUpdateError": "Erro ao atualizar chave de provisionamento",
|
||||
"provisioningKeysUpdated": "Chave de provisionamento atualizada",
|
||||
"provisioningKeysUpdatedDescription": "Suas alterações foram salvas.",
|
||||
"provisioningKeysBannerTitle": "Chaves de provisionamento do site",
|
||||
"provisioningKeysBannerDescription": "Gerar uma chave de provisionamento e usá-la com o conector de Newt para criar automaticamente sites na primeira inicialização — não é necessário configurar credenciais separadas para cada site.",
|
||||
"provisioningKeysBannerButtonText": "Saiba mais",
|
||||
"pendingSitesBannerTitle": "Sites pendentes",
|
||||
"pendingSitesBannerDescription": "Sites que conectam usando uma chave de provisionamento aparecem aqui para revisão. Aprovar cada site antes de se tornar ativo e ganhar acesso a seus recursos.",
|
||||
"pendingSitesBannerButtonText": "Saiba mais",
|
||||
"apiKeysSettings": "Configurações de {apiKeyName}",
|
||||
"userTitle": "Gerir Todos os Utilizadores",
|
||||
"userDescription": "Visualizar e gerir todos os utilizadores no sistema",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Filtrar por estado de aprovação",
|
||||
"approvalListEmpty": "Sem aprovações",
|
||||
"approvalState": "Estado de aprovação",
|
||||
"approvalLoadMore": "Carregue mais",
|
||||
"loadingApprovals": "Carregando aprovações",
|
||||
"approve": "Aprovar",
|
||||
"approved": "Aceito",
|
||||
"denied": "Negado",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Usuário salvo",
|
||||
"userSavedDescription": "O utilizador foi atualizado.",
|
||||
"autoProvisioned": "Auto provisionado",
|
||||
"autoProvisionSettings": "Configurações de provisão automática",
|
||||
"autoProvisionedDescription": "Permitir que este utilizador seja gerido automaticamente pelo provedor de identidade",
|
||||
"accessControlsDescription": "Gerir o que este utilizador pode aceder e fazer na organização",
|
||||
"accessControlsSubmit": "Guardar Controlos de Acesso",
|
||||
"singleRolePerUserPlanNotice": "Seu plano suporta apenas uma função por usuário.",
|
||||
"singleRolePerUserEditionNotice": "Esta edição suporta apenas uma função por usuário.",
|
||||
"roles": "Funções",
|
||||
"accessUsersRoles": "Gerir Utilizadores e Funções",
|
||||
"accessUsersRolesDescription": "Convidar usuários e adicioná-los a funções para gerenciar o acesso à organização",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Falha ao alternar recurso",
|
||||
"resourcesErrorUpdateDescription": "Ocorreu um erro ao atualizar o recurso",
|
||||
"access": "Acesso",
|
||||
"accessControl": "Controle de Acesso",
|
||||
"shareLink": "Link de Compartilhamento {resource}",
|
||||
"resourceSelect": "Selecionar recurso",
|
||||
"shareLinks": "Links de Compartilhamento",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Função removida",
|
||||
"accessRoleRemovedDescription": "A função foi removida com sucesso.",
|
||||
"accessRoleRequiredRemove": "Antes de apagar esta função, selecione uma nova função para transferir os membros existentes.",
|
||||
"network": "Rede",
|
||||
"manage": "Gerir",
|
||||
"sitesNotFound": "Nenhum site encontrado.",
|
||||
"pangolinServerAdmin": "Administrador do Servidor - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Aumentar contagem de sites",
|
||||
"idpManage": "Gerir Provedores de Identidade",
|
||||
"idpManageDescription": "Visualizar e gerir provedores de identidade no sistema",
|
||||
"idpGlobalModeBanner": "Provedores de identidade (Pds) por organização estão desabilitados neste servidor. Ele está usando IdPs globais (compartilhados entre todas as organizações). Gerencie IdPs no painel <adminPanelLink>admin</adminPanelLink>. Para habilitar IdPs por organização, edite a configuração do servidor e defina o modo IdP como org. <configDocsLink>Veja a documentação</configDocsLink>. Se quiser continuar usando IdPs globais e fazer isso desaparecer das configurações da organização, defina explicitamente o modo como global na configuração.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Os provedores de identidade (IdPs) por organização estão desativados neste servidor. Ele está usando IdPs globais (compartilhados entre todas as organizações). Gerencie os IdPs globais no <adminPanelLink>painel administrativo</adminPanelLink>. Para usar provedores de identidade por organização, você deve atualizar para a edição Enterprise.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Os provedores de identidade (IdPs) por organização estão desativados neste servidor. Ele está usando IdPs globais (compartilhados entre todas as organizações). Gerencie os IdPs globais no <adminPanelLink>painel administrativo</adminPanelLink>. Para usar provedores de identidade por organização, é necessário uma licença Enterprise.",
|
||||
"idpDeletedDescription": "Provedor de identidade eliminado com sucesso",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Tem certeza que deseja eliminar permanentemente o provedor de identidade?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Configuração - Pangolin",
|
||||
"orgNameRequired": "O nome da organização é obrigatório",
|
||||
"orgIdRequired": "O ID da organização é obrigatório",
|
||||
"orgIdMaxLength": "ID da organização deve ter no máximo 32 caracteres",
|
||||
"orgErrorCreate": "Ocorreu um erro ao criar a organização",
|
||||
"pageNotFound": "Página Não Encontrada",
|
||||
"pageNotFoundDescription": "Ops! A página que você está procurando não existe.",
|
||||
"overview": "Visão Geral",
|
||||
"home": "Início",
|
||||
"accessControl": "Controle de Acesso",
|
||||
"settings": "Configurações",
|
||||
"usersAll": "Todos os Utilizadores",
|
||||
"license": "Licença",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Obter Usuário",
|
||||
"actionGetOrgUser": "Obter Utilizador da Organização",
|
||||
"actionListOrgDomains": "Listar Domínios da Organização",
|
||||
"actionGetDomain": "Obter domínio",
|
||||
"actionCreateOrgDomain": "Criar domínio",
|
||||
"actionUpdateOrgDomain": "Atualizar domínio",
|
||||
"actionDeleteOrgDomain": "Excluir domínio",
|
||||
"actionGetDNSRecords": "Obter registros de DNS",
|
||||
"actionRestartOrgDomain": "Reiniciar domínio",
|
||||
"actionCreateSite": "Criar Site",
|
||||
"actionDeleteSite": "Eliminar Site",
|
||||
"actionGetSite": "Obter Site",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Digite o token de configuração do console do servidor.",
|
||||
"setupTokenRequired": "Token de configuração é necessário",
|
||||
"actionUpdateSite": "Atualizar Site",
|
||||
"actionResetSiteBandwidth": "Redefinir banda da organização",
|
||||
"actionListSiteRoles": "Listar Funções Permitidas do Site",
|
||||
"actionCreateResource": "Criar Recurso",
|
||||
"actionDeleteResource": "Eliminar Recurso",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Remover Utilizador",
|
||||
"actionListUsers": "Listar Utilizadores",
|
||||
"actionAddUserRole": "Adicionar Função ao Utilizador",
|
||||
"actionSetUserOrgRoles": "Definir funções do usuário",
|
||||
"actionGenerateAccessToken": "Gerar Token de Acesso",
|
||||
"actionDeleteAccessToken": "Eliminar Token de Acesso",
|
||||
"actionListAccessTokens": "Listar Tokens de Acesso",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Visualizar registros",
|
||||
"noneSelected": "Nenhum selecionado",
|
||||
"orgNotFound2": "Nenhuma organização encontrada.",
|
||||
"searchProgress": "Pesquisar...",
|
||||
"searchPlaceholder": "Buscar...",
|
||||
"emptySearchOptions": "Nenhuma opção encontrada",
|
||||
"create": "Criar",
|
||||
"orgs": "Organizações",
|
||||
"loginError": "Ocorreu um erro inesperado. Por favor, tente novamente.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Privado",
|
||||
"sidebarAccessControl": "Controle de Acesso",
|
||||
"sidebarLogsAndAnalytics": "Registros e Análises",
|
||||
"sidebarTeam": "Equipe",
|
||||
"sidebarUsers": "Utilizadores",
|
||||
"sidebarAdmin": "Administrador",
|
||||
"sidebarInvitations": "Convites",
|
||||
"sidebarRoles": "Papéis",
|
||||
"sidebarShareableLinks": "Links",
|
||||
"sidebarApiKeys": "Chaves API",
|
||||
"sidebarProvisioning": "Provisionamento",
|
||||
"sidebarSettings": "Configurações",
|
||||
"sidebarAllUsers": "Todos os utilizadores",
|
||||
"sidebarIdentityProviders": "Provedores de identidade",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Registo & Análise",
|
||||
"sidebarBluePrints": "Diagramas",
|
||||
"sidebarOrganization": "Organização",
|
||||
"sidebarManagement": "Gestão",
|
||||
"sidebarBillingAndLicenses": "Faturamento e Licenças",
|
||||
"sidebarLogsAnalytics": "Análises",
|
||||
"blueprints": "Diagramas",
|
||||
"blueprintsDescription": "Aplicar configurações declarativas e ver execuções anteriores",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Conteúdo analisado (Somente Leitura)",
|
||||
"enableDockerSocket": "Habilitar o Diagrama Docker",
|
||||
"enableDockerSocketDescription": "Ativar a scraping de rótulo Docker para rótulos de diagramas. Caminho de Socket deve ser fornecido para Newt.",
|
||||
"enableDockerSocketLink": "Saiba mais",
|
||||
"viewDockerContainers": "Ver contêineres Docker",
|
||||
"containersIn": "Contêineres em {siteName}",
|
||||
"selectContainerDescription": "Selecione qualquer contêiner para usar como hostname para este alvo. Clique em uma porta para usar uma porta.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Namespace: {namespace}",
|
||||
"domainPickerShowMore": "Mostrar Mais",
|
||||
"regionSelectorTitle": "Selecionar Região",
|
||||
"domainPickerRemoteExitNodeWarning": "Domínios fornecidos não são suportados quando os sites se conectam a nós de saída remota. Para recursos disponíveis em nós remotos, use um domínio personalizado.",
|
||||
"regionSelectorInfo": "Selecionar uma região nos ajuda a fornecer melhor desempenho para sua localização. Você não precisa estar na mesma região que seu servidor.",
|
||||
"regionSelectorPlaceholder": "Escolher uma região",
|
||||
"regionSelectorComingSoon": "Em breve",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Visão Geral dos Limites de Uso",
|
||||
"billingMonitorUsage": "Monitore seu uso em relação aos limites configurados. Se precisar aumentar esses limites, entre em contato conosco support@pangolin.net.",
|
||||
"billingDataUsage": "Uso de Dados",
|
||||
"billingOnlineTime": "Tempo Online do Site",
|
||||
"billingUsers": "Usuários Ativos",
|
||||
"billingDomains": "Domínios Ativos",
|
||||
"billingRemoteExitNodes": "Nodos Auto-Hospedados Ativos",
|
||||
"billingSites": "sites",
|
||||
"billingUsers": "Utilizadores",
|
||||
"billingDomains": "Domínios",
|
||||
"billingOrganizations": "Órgãos",
|
||||
"billingRemoteExitNodes": "Nós remotos",
|
||||
"billingNoLimitConfigured": "Nenhum limite configurado",
|
||||
"billingEstimatedPeriod": "Período Estimado de Cobrança",
|
||||
"billingIncludedUsage": "Uso Incluído",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Falha ao obter URL do portal",
|
||||
"billingPortalError": "Erro do Portal",
|
||||
"billingDataUsageInfo": "Você é cobrado por todos os dados transferidos através de seus túneis seguros quando conectado à nuvem. Isso inclui o tráfego de entrada e saída em todos os seus sites. Quando você atingir o seu limite, seus sites desconectarão até que você atualize seu plano ou reduza o uso. Os dados não serão cobrados ao usar os nós.",
|
||||
"billingOnlineTimeInfo": "Cobrança de acordo com o tempo em que seus sites permanecem conectados à nuvem. Por exemplo, 44,640 minutos é igual a um site que roda 24/7 para um mês inteiro. Quando você atinge o seu limite, seus sites desconectarão até que você faça o upgrade do seu plano ou reduza o uso. O tempo não é cobrado ao usar nós.",
|
||||
"billingUsersInfo": "A cobrança é feita por cada usuário na organização. A cobrança é feita diariamente com base no número de contas de usuário ativas na sua organização.",
|
||||
"billingDomainInfo": "A cobrança é feita por cada domínio da organização. A cobrança é feita diariamente com base no número de contas de domínio ativas na sua organização.",
|
||||
"billingRemoteExitNodesInfo": "Você é cobrado por cada nó gerenciado na organização. A cobrança é calculada diariamente com base no número de nós gerenciados ativos em sua organização.",
|
||||
"billingSInfo": "Quantos sites você pode usar",
|
||||
"billingUsersInfo": "Quantos usuários você pode usar",
|
||||
"billingDomainInfo": "Quantos domínios você pode usar",
|
||||
"billingRemoteExitNodesInfo": "Quantos nós remotos você pode usar",
|
||||
"billingLicenseKeys": "Chaves de Licença",
|
||||
"billingLicenseKeysDescription": "Gerenciar suas subscrições de chave de licença",
|
||||
"billingLicenseSubscription": "Assinatura de Licença",
|
||||
"billingInactive": "Inativo",
|
||||
"billingLicenseItem": "Item de Licença",
|
||||
"billingQuantity": "Quantidade",
|
||||
"billingTotal": "total:",
|
||||
"billingModifyLicenses": "Modificar assinatura de licença",
|
||||
"domainNotFound": "Domínio Não Encontrado",
|
||||
"domainNotFoundDescription": "Este recurso está desativado porque o domínio não existe mais em nosso sistema. Defina um novo domínio para este recurso.",
|
||||
"failed": "Falhou",
|
||||
"createNewOrgDescription": "Crie uma nova organização",
|
||||
"organization": "Organização",
|
||||
"primary": "Primário",
|
||||
"port": "Porta",
|
||||
"securityKeyManage": "Gerir chaves de segurança",
|
||||
"securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Número da porta é obrigatório para recursos não-HTTP",
|
||||
"resourcePortNotAllowed": "Número da porta não deve ser definido para recursos HTTP",
|
||||
"billingPricingCalculatorLink": "Calculadora de Preços",
|
||||
"billingYourPlan": "Seu plano",
|
||||
"billingViewOrModifyPlan": "Ver ou modificar seu plano atual",
|
||||
"billingViewPlanDetails": "Ver detalhes do plano",
|
||||
"billingUsageAndLimits": "Uso e Limites",
|
||||
"billingViewUsageAndLimits": "Ver os limites do seu plano e o uso atual",
|
||||
"billingCurrentUsage": "Uso atual",
|
||||
"billingMaximumLimits": "Limite Máximo",
|
||||
"billingRemoteNodes": "Nós remotos",
|
||||
"billingUnlimited": "Ilimitado",
|
||||
"billingPaidLicenseKeys": "Chaves de licença paga",
|
||||
"billingManageLicenseSubscription": "Gerencie sua assinatura para as chaves de licenças auto-hospedadas pagas",
|
||||
"billingCurrentKeys": "Chaves atuais",
|
||||
"billingModifyCurrentPlan": "Modificar o Plano Atual",
|
||||
"billingConfirmUpgrade": "Confirmar a atualização",
|
||||
"billingConfirmDowngrade": "Confirmar downgrade",
|
||||
"billingConfirmUpgradeDescription": "Você está prestes a atualizar seu plano. Revise os novos limites e preços abaixo.",
|
||||
"billingConfirmDowngradeDescription": "Você está prestes a fazer o downgrade do seu plano. Revise os novos limites e preços abaixo.",
|
||||
"billingPlanIncludes": "Plano Inclui",
|
||||
"billingProcessing": "Processandochar@@0",
|
||||
"billingConfirmUpgradeButton": "Confirmar a atualização",
|
||||
"billingConfirmDowngradeButton": "Confirmar downgrade",
|
||||
"billingLimitViolationWarning": "Uso excede novos limites de plano",
|
||||
"billingLimitViolationDescription": "Seu uso atual excede os limites deste plano. Após desclassificação, todas as ações serão desabilitadas até que você reduza o uso dentro dos novos limites. Por favor, reveja os recursos abaixo que atualmente estão acima dos limites. Limites de violação:",
|
||||
"billingFeatureLossWarning": "Aviso de disponibilidade de recursos",
|
||||
"billingFeatureLossDescription": "Ao fazer o downgrading, recursos não disponíveis no novo plano serão desativados automaticamente. Algumas configurações e configurações podem ser perdidas. Por favor, revise a matriz de preços para entender quais características não estarão mais disponíveis.",
|
||||
"billingUsageExceedsLimit": "Uso atual ({current}) excede o limite ({limit})",
|
||||
"billingPastDueTitle": "Pagamento passado devido",
|
||||
"billingPastDueDescription": "Seu pagamento está vencido. Por favor, atualize seu método de pagamento para continuar usando os recursos do seu plano atual. Se não for resolvido, sua assinatura será cancelada e você será revertido para o nível gratuito.",
|
||||
"billingUnpaidTitle": "Assinatura não paga",
|
||||
"billingUnpaidDescription": "Sua assinatura não foi paga e você voltou para o nível gratuito. Atualize o seu método de pagamento para restaurar sua assinatura.",
|
||||
"billingIncompleteTitle": "Pagamento Incompleto",
|
||||
"billingIncompleteDescription": "Seu pagamento está incompleto. Por favor, complete o processo de pagamento para ativar sua assinatura.",
|
||||
"billingIncompleteExpiredTitle": "Pagamento expirado",
|
||||
"billingIncompleteExpiredDescription": "Seu pagamento nunca foi concluído e expirou. Você foi revertido para o nível gratuito. Por favor, inscreva-se novamente para restaurar o acesso a recursos pagos.",
|
||||
"billingManageSubscription": "Gerencie sua assinatura",
|
||||
"billingResolvePaymentIssue": "Por favor, resolva seu problema de pagamento antes de atualizar ou rebaixar",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Concordo com",
|
||||
"termsOfService": "os termos de serviço",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "O tempo está em segundos",
|
||||
"requireDeviceApproval": "Exigir aprovação do dispositivo",
|
||||
"requireDeviceApprovalDescription": "Usuários com esta função precisam de novos dispositivos aprovados por um administrador antes que eles possam se conectar e acessar recursos.",
|
||||
"sshAccess": "Acesso SSH",
|
||||
"roleAllowSsh": "Permitir SSH",
|
||||
"roleAllowSshAllow": "Autorizar",
|
||||
"roleAllowSshDisallow": "Anular",
|
||||
"roleAllowSshDescription": "Permitir que usuários com esta função se conectem a recursos via SSH. Quando desativado, a função não pode usar o acesso SSH.",
|
||||
"sshSudoMode": "Acesso Sudo",
|
||||
"sshSudoModeNone": "Nenhuma",
|
||||
"sshSudoModeNoneDescription": "O usuário não pode executar comandos com o sudo.",
|
||||
"sshSudoModeFull": "Sudo Completo",
|
||||
"sshSudoModeFullDescription": "O usuário pode executar qualquer comando com sudo.",
|
||||
"sshSudoModeCommands": "Comandos",
|
||||
"sshSudoModeCommandsDescription": "Usuário só pode executar os comandos especificados com sudo.",
|
||||
"sshSudo": "Permitir sudo",
|
||||
"sshSudoCommands": "Comandos Sudo",
|
||||
"sshSudoCommandsDescription": "Lista separada por vírgulas de comandos que o usuário pode executar com sudo.",
|
||||
"sshCreateHomeDir": "Criar Diretório Inicial",
|
||||
"sshUnixGroups": "Grupos Unix",
|
||||
"sshUnixGroupsDescription": "Grupos Unix separados por vírgulas para adicionar o usuário no host alvo.",
|
||||
"retryAttempts": "Tentativas de Repetição",
|
||||
"expectedResponseCodes": "Códigos de Resposta Esperados",
|
||||
"expectedResponseCodesDescription": "Código de status HTTP que indica estado saudável. Se deixado em branco, 200-300 é considerado saudável.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Nodo de Saída",
|
||||
"country": "País",
|
||||
"rulesMatchCountry": "Atualmente baseado no IP de origem",
|
||||
"region": "Região",
|
||||
"selectRegion": "Selecionar região",
|
||||
"searchRegions": "Procurar regiões...",
|
||||
"noRegionFound": "Nenhuma região encontrada.",
|
||||
"rulesMatchRegion": "Selecione um grupo regional de países",
|
||||
"rulesErrorInvalidRegion": "Região inválida",
|
||||
"rulesErrorInvalidRegionDescription": "Por favor, selecione uma região válida.",
|
||||
"regionAfrica": "África",
|
||||
"regionNorthernAfrica": "África do Norte",
|
||||
"regionEasternAfrica": "África Oriental",
|
||||
"regionMiddleAfrica": "África Média",
|
||||
"regionSouthernAfrica": "África Austral",
|
||||
"regionWesternAfrica": "África Ocidental",
|
||||
"regionAmericas": "Américas",
|
||||
"regionCaribbean": "Caribe",
|
||||
"regionCentralAmerica": "América Central",
|
||||
"regionSouthAmerica": "América do Sul",
|
||||
"regionNorthernAmerica": "América do Norte",
|
||||
"regionAsia": "Ásia",
|
||||
"regionCentralAsia": "Ásia Central",
|
||||
"regionEasternAsia": "Ásia Oriental",
|
||||
"regionSouthEasternAsia": "Sudeste da Ásia",
|
||||
"regionSouthernAsia": "Sudeste da Ásia",
|
||||
"regionWesternAsia": "Ásia Ocidental",
|
||||
"regionEurope": "Europa",
|
||||
"regionEasternEurope": "Europa Oriental",
|
||||
"regionNorthernEurope": "Europa do Norte",
|
||||
"regionSouthernEurope": "Europa do Sul",
|
||||
"regionWesternEurope": "Europa Ocidental",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "Austrália e Nova Zelândia",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "Gerenciado Auto-Hospedado",
|
||||
"description": "Servidor Pangolin auto-hospedado mais confiável e com baixa manutenção com sinos extras e assobiamentos",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Valor Inválido",
|
||||
"idpTypeLabel": "Tipo de provedor de identidade",
|
||||
"roleMappingExpressionPlaceholder": "ex.: Contem (grupos, 'administrador') && 'Administrador' 「'Membro'",
|
||||
"roleMappingModeFixedRoles": "Papéis fixos",
|
||||
"roleMappingModeMappingBuilder": "Mapeando Construtor",
|
||||
"roleMappingModeRawExpression": "Expressão Bruta",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Selecione um ou mais papéis",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Digite o nome das funções (correspondência exata por organização)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Atribuir o mesmo conjunto de funções a cada usuário auto-provisionado.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Para políticas padrão, nomes de funções de tipo que existem em cada organização onde os usuários são fornecidos. Nomes devem coincidir exatamente.",
|
||||
"roleMappingClaimPath": "Caminho da Reivindicação",
|
||||
"roleMappingClaimPathPlaceholder": "grupos",
|
||||
"roleMappingClaimPathDescription": "Caminho no payload token que contém valores de origem (por exemplo, grupos).",
|
||||
"roleMappingMatchValue": "Valor Correspondente",
|
||||
"roleMappingAssignRoles": "Atribuir Papéis",
|
||||
"roleMappingAddMappingRule": "Adicionar regra de mapeamento",
|
||||
"roleMappingRawExpressionResultDescription": "Expressão deve retornar à matriz string ou string.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Expressão deve ser avaliada para uma string (um nome de função única).",
|
||||
"roleMappingMatchValuePlaceholder": "Valor do jogo (por exemplo: administrador)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Digite nomes de funções ((exact por org)",
|
||||
"roleMappingBuilderFreeformRowHint": "Nomes de papéis devem corresponder a um papel em cada organizaçãoalvo.",
|
||||
"roleMappingRemoveRule": "Remover",
|
||||
"idpGoogleConfiguration": "Configuração do Google",
|
||||
"idpGoogleConfigurationDescription": "Configurar as credenciais do Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Tem certeza de que deseja remover a marcação das Páginas de Autenticação?",
|
||||
"authPageBrandingDeleteConfirm": "Confirmar Exclusão de Marca",
|
||||
"brandingLogoURL": "URL do Logo",
|
||||
"brandingLogoURLOrPath": "URL ou caminho do logotipo",
|
||||
"brandingLogoPathDescription": "Insira uma URL ou um caminho local.",
|
||||
"brandingLogoURLDescription": "Digite uma URL publicamente acessível para a sua imagem do logotipo.",
|
||||
"brandingPrimaryColor": "Cor Primária",
|
||||
"brandingLogoWidth": "Largura (px)",
|
||||
"brandingLogoHeight": "Altura (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Voltar para entrada padrão",
|
||||
"orgAuthNoAccount": "Não possui uma conta?",
|
||||
"subscriptionRequiredToUse": "Uma assinatura é necessária para usar esse recurso.",
|
||||
"mustUpgradeToUse": "Você deve atualizar sua assinatura para usar este recurso.",
|
||||
"subscriptionRequiredTierToUse": "Esta função requer <tierLink>{tier}</tierLink> ou superior.",
|
||||
"upgradeToTierToUse": "Atualize para <tierLink>{tier}</tierLink> ou superior para usar este recurso.",
|
||||
"subscriptionTierTier1": "Residencial",
|
||||
"subscriptionTierTier2": "Equipe",
|
||||
"subscriptionTierTier3": "Empresas",
|
||||
"subscriptionTierEnterprise": "Empresa",
|
||||
"idpDisabled": "Provedores de identidade estão desabilitados.",
|
||||
"orgAuthPageDisabled": "A página de autenticação da organização está desativada.",
|
||||
"domainRestartedDescription": "Verificação de domínio reiniciado com sucesso",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Obtenha uma licença",
|
||||
"description": "Escolha um plano e nos diga como você planeja usar o Pangolin.",
|
||||
"chooseTier": "Escolha seu plano",
|
||||
"viewPricingLink": "Veja os preços, recursos e limites",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Iniciante",
|
||||
"description": "Recursos de empresa, 25 usuários, 25 sites e apoio da comunidade."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Escala",
|
||||
"description": "Recursos de empresa, 50 usuários, 50 sites e apoio prioritário."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Apenas uso pessoal (licença gratuita — sem check-out)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Continuar com checkout"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Erro no check-out",
|
||||
"description": "Não foi possível iniciar o checkout. Por favor, tente novamente."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Prioridade",
|
||||
"priorityDescription": "Rotas de alta prioridade são avaliadas primeiro. Prioridade = 100 significa ordem automática (decisões do sistema). Use outro número para aplicar prioridade manual.",
|
||||
"instanceName": "Nome da Instância",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Por quanto tempo manter os registros de acesso",
|
||||
"logRetentionActionLabel": "Ação de Retenção no Log",
|
||||
"logRetentionActionDescription": "Por quanto tempo manter os registros de ação",
|
||||
"logRetentionConnectionLabel": "Retenção de registro de conexão",
|
||||
"logRetentionConnectionDescription": "Por quanto tempo manter os registros de conexão",
|
||||
"logRetentionDisabled": "Desabilitado",
|
||||
"logRetention3Days": "3 dias",
|
||||
"logRetention7Days": "7 dias",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Fim do ano seguinte",
|
||||
"actionLogsDescription": "Visualizar histórico de ações realizadas nesta organização",
|
||||
"accessLogsDescription": "Ver solicitações de autenticação de recursos nesta organização",
|
||||
"licenseRequiredToUse": "É necessária uma licença empresarial para usar esse recurso.",
|
||||
"connectionLogs": "Logs da conexão",
|
||||
"connectionLogsDescription": "Ver logs de conexão para túneis nesta organização",
|
||||
"sidebarLogsConnection": "Logs da conexão",
|
||||
"sidebarLogsStreaming": "Transmitindo",
|
||||
"sourceAddress": "Endereço de origem",
|
||||
"destinationAddress": "Endereço de destino",
|
||||
"duration": "Duração",
|
||||
"licenseRequiredToUse": "Uma licença <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> ou <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> é necessária para usar este recurso. <bookADemoLink>Reserve um teste de demonstração ou POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "O <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> é necessário para usar este recurso. Este recurso também está disponível no <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Reserve uma demonstração ou avaliação POC</bookADemoLink>.",
|
||||
"certResolver": "Resolvedor de Certificado",
|
||||
"certResolverDescription": "Selecione o resolvedor de certificados para este recurso.",
|
||||
"selectCertResolver": "Selecionar solucionador de certificado",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Controle de Acesso",
|
||||
"editInternalResourceDialogAccessControlDescription": "Controle quais funções, usuários e clientes de máquina podem acessar este recurso quando conectados. Os administradores sempre têm acesso.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "O intervalo de portas deve ser \"*\" para todas as portas, ou uma lista de portas e intervalos separados por vírgulas (por exemplo, \"80,443,8000-9000\"). As portas devem estar entre 1 e 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "Local do Daemon de autenticação SSH",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Escolha onde o daemon de autenticação SSH funciona: no site (Newt) ou em um host remoto.",
|
||||
"internalResourceAuthDaemonDescription": "A autenticação SSH daemon lida com assinatura de chave SSH e autenticação PAM para este recurso. Escolha se ele é executado no site (Newt) ou em um host remoto separado. Veja <docsLink>a documentação</docsLink> para mais informações.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Selecione a estratégia",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Local:",
|
||||
"internalResourceAuthDaemonSite": "No Site",
|
||||
"internalResourceAuthDaemonSiteDescription": "O serviço de autenticação é executado no site (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Host Remoto",
|
||||
"internalResourceAuthDaemonRemoteDescription": "O serviço de autenticação é executado em um host que não é o site.",
|
||||
"internalResourceAuthDaemonPort": "Porta do Daemon (opcional)",
|
||||
"orgAuthWhatsThis": "Onde posso encontrar meu ID da organização?",
|
||||
"learnMore": "Saiba mais",
|
||||
"backToHome": "Voltar para a página inicial",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Firewall habilitado",
|
||||
"autoUpdatesEnabled": "Atualizações Automáticas Habilitadas",
|
||||
"tpmAvailable": "TPM disponível",
|
||||
"windowsAntivirusEnabled": "Antivírus habilitado",
|
||||
"macosSipEnabled": "Proteção da Integridade do Sistema (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Modo Furtivo do Firewall",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Habilitar Aprovações do Dispositivo",
|
||||
"approvalsEmptyStateStep2Description": "Editar uma função e habilitar a opção 'Exigir aprovação de dispositivos'. Usuários com essa função precisarão de aprovação de administrador para novos dispositivos.",
|
||||
"approvalsEmptyStatePreviewDescription": "Pré-visualização: Quando ativado, solicitações de dispositivo pendentes aparecerão aqui para revisão",
|
||||
"approvalsEmptyStateButtonText": "Gerir Funções"
|
||||
"approvalsEmptyStateButtonText": "Gerir Funções",
|
||||
"domainErrorTitle": "Estamos tendo problemas ao verificar seu domínio",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configurar funções de mapeamento e políticas de organização na aba <policiesTabLink>Auto Provision Settings</policiesTabLink>.",
|
||||
"streamingTitle": "Streaming do Evento",
|
||||
"streamingDescription": "Transmita eventos de sua organização para destinos externos em tempo real.",
|
||||
"streamingUnnamedDestination": "Destino sem nome",
|
||||
"streamingNoUrlConfigured": "Nenhuma URL configurada",
|
||||
"streamingAddDestination": "Adicionar destino",
|
||||
"streamingHttpWebhookTitle": "Webhook HTTP",
|
||||
"streamingHttpWebhookDescription": "Envie os eventos para qualquer endpoint HTTP com autenticação flexível e modelo.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Transmitir eventos para um balde de armazenamento de objetos compatível com S3. Em breve.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Encaminha eventos diretamente para a sua conta no Datadog. Em breve.",
|
||||
"streamingTypePickerDescription": "Escolha um tipo de destino para começar.",
|
||||
"streamingFailedToLoad": "Falha ao carregar destinos",
|
||||
"streamingUnexpectedError": "Ocorreu um erro inesperado.",
|
||||
"streamingFailedToUpdate": "Falha ao atualizar destino",
|
||||
"streamingDeletedSuccess": "Destino apagado com sucesso",
|
||||
"streamingFailedToDelete": "Falha ao excluir destino",
|
||||
"streamingDeleteTitle": "Excluir destino",
|
||||
"streamingDeleteButtonText": "Excluir destino",
|
||||
"streamingDeleteDialogAreYouSure": "Tem certeza de que deseja excluir",
|
||||
"streamingDeleteDialogThisDestination": "este destino",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Todas as configurações serão permanentemente removidas.",
|
||||
"httpDestEditTitle": "Editar destino",
|
||||
"httpDestAddTitle": "Adicionar Destino HTTP",
|
||||
"httpDestEditDescription": "Atualizar a configuração para este destino de transmissão de eventos HTTP.",
|
||||
"httpDestAddDescription": "Configure um novo ponto de extremidade HTTP para receber eventos da sua organização.",
|
||||
"httpDestTabSettings": "Confirgurações",
|
||||
"httpDestTabHeaders": "Cabeçalhos",
|
||||
"httpDestTabBody": "Conteúdo",
|
||||
"httpDestTabLogs": "Registros",
|
||||
"httpDestNamePlaceholder": "Meu destino HTTP",
|
||||
"httpDestUrlLabel": "URL de destino",
|
||||
"httpDestUrlErrorHttpRequired": "A URL deve usar http ou https",
|
||||
"httpDestUrlErrorHttpsRequired": "HTTPS é necessário em implantações em nuvem",
|
||||
"httpDestUrlErrorInvalid": "Informe uma URL válida (por exemplo, https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Autenticação",
|
||||
"httpDestAuthDescription": "Escolha como os pedidos para seu endpoint são autenticados.",
|
||||
"httpDestAuthNoneTitle": "Sem Autenticação",
|
||||
"httpDestAuthNoneDescription": "Envia pedidos sem um cabeçalho de autorização.",
|
||||
"httpDestAuthBearerTitle": "Token do portador",
|
||||
"httpDestAuthBearerDescription": "Adiciona uma autorização: Bearer <token> header a cada requisição.",
|
||||
"httpDestAuthBearerPlaceholder": "Sua chave de API ou token",
|
||||
"httpDestAuthBasicTitle": "Autenticação básica",
|
||||
"httpDestAuthBasicDescription": "Adiciona uma Autorização: cabeçalho <credentials> básico. Forneça credenciais como nome de usuário:senha.",
|
||||
"httpDestAuthBasicPlaceholder": "Usuário:password",
|
||||
"httpDestAuthCustomTitle": "Cabeçalho personalizado",
|
||||
"httpDestAuthCustomDescription": "Especifique um nome e valor de cabeçalho HTTP personalizado para autenticação (por exemplo, X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Nome do cabeçalho (ex: X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Valor do cabeçalho",
|
||||
"httpDestCustomHeadersTitle": "Cabeçalhos HTTP personalizados",
|
||||
"httpDestCustomHeadersDescription": "Adicionar cabeçalhos personalizados a todas as solicitações de saída. Útil para tokens estáticos ou um tipo de conteúdo personalizado. Por padrão, Content-Type: application/json é enviado.",
|
||||
"httpDestNoHeadersConfigured": "Nenhum cabeçalho personalizado configurado. Clique em \"Adicionar Cabeçalho\" para adicionar um.",
|
||||
"httpDestHeaderNamePlaceholder": "Nome do Cabeçalho",
|
||||
"httpDestHeaderValuePlaceholder": "Valor",
|
||||
"httpDestAddHeader": "Adicionar Cabeçalho",
|
||||
"httpDestBodyTemplateTitle": "Modelo de corpo personalizado",
|
||||
"httpDestBodyTemplateDescription": "Controla a estrutura de carga JSON enviada ao seu endpoint. Se desativado, um objeto JSON padrão é enviado para cada evento.",
|
||||
"httpDestEnableBodyTemplate": "Ativar modelo personalizado de corpo",
|
||||
"httpDestBodyTemplateLabel": "Modelo de corpo (JSON)",
|
||||
"httpDestBodyTemplateHint": "Use variáveis de template para referenciar campos de evento em seu payload.",
|
||||
"httpDestPayloadFormatTitle": "Formato de carga",
|
||||
"httpDestPayloadFormatDescription": "Como os eventos são serializados em cada corpo do pedido.",
|
||||
"httpDestFormatJsonArrayTitle": "Matriz JSON",
|
||||
"httpDestFormatJsonArrayDescription": "Um pedido por lote, o corpo é um array JSON. Compatível com a maioria dos webhooks genéricos e Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Um pedido por lote, o corpo é um JSON delimitado por nova-linha — um objeto por linha, sem array exterior. Requerido pelo Splunk HEC, Elástico / OpenSearch, e Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Um Evento por Requisição",
|
||||
"httpDestFormatSingleDescription": "Envia um POST HTTP separado para cada evento. Utilize apenas para endpoints que não podem manipular lotes.",
|
||||
"httpDestLogTypesTitle": "Tipos de log",
|
||||
"httpDestLogTypesDescription": "Escolha quais tipos de log são encaminhados para este destino. Somente serão racionalizados os tipos de logs habilitados.",
|
||||
"httpDestAccessLogsTitle": "Logs de Acesso",
|
||||
"httpDestAccessLogsDescription": "Tentativas de acesso a recursos, incluindo solicitações autenticadas e negadas.",
|
||||
"httpDestActionLogsTitle": "Logs de Ações",
|
||||
"httpDestActionLogsDescription": "Ações administrativas realizadas por usuários dentro da organização.",
|
||||
"httpDestConnectionLogsTitle": "Logs da conexão",
|
||||
"httpDestConnectionLogsDescription": "Eventos de conexão de site e túnel, incluindo conexões e desconexões.",
|
||||
"httpDestRequestLogsTitle": "Registro de pedidos",
|
||||
"httpDestRequestLogsDescription": "Logs de solicitação HTTP para recursos proxy incluindo o método, o caminho e o código de resposta.",
|
||||
"httpDestSaveChanges": "Salvar as alterações",
|
||||
"httpDestCreateDestination": "Criar destino",
|
||||
"httpDestUpdatedSuccess": "Destino atualizado com sucesso",
|
||||
"httpDestCreatedSuccess": "Destino criado com sucesso",
|
||||
"httpDestUpdateFailed": "Falha ao atualizar destino",
|
||||
"httpDestCreateFailed": "Falha ao criar destino"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "Вы состоите в {count, plural, =0 {0 организациях} one {# организации} few {# организациях} many {# организациях} other {# организациях}}.",
|
||||
"componentsInvalidKey": "Обнаружены недействительные или просроченные лицензионные ключи. Соблюдайте условия лицензии для использования всех функций.",
|
||||
"dismiss": "Отменить",
|
||||
"subscriptionViolationMessage": "Вы превысили лимиты для вашего текущего плана. Исправьте проблему, удалив сайты, пользователей или другие ресурсы, чтобы остаться в пределах вашего плана.",
|
||||
"subscriptionViolationViewBilling": "Просмотр биллинга",
|
||||
"componentsLicenseViolation": "Нарушение лицензии: Сервер использует {usedSites} сайтов, что превышает лицензионный лимит в {maxSites} сайтов. Соблюдайте условия лицензии для использования всех функций.",
|
||||
"componentsSupporterMessage": "Спасибо за поддержку Pangolin в качестве {tier}!",
|
||||
"inviteErrorNotValid": "Извините, но это приглашение не было принято или срок его действия истёк.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Создать ссылку",
|
||||
"resourcesNotFound": "Ресурсы не найдены",
|
||||
"resourceSearch": "Поиск ресурсов",
|
||||
"machineSearch": "Поиск машин",
|
||||
"machinesSearch": "Поиск клиентов машины...",
|
||||
"machineNotFound": "Машины не найдены",
|
||||
"userDeviceSearch": "Поиск устройств пользователя",
|
||||
"userDevicesSearch": "Поиск устройств пользователя...",
|
||||
"openMenu": "Открыть меню",
|
||||
"resource": "Ресурс",
|
||||
"title": "Заголовок",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Проксировать запросы через HTTPS с использованием полного доменного имени.",
|
||||
"resourceRaw": "Сырой TCP/UDP-ресурс",
|
||||
"resourceRawDescription": "Проксировать запросы по сырому TCP/UDP с использованием номера порта.",
|
||||
"resourceRawDescriptionCloud": "Прокси запросы через необработанный TCP/UDP с использованием номера порта. Требуется подключение сайтов к удаленному узлу.",
|
||||
"resourceCreate": "Создание ресурса",
|
||||
"resourceCreateDescription": "Следуйте инструкциям ниже для создания нового ресурса",
|
||||
"resourceSeeAll": "Посмотреть все ресурсы",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Выберите протокол",
|
||||
"resourcePortNumber": "Номер порта",
|
||||
"resourcePortNumberDescription": "Внешний номер порта для проксирования запросов.",
|
||||
"back": "Назад",
|
||||
"cancel": "Отмена",
|
||||
"resourceConfig": "Фрагменты конфигурации",
|
||||
"resourceConfigDescription": "Скопируйте и вставьте эти сниппеты для настройки TCP/UDP ресурса",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Произошла ошибка при удалении организации.",
|
||||
"orgDeleted": "Организация удалена",
|
||||
"orgDeletedMessage": "Организация и её данные были удалены.",
|
||||
"deleteAccount": "Удалить аккаунт",
|
||||
"deleteAccountDescription": "Окончательно удалить учетную запись, все организации, которые вы владеете, и все данные этих организаций не могут быть отменены.",
|
||||
"deleteAccountButton": "Удалить аккаунт",
|
||||
"deleteAccountConfirmTitle": "Удалить аккаунт",
|
||||
"deleteAccountConfirmMessage": "Это очистит ваш аккаунт, все организации, которым вы владеете, и все данные этих организаций не могут быть отменены.",
|
||||
"deleteAccountConfirmString": "удалить аккаунт",
|
||||
"deleteAccountSuccess": "Учетная запись удалена",
|
||||
"deleteAccountSuccessMessage": "Ваша учетная запись удалена.",
|
||||
"deleteAccountError": "Не удалось удалить аккаунт",
|
||||
"deleteAccountPreviewAccount": "Ваша учетная запись",
|
||||
"deleteAccountPreviewOrgs": "Организации, которые вы владеете (и все их данные)",
|
||||
"orgMissing": "Отсутствует ID организации",
|
||||
"orgMissingMessage": "Невозможно восстановить приглашение без ID организации.",
|
||||
"accessUsersManage": "Управление пользователями",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "Удаление ключа API",
|
||||
"apiKeysManage": "Управление ключами API",
|
||||
"apiKeysDescription": "Ключи API используются для аутентификации в интеграционном API",
|
||||
"provisioningKeysTitle": "Ключ подготовки",
|
||||
"provisioningKeysManage": "Управление ключами подготовки",
|
||||
"provisioningKeysDescription": "Ключи подготовки используются для аутентификации автоматического обеспечения сайта для вашей организации.",
|
||||
"provisioningManage": "Подготовка",
|
||||
"provisioningDescription": "Управляйте предоставленными ключами и проверять непроверенные сайты, ожидающие утверждения.",
|
||||
"pendingSites": "Ожидающие сайты",
|
||||
"siteApproveSuccess": "Сайт успешно утвержден",
|
||||
"siteApproveError": "Ошибка при утверждении сайта",
|
||||
"provisioningKeys": "Ключи подготовки",
|
||||
"searchProvisioningKeys": "Поиск подготовительных ключей...",
|
||||
"provisioningKeysAdd": "Сгенерировать ключ подготовки",
|
||||
"provisioningKeysErrorDelete": "Ошибка при удалении подготовительного ключа",
|
||||
"provisioningKeysErrorDeleteMessage": "Ошибка при удалении подготовительного ключа",
|
||||
"provisioningKeysQuestionRemove": "Вы уверены, что хотите удалить этот ключ подготовки из организации?",
|
||||
"provisioningKeysMessageRemove": "После удаления ключ больше не может быть использован для размещения сайта.",
|
||||
"provisioningKeysDeleteConfirm": "Подтвердите удаление ключа подготовки",
|
||||
"provisioningKeysDelete": "Удалить ключ подготовки",
|
||||
"provisioningKeysCreate": "Сгенерировать ключ подготовки",
|
||||
"provisioningKeysCreateDescription": "Создать новый подготовительный ключ для организации",
|
||||
"provisioningKeysSeeAll": "Посмотреть все подготовительные ключи",
|
||||
"provisioningKeysSave": "Сохранить ключ подготовки",
|
||||
"provisioningKeysSaveDescription": "Вы сможете увидеть это только один раз. Скопируйте его в безопасное место.",
|
||||
"provisioningKeysErrorCreate": "Ошибка при создании ключа подготовки",
|
||||
"provisioningKeysList": "Новый подготовительный ключ",
|
||||
"provisioningKeysMaxBatchSize": "Макс. размер партии",
|
||||
"provisioningKeysUnlimitedBatchSize": "Неограниченный размер партии (без ограничений)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Неограниченный",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Введите максимальный размер пакета (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Действителен до",
|
||||
"provisioningKeysValidUntilHint": "Оставьте пустым для отсутствия срока действия.",
|
||||
"provisioningKeysValidUntilInvalid": "Введите правильную дату и время.",
|
||||
"provisioningKeysNumUsed": "Использовано раз",
|
||||
"provisioningKeysLastUsed": "Последнее использованное",
|
||||
"provisioningKeysNoExpiry": "Без истечения срока",
|
||||
"provisioningKeysNeverUsed": "Никогда",
|
||||
"provisioningKeysEdit": "Редактировать ключ подготовки",
|
||||
"provisioningKeysEditDescription": "Обновить максимальный размер и срок действия этого ключа.",
|
||||
"provisioningKeysApproveNewSites": "Одобрить новые сайты",
|
||||
"provisioningKeysApproveNewSitesDescription": "Автоматически одобрять сайты, регистрирующиеся с этим ключом.",
|
||||
"provisioningKeysUpdateError": "Ошибка при обновлении ключа подготовки",
|
||||
"provisioningKeysUpdated": "Ключ подготовки обновлен",
|
||||
"provisioningKeysUpdatedDescription": "Ваши изменения были сохранены.",
|
||||
"provisioningKeysBannerTitle": "Ключи подготовки сайта",
|
||||
"provisioningKeysBannerDescription": "Генерировать подготовительный ключ и использовать его вместе с Новым коннектором для автоматического создания сайтов при первом запуске — нет необходимости настраивать отдельные учетные данные для каждого сайта.",
|
||||
"provisioningKeysBannerButtonText": "Узнать больше",
|
||||
"pendingSitesBannerTitle": "Ожидающие сайты",
|
||||
"pendingSitesBannerDescription": "Сайты, связанные с использованием ключа подготовки, появляются здесь для проверки. Одобрите каждый сайт, прежде чем он станет активным и получит доступ к вашим ресурсам.",
|
||||
"pendingSitesBannerButtonText": "Узнать больше",
|
||||
"apiKeysSettings": "Настройки {apiKeyName}",
|
||||
"userTitle": "Управление всеми пользователями",
|
||||
"userDescription": "Просмотр и управление всеми пользователями в системе",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Фильтр по состоянию утверждения",
|
||||
"approvalListEmpty": "Нет утверждений",
|
||||
"approvalState": "Состояние одобрения",
|
||||
"approvalLoadMore": "Загрузить еще",
|
||||
"loadingApprovals": "Загрузка утверждений",
|
||||
"approve": "Одобрить",
|
||||
"approved": "Одобрено",
|
||||
"denied": "Отказано",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Пользователь сохранён",
|
||||
"userSavedDescription": "Пользователь был обновлён.",
|
||||
"autoProvisioned": "Автоподбор",
|
||||
"autoProvisionSettings": "Настройки автоматического обеспечения",
|
||||
"autoProvisionedDescription": "Разрешить автоматическое управление этим пользователем",
|
||||
"accessControlsDescription": "Управляйте тем, к чему этот пользователь может получить доступ и что делать в организации",
|
||||
"accessControlsSubmit": "Сохранить контроль доступа",
|
||||
"singleRolePerUserPlanNotice": "Ваш план поддерживает только одну роль каждого пользователя.",
|
||||
"singleRolePerUserEditionNotice": "Эта редакция поддерживает только одну роль для каждого пользователя.",
|
||||
"roles": "Роли",
|
||||
"accessUsersRoles": "Управление пользователями и ролями",
|
||||
"accessUsersRolesDescription": "Пригласить пользователей и добавить их в роли для управления доступом к организации",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Не удалось переключить ресурс",
|
||||
"resourcesErrorUpdateDescription": "Произошла ошибка при обновлении ресурса",
|
||||
"access": "Доступ",
|
||||
"accessControl": "Контроль доступа",
|
||||
"shareLink": "Общая ссылка {resource}",
|
||||
"resourceSelect": "Выберите ресурс",
|
||||
"shareLinks": "Общие ссылки",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Роль удалена",
|
||||
"accessRoleRemovedDescription": "Роль была успешно удалена.",
|
||||
"accessRoleRequiredRemove": "Перед удалением этой роли выберите новую роль для переноса существующих участников.",
|
||||
"network": "Сеть",
|
||||
"manage": "Управление",
|
||||
"sitesNotFound": "Сайты не найдены.",
|
||||
"pangolinServerAdmin": "Администратор сервера - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Увеличить количество сайтов",
|
||||
"idpManage": "Управление поставщиками удостоверений",
|
||||
"idpManageDescription": "Просмотр и управление поставщиками удостоверений в системе",
|
||||
"idpGlobalModeBanner": "Поставщики удостоверений (IdP) для каждой организации отключены на этом сервере. Используются глобальные IdP (общие для всех организаций). Управляйте глобальными IdP в <adminPanelLink>админ-панели</adminPanelLink>. Чтобы включить IdP для каждой организации, отредактируйте конфигурацию сервера и установите режим IdP в org. <configDocsLink>См. документацию</configDocsLink>. Если вы хотите продолжать использовать глобальные IdP и скрыть это из настроек организации, явно установите режим в глобальном конфиге.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Поставщики удостоверений (IdP) для каждой организации отключены на этом сервере. Используются глобальные IdP (общие для всех организаций). Управляйте глобальными IdP в <adminPanelLink>админ-панели</adminPanelLink>. Чтобы использовать поставщиков удостоверений для каждой организации, необходимо обновить систему до версии Enterprise.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Поставщики удостоверений (IdP) для каждой организации отключены на этом сервере. Используются глобальные IdP (общие для всех организаций). Управляйте глобальными IdP в <adminPanelLink>админ-панели</adminPanelLink>. Для использования поставщиков удостоверений на организацию требуется лицензия Enterprise.",
|
||||
"idpDeletedDescription": "Поставщик удостоверений успешно удалён",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Вы уверены, что хотите навсегда удалить поставщика удостоверений?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Настройка - Pangolin",
|
||||
"orgNameRequired": "Название организации обязательно",
|
||||
"orgIdRequired": "ID организации обязателен",
|
||||
"orgIdMaxLength": "ID организации должен быть не более 32 символов",
|
||||
"orgErrorCreate": "Произошла ошибка при создании организации",
|
||||
"pageNotFound": "Страница не найдена",
|
||||
"pageNotFoundDescription": "Упс! Страница, которую вы ищете, не существует.",
|
||||
"overview": "Обзор",
|
||||
"home": "Главная",
|
||||
"accessControl": "Контроль доступа",
|
||||
"settings": "Настройки",
|
||||
"usersAll": "Все пользователи",
|
||||
"license": "Лицензия",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Получить пользователя",
|
||||
"actionGetOrgUser": "Получить пользователя организации",
|
||||
"actionListOrgDomains": "Список доменов организации",
|
||||
"actionGetDomain": "Получить домен",
|
||||
"actionCreateOrgDomain": "Создать домен",
|
||||
"actionUpdateOrgDomain": "Обновить домен",
|
||||
"actionDeleteOrgDomain": "Удалить домен",
|
||||
"actionGetDNSRecords": "Получить записи DNS",
|
||||
"actionRestartOrgDomain": "Перезапустить домен",
|
||||
"actionCreateSite": "Создать сайт",
|
||||
"actionDeleteSite": "Удалить сайт",
|
||||
"actionGetSite": "Получить сайт",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Введите токен настройки из консоли сервера.",
|
||||
"setupTokenRequired": "Токен настройки обязателен",
|
||||
"actionUpdateSite": "Обновить сайт",
|
||||
"actionResetSiteBandwidth": "Сброс пропускной способности организации",
|
||||
"actionListSiteRoles": "Список разрешенных ролей сайта",
|
||||
"actionCreateResource": "Создать ресурс",
|
||||
"actionDeleteResource": "Удалить ресурс",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Удалить пользователя",
|
||||
"actionListUsers": "Список пользователей",
|
||||
"actionAddUserRole": "Добавить роль пользователя",
|
||||
"actionSetUserOrgRoles": "Установка ролей пользователей",
|
||||
"actionGenerateAccessToken": "Сгенерировать токен доступа",
|
||||
"actionDeleteAccessToken": "Удалить токен доступа",
|
||||
"actionListAccessTokens": "Список токенов доступа",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Просмотр журналов",
|
||||
"noneSelected": "Ничего не выбрано",
|
||||
"orgNotFound2": "Организации не найдены.",
|
||||
"searchProgress": "Поиск...",
|
||||
"searchPlaceholder": "Поиск...",
|
||||
"emptySearchOptions": "Опции не найдены",
|
||||
"create": "Создать",
|
||||
"orgs": "Организации",
|
||||
"loginError": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте еще раз.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Приватный",
|
||||
"sidebarAccessControl": "Контроль доступа",
|
||||
"sidebarLogsAndAnalytics": "Журналы и аналитика",
|
||||
"sidebarTeam": "Команда",
|
||||
"sidebarUsers": "Пользователи",
|
||||
"sidebarAdmin": "Админ",
|
||||
"sidebarInvitations": "Приглашения",
|
||||
"sidebarRoles": "Роли",
|
||||
"sidebarShareableLinks": "Ссылки",
|
||||
"sidebarApiKeys": "API ключи",
|
||||
"sidebarProvisioning": "Подготовка",
|
||||
"sidebarSettings": "Настройки",
|
||||
"sidebarAllUsers": "Все пользователи",
|
||||
"sidebarIdentityProviders": "Поставщики удостоверений",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Журнал и аналитика",
|
||||
"sidebarBluePrints": "Чертежи",
|
||||
"sidebarOrganization": "Организация",
|
||||
"sidebarManagement": "Управление",
|
||||
"sidebarBillingAndLicenses": "Биллинг и лицензии",
|
||||
"sidebarLogsAnalytics": "Статистика",
|
||||
"blueprints": "Чертежи",
|
||||
"blueprintsDescription": "Применить декларирующие конфигурации и просмотреть предыдущие запуски",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Переработанное содержимое (только для чтения)",
|
||||
"enableDockerSocket": "Включить чертёж Docker",
|
||||
"enableDockerSocketDescription": "Включить scraping ярлыка Docker Socket для ярлыков чертежей. Путь к сокету должен быть предоставлен в Newt.",
|
||||
"enableDockerSocketLink": "Узнать больше",
|
||||
"viewDockerContainers": "Просмотр контейнеров Docker",
|
||||
"containersIn": "Контейнеры в {siteName}",
|
||||
"selectContainerDescription": "Выберите любой контейнер для использования в качестве имени хоста для этой цели. Нажмите на порт, чтобы использовать порт.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Пространство имен: {namespace}",
|
||||
"domainPickerShowMore": "Показать еще",
|
||||
"regionSelectorTitle": "Выберите регион",
|
||||
"domainPickerRemoteExitNodeWarning": "Предоставленные домены не поддерживаются при подключении сайтов к удаленным узлам. Для доступа к ресурсам на удаленных узлах используйте пользовательский домен.",
|
||||
"regionSelectorInfo": "Выбор региона помогает нам обеспечить лучшее качество обслуживания для вашего расположения. Вам необязательно находиться в том же регионе, что и ваш сервер.",
|
||||
"regionSelectorPlaceholder": "Выбор региона",
|
||||
"regionSelectorComingSoon": "Скоро будет",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Обзор лимитов использования",
|
||||
"billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@pangolin.net.",
|
||||
"billingDataUsage": "Использование данных",
|
||||
"billingOnlineTime": "Время работы сайта",
|
||||
"billingUsers": "Активные пользователи",
|
||||
"billingDomains": "Активные домены",
|
||||
"billingRemoteExitNodes": "Активные самоуправляемые узлы",
|
||||
"billingSites": "Сайты",
|
||||
"billingUsers": "Пользователи",
|
||||
"billingDomains": "Домены",
|
||||
"billingOrganizations": "Орги",
|
||||
"billingRemoteExitNodes": "Удаленные узлы",
|
||||
"billingNoLimitConfigured": "Лимит не установлен",
|
||||
"billingEstimatedPeriod": "Предполагаемый период выставления счетов",
|
||||
"billingIncludedUsage": "Включенное использование",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Не удалось получить URL-адрес портала",
|
||||
"billingPortalError": "Ошибка портала",
|
||||
"billingDataUsageInfo": "Вы несете ответственность за все данные, переданные через безопасные туннели при подключении к облаку. Это включает как входящий, так и исходящий трафик на всех ваших сайтах. При достижении лимита ваши сайты будут отключаться до тех пор, пока вы не обновите план или не уменьшите его использование. При использовании узлов не взимается плата.",
|
||||
"billingOnlineTimeInfo": "Вы тарифицируете на то, как долго ваши сайты будут подключены к облаку. Например, 44 640 минут равны одному сайту, работающему круглосуточно за весь месяц. Когда вы достигните лимита, ваши сайты будут отключаться до тех пор, пока вы не обновите тарифный план или не сократите нагрузку. При использовании узлов не тарифицируется.",
|
||||
"billingUsersInfo": "Вы оплачиваете за каждого пользователя в организации. Платеж рассчитывается ежедневно в зависимости от количества активных учетных записей в вашем органе.",
|
||||
"billingDomainInfo": "Вы платите за каждый домен в организации. Платеж рассчитывается ежедневно в зависимости от количества активных доменных аккаунтов в вашем органе.",
|
||||
"billingRemoteExitNodesInfo": "Вы платите за каждый управляемый узел организации. Платёж рассчитывается ежедневно на основе количества активных управляемых узлов в вашем органе.",
|
||||
"billingSInfo": "Сколько сайтов вы можете использовать",
|
||||
"billingUsersInfo": "Сколько пользователей вы можете использовать",
|
||||
"billingDomainInfo": "Сколько доменов вы можете использовать",
|
||||
"billingRemoteExitNodesInfo": "Сколько удаленных узлов вы можете использовать",
|
||||
"billingLicenseKeys": "Лицензионные ключи",
|
||||
"billingLicenseKeysDescription": "Управление подписками на лицензионные ключи",
|
||||
"billingLicenseSubscription": "Лицензионное соглашение",
|
||||
"billingInactive": "Неактивный",
|
||||
"billingLicenseItem": "Элемент лицензии",
|
||||
"billingQuantity": "Количество",
|
||||
"billingTotal": "итого",
|
||||
"billingModifyLicenses": "Изменить лицензию подписки",
|
||||
"domainNotFound": "Домен не найден",
|
||||
"domainNotFoundDescription": "Этот ресурс отключен, так как домен больше не существует в нашей системе. Пожалуйста, установите новый домен для этого ресурса.",
|
||||
"failed": "Ошибка",
|
||||
"createNewOrgDescription": "Создать новую организацию",
|
||||
"organization": "Организация",
|
||||
"primary": "Первичный",
|
||||
"port": "Порт",
|
||||
"securityKeyManage": "Управление ключами безопасности",
|
||||
"securityKeyDescription": "Добавить или удалить ключи безопасности для аутентификации без пароля",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "Номер порта необходим для не-HTTP ресурсов",
|
||||
"resourcePortNotAllowed": "Номер порта не должен быть установлен для HTTP ресурсов",
|
||||
"billingPricingCalculatorLink": "Калькулятор расценок",
|
||||
"billingYourPlan": "Ваш план",
|
||||
"billingViewOrModifyPlan": "Просмотреть или изменить ваш текущий тариф",
|
||||
"billingViewPlanDetails": "Подробности плана",
|
||||
"billingUsageAndLimits": "Использование и ограничения",
|
||||
"billingViewUsageAndLimits": "Просмотр лимитов и текущего использования вашего плана",
|
||||
"billingCurrentUsage": "Текущее использование",
|
||||
"billingMaximumLimits": "Максимальные ограничения",
|
||||
"billingRemoteNodes": "Удаленные узлы",
|
||||
"billingUnlimited": "Неограниченный",
|
||||
"billingPaidLicenseKeys": "Платные лицензионные ключи",
|
||||
"billingManageLicenseSubscription": "Управление подпиской на платные лицензионные ключи собственного хостинга",
|
||||
"billingCurrentKeys": "Текущие ключи",
|
||||
"billingModifyCurrentPlan": "Изменить текущий план",
|
||||
"billingConfirmUpgrade": "Подтвердить обновление",
|
||||
"billingConfirmDowngrade": "Подтверждение понижения",
|
||||
"billingConfirmUpgradeDescription": "Вы собираетесь обновить тарифный план. Проверьте новые лимиты и цены ниже.",
|
||||
"billingConfirmDowngradeDescription": "Вы собираетесь понизить тарифный план. Проверьте новые ограничения и цены ниже.",
|
||||
"billingPlanIncludes": "Включает план",
|
||||
"billingProcessing": "Обработка...",
|
||||
"billingConfirmUpgradeButton": "Подтвердить обновление",
|
||||
"billingConfirmDowngradeButton": "Подтверждение понижения",
|
||||
"billingLimitViolationWarning": "Превышено количество новых лимитов плана",
|
||||
"billingLimitViolationDescription": "Ваше текущее использование превышает лимиты этого плана. После понижения значения все действия будут отключены до уменьшения использования в пределах новых лимитов. Пожалуйста, ознакомьтесь с функциями, которые в настоящее время превышают лимиты. Ограничения:",
|
||||
"billingFeatureLossWarning": "Уведомление о доступности функций",
|
||||
"billingFeatureLossDescription": "При переходе на другой тарифный план функции не будут автоматически отключены. Некоторые настройки и конфигурации могут быть потеряны. Пожалуйста, ознакомьтесь с матрицей ценообразования, чтобы понять, какие функции больше не будут доступны.",
|
||||
"billingUsageExceedsLimit": "Текущее использование ({current}) превышает предел ({limit})",
|
||||
"billingPastDueTitle": "Платеж просрочен",
|
||||
"billingPastDueDescription": "Ваш платеж просрочен. Пожалуйста, обновите способ оплаты, чтобы продолжить использовать текущие функции. Если ваша подписка не будет решена, она будет отменена, и вы вернетесь к бесплатному уровню.",
|
||||
"billingUnpaidTitle": "Подписка не оплачена",
|
||||
"billingUnpaidDescription": "Ваша подписка не оплачена, и вы были возвращены к бесплатному уровню. Пожалуйста, обновите способ оплаты, чтобы восстановить вашу подписку.",
|
||||
"billingIncompleteTitle": "Платеж не завершен",
|
||||
"billingIncompleteDescription": "Ваш платеж не завершен. Пожалуйста, завершите процесс оплаты, чтобы активировать вашу подписку.",
|
||||
"billingIncompleteExpiredTitle": "Платеж просрочен",
|
||||
"billingIncompleteExpiredDescription": "Ваш платеж не был завершен и истек. Вы были возвращены к бесплатному уровню. Пожалуйста, подпишитесь снова, чтобы восстановить доступ к платным функциям.",
|
||||
"billingManageSubscription": "Управление подпиской",
|
||||
"billingResolvePaymentIssue": "Пожалуйста, решите проблему оплаты перед обновлением или понижением сорта",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Я согласен с",
|
||||
"termsOfService": "условия использования",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Время указано в секундах",
|
||||
"requireDeviceApproval": "Требовать подтверждения устройства",
|
||||
"requireDeviceApprovalDescription": "Пользователям с этой ролью нужны новые устройства, одобренные администратором, прежде чем они смогут подключаться и получать доступ к ресурсам.",
|
||||
"sshAccess": "SSH доступ",
|
||||
"roleAllowSsh": "Разрешить SSH",
|
||||
"roleAllowSshAllow": "Разрешить",
|
||||
"roleAllowSshDisallow": "Запретить",
|
||||
"roleAllowSshDescription": "Разрешить пользователям с этой ролью подключаться к ресурсам через SSH. Если отключено, роль не может использовать доступ SSH.",
|
||||
"sshSudoMode": "Sudo доступ",
|
||||
"sshSudoModeNone": "Нет",
|
||||
"sshSudoModeNoneDescription": "Пользователь не может запускать команды с sudo.",
|
||||
"sshSudoModeFull": "Полная судо",
|
||||
"sshSudoModeFullDescription": "Пользователь может запускать любую команду с помощью sudo.",
|
||||
"sshSudoModeCommands": "Команды",
|
||||
"sshSudoModeCommandsDescription": "Пользователь может запускать только указанные команды с помощью sudo.",
|
||||
"sshSudo": "Разрешить sudo",
|
||||
"sshSudoCommands": "Sudo Команды",
|
||||
"sshSudoCommandsDescription": "Список команд, разделенных запятыми, которые пользователю разрешено запускать с помощью sudo.",
|
||||
"sshCreateHomeDir": "Создать домашний каталог",
|
||||
"sshUnixGroups": "Unix группы",
|
||||
"sshUnixGroupsDescription": "Группы Unix через запятую, чтобы добавить пользователя на целевой хост.",
|
||||
"retryAttempts": "Количество попыток повторного запроса",
|
||||
"expectedResponseCodes": "Ожидаемые коды ответов",
|
||||
"expectedResponseCodesDescription": "HTTP-код состояния, указывающий на здоровое состояние. Если оставить пустым, 200-300 считается здоровым.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Узел выхода",
|
||||
"country": "Страна",
|
||||
"rulesMatchCountry": "В настоящее время основано на исходном IP",
|
||||
"region": "Регион",
|
||||
"selectRegion": "Выберите регион",
|
||||
"searchRegions": "Поиск регионов...",
|
||||
"noRegionFound": "Регион не найден.",
|
||||
"rulesMatchRegion": "Выберите региональную группу стран",
|
||||
"rulesErrorInvalidRegion": "Некорректный регион",
|
||||
"rulesErrorInvalidRegionDescription": "Пожалуйста, выберите корректный регион.",
|
||||
"regionAfrica": "Африка",
|
||||
"regionNorthernAfrica": "Северная Африка",
|
||||
"regionEasternAfrica": "Восточная Африка",
|
||||
"regionMiddleAfrica": "Центральная Африка",
|
||||
"regionSouthernAfrica": "Южная Африка",
|
||||
"regionWesternAfrica": "Западная Африка",
|
||||
"regionAmericas": "Америка",
|
||||
"regionCaribbean": "Карибы",
|
||||
"regionCentralAmerica": "Центральная Америка",
|
||||
"regionSouthAmerica": "Южная Америка",
|
||||
"regionNorthernAmerica": "Северная Америка",
|
||||
"regionAsia": "Азия",
|
||||
"regionCentralAsia": "Центральная Азия",
|
||||
"regionEasternAsia": "Восточная Азия",
|
||||
"regionSouthEasternAsia": "Юго-Восточная Азия",
|
||||
"regionSouthernAsia": "Южная Азия",
|
||||
"regionWesternAsia": "Западная Азия",
|
||||
"regionEurope": "Европа",
|
||||
"regionEasternEurope": "Восточная Европа",
|
||||
"regionNorthernEurope": "Северная Европа",
|
||||
"regionSouthernEurope": "Южная Европа",
|
||||
"regionWesternEurope": "Западная Европа",
|
||||
"regionOceania": "Океания",
|
||||
"regionAustraliaAndNewZealand": "Австралия и Новая Зеландия",
|
||||
"regionMelanesia": "Меланезия",
|
||||
"regionMicronesia": "Микронезия",
|
||||
"regionPolynesia": "Полинезия",
|
||||
"managedSelfHosted": {
|
||||
"title": "Управляемый с самовывоза",
|
||||
"description": "Более надежный и низко обслуживаемый сервер Pangolin с дополнительными колокольнями и свистками",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Неверное значение",
|
||||
"idpTypeLabel": "Тип поставщика удостоверений",
|
||||
"roleMappingExpressionPlaceholder": "например, contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||
"roleMappingModeFixedRoles": "Фиксированные роли",
|
||||
"roleMappingModeMappingBuilder": "Сопоставляющий конструктор",
|
||||
"roleMappingModeRawExpression": "Сырое выражение",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Выберите одну или несколько ролей",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Тип имен ролей (точное совпадение по организации)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Назначить одну и ту же роль, которая установлена каждому автообеспеченному пользователю.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Для политик по умолчанию, введите имена ролей, которые существуют в каждой организации, где пользователи предоставлены. Имена должны соответствовать точно.",
|
||||
"roleMappingClaimPath": "Путь к заявлению",
|
||||
"roleMappingClaimPathPlaceholder": "группы",
|
||||
"roleMappingClaimPathDescription": "Путь в полезной нагрузке токенов, который содержит исходные значения (например, группы).",
|
||||
"roleMappingMatchValue": "Значение матча",
|
||||
"roleMappingAssignRoles": "Назначить роли",
|
||||
"roleMappingAddMappingRule": "Добавить правило сопоставления",
|
||||
"roleMappingRawExpressionResultDescription": "Выражение должно быть оценено к строке или строковому массиву.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "Выражение должно быть оценено строке (название одной роли).",
|
||||
"roleMappingMatchValuePlaceholder": "Значение совпадения (например: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Введите имена ролей (точное по организациям)",
|
||||
"roleMappingBuilderFreeformRowHint": "Имена ролей должны соответствовать роли в каждой целевой организации.",
|
||||
"roleMappingRemoveRule": "Удалить",
|
||||
"idpGoogleConfiguration": "Конфигурация Google",
|
||||
"idpGoogleConfigurationDescription": "Настройка учетных данных Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Вы уверены, что хотите удалить брендирование для страниц аутентификации?",
|
||||
"authPageBrandingDeleteConfirm": "Подтвердить удаление брендирования",
|
||||
"brandingLogoURL": "URL логотипа",
|
||||
"brandingLogoURLOrPath": "URL логотипа или путь",
|
||||
"brandingLogoPathDescription": "Введите URL или локальный путь.",
|
||||
"brandingLogoURLDescription": "Введите публичный URL для изображения вашего логотипа.",
|
||||
"brandingPrimaryColor": "Основной цвет",
|
||||
"brandingLogoWidth": "Ширина (px)",
|
||||
"brandingLogoHeight": "Высота (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Вернуться к стандартному входу",
|
||||
"orgAuthNoAccount": "Нет учётной записи?",
|
||||
"subscriptionRequiredToUse": "Для использования этой функции требуется подписка.",
|
||||
"mustUpgradeToUse": "Вы должны обновить подписку, чтобы использовать эту функцию.",
|
||||
"subscriptionRequiredTierToUse": "Эта функция требует <tierLink>{tier}</tierLink> или выше.",
|
||||
"upgradeToTierToUse": "Обновитесь до <tierLink>{tier}</tierLink> или выше, чтобы использовать эту функцию.",
|
||||
"subscriptionTierTier1": "Главная",
|
||||
"subscriptionTierTier2": "Команда",
|
||||
"subscriptionTierTier3": "Бизнес",
|
||||
"subscriptionTierEnterprise": "Предприятие",
|
||||
"idpDisabled": "Провайдеры идентификации отключены.",
|
||||
"orgAuthPageDisabled": "Страница авторизации организации отключена.",
|
||||
"domainRestartedDescription": "Проверка домена успешно перезапущена",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Получить лицензию",
|
||||
"description": "Выберите план и расскажите нам, как вы планируете использовать Панголин.",
|
||||
"chooseTier": "Выберите ваш план",
|
||||
"viewPricingLink": "Смотрите цены, возможности и ограничения",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Старт",
|
||||
"description": "Функции предприятия, 25 пользователей, 25 сайтов, и поддержка сообщества."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Масштаб",
|
||||
"description": "Функции предприятия, 50 пользователей, 50 сайтов, а также приоритетная поддержка."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Только для личного пользования (бесплатная лицензия — без оформления)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Продолжить оформление заказа"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Ошибка оформления заказа",
|
||||
"description": "Не удалось начать оформление заказа. Пожалуйста, попробуйте еще раз."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Приоритет",
|
||||
"priorityDescription": "Маршруты с более высоким приоритетом оцениваются первым. Приоритет = 100 означает автоматическое упорядочение (решение системы). Используйте другой номер для обеспечения ручного приоритета.",
|
||||
"instanceName": "Имя экземпляра",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Как долго сохранять журналы доступа",
|
||||
"logRetentionActionLabel": "Сохранение журнала действий",
|
||||
"logRetentionActionDescription": "Как долго хранить журналы действий",
|
||||
"logRetentionConnectionLabel": "Сохранение журнала подключений",
|
||||
"logRetentionConnectionDescription": "Как долго хранить журналы подключений",
|
||||
"logRetentionDisabled": "Отключено",
|
||||
"logRetention3Days": "3 дня",
|
||||
"logRetention7Days": "7 дней",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Конец следующего года",
|
||||
"actionLogsDescription": "Просмотр истории действий, выполненных в этой организации",
|
||||
"accessLogsDescription": "Просмотр запросов авторизации доступа к ресурсам этой организации",
|
||||
"licenseRequiredToUse": "Для использования этой функции требуется лицензия предприятия.",
|
||||
"connectionLogs": "Журнал подключений",
|
||||
"connectionLogsDescription": "Просмотр журналов подключения туннелей в этой организации",
|
||||
"sidebarLogsConnection": "Журнал подключений",
|
||||
"sidebarLogsStreaming": "Вещание",
|
||||
"sourceAddress": "Адрес источника",
|
||||
"destinationAddress": "Адрес назначения",
|
||||
"duration": "Продолжительность",
|
||||
"licenseRequiredToUse": "Требуется лицензия на <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> или <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> для использования этой функции. <bookADemoLink>Забронируйте демонстрацию или пробный POC</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> требуется для использования этой функции. Эта функция также доступна в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Забронируйте демонстрацию или пробный POC</bookADemoLink>.",
|
||||
"certResolver": "Резольвер сертификата",
|
||||
"certResolverDescription": "Выберите резолвер сертификата, который будет использоваться для этого ресурса.",
|
||||
"selectCertResolver": "Выберите резолвер сертификата",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Контроль доступа",
|
||||
"editInternalResourceDialogAccessControlDescription": "Контролируйте, какие роли, пользователи и машинные клиенты имеют доступ к этому ресурсу при подключении. Администраторы всегда имеют доступ.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Диапазон портов должен быть \"*\" для всех портов или списком портов и диапазонов через запятую (например, \"80,443,8000-9000\"). Порты должны находиться в диапазоне от 1 до 65535.",
|
||||
"internalResourceAuthDaemonStrategy": "Местоположение демона по SSH",
|
||||
"internalResourceAuthDaemonStrategyDescription": "Выберите, где работает демон аутентификации SSH: на сайте (Newt) или на удаленном узле.",
|
||||
"internalResourceAuthDaemonDescription": "Демон аутентификации SSH обрабатывает подписание ключей SSH и аутентификацию PAM для этого ресурса. Выберите, запускать ли его на сайте (Newt) или на отдельном удаленном хосте. Подробности смотрите в <docsLink>документации</docsLink>.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Выберите стратегию",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Местоположение",
|
||||
"internalResourceAuthDaemonSite": "На сайте",
|
||||
"internalResourceAuthDaemonSiteDescription": "На сайте работает демон Auth (Newt).",
|
||||
"internalResourceAuthDaemonRemote": "Удаленный хост",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Демон Auth запускается на хост, который не является сайтом.",
|
||||
"internalResourceAuthDaemonPort": "Порт демона (опционально)",
|
||||
"orgAuthWhatsThis": "Где я могу найти ID моей организации?",
|
||||
"learnMore": "Узнать больше",
|
||||
"backToHome": "Вернуться домой",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Брандмауэр включен",
|
||||
"autoUpdatesEnabled": "Автоматические обновления включены",
|
||||
"tpmAvailable": "Доступно TPM",
|
||||
"windowsAntivirusEnabled": "Антивирус включен",
|
||||
"macosSipEnabled": "Защита целостности системы (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Стилс-режим брандмауэра",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Включить утверждения устройства",
|
||||
"approvalsEmptyStateStep2Description": "Редактировать роль и включить опцию 'Требовать утверждения устройств'. Пользователям с этой ролью потребуется подтверждение администратора для новых устройств.",
|
||||
"approvalsEmptyStatePreviewDescription": "Предпросмотр: Если включено, ожидающие запросы на устройство появятся здесь для проверки",
|
||||
"approvalsEmptyStateButtonText": "Управление ролями"
|
||||
"approvalsEmptyStateButtonText": "Управление ролями",
|
||||
"domainErrorTitle": "У нас возникли проблемы с проверкой вашего домена",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Настройте сопоставление ролей и организационные политики на вкладке <policiesTabLink>Настройки авто-предоставления</policiesTabLink>.",
|
||||
"streamingTitle": "Поток событий",
|
||||
"streamingDescription": "Трансляция событий от вашей организации к внешним направлениям в режиме реального времени.",
|
||||
"streamingUnnamedDestination": "Место назначения без имени",
|
||||
"streamingNoUrlConfigured": "URL-адрес не настроен",
|
||||
"streamingAddDestination": "Добавить место назначения",
|
||||
"streamingHttpWebhookTitle": "HTTP вебхук",
|
||||
"streamingHttpWebhookDescription": "Отправлять события на любую конечную точку HTTP с гибкой аутентификацией и шаблоном.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Потоковая передача событий к пакету хранения объектов, совместимому с S3.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Перенаправлять события непосредственно на ваш аккаунт в Datadog. Скоро будет доступно.",
|
||||
"streamingTypePickerDescription": "Выберите тип назначения, чтобы начать.",
|
||||
"streamingFailedToLoad": "Не удалось загрузить места назначения",
|
||||
"streamingUnexpectedError": "Произошла непредвиденная ошибка.",
|
||||
"streamingFailedToUpdate": "Не удалось обновить место назначения",
|
||||
"streamingDeletedSuccess": "Адрес назначения успешно удален",
|
||||
"streamingFailedToDelete": "Не удалось удалить место назначения",
|
||||
"streamingDeleteTitle": "Удалить адрес назначения",
|
||||
"streamingDeleteButtonText": "Удалить адрес назначения",
|
||||
"streamingDeleteDialogAreYouSure": "Вы уверены, что хотите удалить",
|
||||
"streamingDeleteDialogThisDestination": "это место назначения",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Все настройки будут удалены навсегда.",
|
||||
"httpDestEditTitle": "Изменить адрес назначения",
|
||||
"httpDestAddTitle": "Добавить HTTP адрес",
|
||||
"httpDestEditDescription": "Обновление конфигурации для этого HTTP события потокового назначения.",
|
||||
"httpDestAddDescription": "Настройте новую HTTP-конечную точку для получения событий вашей организации.",
|
||||
"httpDestTabSettings": "Настройки",
|
||||
"httpDestTabHeaders": "Заголовки",
|
||||
"httpDestTabBody": "Тело",
|
||||
"httpDestTabLogs": "Логи",
|
||||
"httpDestNamePlaceholder": "Мой HTTP адрес назначения",
|
||||
"httpDestUrlLabel": "URL назначения",
|
||||
"httpDestUrlErrorHttpRequired": "URL должен использовать http или https",
|
||||
"httpDestUrlErrorHttpsRequired": "Требуется HTTPS при развертывании облака",
|
||||
"httpDestUrlErrorInvalid": "Введите действительный URL (например, https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Аутентификация",
|
||||
"httpDestAuthDescription": "Выберите, как запросы к вашей конечной точке аутентифицированы.",
|
||||
"httpDestAuthNoneTitle": "Нет аутентификации",
|
||||
"httpDestAuthNoneDescription": "Отправляет запросы без заголовка авторизации.",
|
||||
"httpDestAuthBearerTitle": "Жетон носителя",
|
||||
"httpDestAuthBearerDescription": "Добавляет заголовок Authorization: Bearer <token> к каждому запросу.",
|
||||
"httpDestAuthBearerPlaceholder": "Ваш ключ API или токен",
|
||||
"httpDestAuthBasicTitle": "Базовая авторизация",
|
||||
"httpDestAuthBasicDescription": "Добавляет Authorization: Basic <credentials> header. Предоставьте учетные данные в качестве имени пользователя:password.",
|
||||
"httpDestAuthBasicPlaceholder": "имя пользователя:пароль",
|
||||
"httpDestAuthCustomTitle": "Пользовательский заголовок",
|
||||
"httpDestAuthCustomDescription": "Укажите пользовательское имя заголовка HTTP и значение для аутентификации (например, X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Имя заголовка (например, X-API-ключ)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Значение заголовка",
|
||||
"httpDestCustomHeadersTitle": "Пользовательские HTTP-заголовки",
|
||||
"httpDestCustomHeadersDescription": "Добавляет пользовательские заголовки к каждому исходящему запросу. Полезно для статических маркеров или пользовательского типа содержимого. По умолчанию отправляется Content-Type: application/json.",
|
||||
"httpDestNoHeadersConfigured": "Пользовательские заголовки не настроены. Нажмите \"Добавить заголовок\", чтобы добавить их.",
|
||||
"httpDestHeaderNamePlaceholder": "Название заголовка",
|
||||
"httpDestHeaderValuePlaceholder": "Значение",
|
||||
"httpDestAddHeader": "Добавить заголовок",
|
||||
"httpDestBodyTemplateTitle": "Пользовательский шаблон тела",
|
||||
"httpDestBodyTemplateDescription": "Контролируйте структуру JSON приложения, отправленную на вашу конечную точку. Если отключено, для каждого события отправляется JSON объект по умолчанию.",
|
||||
"httpDestEnableBodyTemplate": "Включить настраиваемый шаблон тела",
|
||||
"httpDestBodyTemplateLabel": "Шаблон тела (JSON)",
|
||||
"httpDestBodyTemplateHint": "Использовать шаблонные переменные для ссылки поля событий в вашей полезной нагрузке.",
|
||||
"httpDestPayloadFormatTitle": "Формат нагрузки",
|
||||
"httpDestPayloadFormatDescription": "Как события сериализуются в каждый орган запроса.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON массив",
|
||||
"httpDestFormatJsonArrayDescription": "По одному запросу на каждую партию, тело является JSON-массивом. Совместим с большинством общих вебхуков и Датадог.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "По одному запросу на каждую партию, тело - это JSON, разделённый новой строкой, по одному объекту на строку, без внешнего массива. Требуется в Splunk HEC, Elastic / OpenSearch, и Grafana Loki.",
|
||||
"httpDestFormatSingleTitle": "Одно событие на запрос",
|
||||
"httpDestFormatSingleDescription": "Отправляет отдельный HTTP POST для каждого отдельного события. Используйте только для конечных точек, которые не могут обрабатывать пакеты.",
|
||||
"httpDestLogTypesTitle": "Типы журналов",
|
||||
"httpDestLogTypesDescription": "Выберите, какие типы журналов пересылаются в этот пункт назначения. Только включенные типы журналов будут транслированы.",
|
||||
"httpDestAccessLogsTitle": "Журналы доступа",
|
||||
"httpDestAccessLogsDescription": "Попытки доступа к ресурсам, включая аутентифицированные и отклоненные запросы.",
|
||||
"httpDestActionLogsTitle": "Журнал действий",
|
||||
"httpDestActionLogsDescription": "Административные меры, осуществляемые пользователями в рамках организации.",
|
||||
"httpDestConnectionLogsTitle": "Журнал подключений",
|
||||
"httpDestConnectionLogsDescription": "События связи с сайтами и туннелями, включая соединения и отключения.",
|
||||
"httpDestRequestLogsTitle": "Запросить журналы",
|
||||
"httpDestRequestLogsDescription": "Журналы запросов HTTP для проксируемых ресурсов, включая метод, путь и код ответа.",
|
||||
"httpDestSaveChanges": "Сохранить изменения",
|
||||
"httpDestCreateDestination": "Создать адрес назначения",
|
||||
"httpDestUpdatedSuccess": "Адрес назначения успешно обновлен",
|
||||
"httpDestCreatedSuccess": "Адрес назначения успешно создан",
|
||||
"httpDestUpdateFailed": "Не удалось обновить место назначения",
|
||||
"httpDestCreateFailed": "Не удалось создать место назначения"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "{count, plural, =0 {hiçbir organizasyon} one {bir organizasyon} other {# organizasyon}} üyesisiniz.",
|
||||
"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",
|
||||
"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.",
|
||||
"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.",
|
||||
"componentsSupporterMessage": "Pangolin'e {tier} olarak destek olduğunuz için teşekkür ederiz!",
|
||||
"inviteErrorNotValid": "Üzgünüz, ancak erişmeye çalıştığınız davet kabul edilmemiş veya artık geçerli değil gibi görünüyor.",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "Bağlantı Oluştur",
|
||||
"resourcesNotFound": "Hiçbir kaynak bulunamadı",
|
||||
"resourceSearch": "Kaynak ara",
|
||||
"machineSearch": "Makinaları ara",
|
||||
"machinesSearch": "Makina müşteri...",
|
||||
"machineNotFound": "Hiçbir makine bulunamadı",
|
||||
"userDeviceSearch": "Kullanıcı cihazlarını ara",
|
||||
"userDevicesSearch": "Kullanıcı cihazlarını ara...",
|
||||
"openMenu": "Menüyü Aç",
|
||||
"resource": "Kaynak",
|
||||
"title": "Başlık",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "Tam nitelikli bir etki alanı adı kullanarak HTTPS üzerinden proxy isteklerini yönlendirin.",
|
||||
"resourceRaw": "Ham TCP/UDP Kaynağı",
|
||||
"resourceRawDescription": "Port numarası kullanarak ham TCP/UDP üzerinden proxy isteklerini yönlendirin.",
|
||||
"resourceRawDescriptionCloud": "Proxy isteklerini bir port numarası kullanarak ham TCP/UDP üzerinden yapın. Sitelerin uzak bir düğüme bağlanması gereklidir.",
|
||||
"resourceCreate": "Kaynak Oluştur",
|
||||
"resourceCreateDescription": "Yeni bir kaynak oluşturmak için aşağıdaki adımları izleyin",
|
||||
"resourceSeeAll": "Tüm Kaynakları Gör",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "Bir protokol seçin",
|
||||
"resourcePortNumber": "Port Numarası",
|
||||
"resourcePortNumberDescription": "Vekil istekler için harici port numarası.",
|
||||
"back": "Geri",
|
||||
"cancel": "İptal",
|
||||
"resourceConfig": "Yapılandırma Parçaları",
|
||||
"resourceConfigDescription": "TCP/UDP kaynağınızı kurmak için bu yapılandırma parçalarını kopyalayıp yapıştırın",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "Organizasyon silinirken bir hata oluştu.",
|
||||
"orgDeleted": "Organizasyon silindi",
|
||||
"orgDeletedMessage": "Organizasyon ve verileri silindi.",
|
||||
"deleteAccount": "Hesabı Sil",
|
||||
"deleteAccountDescription": "Hesabınızı, sahip olduğunuz tüm organizasyonları ve bu organizasyonlardaki tüm verileri kalıcı olarak silin. Bu geri alınamaz.",
|
||||
"deleteAccountButton": "Hesabı Sil",
|
||||
"deleteAccountConfirmTitle": "Hesabı Sil",
|
||||
"deleteAccountConfirmMessage": "Bu işlem, hesabınızı, sahip olduğunuz tüm organizasyonları ve bu organizasyonlardaki tüm verileri kalıcı olarak silecektir. Bu geri alınamaz.",
|
||||
"deleteAccountConfirmString": "hesabı sil",
|
||||
"deleteAccountSuccess": "Hesap Silindi",
|
||||
"deleteAccountSuccessMessage": "Hesabınız silindi.",
|
||||
"deleteAccountError": "Hesabı silme başarısız oldu",
|
||||
"deleteAccountPreviewAccount": "Hesabınız",
|
||||
"deleteAccountPreviewOrgs": "Sahip olduğunuz organizasyonlar (ve tüm verileri)",
|
||||
"orgMissing": "Organizasyon Kimliği Eksik",
|
||||
"orgMissingMessage": "Organizasyon kimliği olmadan daveti yeniden oluşturmanız mümkün değildir.",
|
||||
"accessUsersManage": "Kullanıcıları Yönet",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "API Anahtarını Sil",
|
||||
"apiKeysManage": "API Anahtarlarını Yönet",
|
||||
"apiKeysDescription": "API anahtarları entegrasyon API'sini doğrulamak için kullanılır",
|
||||
"provisioningKeysTitle": "Tedarik Anahtarı",
|
||||
"provisioningKeysManage": "Tedarik Anahtarlarını Yönet",
|
||||
"provisioningKeysDescription": "Tedarik anahtarları, organizasyonunuz için otomatik site sağlama işlemini doğrulamak için kullanılır.",
|
||||
"provisioningManage": "Tedarik",
|
||||
"provisioningDescription": "Tedarik anahtarlarını yönetin ve onay bekleyen siteleri gözden geçirin.",
|
||||
"pendingSites": "Bekleyen Siteler",
|
||||
"siteApproveSuccess": "Site başarıyla onaylandı",
|
||||
"siteApproveError": "Site onaylanırken hata oluştu",
|
||||
"provisioningKeys": "Tedarik Anahtarları",
|
||||
"searchProvisioningKeys": "Tedarik anahtarlarını ara...",
|
||||
"provisioningKeysAdd": "Tedarik Anahtarı Üret",
|
||||
"provisioningKeysErrorDelete": "Tedarik anahtarı silinirken hata oluştu",
|
||||
"provisioningKeysErrorDeleteMessage": "Tedarik anahtarı silinirken hata oluştu",
|
||||
"provisioningKeysQuestionRemove": "Bu tedarik anahtarını organizasyondan kaldırmak istediğinizden emin misiniz?",
|
||||
"provisioningKeysMessageRemove": "Kaldırıldıktan sonra, anahtar site tedariki için artık kullanılamaz.",
|
||||
"provisioningKeysDeleteConfirm": "Tedarik Anahtarını Silmeyi Onayla",
|
||||
"provisioningKeysDelete": "Tedarik Anahtarını Sil",
|
||||
"provisioningKeysCreate": "Tedarik Anahtarı Üret",
|
||||
"provisioningKeysCreateDescription": "Organizasyon için yeni bir tedarik anahtarı oluşturun",
|
||||
"provisioningKeysSeeAll": "Tüm tedarik anahtarlarını gör",
|
||||
"provisioningKeysSave": "Tedarik anahtarını kaydet",
|
||||
"provisioningKeysSaveDescription": "Bunu yalnızca bir kez görebileceksiniz. Güvenli bir yere kopyalayın.",
|
||||
"provisioningKeysErrorCreate": "Tedarik anahtarı oluşturulurken hata oluştu",
|
||||
"provisioningKeysList": "Yeni tedarik anahtarı",
|
||||
"provisioningKeysMaxBatchSize": "Maksimum toplu iş boyutu",
|
||||
"provisioningKeysUnlimitedBatchSize": "Sınırsız toplu iş boyutu (sınırlama yok)",
|
||||
"provisioningKeysMaxBatchUnlimited": "Sınırsız",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "Geçerli bir maksimum toplu iş boyutu girin (1–1,000,000).",
|
||||
"provisioningKeysValidUntil": "Geçerlilik tarihi",
|
||||
"provisioningKeysValidUntilHint": "Son kullanım tarihi için boş bırakın.",
|
||||
"provisioningKeysValidUntilInvalid": "Geçerli bir tarih ve saat girin.",
|
||||
"provisioningKeysNumUsed": "Kullanım Sayısı",
|
||||
"provisioningKeysLastUsed": "Son kullanım",
|
||||
"provisioningKeysNoExpiry": "Son kullanma tarihi yok",
|
||||
"provisioningKeysNeverUsed": "Asla",
|
||||
"provisioningKeysEdit": "Tedarik Anahtarını Düzenle",
|
||||
"provisioningKeysEditDescription": "Bu anahtar için maksimum toplu iş boyutunu ve son kullanma zamanını güncelleyin.",
|
||||
"provisioningKeysApproveNewSites": "Yeni siteleri onayla",
|
||||
"provisioningKeysApproveNewSitesDescription": "Bu anahtar ile kayıt olan siteleri otomatik olarak onayla.",
|
||||
"provisioningKeysUpdateError": "Tedarik anahtarı güncellenirken hata oluştu",
|
||||
"provisioningKeysUpdated": "Tedarik anahtarı güncellendi",
|
||||
"provisioningKeysUpdatedDescription": "Değişiklikleriniz kaydedildi.",
|
||||
"provisioningKeysBannerTitle": "Site Tedarik Anahtarları",
|
||||
"provisioningKeysBannerDescription": "Tedarik anahtarı oluşturun ve ilk başlangıçta siteleri otomatik olarak oluşturmak için Newt konektörüyle kullanın — her site için ayrı kimlik bilgileri ayarlamaya gerek yoktur.",
|
||||
"provisioningKeysBannerButtonText": "Daha fazla bilgi",
|
||||
"pendingSitesBannerTitle": "Bekleyen Siteler",
|
||||
"pendingSitesBannerDescription": "Tedarik anahtarı kullanarak bağlanan siteler burada incelenmek için görünür. Aktif hale gelmeden ve kaynaklarınıza erişim kazanmadan önce her siteyi onaylayın.",
|
||||
"pendingSitesBannerButtonText": "Daha fazla bilgi",
|
||||
"apiKeysSettings": "{apiKeyName} Ayarları",
|
||||
"userTitle": "Tüm Kullanıcıları Yönet",
|
||||
"userDescription": "Sistemdeki tüm kullanıcıları görün ve yönetin",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "Onay Durumuna Göre Filtrele",
|
||||
"approvalListEmpty": "Onay yok",
|
||||
"approvalState": "Onay Durumu",
|
||||
"approvalLoadMore": "Daha fazla yükle",
|
||||
"loadingApprovals": "Onaylar Yükleniyor",
|
||||
"approve": "Onayla",
|
||||
"approved": "Onaylandı",
|
||||
"denied": "Reddedildi",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "Kullanıcı kaydedildi",
|
||||
"userSavedDescription": "Kullanıcı güncellenmiştir.",
|
||||
"autoProvisioned": "Otomatik Sağlandı",
|
||||
"autoProvisionSettings": "Otomatik Tedarik Ayarları",
|
||||
"autoProvisionedDescription": "Bu kullanıcının kimlik sağlayıcısı tarafından otomatik olarak yönetilmesine izin ver",
|
||||
"accessControlsDescription": "Bu kullanıcının organizasyonda neleri erişebileceğini ve yapabileceğini yönetin",
|
||||
"accessControlsSubmit": "Erişim Kontrollerini Kaydet",
|
||||
"singleRolePerUserPlanNotice": "Planınız yalnızca kullanıcı başına bir rol desteler.",
|
||||
"singleRolePerUserEditionNotice": "Bu sürüm yalnızca kullanıcı başına bir rol destekler.",
|
||||
"roles": "Roller",
|
||||
"accessUsersRoles": "Kullanıcılar ve Roller Yönetin",
|
||||
"accessUsersRolesDescription": "Kullanıcılara davet gönderin ve organizasyona erişimi yönetmek için rollere ekleyin",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "Kaynak değiştirilemedi",
|
||||
"resourcesErrorUpdateDescription": "Kaynak güncellenirken bir hata oluştu",
|
||||
"access": "Erişim",
|
||||
"accessControl": "Erişim Kontrolü",
|
||||
"shareLink": "{resource} Paylaşım Bağlantısı",
|
||||
"resourceSelect": "Kaynak seçin",
|
||||
"shareLinks": "Paylaşım Bağlantıları",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "Rol kaldırıldı",
|
||||
"accessRoleRemovedDescription": "Rol başarıyla kaldırıldı.",
|
||||
"accessRoleRequiredRemove": "Bu rolü silmeden önce, mevcut üyeleri aktarmak için yeni bir rol seçin.",
|
||||
"network": "Ağ",
|
||||
"manage": "Yönet",
|
||||
"sitesNotFound": "Site bulunamadı.",
|
||||
"pangolinServerAdmin": "Sunucu Yöneticisi - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "Site sayısını artır",
|
||||
"idpManage": "Kimlik Sağlayıcılarını Yönet",
|
||||
"idpManageDescription": "Sistem içindeki kimlik sağlayıcıları görün ve yönetin",
|
||||
"idpGlobalModeBanner": "Bu sunucuda örgüt başına kimlik sağlayıcılar (IdP'ler) devre dışı bırakılmıştır. Tüm örgütler arasında paylaşılan küresel IdP'leri kullanıyor. Küresel IdP'leri <adminPanelLink> yönetici panelinde </adminPanelLink>yönetin. Örgüt başına IdP'leri etkinleştirmek için, sunucu yapılandırmasını düzenleyin ve IdP modunu 'org' olarak ayarlayın. <configDocsLink> Belgeleri inceleyin </configDocsLink>. Küresel IdP'leri kullanmaya devam etmek istiyorsanız ve bunun örgüt ayarlarından kaybolmasını istiyorsanız, yapılandırmada modu otomatik olarak 'global' olarak ayarlayın.",
|
||||
"idpGlobalModeBannerUpgradeRequired": "Bu sunucuda örgüt başına kimlik sağlayıcılar (IdP'ler) devre dışı bırakılmıştır. Tüm örgütler arasında paylaşılan küresel IdP'leri kullanıyor. Küresel IdP'leri <adminPanelLink> yönetici panelinde </adminPanelLink>yönetin. Örgüt başına kimlik sağlayıcılar kullanmak için, Enterprise sürümüne yükseltmeniz gerekmektedir.",
|
||||
"idpGlobalModeBannerLicenseRequired": "Bu sunucuda örgüt başına kimlik sağlayıcılar (IdP'ler) devre dışı bırakılmıştır. Tüm örgütler arasında paylaşılan küresel IdP'leri kullanıyor. Küresel IdP'leri <adminPanelLink> yönetici panelinde </adminPanelLink>yönetin. Örgüt başına kimlik sağlayıcılar kullanmak için Enterprise lisansı gereklidir.",
|
||||
"idpDeletedDescription": "Kimlik sağlayıcı başarıyla silindi",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "Kimlik sağlayıcısını kalıcı olarak silmek istediğinizden emin misiniz?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "Kurulum - Pangolin",
|
||||
"orgNameRequired": "Kuruluş adı gereklidir",
|
||||
"orgIdRequired": "Kuruluş ID gereklidir",
|
||||
"orgIdMaxLength": "Organizasyon kimliği en fazla 32 karakter olmalıdır",
|
||||
"orgErrorCreate": "Kuruluş oluşturulurken bir hata oluştu",
|
||||
"pageNotFound": "Sayfa Bulunamadı",
|
||||
"pageNotFoundDescription": "Oops! Aradığınız sayfa mevcut değil.",
|
||||
"overview": "Genel Bakış",
|
||||
"home": "Ana Sayfa",
|
||||
"accessControl": "Erişim Kontrolü",
|
||||
"settings": "Ayarlar",
|
||||
"usersAll": "Tüm Kullanıcılar",
|
||||
"license": "Lisans",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "Kullanıcıyı Getir",
|
||||
"actionGetOrgUser": "Kuruluş Kullanıcısını Al",
|
||||
"actionListOrgDomains": "Kuruluş Alan Adlarını Listele",
|
||||
"actionGetDomain": "Alan Adını Al",
|
||||
"actionCreateOrgDomain": "Alan Adı Oluştur",
|
||||
"actionUpdateOrgDomain": "Alan Adını Güncelle",
|
||||
"actionDeleteOrgDomain": "Alan Adını Sil",
|
||||
"actionGetDNSRecords": "DNS Kayıtlarını Al",
|
||||
"actionRestartOrgDomain": "Alanı Yeniden Başlat",
|
||||
"actionCreateSite": "Site Oluştur",
|
||||
"actionDeleteSite": "Siteyi Sil",
|
||||
"actionGetSite": "Siteyi Al",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "Sunucu konsolundan kurulum simgesini girin.",
|
||||
"setupTokenRequired": "Kurulum simgesi gerekli",
|
||||
"actionUpdateSite": "Siteyi Güncelle",
|
||||
"actionResetSiteBandwidth": "Organizasyon Bant Genişliğini Sıfırla",
|
||||
"actionListSiteRoles": "İzin Verilen Site Rolleri Listele",
|
||||
"actionCreateResource": "Kaynak Oluştur",
|
||||
"actionDeleteResource": "Kaynağı Sil",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "Kullanıcıyı Kaldır",
|
||||
"actionListUsers": "Kullanıcıları Listele",
|
||||
"actionAddUserRole": "Kullanıcı Rolü Ekle",
|
||||
"actionSetUserOrgRoles": "Kullanıcı Rolleri Belirle",
|
||||
"actionGenerateAccessToken": "Erişim Jetonu Oluştur",
|
||||
"actionDeleteAccessToken": "Erişim Jetonunu Sil",
|
||||
"actionListAccessTokens": "Erişim Jetonlarını Listele",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "Kayıtları Görüntüle",
|
||||
"noneSelected": "Hiçbiri seçili değil",
|
||||
"orgNotFound2": "Hiçbir organizasyon bulunamadı.",
|
||||
"searchProgress": "Ara...",
|
||||
"searchPlaceholder": "Ara...",
|
||||
"emptySearchOptions": "Seçenek bulunamadı",
|
||||
"create": "Oluştur",
|
||||
"orgs": "Organizasyonlar",
|
||||
"loginError": "Beklenmeyen bir hata oluştu. Lütfen tekrar deneyin.",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "Özel",
|
||||
"sidebarAccessControl": "Erişim Kontrolü",
|
||||
"sidebarLogsAndAnalytics": "Kayıtlar & Analitik",
|
||||
"sidebarTeam": "Ekip",
|
||||
"sidebarUsers": "Kullanıcılar",
|
||||
"sidebarAdmin": "Yönetici",
|
||||
"sidebarInvitations": "Davetiye",
|
||||
"sidebarRoles": "Roller",
|
||||
"sidebarShareableLinks": "Bağlantılar",
|
||||
"sidebarApiKeys": "API Anahtarları",
|
||||
"sidebarProvisioning": "Tedarik",
|
||||
"sidebarSettings": "Ayarlar",
|
||||
"sidebarAllUsers": "Tüm Kullanıcılar",
|
||||
"sidebarIdentityProviders": "Kimlik Sağlayıcılar",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "Kayıt & Analiz",
|
||||
"sidebarBluePrints": "Planlar",
|
||||
"sidebarOrganization": "Organizasyon",
|
||||
"sidebarManagement": "Yönetim",
|
||||
"sidebarBillingAndLicenses": "Faturalandırma & Lisanslar",
|
||||
"sidebarLogsAnalytics": "Analitik",
|
||||
"blueprints": "Planlar",
|
||||
"blueprintsDescription": "Deklaratif yapılandırmaları uygulayın ve önceki çalışmaları görüntüleyin",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "Verilerin Ayrıştırılmış İçeriği (Salt Okunur)",
|
||||
"enableDockerSocket": "Docker Soketini Etkinleştir",
|
||||
"enableDockerSocketDescription": "Plan etiketleri için Docker Socket etiket toplamasını etkinleştirin. Newt'e soket yolu sağlanmalıdır.",
|
||||
"enableDockerSocketLink": "Daha fazla bilgi",
|
||||
"viewDockerContainers": "Docker Konteynerlerini Görüntüle",
|
||||
"containersIn": "{siteName} içindeki konteynerler",
|
||||
"selectContainerDescription": "Bu hedef için bir ana bilgisayar adı olarak kullanmak üzere herhangi bir konteyner seçin. Bir bağlantı noktası kullanmak için bir bağlantı noktasına tıklayın.",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "Ad Alanı: {namespace}",
|
||||
"domainPickerShowMore": "Daha Fazla Göster",
|
||||
"regionSelectorTitle": "Bölge Seç",
|
||||
"domainPickerRemoteExitNodeWarning": "Belirtilen alan adları, siteler uzak çıkış düğümlerine bağlandığında desteklenmez. Kaynakların uzak düğümlerde kullanılabilir olması için özel bir alan adı kullanın.",
|
||||
"regionSelectorInfo": "Bir bölge seçmek, konumunuz için daha iyi performans sağlamamıza yardımcı olur. Sunucunuzla aynı bölgede olmanıza gerek yoktur.",
|
||||
"regionSelectorPlaceholder": "Bölge Seçin",
|
||||
"regionSelectorComingSoon": "Yakında Geliyor",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "Kullanım Limitleri Genel Görünümü",
|
||||
"billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@pangolin.net adresinden bizimle iletişime geçin.",
|
||||
"billingDataUsage": "Veri Kullanımı",
|
||||
"billingOnlineTime": "Site Çevrimiçi Süresi",
|
||||
"billingUsers": "Aktif Kullanıcılar",
|
||||
"billingDomains": "Aktif Alanlar",
|
||||
"billingRemoteExitNodes": "Aktif Öz-Host Düğümleri",
|
||||
"billingSites": "Siteler",
|
||||
"billingUsers": "Kullanıcılar",
|
||||
"billingDomains": "Alan Adları",
|
||||
"billingOrganizations": "Organizasyonlar",
|
||||
"billingRemoteExitNodes": "Uzak Düğümler",
|
||||
"billingNoLimitConfigured": "Hiçbir limit yapılandırılmadı",
|
||||
"billingEstimatedPeriod": "Tahmini Fatura Dönemi",
|
||||
"billingIncludedUsage": "Dahil Kullanım",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "Portal URL'si alınamadı",
|
||||
"billingPortalError": "Portal Hatası",
|
||||
"billingDataUsageInfo": "Buluta bağlandığınızda, güvenli tünellerinizden aktarılan tüm verilerden ücret alınırsınız. Bu, tüm sitelerinizdeki gelen ve giden trafiği içerir. Limitinize ulaştığınızda, planınızı yükseltmeli veya kullanımı azaltmalısınız, aksi takdirde siteleriniz bağlantıyı keser. Düğümler kullanırken verilerden ücret alınmaz.",
|
||||
"billingOnlineTimeInfo": "Sitelerinizin buluta ne kadar süre bağlı kaldığına göre ücretlendirilirsiniz. Örneğin, 44,640 dakika, bir sitenin 24/7 boyunca tam bir ay boyunca çalışması anlamına gelir. Limitinize ulaştığınızda, planınızı yükseltmeyip kullanımı azaltmazsanız siteleriniz bağlantıyı keser. Düğümler kullanırken zamandan ücret alınmaz.",
|
||||
"billingUsersInfo": "Kuruluşunuzdaki her kullanıcı için ücretlendirilirsiniz. Faturalandırma, organizasyonunuza kayıtlı aktif kullanıcı hesaplarının sayısına göre günlük olarak hesaplanır.",
|
||||
"billingDomainInfo": "Kuruluşunuzdaki her alan adı için ücretlendirilirsiniz. Faturalandırma, organizasyonunuza kayıtlı aktif alan adları hesaplarının sayısına göre günlük olarak hesaplanır.",
|
||||
"billingRemoteExitNodesInfo": "Kuruluşunuzdaki her yönetilen Düğüm için ücretlendirilirsiniz. Faturalandırma, organizasyonunuza kayıtlı aktif yönetilen Düğümler sayısına göre günlük olarak hesaplanır.",
|
||||
"billingSInfo": "Kaç tane site kullanabileceğiniz",
|
||||
"billingUsersInfo": "Kaç tane kullanıcı kullanabileceğiniz",
|
||||
"billingDomainInfo": "Kaç tane alan adı kullanabileceğiniz",
|
||||
"billingRemoteExitNodesInfo": "Kaç tane uzaktan düğüm kullanabileceğiniz",
|
||||
"billingLicenseKeys": "Lisans Anahtarları",
|
||||
"billingLicenseKeysDescription": "Lisans anahtarı aboneliklerinizi yönetin",
|
||||
"billingLicenseSubscription": "Lisans Aboneliği",
|
||||
"billingInactive": "Pasif",
|
||||
"billingLicenseItem": "Lisans Öğesi",
|
||||
"billingQuantity": "Miktar",
|
||||
"billingTotal": "toplam",
|
||||
"billingModifyLicenses": "Lisans Aboneliğini Düzenle",
|
||||
"domainNotFound": "Alan Adı Bulunamadı",
|
||||
"domainNotFoundDescription": "Bu kaynak devre dışıdır çünkü alan adı sistemimizde artık mevcut değil. Bu kaynak için yeni bir alan adı belirleyin.",
|
||||
"failed": "Başarısız",
|
||||
"createNewOrgDescription": "Yeni bir organizasyon oluşturun",
|
||||
"organization": "Kuruluş",
|
||||
"primary": "Birincil",
|
||||
"port": "Bağlantı Noktası",
|
||||
"securityKeyManage": "Güvenlik Anahtarlarını Yönet",
|
||||
"securityKeyDescription": "Şifresiz kimlik doğrulama için güvenlik anahtarları ekleyin veya kaldırın",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "HTTP dışı kaynaklar için bağlantı noktası numarası gereklidir",
|
||||
"resourcePortNotAllowed": "HTTP kaynakları için bağlantı noktası numarası ayarlanmamalı",
|
||||
"billingPricingCalculatorLink": "Fiyat Hesaplayıcı",
|
||||
"billingYourPlan": "Planınız",
|
||||
"billingViewOrModifyPlan": "Mevcut planınızı görüntüleyin veya düzenleyin",
|
||||
"billingViewPlanDetails": "Plan Detaylarını Görüntüle",
|
||||
"billingUsageAndLimits": "Kullanım ve Sınırlar",
|
||||
"billingViewUsageAndLimits": "Planınızın limitlerini ve mevcut kullanım durumunu görüntüleyin",
|
||||
"billingCurrentUsage": "Mevcut Kullanım",
|
||||
"billingMaximumLimits": "Maksimum Sınırlar",
|
||||
"billingRemoteNodes": "Uzak Düğümler",
|
||||
"billingUnlimited": "Sınırsız",
|
||||
"billingPaidLicenseKeys": "Ücretli Lisans Anahtarları",
|
||||
"billingManageLicenseSubscription": "Kendi barındırdığınız ücretli lisans anahtarları için aboneliğinizi yönetin",
|
||||
"billingCurrentKeys": "Mevcut Anahtarlar",
|
||||
"billingModifyCurrentPlan": "Mevcut Planı Düzenle",
|
||||
"billingConfirmUpgrade": "Yükseltmeyi Onayla",
|
||||
"billingConfirmDowngrade": "Düşürmeyi Onayla",
|
||||
"billingConfirmUpgradeDescription": "Planınızı yükseltmek üzeresiniz. Yeni limitleri ve fiyatları aşağıda inceleyin.",
|
||||
"billingConfirmDowngradeDescription": "Planınızı düşürmek üzeresiniz. Yeni limitleri ve fiyatları aşağıda inceleyin.",
|
||||
"billingPlanIncludes": "Plan İçerikleri",
|
||||
"billingProcessing": "İşleniyor...",
|
||||
"billingConfirmUpgradeButton": "Yükseltmeyi Onayla",
|
||||
"billingConfirmDowngradeButton": "Düşürmeyi Onayla",
|
||||
"billingLimitViolationWarning": "Kullanım Yeni Plan Sınırlarını Aşıyor",
|
||||
"billingLimitViolationDescription": "Mevcut kullanımınız bu planın sınırlarını aşıyor. Düzeltmelerden sonra, yeni sınırlar içinde kalana kadar tüm işlemler devre dışı bırakılacak. Lütfen şu anda limitlerin üzerinde olan özellikleri inceleyin. İhlal edilen sınırlar:",
|
||||
"billingFeatureLossWarning": "Özellik Kullanılabilirlik Bildirimi",
|
||||
"billingFeatureLossDescription": "Plan düşürüldüğünde, yeni planda mevcut olmayan özellikler otomatik olarak devre dışı bırakılacaktır. Bazı ayarlar ve yapılar kaybolabilir. Hangi özelliklerin artık mevcut olmayacağını anlamak için fiyat tablosunu inceleyiniz.",
|
||||
"billingUsageExceedsLimit": "Mevcut kullanım ({current}) limitleri ({limit}) aşıyor",
|
||||
"billingPastDueTitle": "Ödeme Geçmiş",
|
||||
"billingPastDueDescription": "Ödemenizın vadesi geçti. Mevcut plan özelliklerinizi kullanmaya devam etmek için lütfen ödeme yöntemini güncelleyin. Sorun çözülmezse aboneliğiniz iptal edilecek ve ücretsiz seviyeye dönüleceksiniz.",
|
||||
"billingUnpaidTitle": "Ödenmemiş Abonelik",
|
||||
"billingUnpaidDescription": "Aboneliğiniz ödenmedi ve ücretsiz seviyeye geri döndünüz. Aboneliğinizi geri yüklemek için lütfen ödeme yöntemini güncelleyin.",
|
||||
"billingIncompleteTitle": "Eksik Ödeme",
|
||||
"billingIncompleteDescription": "Ödemeniz eksik. Aboneliğinizi etkinleştirmek için lütfen ödeme sürecini tamamlayın.",
|
||||
"billingIncompleteExpiredTitle": "Ödeme Süresi Doldu",
|
||||
"billingIncompleteExpiredDescription": "Ödemeniz hiç tamamlanmadı ve süresi doldu. Ücretsiz seviyeye geri döndünüz. Ücretli özelliklere erişimi yeniden sağlamak için lütfen yeniden abone olun.",
|
||||
"billingManageSubscription": "Aboneliğinizi Yönetin",
|
||||
"billingResolvePaymentIssue": "Yükseltmeden veya düşürmeden önce ödeme sorunuzu çözün",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Kabul ediyorum",
|
||||
"termsOfService": "hizmet şartları",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "Zaman saniye cinsindendir",
|
||||
"requireDeviceApproval": "Cihaz Onaylarını Gerektir",
|
||||
"requireDeviceApprovalDescription": "Bu role sahip kullanıcıların yeni cihazlarının bağlanabilmesi ve kaynaklara erişebilmesi için bir yönetici tarafından onaylanması gerekiyor.",
|
||||
"sshAccess": "SSH Erişimi",
|
||||
"roleAllowSsh": "SSH'a İzin Ver",
|
||||
"roleAllowSshAllow": "İzin Ver",
|
||||
"roleAllowSshDisallow": "İzin Verme",
|
||||
"roleAllowSshDescription": "Bu role sahip kullanıcıların SSH aracılığıyla kaynaklara bağlanmasına izin verin. Devre dışı bırakıldığında, rol SSH erişimini kullanamaz.",
|
||||
"sshSudoMode": "Sudo Erişimi",
|
||||
"sshSudoModeNone": "Hiçbiri",
|
||||
"sshSudoModeNoneDescription": "Kullanıcı, sudo komutunu kullanarak komut çalıştıramaz.",
|
||||
"sshSudoModeFull": "Tam Sudo",
|
||||
"sshSudoModeFullDescription": "Kullanıcı, sudo komutuyla her türlü komutu çalıştırabilir.",
|
||||
"sshSudoModeCommands": "Komutlar",
|
||||
"sshSudoModeCommandsDescription": "Kullanıcı sadece belirtilen komutları sudo ile çalıştırabilir.",
|
||||
"sshSudo": "Sudo'ya izin ver",
|
||||
"sshSudoCommands": "Sudo Komutları",
|
||||
"sshSudoCommandsDescription": "Kullanıcının sudo ile çalıştırmasına izin verilen komutların virgülle ayrılmış listesi.",
|
||||
"sshCreateHomeDir": "Ev Dizini Oluştur",
|
||||
"sshUnixGroups": "Unix Grupları",
|
||||
"sshUnixGroupsDescription": "Hedef konakta kullanıcıya eklenecek Unix gruplarının virgülle ayrılmış listesi.",
|
||||
"retryAttempts": "Tekrar Deneme Girişimleri",
|
||||
"expectedResponseCodes": "Beklenen Yanıt Kodları",
|
||||
"expectedResponseCodesDescription": "Sağlıklı durumu gösteren HTTP durum kodu. Boş bırakılırsa, 200-300 arası sağlıklı kabul edilir.",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "Çıkış Düğümü",
|
||||
"country": "Ülke",
|
||||
"rulesMatchCountry": "Şu anda kaynak IP'ye dayanarak",
|
||||
"region": "Bölge",
|
||||
"selectRegion": "Bölgeyi seçin",
|
||||
"searchRegions": "Bölgeleri ara...",
|
||||
"noRegionFound": "Bölge bulunamadı.",
|
||||
"rulesMatchRegion": "Başka ülkelerin bölgesel gruplandırmasını seçin",
|
||||
"rulesErrorInvalidRegion": "Geçersiz bölge",
|
||||
"rulesErrorInvalidRegionDescription": "Lütfen geçerli bir bölge seçin.",
|
||||
"regionAfrica": "Afrika",
|
||||
"regionNorthernAfrica": "Kuzey Afrika",
|
||||
"regionEasternAfrica": "Doğu Afrika",
|
||||
"regionMiddleAfrica": "Orta Afrika",
|
||||
"regionSouthernAfrica": "Güney Afrika",
|
||||
"regionWesternAfrica": "Batı Afrika",
|
||||
"regionAmericas": "Amerika",
|
||||
"regionCaribbean": "Karayipler",
|
||||
"regionCentralAmerica": "Orta Amerika",
|
||||
"regionSouthAmerica": "Güney Amerika",
|
||||
"regionNorthernAmerica": "Kuzey Amerika",
|
||||
"regionAsia": "Asya",
|
||||
"regionCentralAsia": "Orta Asya",
|
||||
"regionEasternAsia": "Doğu Asya",
|
||||
"regionSouthEasternAsia": "Güneydoğu Asya",
|
||||
"regionSouthernAsia": "Güney Asya",
|
||||
"regionWesternAsia": "Batı Asya",
|
||||
"regionEurope": "Avrupa",
|
||||
"regionEasternEurope": "Doğu Avrupa",
|
||||
"regionNorthernEurope": "Kuzey Avrupa",
|
||||
"regionSouthernEurope": "Güney Avrupa",
|
||||
"regionWesternEurope": "Batı Avrupa",
|
||||
"regionOceania": "Okyanusya",
|
||||
"regionAustraliaAndNewZealand": "Avustralya ve Yeni Zelanda",
|
||||
"regionMelanesia": "Melanezya",
|
||||
"regionMicronesia": "Mikronezya",
|
||||
"regionPolynesia": "Polinezya",
|
||||
"managedSelfHosted": {
|
||||
"title": "Yönetilen Self-Hosted",
|
||||
"description": "Daha güvenilir ve düşük bakım gerektiren, ekstra özelliklere sahip kendi kendine barındırabileceğiniz Pangolin sunucusu",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "Geçersiz değer",
|
||||
"idpTypeLabel": "Kimlik Sağlayıcı Türü",
|
||||
"roleMappingExpressionPlaceholder": "örn., contains(gruplar, 'yönetici') && 'Yönetici' || 'Üye'",
|
||||
"roleMappingModeFixedRoles": "Sabit Roller",
|
||||
"roleMappingModeMappingBuilder": "Harita Oluşturucu",
|
||||
"roleMappingModeRawExpression": "Ham İfade",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "Bir veya daha fazla rol seçin",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "Rol isimlerini yazın (organizasyon başına tam eşleşme)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "Her otomatik tedarik edilmiş kullanıcıya aynı rol setini atayın.",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "Varsayılan politikalar için, kullanıcıların sağlandığı her organizasyonda mevcut olan rol isimlerini yazın. İsimler tam olarak eşleşmelidir.",
|
||||
"roleMappingClaimPath": "Hak Talep Yolu",
|
||||
"roleMappingClaimPathPlaceholder": "gruplar",
|
||||
"roleMappingClaimPathDescription": "Kaynak değerleri içeren belirteç yükündeki yol (örneğin, gruplar).",
|
||||
"roleMappingMatchValue": "Eşleme Değeri",
|
||||
"roleMappingAssignRoles": "Rolleri Ata",
|
||||
"roleMappingAddMappingRule": "Eşleme Kuralı Ekle",
|
||||
"roleMappingRawExpressionResultDescription": "İfade bir string veya string dizisine değerlendirilmelidir.",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "İfade bir string (tek rol ismi) olarak değerlendirilmelidir.",
|
||||
"roleMappingMatchValuePlaceholder": "Eşleme değeri (örneğin: admin)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "Rol isimlerini yazın (organizasyon başına tam eşleşme)",
|
||||
"roleMappingBuilderFreeformRowHint": "Rol isimleri her hedef organizasyondaki bir rol ile eşleşmelidir.",
|
||||
"roleMappingRemoveRule": "Kaldır",
|
||||
"idpGoogleConfiguration": "Google Yapılandırması",
|
||||
"idpGoogleConfigurationDescription": "Google OAuth2 kimlik bilgilerinizi yapılandırın",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 İstemci Kimliğiniz",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "Kimlik Sayfaları için markayı kaldırmak istediğinizden emin misiniz?",
|
||||
"authPageBrandingDeleteConfirm": "Markayı Silmeyi Onayla",
|
||||
"brandingLogoURL": "Logo URL",
|
||||
"brandingLogoURLOrPath": "Logo URL veya Yol",
|
||||
"brandingLogoPathDescription": "Bir URL veya yerel bir yol girin.",
|
||||
"brandingLogoURLDescription": "Logo resminiz için genel olarak erişilebilir bir URL girin.",
|
||||
"brandingPrimaryColor": "Ana Renk",
|
||||
"brandingLogoWidth": "Genişlik (px)",
|
||||
"brandingLogoHeight": "Yükseklik (px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "Standart girişe geri dön",
|
||||
"orgAuthNoAccount": "Hesabınız yok mu?",
|
||||
"subscriptionRequiredToUse": "Bu özelliği kullanmak için abonelik gerekmektedir.",
|
||||
"mustUpgradeToUse": "Bu özelliği kullanmak için aboneliğinizi yükseltmelisiniz.",
|
||||
"subscriptionRequiredTierToUse": "Bu özellik <tierLink>{tier}</tierLink> veya daha üstünü gerektirir.",
|
||||
"upgradeToTierToUse": "Bu özelliği kullanmak için <tierLink>{tier}</tierLink> veya daha üst bir seviyeye yükseltin.",
|
||||
"subscriptionTierTier1": "Ana Sayfa",
|
||||
"subscriptionTierTier2": "Takım",
|
||||
"subscriptionTierTier3": "İşletme",
|
||||
"subscriptionTierEnterprise": "Kurumsal",
|
||||
"idpDisabled": "Kimlik sağlayıcılar devre dışı bırakılmıştır.",
|
||||
"orgAuthPageDisabled": "Kuruluş kimlik doğrulama sayfası devre dışı bırakılmıştır.",
|
||||
"domainRestartedDescription": "Alan doğrulaması başarıyla yeniden başlatıldı",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "Bir lisans alın",
|
||||
"description": "Bir plan seçin ve Pangolin'i nasıl kullanmayı planladığınızı anlatın.",
|
||||
"chooseTier": "Planınızı seçin",
|
||||
"viewPricingLink": "Fiyatları, özellikleri ve limitleri görüntüleyin",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "Başlangıç",
|
||||
"description": "Kurumsal özellikler, 25 kullanıcı, 25 site ve topluluk desteği."
|
||||
},
|
||||
"scale": {
|
||||
"title": "Ölçek",
|
||||
"description": "Kurumsal özellikler, 50 kullanıcı, 50 site ve öncelikli destek."
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "Yalnızca kişisel kullanım (ücretsiz lisans — ödeme yapılmaz)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "Ödemeye Devam Et"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "Ödeme Hatası",
|
||||
"description": "Ödeme işlemi başlatılamadı. Lütfen tekrar deneyin."
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "Öncelik",
|
||||
"priorityDescription": "Daha yüksek öncelikli rotalar önce değerlendirilir. Öncelik = 100, otomatik sıralama anlamına gelir (sistem karar verir). Manuel öncelik uygulamak için başka bir numara kullanın.",
|
||||
"instanceName": "Örnek İsmi",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "Erişim günlüklerini ne kadar süre tutacağını belirle",
|
||||
"logRetentionActionLabel": "Eylem Günlüğü Saklama",
|
||||
"logRetentionActionDescription": "Eylem günlüklerini ne kadar süre tutacağını belirle",
|
||||
"logRetentionConnectionLabel": "Bağlantı kayıtlarını ne kadar süre saklayacağınız",
|
||||
"logRetentionConnectionDescription": "Bağlantı kayıtlarını ne kadar süre saklayacağınız",
|
||||
"logRetentionDisabled": "Devre Dışı",
|
||||
"logRetention3Days": "3 gün",
|
||||
"logRetention7Days": "7 gün",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "Bir sonraki yılın sonu",
|
||||
"actionLogsDescription": "Bu organizasyondaki eylemler geçmişini görüntüleyin",
|
||||
"accessLogsDescription": "Bu organizasyondaki kaynaklar için erişim kimlik doğrulama isteklerini görüntüleyin",
|
||||
"licenseRequiredToUse": "Bu özelliği kullanmak için bir kurumsal lisans gereklidir.",
|
||||
"connectionLogs": "Bağlantı Kayıtları",
|
||||
"connectionLogsDescription": "Bu organizasyondaki tüneller için bağlantı geçmişine bakın",
|
||||
"sidebarLogsConnection": "Bağlantı Kayıtları",
|
||||
"sidebarLogsStreaming": "Akış",
|
||||
"sourceAddress": "Kaynak Adresi",
|
||||
"destinationAddress": "Hedef Adresi",
|
||||
"duration": "Süre",
|
||||
"licenseRequiredToUse": "Bu özelliği kullanmak için bir <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisansı veya <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> gereklidir. <bookADemoLink>Tanıtım veya POC denemesi ayarlayın</bookADemoLink>.",
|
||||
"ossEnterpriseEditionRequired": "Bu özelliği kullanmak için <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> gereklidir. Bu özellik ayrıca <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>’da da mevcuttur. <bookADemoLink>Tanıtım veya POC denemesi ayarlayın</bookADemoLink>.",
|
||||
"certResolver": "Sertifika Çözücü",
|
||||
"certResolverDescription": "Bu kaynak için kullanılacak sertifika çözücüsünü seçin.",
|
||||
"selectCertResolver": "Sertifika Çözücü Seçin",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "Erişim Kontrolü",
|
||||
"editInternalResourceDialogAccessControlDescription": "Bağlandığında bu kaynağa erişimi olan roller, kullanıcılar ve makine müşterilerini kontrol edin. Yöneticiler her zaman erişime sahiptir.",
|
||||
"editInternalResourceDialogPortRangeValidationError": "Port aralığı, tüm portlar için \"*\" veya virgülle ayrılmış bir port ve aralık listesi olmalıdır (ör. \"80,443,8000-9000\"). Portlar 1 ile 65535 arasında olmalıdır.",
|
||||
"internalResourceAuthDaemonStrategy": "SSH Kimlik Doğrulama Daemon Yeri",
|
||||
"internalResourceAuthDaemonStrategyDescription": "SSH kimlik doğrulama sunucusunun nerede çalışacağını seçin: sitede (Newt) veya uzak bir ana bilgisayarda.",
|
||||
"internalResourceAuthDaemonDescription": "SSH kimlik doğrulama sunucusu, bu kaynak için SSH anahtar imzalama ve PAM kimlik doğrulamasını yapar. Sitede (Newt) veya ayrı bir uzak ana bilgisayarda çalışıp çalışmayacağını seçin. Daha fazla bilgi için <docsLink> belgeleri</docsLink> görün.",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "Strateji Seçin",
|
||||
"internalResourceAuthDaemonStrategyLabel": "Konum",
|
||||
"internalResourceAuthDaemonSite": "Sitede",
|
||||
"internalResourceAuthDaemonSiteDescription": "Kimlik doğrulama sunucusu sitede (Newt) çalışır.",
|
||||
"internalResourceAuthDaemonRemote": "Uzak Ana Bilgisayar",
|
||||
"internalResourceAuthDaemonRemoteDescription": "Kimlik doğrulama sunucusu, site olmayan bir ana bilgisayarda çalışır.",
|
||||
"internalResourceAuthDaemonPort": "Daemon Portu (isteğe bağlı)",
|
||||
"orgAuthWhatsThis": "Kuruluş kimliğimi nerede bulabilirim?",
|
||||
"learnMore": "Daha fazla bilgi",
|
||||
"backToHome": "Ana sayfaya geri dön",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "Güvenlik Duvarı Etkin",
|
||||
"autoUpdatesEnabled": "Otomatik Güncellemeler Etkin",
|
||||
"tpmAvailable": "TPM Mevcut",
|
||||
"windowsAntivirusEnabled": "Antivirüs Etkinleştirildi",
|
||||
"macosSipEnabled": "Sistem Bütünlüğü Koruması (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "Güvenlik Duvarı Gizlilik Modu",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "Cihaz Onaylarını Etkinleştir",
|
||||
"approvalsEmptyStateStep2Description": "Bir rolü düzenleyin ve 'Cihaz Onaylarını Gerektir' seçeneğini etkinleştirin. Bu role sahip kullanıcıların yeni cihazlar için yönetici onayına ihtiyacı olacaktır.",
|
||||
"approvalsEmptyStatePreviewDescription": "Önizleme: Etkinleştirildiğinde, bekleyen cihaz talepleri incelenmek üzere burada görünecektir.",
|
||||
"approvalsEmptyStateButtonText": "Rolleri Yönet"
|
||||
"approvalsEmptyStateButtonText": "Rolleri Yönet",
|
||||
"domainErrorTitle": "Alan adınızı doğrulamada sorun yaşıyoruz",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Rol eşleme ve organizasyon politikalarını <policiesTabLink>Otomatik Tedarik Ayarları</policiesTabLink> sekmesinde yapılandırın.",
|
||||
"streamingTitle": "Olay Akışı",
|
||||
"streamingDescription": "Olayları organizasyonunuzdan dış hedeflere gerçek zamanlı olarak iletin.",
|
||||
"streamingUnnamedDestination": "Adsız hedef",
|
||||
"streamingNoUrlConfigured": "URL yapılandırılmadı",
|
||||
"streamingAddDestination": "Hedef Ekle",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookDescription": "Esnek kimlik doğrulama ve şablon oluşturmayla her HTTP uç noktasına olaylar gönderin.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Olayları S3 uyumlu bir nesne depolama kovasına iletin. Yakında gelicek.",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "Olayları doğrudan Datadog hesabınıza iletin. Yakında gelicek.",
|
||||
"streamingTypePickerDescription": "Başlamak için bir hedef türü seçin.",
|
||||
"streamingFailedToLoad": "Hedefler yüklenemedi",
|
||||
"streamingUnexpectedError": "Beklenmeyen bir hata oluştu.",
|
||||
"streamingFailedToUpdate": "Hedef güncellenemedi",
|
||||
"streamingDeletedSuccess": "Hedef başarıyla silindi",
|
||||
"streamingFailedToDelete": "Hedef silinemedi",
|
||||
"streamingDeleteTitle": "Hedefi Sil",
|
||||
"streamingDeleteButtonText": "Hedefi Sil",
|
||||
"streamingDeleteDialogAreYouSure": "Silmek istediğinizden emin misiniz",
|
||||
"streamingDeleteDialogThisDestination": "bu hedefi",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? Tüm yapılandırma kalıcı olarak kaldırılacak.",
|
||||
"httpDestEditTitle": "Hedefi Düzenle",
|
||||
"httpDestAddTitle": "HTTP Hedefi Ekle",
|
||||
"httpDestEditDescription": "Bu HTTP olay akışı hedefine yapılandırmayı güncelleyin.",
|
||||
"httpDestAddDescription": "Organizasyonunuzun olaylarını almak için yeni bir HTTP uç noktası yapılandırın.",
|
||||
"httpDestTabSettings": "Ayarlar",
|
||||
"httpDestTabHeaders": "Başlıklar",
|
||||
"httpDestTabBody": "Gövde",
|
||||
"httpDestTabLogs": "Kayıtlar",
|
||||
"httpDestNamePlaceholder": "Benim HTTP hedefim",
|
||||
"httpDestUrlLabel": "Hedef URL",
|
||||
"httpDestUrlErrorHttpRequired": "URL http veya https kullanmalıdır",
|
||||
"httpDestUrlErrorHttpsRequired": "Bulut dağıtımlarında HTTPS gereklidir",
|
||||
"httpDestUrlErrorInvalid": "Geçerli bir URL girin (örn. https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "Kimlik Doğrulama",
|
||||
"httpDestAuthDescription": "Uç noktanıza yapılan isteklerin nasıl kimlik doğrulandığını seçin.",
|
||||
"httpDestAuthNoneTitle": "Kimlik Doğrulama Yok",
|
||||
"httpDestAuthNoneDescription": "Yetkilendirme başlığı olmadan istekler gönderir.",
|
||||
"httpDestAuthBearerTitle": "Taşıyıcı Jetonu",
|
||||
"httpDestAuthBearerDescription": "Her isteğe bir Yetkilendirme: Taşıyıcı <token> başlığı ekler.",
|
||||
"httpDestAuthBearerPlaceholder": "API anahtarınız veya jetonunuz",
|
||||
"httpDestAuthBasicTitle": "Temel Kimlik Doğrulama",
|
||||
"httpDestAuthBasicDescription": "Authorization: Temel <belirtecikler> başlığı ekler. Yetkilendirmeleri kullanıcı adı:şifre olarak sağlayın.",
|
||||
"httpDestAuthBasicPlaceholder": "kullanıcı adı:şifre",
|
||||
"httpDestAuthCustomTitle": "Özel Başlık",
|
||||
"httpDestAuthCustomDescription": "Kimlik doğrulama için özel bir HTTP başlık adı ve değer belirtin (örn. X-API-Key).",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "Başlık adı (örn. X-API-Key)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "Başlık değeri",
|
||||
"httpDestCustomHeadersTitle": "Özel HTTP Başlıkları",
|
||||
"httpDestCustomHeadersDescription": "Her giden isteğe özel başlıklar ekleyin. Statik jetonlar veya özel bir İçerik Türü için kullanışlıdır. Varsayılan olarak İçerik Türü: application/json gönderilir.",
|
||||
"httpDestNoHeadersConfigured": "Özel başlık yapılandırılmamış. Bir tane eklemek için \"Başlık Ekle\"ye tıklayın.",
|
||||
"httpDestHeaderNamePlaceholder": "Başlık adı",
|
||||
"httpDestHeaderValuePlaceholder": "Değer",
|
||||
"httpDestAddHeader": "Başlık Ekle",
|
||||
"httpDestBodyTemplateTitle": "Özel Gövde Şablonu",
|
||||
"httpDestBodyTemplateDescription": "Uç noktanıza gönderilen JSON yük yapısını kontrol edin. Devre dışı bırakılırsa, her olay için varsayılan bir JSON nesnesi gönderilir.",
|
||||
"httpDestEnableBodyTemplate": "Özel gövde şablonunu etkinleştir",
|
||||
"httpDestBodyTemplateLabel": "Gövde Şablonu (JSON)",
|
||||
"httpDestBodyTemplateHint": "Yükünüzdeki olay alanlarına atıfta bulunmak için şablon değişkenlerini kullanın.",
|
||||
"httpDestPayloadFormatTitle": "Yük Formatı",
|
||||
"httpDestPayloadFormatDescription": "Her bir istek gövdesine olayların nasıl serileştirildiği.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON Dizisi",
|
||||
"httpDestFormatJsonArrayDescription": "Her bir toplu işte bir istek, gövde bir JSON dizisidir. Çoğu genel webhook ve Datadog ile uyumludur.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Her bir toplu işte bir istek, gövde satırlarla ayrılmış JSON'dur - her satıra bir nesne, dış dizi yoktur. Splunk HEC, Elastic / OpenSearch ve Grafana Loki tarafından gereklidir.",
|
||||
"httpDestFormatSingleTitle": "Her İstek Başına Bir Olay",
|
||||
"httpDestFormatSingleDescription": "Her olay için ayrı bir HTTP POST gönderir. Toplu işlere yetkemeyen uç noktalar için kullanın.",
|
||||
"httpDestLogTypesTitle": "Kayıt Türleri",
|
||||
"httpDestLogTypesDescription": "Bu hedefe hangi kayıt türlerinin iletileceğini seçin. Yalnızca etkin kayıt türleri yayınlanacaktır.",
|
||||
"httpDestAccessLogsTitle": "Erişim Kayıtları",
|
||||
"httpDestAccessLogsDescription": "Kimlik doğrulanmış ve reddedilen talepler dahil kaynak erişim denemeleri.",
|
||||
"httpDestActionLogsTitle": "Eylem Kayıtları",
|
||||
"httpDestActionLogsDescription": "Kullanıcılar tarafından organizasyon içerisinde yapılan yönetici eylemleri.",
|
||||
"httpDestConnectionLogsTitle": "Bağlantı Kayıtları",
|
||||
"httpDestConnectionLogsDescription": "Site ve tünel bağlantı olayları, bağlantılar ve bağlantı kesilmeleri dahil.",
|
||||
"httpDestRequestLogsTitle": "İstek Kayıtları",
|
||||
"httpDestRequestLogsDescription": "Yönlendirilmiş kaynaklar için HTTP istek kayıtları, yöntem, yol ve yanıt kodu dahil.",
|
||||
"httpDestSaveChanges": "Değişiklikleri Kaydet",
|
||||
"httpDestCreateDestination": "Hedef Oluştur",
|
||||
"httpDestUpdatedSuccess": "Hedef başarıyla güncellendi",
|
||||
"httpDestCreatedSuccess": "Hedef başarıyla oluşturuldu",
|
||||
"httpDestUpdateFailed": "Hedef güncellenemedi",
|
||||
"httpDestCreateFailed": "Hedef oluşturulamadı"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"componentsMember": "您属于{count, plural, =0 {没有组织} one {一个组织} other {# 个组织}}。",
|
||||
"componentsInvalidKey": "检测到无效或过期的许可证密钥。按照许可证条款操作以继续使用所有功能。",
|
||||
"dismiss": "忽略",
|
||||
"subscriptionViolationMessage": "您的当前计划超出了您的限制。通过移除站点、用户或其他资源以保持在您的计划范围内来纠正问题。",
|
||||
"subscriptionViolationViewBilling": "查看计费",
|
||||
"componentsLicenseViolation": "许可证超限:该服务器使用了 {usedSites} 个站点,已超过授权的 {maxSites} 个。请遵守许可证条款以继续使用全部功能。",
|
||||
"componentsSupporterMessage": "感谢您的支持!您现在是 Pangolin 的 {tier} 用户。",
|
||||
"inviteErrorNotValid": "很抱歉,但看起来你试图访问的邀请尚未被接受或不再有效。",
|
||||
@@ -146,6 +148,11 @@
|
||||
"createLink": "创建链接",
|
||||
"resourcesNotFound": "找不到资源",
|
||||
"resourceSearch": "搜索资源",
|
||||
"machineSearch": "搜索机",
|
||||
"machinesSearch": "搜索机器客户端...",
|
||||
"machineNotFound": "未找到任何机",
|
||||
"userDeviceSearch": "搜索用户设备",
|
||||
"userDevicesSearch": "搜索用户设备...",
|
||||
"openMenu": "打开菜单",
|
||||
"resource": "资源",
|
||||
"title": "标题",
|
||||
@@ -173,6 +180,7 @@
|
||||
"resourceHTTPDescription": "通过使用完全限定的域名的HTTPS代理请求。",
|
||||
"resourceRaw": "TCP/UDP 资源",
|
||||
"resourceRawDescription": "通过使用端口号的原始TCP/UDP代理请求。",
|
||||
"resourceRawDescriptionCloud": "正在使用端口号使用 TCP/UDP 代理请求。需要站点连接到远程节点。",
|
||||
"resourceCreate": "创建资源",
|
||||
"resourceCreateDescription": "按照下面的步骤创建新资源",
|
||||
"resourceSeeAll": "查看所有资源",
|
||||
@@ -199,6 +207,7 @@
|
||||
"protocolSelect": "选择协议",
|
||||
"resourcePortNumber": "端口号",
|
||||
"resourcePortNumberDescription": "代理请求的外部端口号。",
|
||||
"back": "后退",
|
||||
"cancel": "取消",
|
||||
"resourceConfig": "配置片段",
|
||||
"resourceConfigDescription": "复制并粘贴这些配置片段以设置 TCP/UDP 资源",
|
||||
@@ -244,6 +253,17 @@
|
||||
"orgErrorDeleteMessage": "删除组织时出错。",
|
||||
"orgDeleted": "组织已删除",
|
||||
"orgDeletedMessage": "组织及其数据已被删除。",
|
||||
"deleteAccount": "删除帐户",
|
||||
"deleteAccountDescription": "永久删除您的帐户、您拥有的所有组织以及这些组织中的所有数据。此操作无法撤消。",
|
||||
"deleteAccountButton": "删除帐户",
|
||||
"deleteAccountConfirmTitle": "删除帐户",
|
||||
"deleteAccountConfirmMessage": "这将永久擦除您的帐户、您拥有的所有组织以及这些组织中的所有数据。这不能撤消。",
|
||||
"deleteAccountConfirmString": "删除帐户",
|
||||
"deleteAccountSuccess": "账户已删除",
|
||||
"deleteAccountSuccessMessage": "您的帐户已被删除。",
|
||||
"deleteAccountError": "删除帐户失败",
|
||||
"deleteAccountPreviewAccount": "您的帐户",
|
||||
"deleteAccountPreviewOrgs": "您拥有的组织 (和所有数据)",
|
||||
"orgMissing": "缺少组织 ID",
|
||||
"orgMissingMessage": "没有组织ID,无法重新生成邀请。",
|
||||
"accessUsersManage": "管理用户",
|
||||
@@ -308,6 +328,54 @@
|
||||
"apiKeysDelete": "删除 API 密钥",
|
||||
"apiKeysManage": "管理 API 密钥",
|
||||
"apiKeysDescription": "API 密钥用于认证集成 API",
|
||||
"provisioningKeysTitle": "置备密钥",
|
||||
"provisioningKeysManage": "管理置备键",
|
||||
"provisioningKeysDescription": "置备密钥用于验证您组织的自动站点配置。",
|
||||
"provisioningManage": "置备中",
|
||||
"provisioningDescription": "管理预配键和审查等待批准的站点。",
|
||||
"pendingSites": "待定站点",
|
||||
"siteApproveSuccess": "站点批准成功",
|
||||
"siteApproveError": "批准站点出错",
|
||||
"provisioningKeys": "置备键",
|
||||
"searchProvisioningKeys": "搜索配备密钥...",
|
||||
"provisioningKeysAdd": "生成置备键",
|
||||
"provisioningKeysErrorDelete": "删除预配键时出错",
|
||||
"provisioningKeysErrorDeleteMessage": "删除预配键时出错",
|
||||
"provisioningKeysQuestionRemove": "您确定要从组织中删除此预配键吗?",
|
||||
"provisioningKeysMessageRemove": "一旦移除,密钥不能再用于站点预配。",
|
||||
"provisioningKeysDeleteConfirm": "确认删除置备键",
|
||||
"provisioningKeysDelete": "删除置备键",
|
||||
"provisioningKeysCreate": "生成置备键",
|
||||
"provisioningKeysCreateDescription": "为组织生成一个新的预置密钥",
|
||||
"provisioningKeysSeeAll": "查看所有预配键",
|
||||
"provisioningKeysSave": "保存预配键",
|
||||
"provisioningKeysSaveDescription": "您只能看到一次。复制它到一个安全的地方。",
|
||||
"provisioningKeysErrorCreate": "创建预配键时出错",
|
||||
"provisioningKeysList": "新建预配键",
|
||||
"provisioningKeysMaxBatchSize": "最大批量大小",
|
||||
"provisioningKeysUnlimitedBatchSize": "无限批量大小(无限制)",
|
||||
"provisioningKeysMaxBatchUnlimited": "无限制",
|
||||
"provisioningKeysMaxBatchSizeInvalid": "输入一个有效的最大批处理大小(1-1,000,000)。",
|
||||
"provisioningKeysValidUntil": "有效期至",
|
||||
"provisioningKeysValidUntilHint": "留空为无过期。",
|
||||
"provisioningKeysValidUntilInvalid": "输入一个有效的日期和时间。",
|
||||
"provisioningKeysNumUsed": "使用的时间",
|
||||
"provisioningKeysLastUsed": "上次使用",
|
||||
"provisioningKeysNoExpiry": "没有过期",
|
||||
"provisioningKeysNeverUsed": "永不过期",
|
||||
"provisioningKeysEdit": "编辑置备键",
|
||||
"provisioningKeysEditDescription": "更新此密钥的最大批量大小和过期时间。",
|
||||
"provisioningKeysApproveNewSites": "批准新站点",
|
||||
"provisioningKeysApproveNewSitesDescription": "自动批准使用此密钥注册的站点。",
|
||||
"provisioningKeysUpdateError": "更新预配键时出错",
|
||||
"provisioningKeysUpdated": "置备密钥已更新",
|
||||
"provisioningKeysUpdatedDescription": "您的更改已保存。",
|
||||
"provisioningKeysBannerTitle": "站点置备密钥",
|
||||
"provisioningKeysBannerDescription": "生成一个预配键并使用它来在首次启动时自动创建站点——无需为每个站点设置单独的凭证。",
|
||||
"provisioningKeysBannerButtonText": "了解更多",
|
||||
"pendingSitesBannerTitle": "待定站点",
|
||||
"pendingSitesBannerDescription": "使用预配键连接的站点会出现在这里供审核。在站点开始运行之前批准并获取对您资源的访问权限。",
|
||||
"pendingSitesBannerButtonText": "了解更多",
|
||||
"apiKeysSettings": "{apiKeyName} 设置",
|
||||
"userTitle": "管理所有用户",
|
||||
"userDescription": "查看和管理系统中的所有用户",
|
||||
@@ -459,6 +527,8 @@
|
||||
"filterByApprovalState": "按批准状态过滤",
|
||||
"approvalListEmpty": "无批准",
|
||||
"approvalState": "审批状态",
|
||||
"approvalLoadMore": "加载更多",
|
||||
"loadingApprovals": "正在加载批准",
|
||||
"approve": "批准",
|
||||
"approved": "已批准",
|
||||
"denied": "被拒绝",
|
||||
@@ -492,9 +562,12 @@
|
||||
"userSaved": "用户已保存",
|
||||
"userSavedDescription": "用户已更新。",
|
||||
"autoProvisioned": "自动设置",
|
||||
"autoProvisionSettings": "自动提供设置",
|
||||
"autoProvisionedDescription": "允许此用户由身份提供商自动管理",
|
||||
"accessControlsDescription": "管理此用户在组织中可以访问和做什么",
|
||||
"accessControlsSubmit": "保存访问控制",
|
||||
"singleRolePerUserPlanNotice": "您的计划仅支持每个用户一个角色。",
|
||||
"singleRolePerUserEditionNotice": "此版本仅支持每个用户一个角色。",
|
||||
"roles": "角色",
|
||||
"accessUsersRoles": "管理用户和角色",
|
||||
"accessUsersRolesDescription": "邀请用户加入角色来管理访问组织",
|
||||
@@ -634,6 +707,7 @@
|
||||
"resourcesErrorUpdate": "切换资源失败",
|
||||
"resourcesErrorUpdateDescription": "更新资源时出错",
|
||||
"access": "访问权限",
|
||||
"accessControl": "访问控制",
|
||||
"shareLink": "{resource} 的分享链接",
|
||||
"resourceSelect": "选择资源",
|
||||
"shareLinks": "分享链接",
|
||||
@@ -774,6 +848,7 @@
|
||||
"accessRoleRemoved": "角色已删除",
|
||||
"accessRoleRemovedDescription": "角色已成功删除。",
|
||||
"accessRoleRequiredRemove": "删除此角色之前,请选择一个新角色来转移现有成员。",
|
||||
"network": "网络",
|
||||
"manage": "管理",
|
||||
"sitesNotFound": "未找到站点。",
|
||||
"pangolinServerAdmin": "服务器管理员 - Pangolin",
|
||||
@@ -789,6 +864,9 @@
|
||||
"sitestCountIncrease": "增加站点数量",
|
||||
"idpManage": "管理身份提供商",
|
||||
"idpManageDescription": "查看和管理系统中的身份提供商",
|
||||
"idpGlobalModeBanner": "此服务器上禁用了每个组织的身份提供商(Idps)。 它正在使用全局IdP(所有组织共享)。在 <adminPanelLink>管理面板</adminPanelLink>中管理全局IdP。 要启用每个组织的 IdP,请编辑服务器配置并将 IdP 模式设置为 org。 <configDocsLink>请参阅文档</configDocsLink>。 如果您想要继续使用全局IdP并使其从组织设置中消失,请在配置中将模式设置为全局模式。",
|
||||
"idpGlobalModeBannerUpgradeRequired": "此服务器上禁用了每个组织的身份提供商(Idps)。它正在使用全局身份提供商(所有组织共享)。 在 <adminPanelLink>管理面板</adminPanelLink>管理全局身份。要使用每个组织的身份提供者,您必须升级到企业版本。",
|
||||
"idpGlobalModeBannerLicenseRequired": "此服务器上禁用了每个组织的身份提供商(Idps)。它正在使用全局身份提供商(所有组织共享)。 在 <adminPanelLink>管理面板</adminPanelLink>管理全局身份。要使用每个组织的身份提供者,需要企业许可证。",
|
||||
"idpDeletedDescription": "身份提供商删除成功",
|
||||
"idpOidc": "OAuth2/OIDC",
|
||||
"idpQuestionRemove": "您确定要永久删除身份提供者吗?",
|
||||
@@ -1012,12 +1090,12 @@
|
||||
"pangolinSetup": "认证 - Pangolin",
|
||||
"orgNameRequired": "组织名称是必需的",
|
||||
"orgIdRequired": "组织ID是必需的",
|
||||
"orgIdMaxLength": "组织 ID 必须至少 32 个字符",
|
||||
"orgErrorCreate": "创建组织时出错",
|
||||
"pageNotFound": "找不到页面",
|
||||
"pageNotFoundDescription": "哎呀!您正在查找的页面不存在。",
|
||||
"overview": "概览",
|
||||
"home": "首页",
|
||||
"accessControl": "访问控制",
|
||||
"settings": "设置",
|
||||
"usersAll": "所有用户",
|
||||
"license": "许可协议",
|
||||
@@ -1080,6 +1158,12 @@
|
||||
"actionGetUser": "获取用户",
|
||||
"actionGetOrgUser": "获取组织用户",
|
||||
"actionListOrgDomains": "列出组织域",
|
||||
"actionGetDomain": "获取域",
|
||||
"actionCreateOrgDomain": "创建域",
|
||||
"actionUpdateOrgDomain": "更新域",
|
||||
"actionDeleteOrgDomain": "删除域",
|
||||
"actionGetDNSRecords": "获取 DNS 记录",
|
||||
"actionRestartOrgDomain": "重新启动域",
|
||||
"actionCreateSite": "创建站点",
|
||||
"actionDeleteSite": "删除站点",
|
||||
"actionGetSite": "获取站点",
|
||||
@@ -1091,6 +1175,7 @@
|
||||
"setupTokenDescription": "从服务器控制台输入设置令牌。",
|
||||
"setupTokenRequired": "需要设置令牌",
|
||||
"actionUpdateSite": "更新站点",
|
||||
"actionResetSiteBandwidth": "重置组织带宽",
|
||||
"actionListSiteRoles": "允许站点角色列表",
|
||||
"actionCreateResource": "创建资源",
|
||||
"actionDeleteResource": "删除资源",
|
||||
@@ -1120,6 +1205,7 @@
|
||||
"actionRemoveUser": "删除用户",
|
||||
"actionListUsers": "列出用户",
|
||||
"actionAddUserRole": "添加用户角色",
|
||||
"actionSetUserOrgRoles": "设置用户角色",
|
||||
"actionGenerateAccessToken": "生成访问令牌",
|
||||
"actionDeleteAccessToken": "删除访问令牌",
|
||||
"actionListAccessTokens": "访问令牌",
|
||||
@@ -1164,7 +1250,8 @@
|
||||
"actionViewLogs": "查看日志",
|
||||
"noneSelected": "未选择",
|
||||
"orgNotFound2": "未找到组织。",
|
||||
"searchProgress": "搜索中...",
|
||||
"searchPlaceholder": "搜索...",
|
||||
"emptySearchOptions": "未找到选项",
|
||||
"create": "创建",
|
||||
"orgs": "组织",
|
||||
"loginError": "发生意外错误。请重试。",
|
||||
@@ -1228,12 +1315,14 @@
|
||||
"sidebarClientResources": "非公开的",
|
||||
"sidebarAccessControl": "访问控制",
|
||||
"sidebarLogsAndAnalytics": "日志与分析",
|
||||
"sidebarTeam": "团队",
|
||||
"sidebarUsers": "用户",
|
||||
"sidebarAdmin": "管理员",
|
||||
"sidebarInvitations": "邀请",
|
||||
"sidebarRoles": "角色",
|
||||
"sidebarShareableLinks": "链接",
|
||||
"sidebarApiKeys": "API密钥",
|
||||
"sidebarProvisioning": "置备中",
|
||||
"sidebarSettings": "设置",
|
||||
"sidebarAllUsers": "所有用户",
|
||||
"sidebarIdentityProviders": "身份提供商",
|
||||
@@ -1246,6 +1335,8 @@
|
||||
"sidebarLogAndAnalytics": "日志与分析",
|
||||
"sidebarBluePrints": "蓝图",
|
||||
"sidebarOrganization": "组织",
|
||||
"sidebarManagement": "管理",
|
||||
"sidebarBillingAndLicenses": "帐单和许可证",
|
||||
"sidebarLogsAnalytics": "分析",
|
||||
"blueprints": "蓝图",
|
||||
"blueprintsDescription": "应用声明配置并查看先前运行的",
|
||||
@@ -1267,7 +1358,6 @@
|
||||
"parsedContents": "解析内容 (只读)",
|
||||
"enableDockerSocket": "启用 Docker 蓝图",
|
||||
"enableDockerSocketDescription": "启用 Docker Socket 标签擦除蓝图标签。套接字路径必须提供给新的。",
|
||||
"enableDockerSocketLink": "了解更多",
|
||||
"viewDockerContainers": "查看停靠容器",
|
||||
"containersIn": "{siteName} 中的容器",
|
||||
"selectContainerDescription": "选择任何容器作为目标的主机名。点击端口使用端口。",
|
||||
@@ -1395,6 +1485,7 @@
|
||||
"domainPickerNamespace": "命名空间:{namespace}",
|
||||
"domainPickerShowMore": "显示更多",
|
||||
"regionSelectorTitle": "选择区域",
|
||||
"domainPickerRemoteExitNodeWarning": "当站点连接到远程退出节点时不支持所提供的域。为了资源可在远程节点上使用,请使用自定义域名。",
|
||||
"regionSelectorInfo": "选择区域以帮助提升您所在地的性能。您不必与服务器在相同的区域。",
|
||||
"regionSelectorPlaceholder": "选择一个区域",
|
||||
"regionSelectorComingSoon": "即将推出",
|
||||
@@ -1404,10 +1495,11 @@
|
||||
"billingUsageLimitsOverview": "使用限制概览",
|
||||
"billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@pangolin.net。",
|
||||
"billingDataUsage": "数据使用情况",
|
||||
"billingOnlineTime": "站点在线时间",
|
||||
"billingUsers": "活跃用户",
|
||||
"billingDomains": "活跃域",
|
||||
"billingRemoteExitNodes": "活跃自托管节点",
|
||||
"billingSites": "站点",
|
||||
"billingUsers": "用户",
|
||||
"billingDomains": "域",
|
||||
"billingOrganizations": "球队",
|
||||
"billingRemoteExitNodes": "远程节点",
|
||||
"billingNoLimitConfigured": "未配置限制",
|
||||
"billingEstimatedPeriod": "估计结算周期",
|
||||
"billingIncludedUsage": "包含的使用量",
|
||||
@@ -1432,15 +1524,24 @@
|
||||
"billingFailedToGetPortalUrl": "无法获取门户网址",
|
||||
"billingPortalError": "门户错误",
|
||||
"billingDataUsageInfo": "当连接到云端时,您将为通过安全隧道传输的所有数据收取费用。 这包括您所有站点的进出流量。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取数据。",
|
||||
"billingOnlineTimeInfo": "您要根据您的网站连接到云端的时间长短收取费用。 例如,44,640分钟等于一个24/7全月运行的网站。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取费用。",
|
||||
"billingUsersInfo": "您为组织中的每个用户收取费用。每日计费是根据您组织中活跃用户帐户的数量计算的。",
|
||||
"billingDomainInfo": "您在组织中的每个域都要收取费用。每日计费是根据您组织中的活动域帐户数计算的。",
|
||||
"billingRemoteExitNodesInfo": "您为组织中的每个管理节点收取费用。计费是每日根据您组织中活跃的管理节点数计算的。",
|
||||
"billingSInfo": "您可以使用多少站点",
|
||||
"billingUsersInfo": "您可以使用多少用户",
|
||||
"billingDomainInfo": "您可以使用多少域",
|
||||
"billingRemoteExitNodesInfo": "您可以使用多少远程节点",
|
||||
"billingLicenseKeys": "许可证密钥",
|
||||
"billingLicenseKeysDescription": "管理您的许可证密钥订阅",
|
||||
"billingLicenseSubscription": "许可订阅",
|
||||
"billingInactive": "未激活",
|
||||
"billingLicenseItem": "许可证项目",
|
||||
"billingQuantity": "数量",
|
||||
"billingTotal": "总计",
|
||||
"billingModifyLicenses": "修改许可订阅",
|
||||
"domainNotFound": "域未找到",
|
||||
"domainNotFoundDescription": "此资源已禁用,因为该域不再在我们的系统中存在。请为此资源设置一个新域。",
|
||||
"failed": "失败",
|
||||
"createNewOrgDescription": "创建一个新组织",
|
||||
"organization": "组织",
|
||||
"primary": "主要的",
|
||||
"port": "端口",
|
||||
"securityKeyManage": "管理安全密钥",
|
||||
"securityKeyDescription": "添加或删除用于无密码认证的安全密钥",
|
||||
@@ -1512,6 +1613,42 @@
|
||||
"resourcePortRequired": "非 HTTP 资源必须输入端口号",
|
||||
"resourcePortNotAllowed": "HTTP 资源不应设置端口号",
|
||||
"billingPricingCalculatorLink": "价格计算器",
|
||||
"billingYourPlan": "您的计划",
|
||||
"billingViewOrModifyPlan": "查看或修改您当前的计划",
|
||||
"billingViewPlanDetails": "查看计划详细信息",
|
||||
"billingUsageAndLimits": "用法和限制",
|
||||
"billingViewUsageAndLimits": "查看您的计划限制和当前使用情况",
|
||||
"billingCurrentUsage": "当前使用情况",
|
||||
"billingMaximumLimits": "最大限制",
|
||||
"billingRemoteNodes": "远程节点",
|
||||
"billingUnlimited": "无限制",
|
||||
"billingPaidLicenseKeys": "付费许可证密钥",
|
||||
"billingManageLicenseSubscription": "管理您对付费的自托管许可证密钥的订阅",
|
||||
"billingCurrentKeys": "当前密钥",
|
||||
"billingModifyCurrentPlan": "修改当前计划",
|
||||
"billingConfirmUpgrade": "确认升级",
|
||||
"billingConfirmDowngrade": "确认降级",
|
||||
"billingConfirmUpgradeDescription": "您即将升级您的计划。请检查下面的新限额和定价。",
|
||||
"billingConfirmDowngradeDescription": "您即将降级计划。请检查下面的新限额和定价。",
|
||||
"billingPlanIncludes": "计划包含",
|
||||
"billingProcessing": "正在处理...",
|
||||
"billingConfirmUpgradeButton": "确认升级",
|
||||
"billingConfirmDowngradeButton": "确认降级",
|
||||
"billingLimitViolationWarning": "超出新计划限制",
|
||||
"billingLimitViolationDescription": "您当前的使用量超过了此计划的限制。降级后,所有操作都将被禁用,直到您在新的限制范围内减少使用量。 请查看以下当前超出限制的特性:",
|
||||
"billingFeatureLossWarning": "功能可用通知",
|
||||
"billingFeatureLossDescription": "如果降级,新计划中不可用的功能将被自动禁用。一些设置和配置可能会丢失。 请查看定价矩阵以了解哪些功能将不再可用。",
|
||||
"billingUsageExceedsLimit": "当前使用量 ({current}) 超出限制 ({limit})",
|
||||
"billingPastDueTitle": "过去到期的付款",
|
||||
"billingPastDueDescription": "您的付款已过期。请更新您的付款方法以继续使用您当前的计划功能。 如果不解决,您的订阅将被取消,您将被恢复到免费等级。",
|
||||
"billingUnpaidTitle": "订阅未付款",
|
||||
"billingUnpaidDescription": "您的订阅未付,您已恢复到免费等级。请更新您的付款方法以恢复您的订阅。",
|
||||
"billingIncompleteTitle": "付款不完成",
|
||||
"billingIncompleteDescription": "您的付款不完整。请完成付款过程以激活您的订阅。",
|
||||
"billingIncompleteExpiredTitle": "付款已过期",
|
||||
"billingIncompleteExpiredDescription": "您的付款尚未完成且已过期。您已恢复到免费级别。请再次订阅以恢复对已支付功能的访问。",
|
||||
"billingManageSubscription": "管理您的订阅",
|
||||
"billingResolvePaymentIssue": "请在升级或降级之前解决您的付款问题",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "我同意",
|
||||
"termsOfService": "服务条款",
|
||||
@@ -1585,6 +1722,24 @@
|
||||
"timeIsInSeconds": "时间以秒为单位",
|
||||
"requireDeviceApproval": "需要设备批准",
|
||||
"requireDeviceApprovalDescription": "具有此角色的用户需要管理员批准的新设备才能连接和访问资源。",
|
||||
"sshAccess": "SSH 访问",
|
||||
"roleAllowSsh": "允许 SSH",
|
||||
"roleAllowSshAllow": "允许",
|
||||
"roleAllowSshDisallow": "不允许",
|
||||
"roleAllowSshDescription": "允许具有此角色的用户通过 SSH 连接到资源。禁用时,角色不能使用 SSH 访问。",
|
||||
"sshSudoMode": "Sudo 访问",
|
||||
"sshSudoModeNone": "无",
|
||||
"sshSudoModeNoneDescription": "用户不能用sudo运行命令。",
|
||||
"sshSudoModeFull": "全苏多",
|
||||
"sshSudoModeFullDescription": "用户可以用 sudo 运行任何命令。",
|
||||
"sshSudoModeCommands": "命令",
|
||||
"sshSudoModeCommandsDescription": "用户只能用 sudo 运行指定的命令。",
|
||||
"sshSudo": "允许Sudo",
|
||||
"sshSudoCommands": "Sudo 命令",
|
||||
"sshSudoCommandsDescription": "逗号分隔的用户允许使用 sudo 运行的命令列表。",
|
||||
"sshCreateHomeDir": "创建主目录",
|
||||
"sshUnixGroups": "Unix 组",
|
||||
"sshUnixGroupsDescription": "用逗号分隔了Unix组,将用户添加到目标主机上。",
|
||||
"retryAttempts": "重试次数",
|
||||
"expectedResponseCodes": "期望响应代码",
|
||||
"expectedResponseCodesDescription": "HTTP 状态码表示健康状态。如留空,200-300 被视为健康。",
|
||||
@@ -1793,6 +1948,40 @@
|
||||
"exitNode": "出口节点",
|
||||
"country": "国家",
|
||||
"rulesMatchCountry": "当前基于源 IP",
|
||||
"region": "地区",
|
||||
"selectRegion": "选择区域",
|
||||
"searchRegions": "搜索区域...",
|
||||
"noRegionFound": "未找到区域。",
|
||||
"rulesMatchRegion": "选择一个区域国家组",
|
||||
"rulesErrorInvalidRegion": "无效区域",
|
||||
"rulesErrorInvalidRegionDescription": "请选择一个有效的区域。",
|
||||
"regionAfrica": "非洲",
|
||||
"regionNorthernAfrica": "B. 北非地区",
|
||||
"regionEasternAfrica": "东部非洲",
|
||||
"regionMiddleAfrica": "中东",
|
||||
"regionSouthernAfrica": "D. 南 非",
|
||||
"regionWesternAfrica": "D. 西部非洲",
|
||||
"regionAmericas": "Americas",
|
||||
"regionCaribbean": "加勒比",
|
||||
"regionCentralAmerica": "中美洲:",
|
||||
"regionSouthAmerica": "南 非",
|
||||
"regionNorthernAmerica": "北美洲:",
|
||||
"regionAsia": "亚洲",
|
||||
"regionCentralAsia": "B. 亚 洲",
|
||||
"regionEasternAsia": "东亚",
|
||||
"regionSouthEasternAsia": "D. 东南亚区域",
|
||||
"regionSouthernAsia": "D. 亚 洲",
|
||||
"regionWesternAsia": "西亚",
|
||||
"regionEurope": "欧洲",
|
||||
"regionEasternEurope": "D. 欧 洲",
|
||||
"regionNorthernEurope": "北欧洲",
|
||||
"regionSouthernEurope": "南欧洲",
|
||||
"regionWesternEurope": "西欧洲",
|
||||
"regionOceania": "Oceania",
|
||||
"regionAustraliaAndNewZealand": "澳大利亚和新西兰",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
"regionPolynesia": "Polynesia",
|
||||
"managedSelfHosted": {
|
||||
"title": "托管自托管",
|
||||
"description": "更可靠和低维护自我托管的 Pangolin 服务器,带有额外的铃声和告密器",
|
||||
@@ -1841,6 +2030,25 @@
|
||||
"invalidValue": "无效的值",
|
||||
"idpTypeLabel": "身份提供者类型",
|
||||
"roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'",
|
||||
"roleMappingModeFixedRoles": "固定角色",
|
||||
"roleMappingModeMappingBuilder": "映射构建器",
|
||||
"roleMappingModeRawExpression": "原始表达式",
|
||||
"roleMappingFixedRolesPlaceholderSelect": "选择一个或多个角色",
|
||||
"roleMappingFixedRolesPlaceholderFreeform": "输入角色名称 (每个组织确切匹配)",
|
||||
"roleMappingFixedRolesDescriptionSameForAll": "将相同的角色分配给每个自动配备的用户。",
|
||||
"roleMappingFixedRolesDescriptionDefaultPolicy": "对于缺省策略,每个提供用户的组织中存在的角色名称类型。名称必须完全匹配。",
|
||||
"roleMappingClaimPath": "认领路径",
|
||||
"roleMappingClaimPathPlaceholder": "组",
|
||||
"roleMappingClaimPathDescription": "包含源值的 token 有效负载路径 (例如组)。",
|
||||
"roleMappingMatchValue": "匹配值",
|
||||
"roleMappingAssignRoles": "分配角色",
|
||||
"roleMappingAddMappingRule": "添加映射规则",
|
||||
"roleMappingRawExpressionResultDescription": "表达式必须值为字符串或字符串。",
|
||||
"roleMappingRawExpressionResultDescriptionSingleRole": "表达式必须计算到字符串(单个角色名称)。",
|
||||
"roleMappingMatchValuePlaceholder": "匹配值(例如: 管理员)",
|
||||
"roleMappingAssignRolesPlaceholderFreeform": "输入角色名称 (每个组织确切)",
|
||||
"roleMappingBuilderFreeformRowHint": "角色名称必须匹配每个目标组织的角色。",
|
||||
"roleMappingRemoveRule": "删除",
|
||||
"idpGoogleConfiguration": "Google 配置",
|
||||
"idpGoogleConfigurationDescription": "配置 Google OAuth2 凭据",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
@@ -1877,6 +2085,9 @@
|
||||
"authPageBrandingQuestionRemove": "您确定要移除授权页面的品牌吗?",
|
||||
"authPageBrandingDeleteConfirm": "确认删除品牌",
|
||||
"brandingLogoURL": "Logo URL",
|
||||
"brandingLogoURLOrPath": "徽标URL或路径",
|
||||
"brandingLogoPathDescription": "输入网址或本地路径。",
|
||||
"brandingLogoURLDescription": "请在您的徽标图片中输入一个可公开访问的 URL。",
|
||||
"brandingPrimaryColor": "主要颜色",
|
||||
"brandingLogoWidth": "宽度(px)",
|
||||
"brandingLogoHeight": "高度(px)",
|
||||
@@ -1926,6 +2137,13 @@
|
||||
"orgAuthBackToSignIn": "返回标准登录",
|
||||
"orgAuthNoAccount": "没有账户?",
|
||||
"subscriptionRequiredToUse": "需要订阅才能使用此功能。",
|
||||
"mustUpgradeToUse": "您必须升级您的订阅才能使用此功能。",
|
||||
"subscriptionRequiredTierToUse": "此功能需要 <tierLink>{tier}</tierLink> 或更高级别。",
|
||||
"upgradeToTierToUse": "升级到 <tierLink>{tier}</tierLink> 或更高级别以使用此功能。",
|
||||
"subscriptionTierTier1": "首页",
|
||||
"subscriptionTierTier2": "团队",
|
||||
"subscriptionTierTier3": "业务",
|
||||
"subscriptionTierEnterprise": "企业",
|
||||
"idpDisabled": "身份提供者已禁用。",
|
||||
"orgAuthPageDisabled": "组织认证页面已禁用。",
|
||||
"domainRestartedDescription": "域验证重新启动成功",
|
||||
@@ -2113,6 +2331,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"newPricingLicenseForm": {
|
||||
"title": "获取许可证",
|
||||
"description": "选择一个计划,告诉我们你计划如何使用 Pangolin。",
|
||||
"chooseTier": "选择您的计划",
|
||||
"viewPricingLink": "查看价格、特征和限制",
|
||||
"tiers": {
|
||||
"starter": {
|
||||
"title": "启动器",
|
||||
"description": "企业特征,25个用户,25个站点和社区支持。"
|
||||
},
|
||||
"scale": {
|
||||
"title": "缩放比例",
|
||||
"description": "企业特征、50个用户、50个站点和优先支持。"
|
||||
}
|
||||
},
|
||||
"personalUseOnly": "仅供个人使用 (免费许可证-无签出)",
|
||||
"buttons": {
|
||||
"continueToCheckout": "继续签出"
|
||||
},
|
||||
"toasts": {
|
||||
"checkoutError": {
|
||||
"title": "签出错误",
|
||||
"description": "无法启动结帐。请重试。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": "优先权",
|
||||
"priorityDescription": "先评估更高优先级线路。优先级 = 100意味着自动排序(系统决定). 使用另一个数字强制执行手动优先级。",
|
||||
"instanceName": "实例名称",
|
||||
@@ -2201,6 +2445,8 @@
|
||||
"logRetentionAccessDescription": "保留访问日志的时间",
|
||||
"logRetentionActionLabel": "动作日志保留",
|
||||
"logRetentionActionDescription": "保留操作日志的时间",
|
||||
"logRetentionConnectionLabel": "连接日志保留",
|
||||
"logRetentionConnectionDescription": "保留连接日志的时间",
|
||||
"logRetentionDisabled": "已禁用",
|
||||
"logRetention3Days": "3 天",
|
||||
"logRetention7Days": "7 天",
|
||||
@@ -2211,7 +2457,15 @@
|
||||
"logRetentionEndOfFollowingYear": "下一年结束",
|
||||
"actionLogsDescription": "查看此机构执行的操作历史",
|
||||
"accessLogsDescription": "查看此机构资源的访问认证请求",
|
||||
"licenseRequiredToUse": "需要企业许可证才能使用此功能。",
|
||||
"connectionLogs": "连接日志",
|
||||
"connectionLogsDescription": "查看此机构隧道的连接日志",
|
||||
"sidebarLogsConnection": "连接日志",
|
||||
"sidebarLogsStreaming": "流流",
|
||||
"sourceAddress": "源地址",
|
||||
"destinationAddress": "目的地址",
|
||||
"duration": "期限",
|
||||
"licenseRequiredToUse": "使用此功能需要<enterpriseLicenseLink>企业版</enterpriseLicenseLink>许可证或<pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>。<bookADemoLink>预约演示或POC试用</bookADemoLink>。",
|
||||
"ossEnterpriseEditionRequired": "需要 <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> 才能使用此功能。 此功能也可在 <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>上获取。 <bookADemoLink>预订演示或POC 试用</bookADemoLink>。",
|
||||
"certResolver": "证书解决器",
|
||||
"certResolverDescription": "选择用于此资源的证书解析器。",
|
||||
"selectCertResolver": "选择证书解析",
|
||||
@@ -2408,6 +2662,17 @@
|
||||
"editInternalResourceDialogAccessControl": "访问控制",
|
||||
"editInternalResourceDialogAccessControlDescription": "控制当连接到此资源时,哪些角色、用户和机器客户端可以访问。管理员始终具有访问权。",
|
||||
"editInternalResourceDialogPortRangeValidationError": "端口范围必须为\"*\"表示所有端口,或一个用逗号分隔的端口和范围列表(例如:\"80,443,8000-9000\")。端口必须在1到65535之间。",
|
||||
"internalResourceAuthDaemonStrategy": "SSH 认证守护进程位置",
|
||||
"internalResourceAuthDaemonStrategyDescription": "选择 SSH 身份验证守护进程在哪里运行:站点(新建) 或远程主机。",
|
||||
"internalResourceAuthDaemonDescription": "SSH 身份验证守护程序处理此资源的 SSH 密钥签名和PAM 身份验证。 选择它是在站点(新建)还是在单独的远程主机上运行。请参阅 <docsLink>文档</docsLink>。",
|
||||
"internalResourceAuthDaemonDocsUrl": "https://docs.pangolin.net",
|
||||
"internalResourceAuthDaemonStrategyPlaceholder": "选择策略",
|
||||
"internalResourceAuthDaemonStrategyLabel": "地点",
|
||||
"internalResourceAuthDaemonSite": "在站点",
|
||||
"internalResourceAuthDaemonSiteDescription": "认证守护进程在站点上运行(新建)。",
|
||||
"internalResourceAuthDaemonRemote": "远程主机",
|
||||
"internalResourceAuthDaemonRemoteDescription": "认证守护进程运行在不是站点的主机上。",
|
||||
"internalResourceAuthDaemonPort": "守护进程端口(可选)",
|
||||
"orgAuthWhatsThis": "我的组织ID在哪里可以找到?",
|
||||
"learnMore": "了解更多",
|
||||
"backToHome": "返回首页",
|
||||
@@ -2510,6 +2775,7 @@
|
||||
"firewallEnabled": "防火墙已启用",
|
||||
"autoUpdatesEnabled": "启用自动更新",
|
||||
"tpmAvailable": "TPM 可用",
|
||||
"windowsAntivirusEnabled": "抗病毒已启用",
|
||||
"macosSipEnabled": "系统完整性保护 (SIP)",
|
||||
"macosGatekeeperEnabled": "Gatekeeper",
|
||||
"macosFirewallStealthMode": "防火墙隐形模式",
|
||||
@@ -2536,5 +2802,91 @@
|
||||
"approvalsEmptyStateStep2Title": "启用设备批准",
|
||||
"approvalsEmptyStateStep2Description": "编辑角色并启用“需要设备审批”选项。具有此角色的用户需要管理员批准新设备。",
|
||||
"approvalsEmptyStatePreviewDescription": "预览:如果启用,待处理设备请求将出现在这里供审核",
|
||||
"approvalsEmptyStateButtonText": "管理角色"
|
||||
"approvalsEmptyStateButtonText": "管理角色",
|
||||
"domainErrorTitle": "我们在验证您的域名时遇到了问题",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "在 <policiesTabLink>自动供应设置</policiesTabLink> 选项卡上配置角色映射和组织策略。",
|
||||
"streamingTitle": "事件流",
|
||||
"streamingDescription": "实时将事件从您的组织流到外部目的地。",
|
||||
"streamingUnnamedDestination": "未命名目标",
|
||||
"streamingNoUrlConfigured": "未配置URL",
|
||||
"streamingAddDestination": "添加目标",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookDescription": "将事件发送到任意HTTP端点并灵活验证和模板。",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "将事件串流到 S3 兼容的对象存储桶。即将推出。",
|
||||
"streamingDatadogTitle": "Datadog",
|
||||
"streamingDatadogDescription": "直接转发事件到您的Datadog 帐户。即将推出。",
|
||||
"streamingTypePickerDescription": "选择要开始的目标类型。",
|
||||
"streamingFailedToLoad": "加载目的地失败",
|
||||
"streamingUnexpectedError": "发生意外错误.",
|
||||
"streamingFailedToUpdate": "更新目标失败",
|
||||
"streamingDeletedSuccess": "目标删除成功",
|
||||
"streamingFailedToDelete": "删除目标失败",
|
||||
"streamingDeleteTitle": "删除目标",
|
||||
"streamingDeleteButtonText": "删除目标",
|
||||
"streamingDeleteDialogAreYouSure": "您确定要删除吗?",
|
||||
"streamingDeleteDialogThisDestination": "这个目标",
|
||||
"streamingDeleteDialogPermanentlyRemoved": "? 所有配置将被永久删除。",
|
||||
"httpDestEditTitle": "编辑目标",
|
||||
"httpDestAddTitle": "添加 HTTP 目标",
|
||||
"httpDestEditDescription": "更新此 HTTP 事件流媒体目的地的配置。",
|
||||
"httpDestAddDescription": "配置新的 HTTP 端点来接收您的组织事件。",
|
||||
"httpDestTabSettings": "设置",
|
||||
"httpDestTabHeaders": "信头",
|
||||
"httpDestTabBody": "正文内容",
|
||||
"httpDestTabLogs": "日志",
|
||||
"httpDestNamePlaceholder": "我的 HTTP 目标",
|
||||
"httpDestUrlLabel": "目标网址",
|
||||
"httpDestUrlErrorHttpRequired": "URL 必须使用 http 或 https",
|
||||
"httpDestUrlErrorHttpsRequired": "云端部署需要HTTPS",
|
||||
"httpDestUrlErrorInvalid": "输入一个有效的 URL (例如,https://example.com/webhook)",
|
||||
"httpDestAuthTitle": "认证",
|
||||
"httpDestAuthDescription": "选择如何验证您的端点的请求。",
|
||||
"httpDestAuthNoneTitle": "无身份验证",
|
||||
"httpDestAuthNoneDescription": "在没有授权头的情况下发送请求。",
|
||||
"httpDestAuthBearerTitle": "持有者令牌",
|
||||
"httpDestAuthBearerDescription": "添加授权:每个请求的标题为 <token>。",
|
||||
"httpDestAuthBearerPlaceholder": "您的 API 密钥或令牌",
|
||||
"httpDestAuthBasicTitle": "基本认证",
|
||||
"httpDestAuthBasicDescription": "添加授权:基本 <credentials> 头。提供用户名:密码的凭据。",
|
||||
"httpDestAuthBasicPlaceholder": "用户名:密码",
|
||||
"httpDestAuthCustomTitle": "自定义标题",
|
||||
"httpDestAuthCustomDescription": "指定自定义 HTTP 头名称和身份验证值 (例如,X-API 键)。",
|
||||
"httpDestAuthCustomHeaderNamePlaceholder": "标题名称(例如X-API-键)",
|
||||
"httpDestAuthCustomHeaderValuePlaceholder": "页眉值",
|
||||
"httpDestCustomHeadersTitle": "自定义 HTTP 头",
|
||||
"httpDestCustomHeadersDescription": "向每个输出请求添加自定义标题。用于静态令牌或自定义内容类型。默认情况下,内容类型:应用程序/json已发送。",
|
||||
"httpDestNoHeadersConfigured": "未配置自定义头。单击\"添加头\"以添加一个。",
|
||||
"httpDestHeaderNamePlaceholder": "标题名称",
|
||||
"httpDestHeaderValuePlaceholder": "值",
|
||||
"httpDestAddHeader": "添加标题",
|
||||
"httpDestBodyTemplateTitle": "自定义实体模板",
|
||||
"httpDestBodyTemplateDescription": "控制发送到您的端点的 JSON 有效载荷结构。如果禁用,将为每个事件发送一个 JSON 默认对象。",
|
||||
"httpDestEnableBodyTemplate": "启用自定义实体模板",
|
||||
"httpDestBodyTemplateLabel": "身体模板 (JSON)",
|
||||
"httpDestBodyTemplateHint": "将模板变量用于您有效载荷中的参考事件字段。",
|
||||
"httpDestPayloadFormatTitle": "有效载荷格式",
|
||||
"httpDestPayloadFormatDescription": "事件如何序列化为每个请求实体。",
|
||||
"httpDestFormatJsonArrayTitle": "JSON 数组",
|
||||
"httpDestFormatJsonArrayDescription": "每批一个请求,实体是一个 JSON 数组。与大多数通用的 Web 钩子和数据兼容。",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "每批有一个请求,物体是换行符限制的 JSON ——每行一个对象,不是外部数组。 Sluk HEC、Elastic / OpenSearch和Grafana Loki所需。",
|
||||
"httpDestFormatSingleTitle": "每个请求一个事件",
|
||||
"httpDestFormatSingleDescription": "为每个事件单独发送一个 HTTP POST。仅用于无法处理批量的端点。",
|
||||
"httpDestLogTypesTitle": "日志类型",
|
||||
"httpDestLogTypesDescription": "选择转发到此目的地的日志类型。只有启用的日志类型才会被连续使用。",
|
||||
"httpDestAccessLogsTitle": "访问日志",
|
||||
"httpDestAccessLogsDescription": "资源访问尝试,包括已验证和拒绝的请求。",
|
||||
"httpDestActionLogsTitle": "操作日志",
|
||||
"httpDestActionLogsDescription": "组织内部用户采取的行政行动。",
|
||||
"httpDestConnectionLogsTitle": "连接日志",
|
||||
"httpDestConnectionLogsDescription": "站点和隧道连接事件,包括连接和断开连接。",
|
||||
"httpDestRequestLogsTitle": "请求日志",
|
||||
"httpDestRequestLogsDescription": "HTTP 请求代理资源日志,包括方法、路径和响应代码。",
|
||||
"httpDestSaveChanges": "保存更改",
|
||||
"httpDestCreateDestination": "创建目标",
|
||||
"httpDestUpdatedSuccess": "目标已成功更新",
|
||||
"httpDestCreatedSuccess": "目标创建成功",
|
||||
"httpDestUpdateFailed": "更新目标失败",
|
||||
"httpDestCreateFailed": "创建目标失败"
|
||||
}
|
||||
|
||||
@@ -1091,6 +1091,7 @@
|
||||
"actionRemoveUser": "刪除用戶",
|
||||
"actionListUsers": "列出用戶",
|
||||
"actionAddUserRole": "添加用戶角色",
|
||||
"actionSetUserOrgRoles": "Set User Roles",
|
||||
"actionGenerateAccessToken": "生成訪問令牌",
|
||||
"actionDeleteAccessToken": "刪除訪問令牌",
|
||||
"actionListAccessTokens": "訪問令牌",
|
||||
|
||||
9257
package-lock.json
generated
9257
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
137
package.json
137
package.json
@@ -12,30 +12,29 @@
|
||||
"license": "SEE LICENSE IN LICENSE AND README.md",
|
||||
"scripts": {
|
||||
"dev": "NODE_ENV=development ENVIRONMENT=dev tsx watch server/index.ts",
|
||||
"db:pg:generate": "drizzle-kit generate --config=./drizzle.pg.config.ts",
|
||||
"db:sqlite:generate": "drizzle-kit generate --config=./drizzle.sqlite.config.ts",
|
||||
"db:pg:push": "npx tsx server/db/pg/migrate.ts",
|
||||
"db:sqlite:push": "npx tsx server/db/sqlite/migrate.ts",
|
||||
"db:sqlite:studio": "drizzle-kit studio --config=./drizzle.sqlite.config.ts",
|
||||
"db:pg:studio": "drizzle-kit studio --config=./drizzle.pg.config.ts",
|
||||
"dev:check": "npx tsc --noEmit && npm run format:check",
|
||||
"dev:setup": "cp config/config.example.yml config/config.yml && npm run set:oss && npm run set:sqlite && npm run db:sqlite:generate && npm run db:sqlite:push",
|
||||
"db:generate": "drizzle-kit generate --config=./drizzle.config.ts",
|
||||
"db:push": "npx tsx server/db/migrate.ts",
|
||||
"db:studio": "drizzle-kit studio --config=./drizzle.config.ts",
|
||||
"db:clear-migrations": "rm -rf server/migrations",
|
||||
"set:oss": "echo 'export const build = \"oss\" as \"saas\" | \"enterprise\" | \"oss\";' > server/build.ts && cp tsconfig.oss.json tsconfig.json",
|
||||
"set:saas": "echo 'export const build = \"saas\" as \"saas\" | \"enterprise\" | \"oss\";' > server/build.ts && cp tsconfig.saas.json tsconfig.json",
|
||||
"set:enterprise": "echo 'export const build = \"enterprise\" as \"saas\" | \"enterprise\" | \"oss\";' > server/build.ts && cp tsconfig.enterprise.json tsconfig.json",
|
||||
"set:sqlite": "echo 'export * from \"./sqlite\";\nexport const driver: \"pg\" | \"sqlite\" = \"sqlite\";' > server/db/index.ts",
|
||||
"set:pg": "echo 'export * from \"./pg\";\nexport const driver: \"pg\" | \"sqlite\" = \"pg\";' > server/db/index.ts",
|
||||
"next:build": "next build",
|
||||
"build:sqlite": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs",
|
||||
"build:pg": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs",
|
||||
"set:sqlite": "echo 'export * from \"./sqlite\";\nexport const driver: \"pg\" | \"sqlite\" = \"sqlite\";' > server/db/index.ts && cp drizzle.sqlite.config.ts drizzle.config.ts && cp server/setup/migrationsSqlite.ts server/setup/migrations.ts",
|
||||
"set:pg": "echo 'export * from \"./pg\";\nexport const driver: \"pg\" | \"sqlite\" = \"pg\";' > server/db/index.ts && cp drizzle.pg.config.ts drizzle.config.ts && cp server/setup/migrationsPg.ts server/setup/migrations.ts",
|
||||
"build:next": "next build",
|
||||
"build": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrations.ts -o dist/migrations.mjs",
|
||||
"start": "ENVIRONMENT=prod node dist/migrations.mjs && ENVIRONMENT=prod NODE_ENV=development node --enable-source-maps dist/server.mjs",
|
||||
"email": "email dev --dir server/emails/templates --port 3005",
|
||||
"build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs",
|
||||
"format:check": "prettier --check .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "8.4.0",
|
||||
"@aws-sdk/client-s3": "3.971.0",
|
||||
"@faker-js/faker": "10.2.0",
|
||||
"@asteasolutions/zod-to-openapi": "8.4.1",
|
||||
"@aws-sdk/client-s3": "3.1011.0",
|
||||
"@faker-js/faker": "10.3.0",
|
||||
"@headlessui/react": "2.2.9",
|
||||
"@hookform/resolvers": "5.2.2",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
@@ -60,90 +59,83 @@
|
||||
"@radix-ui/react-tabs": "1.1.13",
|
||||
"@radix-ui/react-toast": "1.2.15",
|
||||
"@radix-ui/react-tooltip": "1.2.8",
|
||||
"@react-email/components": "1.0.2",
|
||||
"@react-email/render": "2.0.0",
|
||||
"@react-email/tailwind": "2.0.2",
|
||||
"@simplewebauthn/browser": "13.2.2",
|
||||
"@simplewebauthn/server": "13.2.2",
|
||||
"@react-email/components": "1.0.8",
|
||||
"@react-email/render": "2.0.4",
|
||||
"@react-email/tailwind": "2.0.5",
|
||||
"@simplewebauthn/browser": "13.3.0",
|
||||
"@simplewebauthn/server": "13.3.0",
|
||||
"@tailwindcss/forms": "0.5.11",
|
||||
"@tanstack/react-query": "5.90.12",
|
||||
"@tanstack/react-query": "5.90.21",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"arctic": "3.7.0",
|
||||
"axios": "1.13.2",
|
||||
"axios": "1.13.5",
|
||||
"better-sqlite3": "11.9.1",
|
||||
"canvas-confetti": "1.9.4",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
"cmdk": "1.1.1",
|
||||
"cookie": "1.1.1",
|
||||
"cookie-parser": "1.4.7",
|
||||
"cookies": "0.9.1",
|
||||
"cors": "2.8.5",
|
||||
"cors": "2.8.6",
|
||||
"crypto-js": "4.2.0",
|
||||
"d3": "7.9.0",
|
||||
"date-fns": "4.1.0",
|
||||
"drizzle-orm": "0.45.1",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-next": "16.1.0",
|
||||
"express": "5.2.1",
|
||||
"express-rate-limit": "8.2.1",
|
||||
"glob": "13.0.0",
|
||||
"express-rate-limit": "8.3.0",
|
||||
"glob": "13.0.6",
|
||||
"helmet": "8.1.0",
|
||||
"http-errors": "2.0.1",
|
||||
"i": "0.3.7",
|
||||
"input-otp": "1.4.2",
|
||||
"ioredis": "5.9.2",
|
||||
"ioredis": "5.10.0",
|
||||
"jmespath": "0.16.0",
|
||||
"js-yaml": "4.1.1",
|
||||
"jsonwebtoken": "9.0.3",
|
||||
"lucide-react": "0.562.0",
|
||||
"maxmind": "5.0.1",
|
||||
"lucide-react": "0.577.0",
|
||||
"maxmind": "5.0.5",
|
||||
"moment": "2.30.1",
|
||||
"next": "15.5.9",
|
||||
"next-intl": "4.7.0",
|
||||
"next": "15.5.14",
|
||||
"next-intl": "4.8.3",
|
||||
"next-themes": "0.4.6",
|
||||
"nextjs-toploader": "3.9.17",
|
||||
"node-cache": "5.1.2",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "7.0.11",
|
||||
"npm": "11.7.0",
|
||||
"nprogress": "0.2.0",
|
||||
"nodemailer": "8.0.4",
|
||||
"oslo": "1.2.1",
|
||||
"pg": "8.17.1",
|
||||
"posthog-node": "5.23.0",
|
||||
"pg": "8.20.0",
|
||||
"posthog-node": "5.28.0",
|
||||
"qrcode.react": "4.2.0",
|
||||
"react": "19.2.3",
|
||||
"react-day-picker": "9.13.0",
|
||||
"react-dom": "19.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-day-picker": "9.14.0",
|
||||
"react-dom": "19.2.4",
|
||||
"react-easy-sort": "1.8.0",
|
||||
"react-hook-form": "7.71.1",
|
||||
"react-icons": "5.5.0",
|
||||
"rebuild": "0.1.2",
|
||||
"react-hook-form": "7.71.2",
|
||||
"react-icons": "5.6.0",
|
||||
"recharts": "2.15.4",
|
||||
"reodotdev": "1.0.0",
|
||||
"resend": "6.8.0",
|
||||
"semver": "7.7.3",
|
||||
"stripe": "20.2.0",
|
||||
"reodotdev": "1.1.0",
|
||||
"resend": "6.9.2",
|
||||
"semver": "7.7.4",
|
||||
"sshpk": "1.18.0",
|
||||
"stripe": "20.4.1",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"tailwind-merge": "3.4.0",
|
||||
"tailwind-merge": "3.5.0",
|
||||
"topojson-client": "3.1.0",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"use-debounce": "10.1.0",
|
||||
"uuid": "13.0.0",
|
||||
"vaul": "1.1.2",
|
||||
"visionscarto-world-atlas": "1.0.0",
|
||||
"winston": "3.19.0",
|
||||
"winston-daily-rotate-file": "5.0.0",
|
||||
"ws": "8.19.0",
|
||||
"yaml": "2.8.2",
|
||||
"yaml": "2.8.3",
|
||||
"yargs": "18.0.0",
|
||||
"zod": "4.3.5",
|
||||
"zod": "4.3.6",
|
||||
"zod-validation-error": "5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dotenvx/dotenvx": "1.51.2",
|
||||
"@dotenvx/dotenvx": "1.54.1",
|
||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||
"@tailwindcss/postcss": "4.1.18",
|
||||
"@tanstack/react-query-devtools": "5.91.1",
|
||||
"@react-email/preview-server": "5.2.10",
|
||||
"@tailwindcss/postcss": "4.2.2",
|
||||
"@tanstack/react-query-devtools": "5.91.3",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/cookie-parser": "1.4.10",
|
||||
"@types/cors": "2.8.19",
|
||||
@@ -152,30 +144,37 @@
|
||||
"@types/express": "5.0.6",
|
||||
"@types/express-session": "1.18.2",
|
||||
"@types/jmespath": "0.15.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsonwebtoken": "9.0.10",
|
||||
"@types/node": "24.10.2",
|
||||
"@types/nodemailer": "7.0.4",
|
||||
"@types/node": "25.3.5",
|
||||
"@types/nodemailer": "7.0.11",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@types/pg": "8.16.0",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/pg": "8.18.0",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/semver": "7.7.1",
|
||||
"@types/sshpk": "1.17.4",
|
||||
"@types/swagger-ui-express": "4.1.8",
|
||||
"@types/topojson-client": "3.1.5",
|
||||
"@types/ws": "8.18.1",
|
||||
"@types/yargs": "17.0.35",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"babel-plugin-react-compiler": "1.0.0",
|
||||
"drizzle-kit": "0.31.8",
|
||||
"esbuild": "0.27.2",
|
||||
"drizzle-kit": "0.31.10",
|
||||
"esbuild": "0.27.4",
|
||||
"esbuild-node-externals": "1.20.1",
|
||||
"postcss": "8.5.6",
|
||||
"prettier": "3.8.0",
|
||||
"react-email": "5.2.5",
|
||||
"tailwindcss": "4.1.18",
|
||||
"eslint": "10.0.3",
|
||||
"eslint-config-next": "16.1.7",
|
||||
"postcss": "8.5.8",
|
||||
"prettier": "3.8.1",
|
||||
"react-email": "5.2.10",
|
||||
"tailwindcss": "4.2.2",
|
||||
"tsc-alias": "1.8.16",
|
||||
"tsx": "4.21.0",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.53.1"
|
||||
"typescript-eslint": "8.56.1"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "0.27.4",
|
||||
"dompurify": "3.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/third-party/dd.png
vendored
Normal file
BIN
public/third-party/dd.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
BIN
public/third-party/s3.png
vendored
Normal file
BIN
public/third-party/s3.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -1,9 +1,10 @@
|
||||
import { Request } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { userActions, roleActions, userOrgs } from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { userActions, roleActions } from "@server/db";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import createHttpError from "http-errors";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import { getUserOrgRoleIds } from "@server/lib/userOrgRoles";
|
||||
|
||||
export enum ActionsEnum {
|
||||
createOrgUser = "createOrgUser",
|
||||
@@ -19,6 +20,7 @@ export enum ActionsEnum {
|
||||
getSite = "getSite",
|
||||
listSites = "listSites",
|
||||
updateSite = "updateSite",
|
||||
resetSiteBandwidth = "resetSiteBandwidth",
|
||||
reGenerateSecret = "reGenerateSecret",
|
||||
createResource = "createResource",
|
||||
deleteResource = "deleteResource",
|
||||
@@ -52,6 +54,8 @@ export enum ActionsEnum {
|
||||
listRoleResources = "listRoleResources",
|
||||
// listRoleActions = "listRoleActions",
|
||||
addUserRole = "addUserRole",
|
||||
removeUserRole = "removeUserRole",
|
||||
setUserOrgRoles = "setUserOrgRoles",
|
||||
// addUserSite = "addUserSite",
|
||||
// addUserAction = "addUserAction",
|
||||
// removeUserAction = "removeUserAction",
|
||||
@@ -108,6 +112,10 @@ export enum ActionsEnum {
|
||||
listApiKeyActions = "listApiKeyActions",
|
||||
listApiKeys = "listApiKeys",
|
||||
getApiKey = "getApiKey",
|
||||
createSiteProvisioningKey = "createSiteProvisioningKey",
|
||||
listSiteProvisioningKeys = "listSiteProvisioningKeys",
|
||||
updateSiteProvisioningKey = "updateSiteProvisioningKey",
|
||||
deleteSiteProvisioningKey = "deleteSiteProvisioningKey",
|
||||
getCertificate = "getCertificate",
|
||||
restartCertificate = "restartCertificate",
|
||||
billing = "billing",
|
||||
@@ -131,7 +139,12 @@ export enum ActionsEnum {
|
||||
viewLogs = "viewLogs",
|
||||
exportLogs = "exportLogs",
|
||||
listApprovals = "listApprovals",
|
||||
updateApprovals = "updateApprovals"
|
||||
updateApprovals = "updateApprovals",
|
||||
signSshKey = "signSshKey",
|
||||
createEventStreamingDestination = "createEventStreamingDestination",
|
||||
updateEventStreamingDestination = "updateEventStreamingDestination",
|
||||
deleteEventStreamingDestination = "deleteEventStreamingDestination",
|
||||
listEventStreamingDestinations = "listEventStreamingDestinations"
|
||||
}
|
||||
|
||||
export async function checkUserActionPermission(
|
||||
@@ -152,29 +165,16 @@ export async function checkUserActionPermission(
|
||||
}
|
||||
|
||||
try {
|
||||
let userOrgRoleId = req.userOrgRoleId;
|
||||
let userOrgRoleIds = req.userOrgRoleIds;
|
||||
|
||||
// If userOrgRoleId is not available on the request, fetch it
|
||||
if (userOrgRoleId === undefined) {
|
||||
const userOrgRole = await db
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgs.userId, userId),
|
||||
eq(userOrgs.orgId, req.userOrgId!)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (userOrgRole.length === 0) {
|
||||
if (userOrgRoleIds === undefined) {
|
||||
userOrgRoleIds = await getUserOrgRoleIds(userId, req.userOrgId!);
|
||||
if (userOrgRoleIds.length === 0) {
|
||||
throw createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"User does not have access to this organization"
|
||||
);
|
||||
}
|
||||
|
||||
userOrgRoleId = userOrgRole[0].roleId;
|
||||
}
|
||||
|
||||
// Check if the user has direct permission for the action in the current org
|
||||
@@ -185,7 +185,7 @@ export async function checkUserActionPermission(
|
||||
and(
|
||||
eq(userActions.userId, userId),
|
||||
eq(userActions.actionId, actionId),
|
||||
eq(userActions.orgId, req.userOrgId!) // TODO: we cant pass the org id if we are not checking the org
|
||||
eq(userActions.orgId, req.userOrgId!)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
@@ -194,14 +194,14 @@ export async function checkUserActionPermission(
|
||||
return true;
|
||||
}
|
||||
|
||||
// If no direct permission, check role-based permission
|
||||
// If no direct permission, check role-based permission (any of user's roles)
|
||||
const roleActionPermission = await db
|
||||
.select()
|
||||
.from(roleActions)
|
||||
.where(
|
||||
and(
|
||||
eq(roleActions.actionId, actionId),
|
||||
eq(roleActions.roleId, userOrgRoleId!),
|
||||
inArray(roleActions.roleId, userOrgRoleIds),
|
||||
eq(roleActions.orgId, req.userOrgId!)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
import { db } from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import { roleResources, userResources } from "@server/db";
|
||||
|
||||
export async function canUserAccessResource({
|
||||
userId,
|
||||
resourceId,
|
||||
roleId
|
||||
roleIds
|
||||
}: {
|
||||
userId: string;
|
||||
resourceId: number;
|
||||
roleId: number;
|
||||
roleIds: number[];
|
||||
}): Promise<boolean> {
|
||||
const roleResourceAccess = await db
|
||||
.select()
|
||||
.from(roleResources)
|
||||
.where(
|
||||
and(
|
||||
eq(roleResources.resourceId, resourceId),
|
||||
eq(roleResources.roleId, roleId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
const roleResourceAccess =
|
||||
roleIds.length > 0
|
||||
? await db
|
||||
.select()
|
||||
.from(roleResources)
|
||||
.where(
|
||||
and(
|
||||
eq(roleResources.resourceId, resourceId),
|
||||
inArray(roleResources.roleId, roleIds)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
: [];
|
||||
|
||||
if (roleResourceAccess.length > 0) {
|
||||
return true;
|
||||
|
||||
48
server/auth/canUserAccessSiteResource.ts
Normal file
48
server/auth/canUserAccessSiteResource.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { db } from "@server/db";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import { roleSiteResources, userSiteResources } from "@server/db";
|
||||
|
||||
export async function canUserAccessSiteResource({
|
||||
userId,
|
||||
resourceId,
|
||||
roleIds
|
||||
}: {
|
||||
userId: string;
|
||||
resourceId: number;
|
||||
roleIds: number[];
|
||||
}): Promise<boolean> {
|
||||
const roleResourceAccess =
|
||||
roleIds.length > 0
|
||||
? await db
|
||||
.select()
|
||||
.from(roleSiteResources)
|
||||
.where(
|
||||
and(
|
||||
eq(roleSiteResources.siteResourceId, resourceId),
|
||||
inArray(roleSiteResources.roleId, roleIds)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
: [];
|
||||
|
||||
if (roleResourceAccess.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const userResourceAccess = await db
|
||||
.select()
|
||||
.from(userSiteResources)
|
||||
.where(
|
||||
and(
|
||||
eq(userSiteResources.userId, userId),
|
||||
eq(userSiteResources.siteResourceId, resourceId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (userResourceAccess.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -3,7 +3,14 @@ import {
|
||||
encodeHexLowerCase
|
||||
} from "@oslojs/encoding";
|
||||
import { sha256 } from "@oslojs/crypto/sha2";
|
||||
import { resourceSessions, Session, sessions, User, users } from "@server/db";
|
||||
import {
|
||||
resourceSessions,
|
||||
safeRead,
|
||||
Session,
|
||||
sessions,
|
||||
User,
|
||||
users
|
||||
} from "@server/db";
|
||||
import { db } from "@server/db";
|
||||
import { eq, inArray } from "drizzle-orm";
|
||||
import config from "@server/lib/config";
|
||||
@@ -54,11 +61,15 @@ export async function validateSessionToken(
|
||||
const sessionId = encodeHexLowerCase(
|
||||
sha256(new TextEncoder().encode(token))
|
||||
);
|
||||
const result = await db
|
||||
.select({ user: users, session: sessions })
|
||||
.from(sessions)
|
||||
.innerJoin(users, eq(sessions.userId, users.userId))
|
||||
.where(eq(sessions.sessionId, sessionId));
|
||||
|
||||
const result = await safeRead((db) =>
|
||||
db
|
||||
.select({ user: users, session: sessions })
|
||||
.from(sessions)
|
||||
.innerJoin(users, eq(sessions.userId, users.userId))
|
||||
.where(eq(sessions.sessionId, sessionId))
|
||||
);
|
||||
|
||||
if (result.length < 1) {
|
||||
return { session: null, user: null };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||
import { sha256 } from "@oslojs/crypto/sha2";
|
||||
import { resourceSessions, ResourceSession } from "@server/db";
|
||||
import { db } from "@server/db";
|
||||
import { db, safeRead } from "@server/db";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import config from "@server/lib/config";
|
||||
|
||||
@@ -66,15 +66,17 @@ export async function validateResourceSessionToken(
|
||||
const sessionId = encodeHexLowerCase(
|
||||
sha256(new TextEncoder().encode(token))
|
||||
);
|
||||
const result = await db
|
||||
.select()
|
||||
.from(resourceSessions)
|
||||
.where(
|
||||
and(
|
||||
eq(resourceSessions.sessionId, sessionId),
|
||||
eq(resourceSessions.resourceId, resourceId)
|
||||
const result = await safeRead((db) =>
|
||||
db
|
||||
.select()
|
||||
.from(resourceSessions)
|
||||
.where(
|
||||
and(
|
||||
eq(resourceSessions.sessionId, sessionId),
|
||||
eq(resourceSessions.resourceId, resourceId)
|
||||
)
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
if (result.length < 1) {
|
||||
return { resourceSession: null };
|
||||
@@ -85,7 +87,7 @@ export async function validateResourceSessionToken(
|
||||
if (Date.now() >= resourceSession.expiresAt) {
|
||||
await db
|
||||
.delete(resourceSessions)
|
||||
.where(eq(resourceSessions.sessionId, resourceSessions.sessionId));
|
||||
.where(eq(resourceSessions.sessionId, sessionId));
|
||||
return { resourceSession: null };
|
||||
} else if (
|
||||
Date.now() >=
|
||||
@@ -179,7 +181,7 @@ export function serializeResourceSessionCookie(
|
||||
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${domain}`;
|
||||
} else {
|
||||
if (expiresAt === undefined) {
|
||||
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=$domain}`;
|
||||
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=${domain}`;
|
||||
}
|
||||
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${domain}`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { flushBandwidthToDb } from "@server/routers/newt/handleReceiveBandwidthMessage";
|
||||
import { flushConnectionLogToDb } from "#dynamic/routers/newt";
|
||||
import { flushSiteBandwidthToDb } from "@server/routers/gerbil/receiveBandwidth";
|
||||
import { stopPingAccumulator } from "@server/routers/newt/pingAccumulator";
|
||||
import { cleanup as wsCleanup } from "#dynamic/routers/ws";
|
||||
|
||||
async function cleanup() {
|
||||
await stopPingAccumulator();
|
||||
await flushBandwidthToDb();
|
||||
await flushConnectionLogToDb();
|
||||
await flushSiteBandwidthToDb();
|
||||
await wsCleanup();
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -56,15 +56,15 @@ Ensure drizzle-kit is installed.
|
||||
You must have a connection string in your config file, as shown above.
|
||||
|
||||
```bash
|
||||
npm run db:pg:generate
|
||||
npm run db:pg:push
|
||||
npm run db:generate
|
||||
npm run db:push
|
||||
```
|
||||
|
||||
### SQLite
|
||||
|
||||
```bash
|
||||
npm run db:sqlite:generate
|
||||
npm run db:sqlite:push
|
||||
npm run db:generate
|
||||
npm run db:push
|
||||
```
|
||||
|
||||
## Build Time
|
||||
|
||||
3
server/db/migrate.ts
Normal file
3
server/db/migrate.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { runMigrations } from "./";
|
||||
|
||||
await runMigrations();
|
||||
@@ -1,7 +1,7 @@
|
||||
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
|
||||
import { Pool } from "pg";
|
||||
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||
import { withReplicas } from "drizzle-orm/pg-core";
|
||||
import { createPool } from "./poolConfig";
|
||||
|
||||
function createDb() {
|
||||
const config = readConfigFile();
|
||||
@@ -39,12 +39,17 @@ function createDb() {
|
||||
|
||||
// Create connection pools instead of individual connections
|
||||
const poolConfig = config.postgres.pool;
|
||||
const primaryPool = new Pool({
|
||||
const maxConnections = poolConfig?.max_connections || 20;
|
||||
const idleTimeoutMs = poolConfig?.idle_timeout_ms || 30000;
|
||||
const connectionTimeoutMs = poolConfig?.connection_timeout_ms || 5000;
|
||||
|
||||
const primaryPool = createPool(
|
||||
connectionString,
|
||||
max: poolConfig?.max_connections || 20,
|
||||
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000,
|
||||
connectionTimeoutMillis: poolConfig?.connection_timeout_ms || 5000
|
||||
});
|
||||
maxConnections,
|
||||
idleTimeoutMs,
|
||||
connectionTimeoutMs,
|
||||
"primary"
|
||||
);
|
||||
|
||||
const replicas = [];
|
||||
|
||||
@@ -55,14 +60,15 @@ function createDb() {
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const maxReplicaConnections = poolConfig?.max_replica_connections || 20;
|
||||
for (const conn of replicaConnections) {
|
||||
const replicaPool = new Pool({
|
||||
connectionString: conn.connection_string,
|
||||
max: poolConfig?.max_replica_connections || 20,
|
||||
idleTimeoutMillis: poolConfig?.idle_timeout_ms || 30000,
|
||||
connectionTimeoutMillis:
|
||||
poolConfig?.connection_timeout_ms || 5000
|
||||
});
|
||||
const replicaPool = createPool(
|
||||
conn.connection_string,
|
||||
maxReplicaConnections,
|
||||
idleTimeoutMs,
|
||||
connectionTimeoutMs,
|
||||
"replica"
|
||||
);
|
||||
replicas.push(
|
||||
DrizzlePostgres(replicaPool, {
|
||||
logger: process.env.QUERY_LOGGING == "true"
|
||||
@@ -85,3 +91,4 @@ export const primaryDb = db.$primary;
|
||||
export type Transaction = Parameters<
|
||||
Parameters<(typeof db)["transaction"]>[0]
|
||||
>[0];
|
||||
export const DB_TYPE: "pg" | "sqlite" = "pg";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from "./driver";
|
||||
export * from "./logsDriver";
|
||||
export * from "./safeRead";
|
||||
export * from "./schema/schema";
|
||||
export * from "./schema/privateSchema";
|
||||
export * from "./migrate";
|
||||
|
||||
94
server/db/pg/logsDriver.ts
Normal file
94
server/db/pg/logsDriver.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
|
||||
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||
import { withReplicas } from "drizzle-orm/pg-core";
|
||||
import { build } from "@server/build";
|
||||
import { db as mainDb, primaryDb as mainPrimaryDb } from "./driver";
|
||||
import { createPool } from "./poolConfig";
|
||||
|
||||
function createLogsDb() {
|
||||
// Only use separate logs database in SaaS builds
|
||||
if (build !== "saas") {
|
||||
return mainDb;
|
||||
}
|
||||
|
||||
const config = readConfigFile();
|
||||
|
||||
// Merge configs, prioritizing private config
|
||||
const logsConfig = config.postgres_logs;
|
||||
|
||||
// Check environment variable first
|
||||
let connectionString = process.env.POSTGRES_LOGS_CONNECTION_STRING;
|
||||
let replicaConnections: Array<{ connection_string: string }> = [];
|
||||
|
||||
if (!connectionString && logsConfig) {
|
||||
connectionString = logsConfig.connection_string;
|
||||
replicaConnections = logsConfig.replicas || [];
|
||||
}
|
||||
|
||||
// If POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS is set, use it
|
||||
if (process.env.POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS) {
|
||||
replicaConnections =
|
||||
process.env.POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS.split(",").map(
|
||||
(conn) => ({
|
||||
connection_string: conn.trim()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// If no logs database is configured, fall back to main database
|
||||
if (!connectionString) {
|
||||
return mainDb;
|
||||
}
|
||||
|
||||
// Create separate connection pool for logs database
|
||||
const poolConfig = logsConfig?.pool || config.postgres?.pool;
|
||||
const maxConnections = poolConfig?.max_connections || 20;
|
||||
const idleTimeoutMs = poolConfig?.idle_timeout_ms || 30000;
|
||||
const connectionTimeoutMs = poolConfig?.connection_timeout_ms || 5000;
|
||||
|
||||
const primaryPool = createPool(
|
||||
connectionString,
|
||||
maxConnections,
|
||||
idleTimeoutMs,
|
||||
connectionTimeoutMs,
|
||||
"logs-primary"
|
||||
);
|
||||
|
||||
const replicas = [];
|
||||
|
||||
if (!replicaConnections.length) {
|
||||
replicas.push(
|
||||
DrizzlePostgres(primaryPool, {
|
||||
logger: process.env.QUERY_LOGGING == "true"
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const maxReplicaConnections =
|
||||
poolConfig?.max_replica_connections || 20;
|
||||
for (const conn of replicaConnections) {
|
||||
const replicaPool = createPool(
|
||||
conn.connection_string,
|
||||
maxReplicaConnections,
|
||||
idleTimeoutMs,
|
||||
connectionTimeoutMs,
|
||||
"logs-replica"
|
||||
);
|
||||
replicas.push(
|
||||
DrizzlePostgres(replicaPool, {
|
||||
logger: process.env.QUERY_LOGGING == "true"
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return withReplicas(
|
||||
DrizzlePostgres(primaryPool, {
|
||||
logger: process.env.QUERY_LOGGING == "true"
|
||||
}),
|
||||
replicas as any
|
||||
);
|
||||
}
|
||||
|
||||
export const logsDb = createLogsDb();
|
||||
export default logsDb;
|
||||
export const primaryLogsDb = logsDb.$primary;
|
||||
@@ -4,7 +4,7 @@ import path from "path";
|
||||
|
||||
const migrationsFolder = path.join("server/migrations");
|
||||
|
||||
const runMigrations = async () => {
|
||||
export const runMigrations = async () => {
|
||||
console.log("Running migrations...");
|
||||
try {
|
||||
await migrate(db as any, {
|
||||
@@ -17,5 +17,3 @@ const runMigrations = async () => {
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
runMigrations();
|
||||
|
||||
63
server/db/pg/poolConfig.ts
Normal file
63
server/db/pg/poolConfig.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Pool, PoolConfig } from "pg";
|
||||
import logger from "@server/logger";
|
||||
|
||||
export function createPoolConfig(
|
||||
connectionString: string,
|
||||
maxConnections: number,
|
||||
idleTimeoutMs: number,
|
||||
connectionTimeoutMs: number
|
||||
): PoolConfig {
|
||||
return {
|
||||
connectionString,
|
||||
max: maxConnections,
|
||||
idleTimeoutMillis: idleTimeoutMs,
|
||||
connectionTimeoutMillis: connectionTimeoutMs,
|
||||
// TCP keepalive to prevent silent connection drops by NAT gateways,
|
||||
// load balancers, and other intermediate network devices (e.g. AWS
|
||||
// NAT Gateway drops idle TCP connections after ~350s)
|
||||
keepAlive: true,
|
||||
keepAliveInitialDelayMillis: 10000, // send first keepalive after 10s of idle
|
||||
// Allow connections to be released and recreated more aggressively
|
||||
// to avoid stale connections building up
|
||||
allowExitOnIdle: false
|
||||
};
|
||||
}
|
||||
|
||||
export function attachPoolErrorHandlers(pool: Pool, label: string): void {
|
||||
pool.on("error", (err) => {
|
||||
// This catches errors on idle clients in the pool. Without this
|
||||
// handler an unexpected disconnect would crash the process.
|
||||
logger.error(
|
||||
`Unexpected error on idle ${label} database client: ${err.message}`
|
||||
);
|
||||
});
|
||||
|
||||
pool.on("connect", (client) => {
|
||||
// Set a statement timeout on every new connection so a single slow
|
||||
// query can't block the pool forever
|
||||
client.query("SET statement_timeout = '30s'").catch((err: Error) => {
|
||||
logger.warn(
|
||||
`Failed to set statement_timeout on ${label} client: ${err.message}`
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function createPool(
|
||||
connectionString: string,
|
||||
maxConnections: number,
|
||||
idleTimeoutMs: number,
|
||||
connectionTimeoutMs: number,
|
||||
label: string
|
||||
): Pool {
|
||||
const pool = new Pool(
|
||||
createPoolConfig(
|
||||
connectionString,
|
||||
maxConnections,
|
||||
idleTimeoutMs,
|
||||
connectionTimeoutMs
|
||||
)
|
||||
);
|
||||
attachPoolErrorHandlers(pool, label);
|
||||
return pool;
|
||||
}
|
||||
24
server/db/pg/safeRead.ts
Normal file
24
server/db/pg/safeRead.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { db, primaryDb } from "./driver";
|
||||
|
||||
/**
|
||||
* Runs a read query with replica fallback for Postgres.
|
||||
* Executes the query against the replica first (when replicas exist).
|
||||
* If the query throws or returns no data (null, undefined, or empty array),
|
||||
* runs the same query against the primary.
|
||||
*/
|
||||
export async function safeRead<T>(
|
||||
query: (d: typeof db | typeof primaryDb) => Promise<T>
|
||||
): Promise<T> {
|
||||
try {
|
||||
const result = await query(db);
|
||||
if (result === undefined || result === null) {
|
||||
return query(primaryDb);
|
||||
}
|
||||
if (Array.isArray(result) && result.length === 0) {
|
||||
return query(primaryDb);
|
||||
}
|
||||
return result;
|
||||
} catch {
|
||||
return query(primaryDb);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
bigint,
|
||||
real,
|
||||
text,
|
||||
index
|
||||
index,
|
||||
primaryKey,
|
||||
uniqueIndex
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { InferSelectModel } from "drizzle-orm";
|
||||
import {
|
||||
@@ -17,7 +19,9 @@ import {
|
||||
users,
|
||||
exitNodes,
|
||||
sessions,
|
||||
clients
|
||||
clients,
|
||||
siteResources,
|
||||
sites
|
||||
} from "./schema";
|
||||
|
||||
export const certificates = pgTable("certificates", {
|
||||
@@ -82,11 +86,16 @@ export const subscriptions = pgTable("subscriptions", {
|
||||
canceledAt: bigint("canceledAt", { mode: "number" }),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||
updatedAt: bigint("updatedAt", { mode: "number" }),
|
||||
billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" })
|
||||
version: integer("version"),
|
||||
billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" }),
|
||||
type: varchar("type", { length: 50 }) // tier1, tier2, tier3, or license
|
||||
});
|
||||
|
||||
export const subscriptionItems = pgTable("subscriptionItems", {
|
||||
subscriptionItemId: serial("subscriptionItemId").primaryKey(),
|
||||
stripeSubscriptionItemId: varchar("stripeSubscriptionItemId", {
|
||||
length: 255
|
||||
}),
|
||||
subscriptionId: varchar("subscriptionId", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => subscriptions.subscriptionId, {
|
||||
@@ -94,6 +103,7 @@ export const subscriptionItems = pgTable("subscriptionItems", {
|
||||
}),
|
||||
planId: varchar("planId", { length: 255 }).notNull(),
|
||||
priceId: varchar("priceId", { length: 255 }),
|
||||
featureId: varchar("featureId", { length: 255 }),
|
||||
meterId: varchar("meterId", { length: 255 }),
|
||||
unitAmount: real("unitAmount"),
|
||||
tiers: text("tiers"),
|
||||
@@ -136,6 +146,7 @@ export const limits = pgTable("limits", {
|
||||
})
|
||||
.notNull(),
|
||||
value: real("value"),
|
||||
override: boolean("override").default(false),
|
||||
description: text("description")
|
||||
});
|
||||
|
||||
@@ -281,6 +292,7 @@ export const accessAuditLog = pgTable(
|
||||
actor: varchar("actor", { length: 255 }),
|
||||
actorId: varchar("actorId", { length: 255 }),
|
||||
resourceId: integer("resourceId"),
|
||||
siteResourceId: integer("siteResourceId"),
|
||||
ip: varchar("ip", { length: 45 }),
|
||||
type: varchar("type", { length: 100 }).notNull(),
|
||||
action: boolean("action").notNull(),
|
||||
@@ -297,6 +309,45 @@ export const accessAuditLog = pgTable(
|
||||
]
|
||||
);
|
||||
|
||||
export const connectionAuditLog = pgTable(
|
||||
"connectionAuditLog",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
sessionId: text("sessionId").notNull(),
|
||||
siteResourceId: integer("siteResourceId").references(
|
||||
() => siteResources.siteResourceId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
orgId: text("orgId").references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
siteId: integer("siteId").references(() => sites.siteId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
clientId: integer("clientId").references(() => clients.clientId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
userId: text("userId").references(() => users.userId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
sourceAddr: text("sourceAddr").notNull(),
|
||||
destAddr: text("destAddr").notNull(),
|
||||
protocol: text("protocol").notNull(),
|
||||
startedAt: integer("startedAt").notNull(),
|
||||
endedAt: integer("endedAt"),
|
||||
bytesTx: integer("bytesTx"),
|
||||
bytesRx: integer("bytesRx")
|
||||
},
|
||||
(table) => [
|
||||
index("idx_accessAuditLog_startedAt").on(table.startedAt),
|
||||
index("idx_accessAuditLog_org_startedAt").on(
|
||||
table.orgId,
|
||||
table.startedAt
|
||||
),
|
||||
index("idx_accessAuditLog_siteResourceId").on(table.siteResourceId)
|
||||
]
|
||||
);
|
||||
|
||||
export const approvals = pgTable("approvals", {
|
||||
approvalId: serial("approvalId").primaryKey(),
|
||||
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
|
||||
@@ -323,6 +374,90 @@ export const approvals = pgTable("approvals", {
|
||||
.notNull()
|
||||
});
|
||||
|
||||
export const bannedEmails = pgTable("bannedEmails", {
|
||||
email: varchar("email", { length: 255 }).primaryKey()
|
||||
});
|
||||
|
||||
export const bannedIps = pgTable("bannedIps", {
|
||||
ip: varchar("ip", { length: 255 }).primaryKey()
|
||||
});
|
||||
|
||||
export const siteProvisioningKeys = pgTable("siteProvisioningKeys", {
|
||||
siteProvisioningKeyId: varchar("siteProvisioningKeyId", {
|
||||
length: 255
|
||||
}).primaryKey(),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
siteProvisioningKeyHash: text("siteProvisioningKeyHash").notNull(),
|
||||
lastChars: varchar("lastChars", { length: 4 }).notNull(),
|
||||
createdAt: varchar("dateCreated", { length: 255 }).notNull(),
|
||||
lastUsed: varchar("lastUsed", { length: 255 }),
|
||||
maxBatchSize: integer("maxBatchSize"), // null = no limit
|
||||
numUsed: integer("numUsed").notNull().default(0),
|
||||
validUntil: varchar("validUntil", { length: 255 }),
|
||||
approveNewSites: boolean("approveNewSites").notNull().default(true)
|
||||
});
|
||||
|
||||
export const siteProvisioningKeyOrg = pgTable(
|
||||
"siteProvisioningKeyOrg",
|
||||
{
|
||||
siteProvisioningKeyId: varchar("siteProvisioningKeyId", {
|
||||
length: 255
|
||||
})
|
||||
.notNull()
|
||||
.references(() => siteProvisioningKeys.siteProvisioningKeyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
orgId: varchar("orgId", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||
},
|
||||
(table) => [
|
||||
primaryKey({
|
||||
columns: [table.siteProvisioningKeyId, table.orgId]
|
||||
})
|
||||
]
|
||||
);
|
||||
|
||||
export const eventStreamingDestinations = pgTable(
|
||||
"eventStreamingDestinations",
|
||||
{
|
||||
destinationId: serial("destinationId").primaryKey(),
|
||||
orgId: varchar("orgId", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
sendConnectionLogs: boolean("sendConnectionLogs").notNull().default(false),
|
||||
sendRequestLogs: boolean("sendRequestLogs").notNull().default(false),
|
||||
sendActionLogs: boolean("sendActionLogs").notNull().default(false),
|
||||
sendAccessLogs: boolean("sendAccessLogs").notNull().default(false),
|
||||
type: varchar("type", { length: 50 }).notNull(), // e.g. "http", "kafka", etc.
|
||||
config: text("config").notNull(), // JSON string with the configuration for the destination
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
||||
}
|
||||
);
|
||||
|
||||
export const eventStreamingCursors = pgTable(
|
||||
"eventStreamingCursors",
|
||||
{
|
||||
cursorId: serial("cursorId").primaryKey(),
|
||||
destinationId: integer("destinationId")
|
||||
.notNull()
|
||||
.references(() => eventStreamingDestinations.destinationId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
logType: varchar("logType", { length: 50 }).notNull(), // "request" | "action" | "access" | "connection"
|
||||
lastSentId: bigint("lastSentId", { mode: "number" }).notNull().default(0),
|
||||
lastSentAt: bigint("lastSentAt", { mode: "number" }) // epoch milliseconds, null if never sent
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex("idx_eventStreamingCursors_dest_type").on(
|
||||
table.destinationId,
|
||||
table.logType
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
export type Approval = InferSelectModel<typeof approvals>;
|
||||
export type Limit = InferSelectModel<typeof limits>;
|
||||
export type Account = InferSelectModel<typeof account>;
|
||||
@@ -344,3 +479,19 @@ export type LoginPage = InferSelectModel<typeof loginPage>;
|
||||
export type LoginPageBranding = InferSelectModel<typeof loginPageBranding>;
|
||||
export type ActionAuditLog = InferSelectModel<typeof actionAuditLog>;
|
||||
export type AccessAuditLog = InferSelectModel<typeof accessAuditLog>;
|
||||
export type ConnectionAuditLog = InferSelectModel<typeof connectionAuditLog>;
|
||||
export type SessionTransferToken = InferSelectModel<
|
||||
typeof sessionTransferToken
|
||||
>;
|
||||
export type BannedEmail = InferSelectModel<typeof bannedEmails>;
|
||||
export type BannedIp = InferSelectModel<typeof bannedIps>;
|
||||
export type SiteProvisioningKey = InferSelectModel<typeof siteProvisioningKeys>;
|
||||
export type SiteProvisioningKeyOrg = InferSelectModel<
|
||||
typeof siteProvisioningKeyOrg
|
||||
>;
|
||||
export type EventStreamingDestination = InferSelectModel<
|
||||
typeof eventStreamingDestinations
|
||||
>;
|
||||
export type EventStreamingCursor = InferSelectModel<
|
||||
typeof eventStreamingCursors
|
||||
>;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import {
|
||||
pgTable,
|
||||
serial,
|
||||
varchar,
|
||||
boolean,
|
||||
integer,
|
||||
bigint,
|
||||
real,
|
||||
text,
|
||||
index,
|
||||
uniqueIndex
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { InferSelectModel } from "drizzle-orm";
|
||||
import { randomUUID } from "crypto";
|
||||
import { alias } from "yargs";
|
||||
import { InferSelectModel } from "drizzle-orm";
|
||||
import {
|
||||
bigint,
|
||||
boolean,
|
||||
index,
|
||||
integer,
|
||||
pgTable,
|
||||
primaryKey,
|
||||
real,
|
||||
serial,
|
||||
text,
|
||||
unique,
|
||||
varchar
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
export const domains = pgTable("domains", {
|
||||
domainId: varchar("domainId").primaryKey(),
|
||||
@@ -24,7 +24,8 @@ export const domains = pgTable("domains", {
|
||||
tries: integer("tries").notNull().default(0),
|
||||
certResolver: varchar("certResolver"),
|
||||
customCertResolver: varchar("customCertResolver"),
|
||||
preferWildcardCert: boolean("preferWildcardCert")
|
||||
preferWildcardCert: boolean("preferWildcardCert"),
|
||||
errorMessage: text("errorMessage")
|
||||
});
|
||||
|
||||
export const dnsRecords = pgTable("dnsRecords", {
|
||||
@@ -55,7 +56,14 @@ export const orgs = pgTable("orgs", {
|
||||
.default(0),
|
||||
settingsLogRetentionDaysAction: integer("settingsLogRetentionDaysAction") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||
.notNull()
|
||||
.default(0)
|
||||
.default(0),
|
||||
settingsLogRetentionDaysConnection: integer("settingsLogRetentionDaysConnection") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||
.notNull()
|
||||
.default(0),
|
||||
sshCaPrivateKey: text("sshCaPrivateKey"), // Encrypted SSH CA private key (PEM format)
|
||||
sshCaPublicKey: text("sshCaPublicKey"), // SSH CA public key (OpenSSH format)
|
||||
isBillingOrg: boolean("isBillingOrg"),
|
||||
billingOrgId: varchar("billingOrgId")
|
||||
});
|
||||
|
||||
export const orgDomains = pgTable("orgDomains", {
|
||||
@@ -86,12 +94,14 @@ export const sites = pgTable("sites", {
|
||||
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
|
||||
type: varchar("type").notNull(), // "newt" or "wireguard"
|
||||
online: boolean("online").notNull().default(false),
|
||||
lastPing: integer("lastPing"),
|
||||
address: varchar("address"),
|
||||
endpoint: varchar("endpoint"),
|
||||
publicKey: varchar("publicKey"),
|
||||
lastHolePunch: bigint("lastHolePunch", { mode: "number" }),
|
||||
listenPort: integer("listenPort"),
|
||||
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true)
|
||||
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true),
|
||||
status: varchar("status").$type<"pending" | "approved">().default("approved")
|
||||
});
|
||||
|
||||
export const resources = pgTable("resources", {
|
||||
@@ -142,7 +152,8 @@ export const resources = pgTable("resources", {
|
||||
}).default("forced"), // "forced" = always show, "automatic" = only when down
|
||||
maintenanceTitle: text("maintenanceTitle"),
|
||||
maintenanceMessage: text("maintenanceMessage"),
|
||||
maintenanceEstimatedTime: text("maintenanceEstimatedTime")
|
||||
maintenanceEstimatedTime: text("maintenanceEstimatedTime"),
|
||||
postAuthPath: text("postAuthPath")
|
||||
});
|
||||
|
||||
export const targets = pgTable("targets", {
|
||||
@@ -187,7 +198,9 @@ export const targetHealthCheck = pgTable("targetHealthCheck", {
|
||||
hcFollowRedirects: boolean("hcFollowRedirects").default(true),
|
||||
hcMethod: varchar("hcMethod").default("GET"),
|
||||
hcStatus: integer("hcStatus"), // http code
|
||||
hcHealth: text("hcHealth").default("unknown"), // "unknown", "healthy", "unhealthy"
|
||||
hcHealth: text("hcHealth")
|
||||
.$type<"unknown" | "healthy" | "unhealthy">()
|
||||
.default("unknown"), // "unknown", "healthy", "unhealthy"
|
||||
hcTlsServerName: text("hcTlsServerName")
|
||||
});
|
||||
|
||||
@@ -217,7 +230,7 @@ export const siteResources = pgTable("siteResources", {
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
niceId: varchar("niceId").notNull(),
|
||||
name: varchar("name").notNull(),
|
||||
mode: varchar("mode").notNull(), // "host" | "cidr" | "port"
|
||||
mode: varchar("mode").$type<"host" | "cidr">().notNull(), // "host" | "cidr" | "port"
|
||||
protocol: varchar("protocol"), // only for port mode
|
||||
proxyPort: integer("proxyPort"), // only for port mode
|
||||
destinationPort: integer("destinationPort"), // only for port mode
|
||||
@@ -227,7 +240,11 @@ export const siteResources = pgTable("siteResources", {
|
||||
aliasAddress: varchar("aliasAddress"),
|
||||
tcpPortRangeString: varchar("tcpPortRangeString").notNull().default("*"),
|
||||
udpPortRangeString: varchar("udpPortRangeString").notNull().default("*"),
|
||||
disableIcmp: boolean("disableIcmp").notNull().default(false)
|
||||
disableIcmp: boolean("disableIcmp").notNull().default(false),
|
||||
authDaemonPort: integer("authDaemonPort").default(22123),
|
||||
authDaemonMode: varchar("authDaemonMode", { length: 32 })
|
||||
.$type<"site" | "remote">()
|
||||
.default("site")
|
||||
});
|
||||
|
||||
export const clientSiteResources = pgTable("clientSiteResources", {
|
||||
@@ -274,8 +291,10 @@ export const users = pgTable("user", {
|
||||
dateCreated: varchar("dateCreated").notNull(),
|
||||
termsAcceptedTimestamp: varchar("termsAcceptedTimestamp"),
|
||||
termsVersion: varchar("termsVersion"),
|
||||
marketingEmailConsent: boolean("marketingEmailConsent").default(false),
|
||||
serverAdmin: boolean("serverAdmin").notNull().default(false),
|
||||
lastPasswordChange: bigint("lastPasswordChange", { mode: "number" })
|
||||
lastPasswordChange: bigint("lastPasswordChange", { mode: "number" }),
|
||||
locale: varchar("locale")
|
||||
});
|
||||
|
||||
export const newts = pgTable("newt", {
|
||||
@@ -323,11 +342,9 @@ export const userOrgs = pgTable("userOrgs", {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId),
|
||||
isOwner: boolean("isOwner").notNull().default(false),
|
||||
autoProvisioned: boolean("autoProvisioned").default(false)
|
||||
autoProvisioned: boolean("autoProvisioned").default(false),
|
||||
pamUsername: varchar("pamUsername") // cleaned username for ssh and such
|
||||
});
|
||||
|
||||
export const emailVerificationCodes = pgTable("emailVerificationCodes", {
|
||||
@@ -366,9 +383,29 @@ export const roles = pgTable("roles", {
|
||||
isAdmin: boolean("isAdmin"),
|
||||
name: varchar("name").notNull(),
|
||||
description: varchar("description"),
|
||||
requireDeviceApproval: boolean("requireDeviceApproval").default(false)
|
||||
requireDeviceApproval: boolean("requireDeviceApproval").default(false),
|
||||
sshSudoMode: varchar("sshSudoMode", { length: 32 }).default("none"), // "none" | "full" | "commands"
|
||||
sshSudoCommands: text("sshSudoCommands").default("[]"),
|
||||
sshCreateHomeDir: boolean("sshCreateHomeDir").default(true),
|
||||
sshUnixGroups: text("sshUnixGroups").default("[]")
|
||||
});
|
||||
|
||||
export const userOrgRoles = pgTable(
|
||||
"userOrgRoles",
|
||||
{
|
||||
userId: varchar("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
orgId: varchar("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||
},
|
||||
(t) => [unique().on(t.userId, t.orgId, t.roleId)]
|
||||
);
|
||||
|
||||
export const roleActions = pgTable("roleActions", {
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
@@ -436,12 +473,22 @@ export const userInvites = pgTable("userInvites", {
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
email: varchar("email").notNull(),
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
|
||||
tokenHash: varchar("token").notNull(),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||
tokenHash: varchar("token").notNull()
|
||||
});
|
||||
|
||||
export const userInviteRoles = pgTable(
|
||||
"userInviteRoles",
|
||||
{
|
||||
inviteId: varchar("inviteId")
|
||||
.notNull()
|
||||
.references(() => userInvites.inviteId, { onDelete: "cascade" }),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.inviteId, t.roleId] })]
|
||||
);
|
||||
|
||||
export const resourcePincode = pgTable("resourcePincode", {
|
||||
pincodeId: serial("pincodeId").primaryKey(),
|
||||
resourceId: integer("resourceId")
|
||||
@@ -705,6 +752,7 @@ export const clientSitesAssociationsCache = pgTable(
|
||||
.notNull(),
|
||||
siteId: integer("siteId").notNull(),
|
||||
isRelayed: boolean("isRelayed").notNull().default(false),
|
||||
isJitMode: boolean("isJitMode").notNull().default(false),
|
||||
endpoint: varchar("endpoint"),
|
||||
publicKey: varchar("publicKey") // this will act as the session's public key for hole punching so we can track when it changes
|
||||
}
|
||||
@@ -983,6 +1031,16 @@ export const deviceWebAuthCodes = pgTable("deviceWebAuthCodes", {
|
||||
})
|
||||
});
|
||||
|
||||
export const roundTripMessageTracker = pgTable("roundTripMessageTracker", {
|
||||
messageId: serial("messageId").primaryKey(),
|
||||
wsClientId: varchar("clientId"),
|
||||
messageType: varchar("messageType"),
|
||||
sentAt: bigint("sentAt", { mode: "number" }).notNull(),
|
||||
receivedAt: bigint("receivedAt", { mode: "number" }),
|
||||
error: text("error"),
|
||||
complete: boolean("complete").notNull().default(false)
|
||||
});
|
||||
|
||||
export type Org = InferSelectModel<typeof orgs>;
|
||||
export type User = InferSelectModel<typeof users>;
|
||||
export type Site = InferSelectModel<typeof sites>;
|
||||
@@ -1006,7 +1064,9 @@ export type UserSite = InferSelectModel<typeof userSites>;
|
||||
export type RoleResource = InferSelectModel<typeof roleResources>;
|
||||
export type UserResource = InferSelectModel<typeof userResources>;
|
||||
export type UserInvite = InferSelectModel<typeof userInvites>;
|
||||
export type UserInviteRole = InferSelectModel<typeof userInviteRoles>;
|
||||
export type UserOrg = InferSelectModel<typeof userOrgs>;
|
||||
export type UserOrgRole = InferSelectModel<typeof userOrgRoles>;
|
||||
export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
||||
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||
@@ -1043,3 +1103,6 @@ export type SecurityKey = InferSelectModel<typeof securityKeys>;
|
||||
export type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;
|
||||
export type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
|
||||
export type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
|
||||
export type RoundTripMessageTracker = InferSelectModel<
|
||||
typeof roundTripMessageTracker
|
||||
>;
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { db, loginPage, LoginPage, loginPageOrg, Org, orgs, roles } from "@server/db";
|
||||
import {
|
||||
db,
|
||||
loginPage,
|
||||
LoginPage,
|
||||
loginPageOrg,
|
||||
Org,
|
||||
orgs,
|
||||
roles
|
||||
} from "@server/db";
|
||||
import {
|
||||
Resource,
|
||||
ResourcePassword,
|
||||
@@ -12,13 +20,12 @@ import {
|
||||
resources,
|
||||
roleResources,
|
||||
sessions,
|
||||
userOrgs,
|
||||
userResources,
|
||||
users,
|
||||
ResourceHeaderAuthExtendedCompatibility,
|
||||
resourceHeaderAuthExtendedCompatibility
|
||||
} from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
|
||||
export type ResourceWithAuth = {
|
||||
resource: Resource | null;
|
||||
@@ -104,24 +111,15 @@ export async function getUserSessionWithUser(
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user organization role
|
||||
* Get role name by role ID (for display).
|
||||
*/
|
||||
export async function getUserOrgRole(userId: string, orgId: string) {
|
||||
const userOrgRole = await db
|
||||
.select({
|
||||
userId: userOrgs.userId,
|
||||
orgId: userOrgs.orgId,
|
||||
roleId: userOrgs.roleId,
|
||||
isOwner: userOrgs.isOwner,
|
||||
autoProvisioned: userOrgs.autoProvisioned,
|
||||
roleName: roles.name
|
||||
})
|
||||
.from(userOrgs)
|
||||
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
|
||||
.leftJoin(roles, eq(userOrgs.roleId, roles.roleId))
|
||||
export async function getRoleName(roleId: number): Promise<string | null> {
|
||||
const [row] = await db
|
||||
.select({ name: roles.name })
|
||||
.from(roles)
|
||||
.where(eq(roles.roleId, roleId))
|
||||
.limit(1);
|
||||
|
||||
return userOrgRole.length > 0 ? userOrgRole[0] : null;
|
||||
return row?.name ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +127,7 @@ export async function getUserOrgRole(userId: string, orgId: string) {
|
||||
*/
|
||||
export async function getRoleResourceAccess(
|
||||
resourceId: number,
|
||||
roleId: number
|
||||
roleIds: number[]
|
||||
) {
|
||||
const roleResourceAccess = await db
|
||||
.select()
|
||||
@@ -137,12 +135,11 @@ export async function getRoleResourceAccess(
|
||||
.where(
|
||||
and(
|
||||
eq(roleResources.resourceId, resourceId),
|
||||
eq(roleResources.roleId, roleId)
|
||||
inArray(roleResources.roleId, roleIds)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
);
|
||||
|
||||
return roleResourceAccess.length > 0 ? roleResourceAccess[0] : null;
|
||||
return roleResourceAccess.length > 0 ? roleResourceAccess : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
196
server/db/regions.ts
Normal file
196
server/db/regions.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
// Regions of the World
|
||||
// as of 2025-10-25
|
||||
//
|
||||
// Adapted according to the United Nations Geoscheme
|
||||
// see https://www.unicode.org/cldr/charts/48/supplemental/territory_containment_un_m_49.html
|
||||
// see https://unstats.un.org/unsd/methodology/m49
|
||||
|
||||
export const REGIONS = [
|
||||
{
|
||||
name: "regionAfrica",
|
||||
id: "002",
|
||||
includes: [
|
||||
{
|
||||
name: "regionNorthernAfrica",
|
||||
id: "015",
|
||||
countries: ["DZ", "EG", "LY", "MA", "SD", "TN", "EH"]
|
||||
},
|
||||
{
|
||||
name: "regionEasternAfrica",
|
||||
id: "014",
|
||||
countries: ["IO", "BI", "KM", "DJ", "ER", "ET", "TF", "KE", "MG", "MW", "MU", "YT", "MZ", "RE", "RW", "SC", "SO", "SS", "UG", "ZM", "ZW"]
|
||||
},
|
||||
{
|
||||
name: "regionMiddleAfrica",
|
||||
id: "017",
|
||||
countries: ["AO", "CM", "CF", "TD", "CG", "CD", "GQ", "GA", "ST"]
|
||||
},
|
||||
{
|
||||
name: "regionSouthernAfrica",
|
||||
id: "018",
|
||||
countries: ["BW", "SZ", "LS", "NA", "ZA"]
|
||||
},
|
||||
{
|
||||
name: "regionWesternAfrica",
|
||||
id: "011",
|
||||
countries: ["BJ", "BF", "CV", "CI", "GM", "GH", "GN", "GW", "LR", "ML", "MR", "NE", "NG", "SH", "SN", "SL", "TG"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "regionAmericas",
|
||||
id: "019",
|
||||
includes: [
|
||||
{
|
||||
name: "regionCaribbean",
|
||||
id: "029",
|
||||
countries: ["AI", "AG", "AW", "BS", "BB", "BQ", "VG", "KY", "CU", "CW", "DM", "DO", "GD", "GP", "HT", "JM", "MQ", "MS", "PR", "BL", "KN", "LC", "MF", "VC", "SX", "TT", "TC", "VI"]
|
||||
},
|
||||
{
|
||||
name: "regionCentralAmerica",
|
||||
id: "013",
|
||||
countries: ["BZ", "CR", "SV", "GT", "HN", "MX", "NI", "PA"]
|
||||
},
|
||||
{
|
||||
name: "regionSouthAmerica",
|
||||
id: "005",
|
||||
countries: ["AR", "BO", "BV", "BR", "CL", "CO", "EC", "FK", "GF", "GY", "PY", "PE", "GS", "SR", "UY", "VE"]
|
||||
},
|
||||
{
|
||||
name: "regionNorthernAmerica",
|
||||
id: "021",
|
||||
countries: ["BM", "CA", "GL", "PM", "US"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "regionAsia",
|
||||
id: "142",
|
||||
includes: [
|
||||
{
|
||||
name: "regionCentralAsia",
|
||||
id: "143",
|
||||
countries: ["KZ", "KG", "TJ", "TM", "UZ"]
|
||||
},
|
||||
{
|
||||
name: "regionEasternAsia",
|
||||
id: "030",
|
||||
countries: ["CN", "HK", "MO", "KP", "JP", "MN", "KR"]
|
||||
},
|
||||
{
|
||||
name: "regionSouthEasternAsia",
|
||||
id: "035",
|
||||
countries: ["BN", "KH", "ID", "LA", "MY", "MM", "PH", "SG", "TH", "TL", "VN"]
|
||||
},
|
||||
{
|
||||
name: "regionSouthernAsia",
|
||||
id: "034",
|
||||
countries: ["AF", "BD", "BT", "IN", "IR", "MV", "NP", "PK", "LK"]
|
||||
},
|
||||
{
|
||||
name: "regionWesternAsia",
|
||||
id: "145",
|
||||
countries: ["AM", "AZ", "BH", "CY", "GE", "IQ", "IL", "JO", "KW", "LB", "OM", "QA", "SA", "PS", "SY", "TR", "AE", "YE"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "regionEurope",
|
||||
id: "150",
|
||||
includes: [
|
||||
{
|
||||
name: "regionEasternEurope",
|
||||
id: "151",
|
||||
countries: ["BY", "BG", "CZ", "HU", "PL", "MD", "RO", "RU", "SK", "UA"]
|
||||
},
|
||||
{
|
||||
name: "regionNorthernEurope",
|
||||
id: "154",
|
||||
countries: ["AX", "DK", "EE", "FO", "FI", "GG", "IS", "IE", "IM", "JE", "LV", "LT", "NO", "SJ", "SE", "GB"]
|
||||
},
|
||||
{
|
||||
name: "regionSouthernEurope",
|
||||
id: "039",
|
||||
countries: ["AL", "AD", "BA", "HR", "GI", "GR", "VA", "IT", "MT", "ME", "MK", "PT", "SM", "RS", "SI", "ES"]
|
||||
},
|
||||
{
|
||||
name: "regionWesternEurope",
|
||||
id: "155",
|
||||
countries: ["AT", "BE", "FR", "DE", "LI", "LU", "MC", "NL", "CH"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "regionOceania",
|
||||
id: "009",
|
||||
includes: [
|
||||
{
|
||||
name: "regionAustraliaAndNewZealand",
|
||||
id: "053",
|
||||
countries: ["AU", "CX", "CC", "HM", "NZ", "NF"]
|
||||
},
|
||||
{
|
||||
name: "regionMelanesia",
|
||||
id: "054",
|
||||
countries: ["FJ", "NC", "PG", "SB", "VU"]
|
||||
},
|
||||
{
|
||||
name: "regionMicronesia",
|
||||
id: "057",
|
||||
countries: ["GU", "KI", "MH", "FM", "NR", "MP", "PW", "UM"]
|
||||
},
|
||||
{
|
||||
name: "regionPolynesia",
|
||||
id: "061",
|
||||
countries: ["AS", "CK", "PF", "NU", "PN", "WS", "TK", "TO", "TV", "WF"]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
type Subregion = {
|
||||
name: string;
|
||||
id: string;
|
||||
countries: string[];
|
||||
};
|
||||
|
||||
type Region = {
|
||||
name: string;
|
||||
id: string;
|
||||
includes: Subregion[];
|
||||
};
|
||||
|
||||
export function getRegionNameById(regionId: string): string | undefined {
|
||||
// Check top-level regions
|
||||
const region = REGIONS.find((r) => r.id === regionId);
|
||||
if (region) {
|
||||
return region.name;
|
||||
}
|
||||
|
||||
// Check subregions
|
||||
for (const region of REGIONS) {
|
||||
for (const subregion of region.includes) {
|
||||
if (subregion.id === regionId) {
|
||||
return subregion.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isValidRegionId(regionId: string): boolean {
|
||||
// Check top-level regions
|
||||
if (REGIONS.find((r) => r.id === regionId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check subregions
|
||||
for (const region of REGIONS) {
|
||||
if (region.includes.find((s) => s.id === regionId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -23,7 +23,8 @@ export default db;
|
||||
export const primaryDb = db;
|
||||
export type Transaction = Parameters<
|
||||
Parameters<(typeof db)["transaction"]>[0]
|
||||
>[0];
|
||||
>[0];
|
||||
export const DB_TYPE: "pg" | "sqlite" = "sqlite";
|
||||
|
||||
function checkFileExists(filePath: string): boolean {
|
||||
try {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from "./driver";
|
||||
export * from "./logsDriver";
|
||||
export * from "./safeRead";
|
||||
export * from "./schema/schema";
|
||||
export * from "./schema/privateSchema";
|
||||
export * from "./migrate";
|
||||
|
||||
7
server/db/sqlite/logsDriver.ts
Normal file
7
server/db/sqlite/logsDriver.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { db as mainDb } from "./driver";
|
||||
|
||||
// SQLite doesn't support separate databases for logs in the same way as Postgres
|
||||
// Always use the main database connection for SQLite
|
||||
export const logsDb = mainDb;
|
||||
export default logsDb;
|
||||
export const primaryLogsDb = logsDb;
|
||||
@@ -4,7 +4,7 @@ import path from "path";
|
||||
|
||||
const migrationsFolder = path.join("server/migrations");
|
||||
|
||||
const runMigrations = async () => {
|
||||
export const runMigrations = async () => {
|
||||
console.log("Running migrations...");
|
||||
try {
|
||||
migrate(db as any, {
|
||||
@@ -16,5 +16,3 @@ const runMigrations = async () => {
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
runMigrations();
|
||||
|
||||
11
server/db/sqlite/safeRead.ts
Normal file
11
server/db/sqlite/safeRead.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { db } from "./driver";
|
||||
|
||||
/**
|
||||
* Runs a read query. For SQLite there is no replica/primary distinction,
|
||||
* so the query is executed once against the database.
|
||||
*/
|
||||
export async function safeRead<T>(
|
||||
query: (d: typeof db) => Promise<T>
|
||||
): Promise<T> {
|
||||
return query(db);
|
||||
}
|
||||
@@ -2,11 +2,22 @@ import { InferSelectModel } from "drizzle-orm";
|
||||
import {
|
||||
index,
|
||||
integer,
|
||||
primaryKey,
|
||||
real,
|
||||
sqliteTable,
|
||||
text
|
||||
text,
|
||||
uniqueIndex
|
||||
} from "drizzle-orm/sqlite-core";
|
||||
import { clients, domains, exitNodes, orgs, sessions, users } from "./schema";
|
||||
import {
|
||||
clients,
|
||||
domains,
|
||||
exitNodes,
|
||||
orgs,
|
||||
sessions,
|
||||
siteResources,
|
||||
sites,
|
||||
users
|
||||
} from "./schema";
|
||||
|
||||
export const certificates = sqliteTable("certificates", {
|
||||
certId: integer("certId").primaryKey({ autoIncrement: true }),
|
||||
@@ -70,13 +81,16 @@ export const subscriptions = sqliteTable("subscriptions", {
|
||||
canceledAt: integer("canceledAt"),
|
||||
createdAt: integer("createdAt").notNull(),
|
||||
updatedAt: integer("updatedAt"),
|
||||
billingCycleAnchor: integer("billingCycleAnchor")
|
||||
version: integer("version"),
|
||||
billingCycleAnchor: integer("billingCycleAnchor"),
|
||||
type: text("type") // tier1, tier2, tier3, or license
|
||||
});
|
||||
|
||||
export const subscriptionItems = sqliteTable("subscriptionItems", {
|
||||
subscriptionItemId: integer("subscriptionItemId").primaryKey({
|
||||
autoIncrement: true
|
||||
}),
|
||||
stripeSubscriptionItemId: text("stripeSubscriptionItemId"),
|
||||
subscriptionId: text("subscriptionId")
|
||||
.notNull()
|
||||
.references(() => subscriptions.subscriptionId, {
|
||||
@@ -84,6 +98,7 @@ export const subscriptionItems = sqliteTable("subscriptionItems", {
|
||||
}),
|
||||
planId: text("planId").notNull(),
|
||||
priceId: text("priceId"),
|
||||
featureId: text("featureId"),
|
||||
meterId: text("meterId"),
|
||||
unitAmount: real("unitAmount"),
|
||||
tiers: text("tiers"),
|
||||
@@ -126,6 +141,7 @@ export const limits = sqliteTable("limits", {
|
||||
})
|
||||
.notNull(),
|
||||
value: real("value"),
|
||||
override: integer("override", { mode: "boolean" }).default(false),
|
||||
description: text("description")
|
||||
});
|
||||
|
||||
@@ -273,6 +289,7 @@ export const accessAuditLog = sqliteTable(
|
||||
actor: text("actor"),
|
||||
actorId: text("actorId"),
|
||||
resourceId: integer("resourceId"),
|
||||
siteResourceId: integer("siteResourceId"),
|
||||
ip: text("ip"),
|
||||
location: text("location"),
|
||||
type: text("type").notNull(),
|
||||
@@ -289,6 +306,45 @@ export const accessAuditLog = sqliteTable(
|
||||
]
|
||||
);
|
||||
|
||||
export const connectionAuditLog = sqliteTable(
|
||||
"connectionAuditLog",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
sessionId: text("sessionId").notNull(),
|
||||
siteResourceId: integer("siteResourceId").references(
|
||||
() => siteResources.siteResourceId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
orgId: text("orgId").references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
siteId: integer("siteId").references(() => sites.siteId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
clientId: integer("clientId").references(() => clients.clientId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
userId: text("userId").references(() => users.userId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
sourceAddr: text("sourceAddr").notNull(),
|
||||
destAddr: text("destAddr").notNull(),
|
||||
protocol: text("protocol").notNull(),
|
||||
startedAt: integer("startedAt").notNull(),
|
||||
endedAt: integer("endedAt"),
|
||||
bytesTx: integer("bytesTx"),
|
||||
bytesRx: integer("bytesRx")
|
||||
},
|
||||
(table) => [
|
||||
index("idx_accessAuditLog_startedAt").on(table.startedAt),
|
||||
index("idx_accessAuditLog_org_startedAt").on(
|
||||
table.orgId,
|
||||
table.startedAt
|
||||
),
|
||||
index("idx_accessAuditLog_siteResourceId").on(table.siteResourceId)
|
||||
]
|
||||
);
|
||||
|
||||
export const approvals = sqliteTable("approvals", {
|
||||
approvalId: integer("approvalId").primaryKey({ autoIncrement: true }),
|
||||
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
|
||||
@@ -313,6 +369,92 @@ export const approvals = sqliteTable("approvals", {
|
||||
.notNull()
|
||||
});
|
||||
|
||||
export const bannedEmails = sqliteTable("bannedEmails", {
|
||||
email: text("email").primaryKey()
|
||||
});
|
||||
|
||||
export const bannedIps = sqliteTable("bannedIps", {
|
||||
ip: text("ip").primaryKey()
|
||||
});
|
||||
|
||||
export const siteProvisioningKeys = sqliteTable("siteProvisioningKeys", {
|
||||
siteProvisioningKeyId: text("siteProvisioningKeyId").primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
siteProvisioningKeyHash: text("siteProvisioningKeyHash").notNull(),
|
||||
lastChars: text("lastChars").notNull(),
|
||||
createdAt: text("dateCreated").notNull(),
|
||||
lastUsed: text("lastUsed"),
|
||||
maxBatchSize: integer("maxBatchSize"), // null = no limit
|
||||
numUsed: integer("numUsed").notNull().default(0),
|
||||
validUntil: text("validUntil"),
|
||||
approveNewSites: integer("approveNewSites", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(true)
|
||||
});
|
||||
|
||||
export const siteProvisioningKeyOrg = sqliteTable(
|
||||
"siteProvisioningKeyOrg",
|
||||
{
|
||||
siteProvisioningKeyId: text("siteProvisioningKeyId")
|
||||
.notNull()
|
||||
.references(() => siteProvisioningKeys.siteProvisioningKeyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
orgId: text("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||
},
|
||||
(table) => [
|
||||
primaryKey({
|
||||
columns: [table.siteProvisioningKeyId, table.orgId]
|
||||
})
|
||||
]
|
||||
);
|
||||
|
||||
export const eventStreamingDestinations = sqliteTable(
|
||||
"eventStreamingDestinations",
|
||||
{
|
||||
destinationId: integer("destinationId").primaryKey({
|
||||
autoIncrement: true
|
||||
}),
|
||||
orgId: text("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
sendConnectionLogs: integer("sendConnectionLogs", { mode: "boolean" }).notNull().default(false),
|
||||
sendRequestLogs: integer("sendRequestLogs", { mode: "boolean" }).notNull().default(false),
|
||||
sendActionLogs: integer("sendActionLogs", { mode: "boolean" }).notNull().default(false),
|
||||
sendAccessLogs: integer("sendAccessLogs", { mode: "boolean" }).notNull().default(false),
|
||||
type: text("type").notNull(), // e.g. "http", "kafka", etc.
|
||||
config: text("config").notNull(), // JSON string with the configuration for the destination
|
||||
enabled: integer("enabled", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(true),
|
||||
createdAt: integer("createdAt").notNull(),
|
||||
updatedAt: integer("updatedAt").notNull()
|
||||
}
|
||||
);
|
||||
|
||||
export const eventStreamingCursors = sqliteTable(
|
||||
"eventStreamingCursors",
|
||||
{
|
||||
cursorId: integer("cursorId").primaryKey({ autoIncrement: true }),
|
||||
destinationId: integer("destinationId")
|
||||
.notNull()
|
||||
.references(() => eventStreamingDestinations.destinationId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
logType: text("logType").notNull(), // "request" | "action" | "access" | "connection"
|
||||
lastSentId: integer("lastSentId").notNull().default(0),
|
||||
lastSentAt: integer("lastSentAt") // epoch milliseconds, null if never sent
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex("idx_eventStreamingCursors_dest_type").on(
|
||||
table.destinationId,
|
||||
table.logType
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
export type Approval = InferSelectModel<typeof approvals>;
|
||||
export type Limit = InferSelectModel<typeof limits>;
|
||||
export type Account = InferSelectModel<typeof account>;
|
||||
@@ -334,3 +476,13 @@ export type LoginPage = InferSelectModel<typeof loginPage>;
|
||||
export type LoginPageBranding = InferSelectModel<typeof loginPageBranding>;
|
||||
export type ActionAuditLog = InferSelectModel<typeof actionAuditLog>;
|
||||
export type AccessAuditLog = InferSelectModel<typeof accessAuditLog>;
|
||||
export type ConnectionAuditLog = InferSelectModel<typeof connectionAuditLog>;
|
||||
export type BannedEmail = InferSelectModel<typeof bannedEmails>;
|
||||
export type BannedIp = InferSelectModel<typeof bannedIps>;
|
||||
export type SiteProvisioningKey = InferSelectModel<typeof siteProvisioningKeys>;
|
||||
export type EventStreamingDestination = InferSelectModel<
|
||||
typeof eventStreamingDestinations
|
||||
>;
|
||||
export type EventStreamingCursor = InferSelectModel<
|
||||
typeof eventStreamingCursors
|
||||
>;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { randomUUID } from "crypto";
|
||||
import { InferSelectModel } from "drizzle-orm";
|
||||
import {
|
||||
index,
|
||||
integer,
|
||||
primaryKey,
|
||||
sqliteTable,
|
||||
text,
|
||||
integer,
|
||||
index,
|
||||
uniqueIndex
|
||||
unique
|
||||
} from "drizzle-orm/sqlite-core";
|
||||
import { no } from "zod/v4/locales";
|
||||
|
||||
export const domains = sqliteTable("domains", {
|
||||
domainId: text("domainId").primaryKey(),
|
||||
@@ -20,7 +20,8 @@ export const domains = sqliteTable("domains", {
|
||||
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
||||
tries: integer("tries").notNull().default(0),
|
||||
certResolver: text("certResolver"),
|
||||
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" })
|
||||
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" }),
|
||||
errorMessage: text("errorMessage")
|
||||
});
|
||||
|
||||
export const dnsRecords = sqliteTable("dnsRecords", {
|
||||
@@ -52,7 +53,14 @@ export const orgs = sqliteTable("orgs", {
|
||||
.default(0),
|
||||
settingsLogRetentionDaysAction: integer("settingsLogRetentionDaysAction") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||
.notNull()
|
||||
.default(0)
|
||||
.default(0),
|
||||
settingsLogRetentionDaysConnection: integer("settingsLogRetentionDaysConnection") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||
.notNull()
|
||||
.default(0),
|
||||
sshCaPrivateKey: text("sshCaPrivateKey"), // Encrypted SSH CA private key (PEM format)
|
||||
sshCaPublicKey: text("sshCaPublicKey"), // SSH CA public key (OpenSSH format)
|
||||
isBillingOrg: integer("isBillingOrg", { mode: "boolean" }),
|
||||
billingOrgId: text("billingOrgId")
|
||||
});
|
||||
|
||||
export const userDomains = sqliteTable("userDomains", {
|
||||
@@ -92,6 +100,7 @@ export const sites = sqliteTable("sites", {
|
||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||
type: text("type").notNull(), // "newt" or "wireguard"
|
||||
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
||||
lastPing: integer("lastPing"),
|
||||
|
||||
// exit node stuff that is how to connect to the site when it has a wg server
|
||||
address: text("address"), // this is the address of the wireguard interface in newt
|
||||
@@ -101,7 +110,8 @@ export const sites = sqliteTable("sites", {
|
||||
listenPort: integer("listenPort"),
|
||||
dockerSocketEnabled: integer("dockerSocketEnabled", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(true)
|
||||
.default(true),
|
||||
status: text("status").$type<"pending" | "approved">().default("approved")
|
||||
});
|
||||
|
||||
export const resources = sqliteTable("resources", {
|
||||
@@ -162,7 +172,8 @@ export const resources = sqliteTable("resources", {
|
||||
}).default("forced"), // "forced" = always show, "automatic" = only when down
|
||||
maintenanceTitle: text("maintenanceTitle"),
|
||||
maintenanceMessage: text("maintenanceMessage"),
|
||||
maintenanceEstimatedTime: text("maintenanceEstimatedTime")
|
||||
maintenanceEstimatedTime: text("maintenanceEstimatedTime"),
|
||||
postAuthPath: text("postAuthPath")
|
||||
});
|
||||
|
||||
export const targets = sqliteTable("targets", {
|
||||
@@ -213,7 +224,9 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", {
|
||||
}).default(true),
|
||||
hcMethod: text("hcMethod").default("GET"),
|
||||
hcStatus: integer("hcStatus"), // http code
|
||||
hcHealth: text("hcHealth").default("unknown"), // "unknown", "healthy", "unhealthy"
|
||||
hcHealth: text("hcHealth")
|
||||
.$type<"unknown" | "healthy" | "unhealthy">()
|
||||
.default("unknown"), // "unknown", "healthy", "unhealthy"
|
||||
hcTlsServerName: text("hcTlsServerName")
|
||||
});
|
||||
|
||||
@@ -245,7 +258,7 @@ export const siteResources = sqliteTable("siteResources", {
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
niceId: text("niceId").notNull(),
|
||||
name: text("name").notNull(),
|
||||
mode: text("mode").notNull(), // "host" | "cidr" | "port"
|
||||
mode: text("mode").$type<"host" | "cidr">().notNull(), // "host" | "cidr" | "port"
|
||||
protocol: text("protocol"), // only for port mode
|
||||
proxyPort: integer("proxyPort"), // only for port mode
|
||||
destinationPort: integer("destinationPort"), // only for port mode
|
||||
@@ -257,7 +270,11 @@ export const siteResources = sqliteTable("siteResources", {
|
||||
udpPortRangeString: text("udpPortRangeString").notNull().default("*"),
|
||||
disableIcmp: integer("disableIcmp", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false)
|
||||
.default(false),
|
||||
authDaemonPort: integer("authDaemonPort").default(22123),
|
||||
authDaemonMode: text("authDaemonMode")
|
||||
.$type<"site" | "remote">()
|
||||
.default("site")
|
||||
});
|
||||
|
||||
export const clientSiteResources = sqliteTable("clientSiteResources", {
|
||||
@@ -310,10 +327,14 @@ export const users = sqliteTable("user", {
|
||||
dateCreated: text("dateCreated").notNull(),
|
||||
termsAcceptedTimestamp: text("termsAcceptedTimestamp"),
|
||||
termsVersion: text("termsVersion"),
|
||||
marketingEmailConsent: integer("marketingEmailConsent", {
|
||||
mode: "boolean"
|
||||
}).default(false),
|
||||
serverAdmin: integer("serverAdmin", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
lastPasswordChange: integer("lastPasswordChange")
|
||||
lastPasswordChange: integer("lastPasswordChange"),
|
||||
locale: text("locale")
|
||||
});
|
||||
|
||||
export const securityKeys = sqliteTable("webauthnCredentials", {
|
||||
@@ -402,6 +423,9 @@ export const clientSitesAssociationsCache = sqliteTable(
|
||||
isRelayed: integer("isRelayed", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
isJitMode: integer("isJitMode", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
endpoint: text("endpoint"),
|
||||
publicKey: text("publicKey") // this will act as the session's public key for hole punching so we can track when it changes
|
||||
}
|
||||
@@ -631,13 +655,11 @@ export const userOrgs = sqliteTable("userOrgs", {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId),
|
||||
isOwner: integer("isOwner", { mode: "boolean" }).notNull().default(false),
|
||||
autoProvisioned: integer("autoProvisioned", {
|
||||
mode: "boolean"
|
||||
}).default(false)
|
||||
}).default(false),
|
||||
pamUsername: text("pamUsername") // cleaned username for ssh and such
|
||||
});
|
||||
|
||||
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
||||
@@ -678,9 +700,31 @@ export const roles = sqliteTable("roles", {
|
||||
description: text("description"),
|
||||
requireDeviceApproval: integer("requireDeviceApproval", {
|
||||
mode: "boolean"
|
||||
}).default(false)
|
||||
}).default(false),
|
||||
sshSudoMode: text("sshSudoMode").default("none"), // "none" | "full" | "commands"
|
||||
sshSudoCommands: text("sshSudoCommands").default("[]"),
|
||||
sshCreateHomeDir: integer("sshCreateHomeDir", { mode: "boolean" }).default(
|
||||
true
|
||||
),
|
||||
sshUnixGroups: text("sshUnixGroups").default("[]")
|
||||
});
|
||||
|
||||
export const userOrgRoles = sqliteTable(
|
||||
"userOrgRoles",
|
||||
{
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
orgId: text("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||
},
|
||||
(t) => [unique().on(t.userId, t.orgId, t.roleId)]
|
||||
);
|
||||
|
||||
export const roleActions = sqliteTable("roleActions", {
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
@@ -766,12 +810,22 @@ export const userInvites = sqliteTable("userInvites", {
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
email: text("email").notNull(),
|
||||
expiresAt: integer("expiresAt").notNull(),
|
||||
tokenHash: text("token").notNull(),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||
tokenHash: text("token").notNull()
|
||||
});
|
||||
|
||||
export const userInviteRoles = sqliteTable(
|
||||
"userInviteRoles",
|
||||
{
|
||||
inviteId: text("inviteId")
|
||||
.notNull()
|
||||
.references(() => userInvites.inviteId, { onDelete: "cascade" }),
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.inviteId, t.roleId] })]
|
||||
);
|
||||
|
||||
export const resourcePincode = sqliteTable("resourcePincode", {
|
||||
pincodeId: integer("pincodeId").primaryKey({
|
||||
autoIncrement: true
|
||||
@@ -1079,6 +1133,16 @@ export const deviceWebAuthCodes = sqliteTable("deviceWebAuthCodes", {
|
||||
})
|
||||
});
|
||||
|
||||
export const roundTripMessageTracker = sqliteTable("roundTripMessageTracker", {
|
||||
messageId: integer("messageId").primaryKey({ autoIncrement: true }),
|
||||
wsClientId: text("clientId"),
|
||||
messageType: text("messageType"),
|
||||
sentAt: integer("sentAt").notNull(),
|
||||
receivedAt: integer("receivedAt"),
|
||||
error: text("error"),
|
||||
complete: integer("complete", { mode: "boolean" }).notNull().default(false)
|
||||
});
|
||||
|
||||
export type Org = InferSelectModel<typeof orgs>;
|
||||
export type User = InferSelectModel<typeof users>;
|
||||
export type Site = InferSelectModel<typeof sites>;
|
||||
@@ -1104,7 +1168,9 @@ export type UserSite = InferSelectModel<typeof userSites>;
|
||||
export type RoleResource = InferSelectModel<typeof roleResources>;
|
||||
export type UserResource = InferSelectModel<typeof userResources>;
|
||||
export type UserInvite = InferSelectModel<typeof userInvites>;
|
||||
export type UserInviteRole = InferSelectModel<typeof userInviteRoles>;
|
||||
export type UserOrg = InferSelectModel<typeof userOrgs>;
|
||||
export type UserOrgRole = InferSelectModel<typeof userOrgRoles>;
|
||||
export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
||||
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||
@@ -1140,3 +1206,6 @@ export type SecurityKey = InferSelectModel<typeof securityKeys>;
|
||||
export type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;
|
||||
export type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
|
||||
export type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
|
||||
export type RoundTripMessageTracker = InferSelectModel<
|
||||
typeof roundTripMessageTracker
|
||||
>;
|
||||
|
||||
118
server/emails/templates/EnterpriseEditionKeyGenerated.tsx
Normal file
118
server/emails/templates/EnterpriseEditionKeyGenerated.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React from "react";
|
||||
import { Body, Head, Html, Preview, Tailwind } from "@react-email/components";
|
||||
import { themeColors } from "./lib/theme";
|
||||
import {
|
||||
EmailContainer,
|
||||
EmailFooter,
|
||||
EmailGreeting,
|
||||
EmailHeading,
|
||||
EmailInfoSection,
|
||||
EmailLetterHead,
|
||||
EmailSection,
|
||||
EmailSignature,
|
||||
EmailText
|
||||
} from "./components/Email";
|
||||
import CopyCodeBox from "./components/CopyCodeBox";
|
||||
import ButtonLink from "./components/ButtonLink";
|
||||
|
||||
type EnterpriseEditionKeyGeneratedProps = {
|
||||
keyValue: string;
|
||||
personalUseOnly: boolean;
|
||||
users: number;
|
||||
sites: number;
|
||||
modifySubscriptionLink?: string;
|
||||
};
|
||||
|
||||
export const EnterpriseEditionKeyGenerated = ({
|
||||
keyValue,
|
||||
personalUseOnly,
|
||||
users,
|
||||
sites,
|
||||
modifySubscriptionLink
|
||||
}: EnterpriseEditionKeyGeneratedProps) => {
|
||||
const previewText = personalUseOnly
|
||||
? "Your Enterprise Edition key for personal use is ready"
|
||||
: "Thank you for your purchase — your Enterprise Edition key is ready";
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind config={themeColors}>
|
||||
<Body className="font-sans bg-gray-50">
|
||||
<EmailContainer>
|
||||
<EmailLetterHead />
|
||||
|
||||
<EmailGreeting>Hi there,</EmailGreeting>
|
||||
|
||||
{personalUseOnly ? (
|
||||
<EmailText>
|
||||
Your Enterprise Edition license key has been
|
||||
generated. Qualifying users can use the
|
||||
Enterprise Edition for free for{" "}
|
||||
<strong>personal use only</strong>.
|
||||
</EmailText>
|
||||
) : (
|
||||
<>
|
||||
<EmailText>
|
||||
Thank you for your purchase. Your Enterprise
|
||||
Edition license key is ready. Below are the
|
||||
terms of your license.
|
||||
</EmailText>
|
||||
<EmailInfoSection
|
||||
title="License details"
|
||||
items={[
|
||||
{
|
||||
label: "Licensed users",
|
||||
value: users
|
||||
},
|
||||
{
|
||||
label: "Licensed sites",
|
||||
value: sites
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{modifySubscriptionLink && (
|
||||
<EmailSection>
|
||||
<ButtonLink
|
||||
href={modifySubscriptionLink}
|
||||
>
|
||||
Modify subscription
|
||||
</ButtonLink>
|
||||
</EmailSection>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<EmailSection>
|
||||
<EmailText>Your license key:</EmailText>
|
||||
<CopyCodeBox
|
||||
text={keyValue}
|
||||
hint="Copy this key and use it when activating Enterprise Edition on your Pangolin host."
|
||||
/>
|
||||
</EmailSection>
|
||||
|
||||
<EmailText>
|
||||
If you need to purchase additional license keys or
|
||||
modify your existing license, please reach out to
|
||||
our support team at{" "}
|
||||
<a
|
||||
href="mailto:support@pangolin.net"
|
||||
className="text-primary font-medium"
|
||||
>
|
||||
support@pangolin.net
|
||||
</a>
|
||||
.
|
||||
</EmailText>
|
||||
|
||||
<EmailFooter>
|
||||
<EmailSignature />
|
||||
</EmailFooter>
|
||||
</EmailContainer>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnterpriseEditionKeyGenerated;
|
||||
@@ -1,6 +1,14 @@
|
||||
import React from "react";
|
||||
|
||||
export default function CopyCodeBox({ text }: { text: string }) {
|
||||
const DEFAULT_HINT = "Copy and paste this code when prompted";
|
||||
|
||||
export default function CopyCodeBox({
|
||||
text,
|
||||
hint
|
||||
}: {
|
||||
text: string;
|
||||
hint?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="inline-block">
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg px-6 py-4 mx-auto">
|
||||
@@ -8,9 +16,7 @@ export default function CopyCodeBox({ text }: { text: string }) {
|
||||
{text}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
Copy and paste this code when prompted
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-2">{hint ?? DEFAULT_HINT}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ declare global {
|
||||
session: Session;
|
||||
userOrg?: UserOrg;
|
||||
apiKeyOrg?: ApiKeyOrg;
|
||||
userOrgRoleId?: number;
|
||||
userOrgRoleIds?: number[];
|
||||
userOrgId?: string;
|
||||
userOrgIds?: string[];
|
||||
remoteExitNode?: RemoteExitNode;
|
||||
|
||||
@@ -17,6 +17,7 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
import { APP_PATH } from "./lib/consts";
|
||||
import yaml from "js-yaml";
|
||||
import { z } from "zod";
|
||||
|
||||
const dev = process.env.ENVIRONMENT !== "prod";
|
||||
const externalPort = config.getRawConfig().server.integration_port;
|
||||
@@ -38,12 +39,24 @@ export function createIntegrationApiServer() {
|
||||
apiServer.use(cookieParser());
|
||||
apiServer.use(express.json());
|
||||
|
||||
const openApiDocumentation = getOpenApiDocumentation();
|
||||
|
||||
apiServer.use(
|
||||
"/v1/docs",
|
||||
swaggerUi.serve,
|
||||
swaggerUi.setup(getOpenApiDocumentation())
|
||||
swaggerUi.setup(openApiDocumentation)
|
||||
);
|
||||
|
||||
// Unauthenticated OpenAPI spec endpoints
|
||||
apiServer.get("/v1/openapi.json", (_req, res) => {
|
||||
res.json(openApiDocumentation);
|
||||
});
|
||||
|
||||
apiServer.get("/v1/openapi.yaml", (_req, res) => {
|
||||
const yamlOutput = yaml.dump(openApiDocumentation);
|
||||
res.type("application/yaml").send(yamlOutput);
|
||||
});
|
||||
|
||||
// API routes
|
||||
const prefix = `/v1`;
|
||||
apiServer.use(logIncomingMiddleware);
|
||||
@@ -75,16 +88,6 @@ function getOpenApiDocumentation() {
|
||||
}
|
||||
);
|
||||
|
||||
for (const def of registry.definitions) {
|
||||
if (def.type === "route") {
|
||||
def.route.security = [
|
||||
{
|
||||
[bearerAuth.name]: []
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
registry.registerPath({
|
||||
method: "get",
|
||||
path: "/",
|
||||
@@ -94,6 +97,74 @@ function getOpenApiDocumentation() {
|
||||
responses: {}
|
||||
});
|
||||
|
||||
registry.registerPath({
|
||||
method: "get",
|
||||
path: "/openapi.json",
|
||||
description: "Get OpenAPI specification as JSON",
|
||||
tags: [],
|
||||
request: {},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OpenAPI specification as JSON",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registry.registerPath({
|
||||
method: "get",
|
||||
path: "/openapi.yaml",
|
||||
description: "Get OpenAPI specification as YAML",
|
||||
tags: [],
|
||||
request: {},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OpenAPI specification as YAML",
|
||||
content: {
|
||||
"application/yaml": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const def of registry.definitions) {
|
||||
if (def.type === "route") {
|
||||
def.route.security = [
|
||||
{
|
||||
[bearerAuth.name]: []
|
||||
}
|
||||
];
|
||||
|
||||
// Ensure every route has a generic JSON response schema so Swagger UI can render responses
|
||||
const existingResponses = def.route.responses;
|
||||
const hasExistingResponses =
|
||||
existingResponses && Object.keys(existingResponses).length > 0;
|
||||
|
||||
if (!hasExistingResponses) {
|
||||
def.route.responses = {
|
||||
"*": {
|
||||
description: "",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const generator = new OpenApiGeneratorV3(registry.definitions);
|
||||
|
||||
const generated = generator.generateDocument({
|
||||
@@ -105,11 +176,13 @@ function getOpenApiDocumentation() {
|
||||
servers: [{ url: "/v1" }]
|
||||
});
|
||||
|
||||
// convert to yaml and save to file
|
||||
const outputPath = path.join(APP_PATH, "openapi.yaml");
|
||||
const yamlOutput = yaml.dump(generated);
|
||||
fs.writeFileSync(outputPath, yamlOutput, "utf8");
|
||||
logger.info(`OpenAPI documentation saved to ${outputPath}`);
|
||||
if (!process.env.DISABLE_GEN_OPENAPI) {
|
||||
// convert to yaml and save to file
|
||||
const outputPath = path.join(APP_PATH, "openapi.yaml");
|
||||
const yamlOutput = yaml.dump(generated);
|
||||
fs.writeFileSync(outputPath, yamlOutput, "utf8");
|
||||
logger.info(`OpenAPI documentation saved to ${outputPath}`);
|
||||
}
|
||||
|
||||
return generated;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ const internalPort = config.getRawConfig().server.internal_port;
|
||||
export function createInternalServer() {
|
||||
const internalServer = express();
|
||||
|
||||
const trustProxy = config.getRawConfig().server.trust_proxy;
|
||||
if (trustProxy) {
|
||||
internalServer.set("trust proxy", trustProxy);
|
||||
}
|
||||
|
||||
internalServer.use(helmet());
|
||||
internalServer.use(cors());
|
||||
internalServer.use(stripDuplicateSesions);
|
||||
|
||||
@@ -1,30 +1,44 @@
|
||||
import Stripe from "stripe";
|
||||
|
||||
export enum FeatureId {
|
||||
SITE_UPTIME = "siteUptime",
|
||||
USERS = "users",
|
||||
SITES = "sites",
|
||||
EGRESS_DATA_MB = "egressDataMb",
|
||||
DOMAINS = "domains",
|
||||
REMOTE_EXIT_NODES = "remoteExitNodes"
|
||||
REMOTE_EXIT_NODES = "remoteExitNodes",
|
||||
ORGINIZATIONS = "organizations",
|
||||
TIER1 = "tier1"
|
||||
}
|
||||
|
||||
export const FeatureMeterIds: Record<FeatureId, string> = {
|
||||
[FeatureId.SITE_UPTIME]: "mtr_61Srrej5wUJuiTWgo41D3Ee2Ir7WmDLU",
|
||||
[FeatureId.USERS]: "mtr_61SrreISyIWpwUNGR41D3Ee2Ir7WmQro",
|
||||
[FeatureId.EGRESS_DATA_MB]: "mtr_61Srreh9eWrExDSCe41D3Ee2Ir7Wm5YW",
|
||||
[FeatureId.DOMAINS]: "mtr_61Ss9nIKDNMw0LDRU41D3Ee2Ir7WmRPU",
|
||||
[FeatureId.REMOTE_EXIT_NODES]: "mtr_61T86UXnfxTVXy9sD41D3Ee2Ir7WmFTE"
|
||||
export async function getFeatureDisplayName(featureId: FeatureId): Promise<string> {
|
||||
switch (featureId) {
|
||||
case FeatureId.USERS:
|
||||
return "Users";
|
||||
case FeatureId.SITES:
|
||||
return "Sites";
|
||||
case FeatureId.EGRESS_DATA_MB:
|
||||
return "Egress Data (MB)";
|
||||
case FeatureId.DOMAINS:
|
||||
return "Domains";
|
||||
case FeatureId.REMOTE_EXIT_NODES:
|
||||
return "Remote Exit Nodes";
|
||||
case FeatureId.ORGINIZATIONS:
|
||||
return "Organizations";
|
||||
case FeatureId.TIER1:
|
||||
return "Home Lab";
|
||||
default:
|
||||
return featureId;
|
||||
}
|
||||
}
|
||||
|
||||
// this is from the old system
|
||||
export const FeatureMeterIds: Partial<Record<FeatureId, string>> = { // right now we are not charging for any data
|
||||
// [FeatureId.EGRESS_DATA_MB]: "mtr_61Srreh9eWrExDSCe41D3Ee2Ir7Wm5YW"
|
||||
};
|
||||
|
||||
export const FeatureMeterIdsSandbox: Record<FeatureId, string> = {
|
||||
[FeatureId.SITE_UPTIME]: "mtr_test_61Snh3cees4w60gv841DCpkOb237BDEu",
|
||||
[FeatureId.USERS]: "mtr_test_61Sn5fLtq1gSfRkyA41DCpkOb237B6au",
|
||||
[FeatureId.EGRESS_DATA_MB]: "mtr_test_61Snh2a2m6qome5Kv41DCpkOb237B3dQ",
|
||||
[FeatureId.DOMAINS]: "mtr_test_61SsA8qrdAlgPpFRQ41DCpkOb237BGts",
|
||||
[FeatureId.REMOTE_EXIT_NODES]: "mtr_test_61T86Vqmwa3D9ra3341DCpkOb237B94K"
|
||||
export const FeatureMeterIdsSandbox: Partial<Record<FeatureId, string>> = {
|
||||
// [FeatureId.EGRESS_DATA_MB]: "mtr_test_61Snh2a2m6qome5Kv41DCpkOb237B3dQ"
|
||||
};
|
||||
|
||||
export function getFeatureMeterId(featureId: FeatureId): string {
|
||||
export function getFeatureMeterId(featureId: FeatureId): string | undefined {
|
||||
if (
|
||||
process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true"
|
||||
@@ -43,45 +57,81 @@ export function getFeatureIdByMetricId(
|
||||
)?.[0];
|
||||
}
|
||||
|
||||
export type FeaturePriceSet = {
|
||||
[key in Exclude<FeatureId, FeatureId.DOMAINS>]: string;
|
||||
} & {
|
||||
[FeatureId.DOMAINS]?: string; // Optional since domains are not billed
|
||||
export type FeaturePriceSet = Partial<Record<FeatureId, string>>;
|
||||
|
||||
export const tier1FeaturePriceSet: FeaturePriceSet = {
|
||||
[FeatureId.TIER1]: "price_1SzVE3D3Ee2Ir7Wm6wT5Dl3G"
|
||||
};
|
||||
|
||||
export const standardFeaturePriceSet: FeaturePriceSet = {
|
||||
// Free tier matches the freeLimitSet
|
||||
[FeatureId.SITE_UPTIME]: "price_1RrQc4D3Ee2Ir7WmaJGZ3MtF",
|
||||
[FeatureId.USERS]: "price_1RrQeJD3Ee2Ir7WmgveP3xea",
|
||||
[FeatureId.EGRESS_DATA_MB]: "price_1RrQXFD3Ee2Ir7WmvGDlgxQk",
|
||||
// [FeatureId.DOMAINS]: "price_1Rz3tMD3Ee2Ir7Wm5qLeASzC",
|
||||
[FeatureId.REMOTE_EXIT_NODES]: "price_1S46weD3Ee2Ir7Wm94KEHI4h"
|
||||
export const tier1FeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
[FeatureId.TIER1]: "price_1SxgpPDCpkOb237Bfo4rIsoT"
|
||||
};
|
||||
|
||||
export const standardFeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
// Free tier matches the freeLimitSet
|
||||
[FeatureId.SITE_UPTIME]: "price_1RefFBDCpkOb237BPrKZ8IEU",
|
||||
[FeatureId.USERS]: "price_1ReNa4DCpkOb237Bc67G5muF",
|
||||
[FeatureId.EGRESS_DATA_MB]: "price_1Rfp9LDCpkOb237BwuN5Oiu0",
|
||||
// [FeatureId.DOMAINS]: "price_1Ryi88DCpkOb237B2D6DM80b",
|
||||
[FeatureId.REMOTE_EXIT_NODES]: "price_1RyiZvDCpkOb237BXpmoIYJL"
|
||||
};
|
||||
|
||||
export function getStandardFeaturePriceSet(): FeaturePriceSet {
|
||||
export function getTier1FeaturePriceSet(): FeaturePriceSet {
|
||||
if (
|
||||
process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true"
|
||||
) {
|
||||
return standardFeaturePriceSet;
|
||||
return tier1FeaturePriceSet;
|
||||
} else {
|
||||
return standardFeaturePriceSetSandbox;
|
||||
return tier1FeaturePriceSetSandbox;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLineItems(
|
||||
featurePriceSet: FeaturePriceSet
|
||||
): Stripe.Checkout.SessionCreateParams.LineItem[] {
|
||||
return Object.entries(featurePriceSet).map(([featureId, priceId]) => ({
|
||||
price: priceId
|
||||
}));
|
||||
export const tier2FeaturePriceSet: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SzVCcD3Ee2Ir7Wmn6U3KvPN"
|
||||
};
|
||||
|
||||
export const tier2FeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SxaEHDCpkOb237BD9lBkPiR"
|
||||
};
|
||||
|
||||
export function getTier2FeaturePriceSet(): FeaturePriceSet {
|
||||
if (
|
||||
process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true"
|
||||
) {
|
||||
return tier2FeaturePriceSet;
|
||||
} else {
|
||||
return tier2FeaturePriceSetSandbox;
|
||||
}
|
||||
}
|
||||
|
||||
export const tier3FeaturePriceSet: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SzVDKD3Ee2Ir7WmPtOKNusv"
|
||||
};
|
||||
|
||||
export const tier3FeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SxaEODCpkOb237BiXdCBSfs"
|
||||
};
|
||||
|
||||
export function getTier3FeaturePriceSet(): FeaturePriceSet {
|
||||
if (
|
||||
process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true"
|
||||
) {
|
||||
return tier3FeaturePriceSet;
|
||||
} else {
|
||||
return tier3FeaturePriceSetSandbox;
|
||||
}
|
||||
}
|
||||
|
||||
export function getFeatureIdByPriceId(priceId: string): FeatureId | undefined {
|
||||
// Check all feature price sets
|
||||
const allPriceSets = [
|
||||
getTier1FeaturePriceSet(),
|
||||
getTier2FeaturePriceSet(),
|
||||
getTier3FeaturePriceSet()
|
||||
];
|
||||
|
||||
for (const priceSet of allPriceSets) {
|
||||
const entry = (Object.entries(priceSet) as [FeatureId, string][]).find(
|
||||
([_, price]) => price === priceId
|
||||
);
|
||||
if (entry) {
|
||||
return entry[0];
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
25
server/lib/billing/getLineItems.ts
Normal file
25
server/lib/billing/getLineItems.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import Stripe from "stripe";
|
||||
import { FeatureId, FeaturePriceSet } from "./features";
|
||||
import { usageService } from "./usageService";
|
||||
|
||||
export async function getLineItems(
|
||||
featurePriceSet: FeaturePriceSet,
|
||||
orgId: string,
|
||||
): Promise<Stripe.Checkout.SessionCreateParams.LineItem[]> {
|
||||
const users = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
|
||||
return Object.entries(featurePriceSet).map(([featureId, priceId]) => {
|
||||
let quantity: number | undefined;
|
||||
|
||||
if (featureId === FeatureId.USERS) {
|
||||
quantity = users?.instantaneousValue || 1;
|
||||
} else if (featureId === FeatureId.TIER1) {
|
||||
quantity = 1;
|
||||
}
|
||||
|
||||
return {
|
||||
price: priceId,
|
||||
quantity: quantity
|
||||
};
|
||||
});
|
||||
}
|
||||
37
server/lib/billing/licenses.ts
Normal file
37
server/lib/billing/licenses.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export enum LicenseId {
|
||||
SMALL_LICENSE = "small_license",
|
||||
BIG_LICENSE = "big_license"
|
||||
}
|
||||
|
||||
export type LicensePriceSet = {
|
||||
[key in LicenseId]: string;
|
||||
};
|
||||
|
||||
export const licensePriceSet: LicensePriceSet = {
|
||||
// Free license matches the freeLimitSet
|
||||
[LicenseId.SMALL_LICENSE]: "price_1SxKHiD3Ee2Ir7WmvtEh17A8",
|
||||
[LicenseId.BIG_LICENSE]: "price_1SxKHiD3Ee2Ir7WmMUiP0H6Y"
|
||||
};
|
||||
|
||||
export const licensePriceSetSandbox: LicensePriceSet = {
|
||||
// Free license matches the freeLimitSet
|
||||
// when matching license the keys closer to 0 index are matched first so list the licenses in descending order of value
|
||||
[LicenseId.SMALL_LICENSE]: "price_1SxDwuDCpkOb237Bz0yTiOgN",
|
||||
[LicenseId.BIG_LICENSE]: "price_1SxDy0DCpkOb237BWJxrxYkl"
|
||||
};
|
||||
|
||||
export function getLicensePriceSet(
|
||||
environment?: string,
|
||||
sandbox_mode?: boolean
|
||||
): LicensePriceSet {
|
||||
if (
|
||||
(process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true") ||
|
||||
(environment === "prod" && sandbox_mode !== true)
|
||||
) {
|
||||
// THIS GETS LOADED CLIENT SIDE AND SERVER SIDE
|
||||
return licensePriceSet;
|
||||
} else {
|
||||
return licensePriceSetSandbox;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,70 @@
|
||||
import { FeatureId } from "./features";
|
||||
|
||||
export type LimitSet = {
|
||||
export type LimitSet = Partial<{
|
||||
[key in FeatureId]: {
|
||||
value: number | null; // null indicates no limit
|
||||
description?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const sandboxLimitSet: LimitSet = {
|
||||
[FeatureId.SITE_UPTIME]: { value: 2880, description: "Sandbox limit" }, // 1 site up for 2 days
|
||||
[FeatureId.USERS]: { value: 1, description: "Sandbox limit" },
|
||||
[FeatureId.EGRESS_DATA_MB]: { value: 1000, description: "Sandbox limit" }, // 1 GB
|
||||
[FeatureId.DOMAINS]: { value: 0, description: "Sandbox limit" },
|
||||
[FeatureId.REMOTE_EXIT_NODES]: { value: 0, description: "Sandbox limit" }
|
||||
};
|
||||
}>;
|
||||
|
||||
export const freeLimitSet: LimitSet = {
|
||||
[FeatureId.SITE_UPTIME]: { value: 46080, description: "Free tier limit" }, // 1 site up for 32 days
|
||||
[FeatureId.USERS]: { value: 3, description: "Free tier limit" },
|
||||
[FeatureId.EGRESS_DATA_MB]: {
|
||||
value: 25000,
|
||||
description: "Free tier limit"
|
||||
}, // 25 GB
|
||||
[FeatureId.DOMAINS]: { value: 3, description: "Free tier limit" },
|
||||
[FeatureId.REMOTE_EXIT_NODES]: { value: 1, description: "Free tier limit" }
|
||||
[FeatureId.SITES]: { value: 5, description: "Basic limit" },
|
||||
[FeatureId.USERS]: { value: 5, description: "Basic limit" },
|
||||
[FeatureId.DOMAINS]: { value: 5, description: "Basic limit" },
|
||||
[FeatureId.REMOTE_EXIT_NODES]: { value: 1, description: "Basic limit" },
|
||||
[FeatureId.ORGINIZATIONS]: { value: 1, description: "Basic limit" },
|
||||
};
|
||||
|
||||
export const subscribedLimitSet: LimitSet = {
|
||||
[FeatureId.SITE_UPTIME]: {
|
||||
value: 2232000,
|
||||
description: "Contact us to increase soft limit."
|
||||
}, // 50 sites up for 31 days
|
||||
export const tier1LimitSet: LimitSet = {
|
||||
[FeatureId.USERS]: { value: 7, description: "Home limit" },
|
||||
[FeatureId.SITES]: { value: 10, description: "Home limit" },
|
||||
[FeatureId.DOMAINS]: { value: 10, description: "Home limit" },
|
||||
[FeatureId.REMOTE_EXIT_NODES]: { value: 1, description: "Home limit" },
|
||||
[FeatureId.ORGINIZATIONS]: { value: 1, description: "Home limit" },
|
||||
};
|
||||
|
||||
export const tier2LimitSet: LimitSet = {
|
||||
[FeatureId.USERS]: {
|
||||
value: 150,
|
||||
description: "Contact us to increase soft limit."
|
||||
value: 100,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.SITES]: {
|
||||
value: 50,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.EGRESS_DATA_MB]: {
|
||||
value: 12000000,
|
||||
description: "Contact us to increase soft limit."
|
||||
}, // 12000 GB
|
||||
[FeatureId.DOMAINS]: {
|
||||
value: 25,
|
||||
description: "Contact us to increase soft limit."
|
||||
value: 50,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.REMOTE_EXIT_NODES]: {
|
||||
value: 5,
|
||||
description: "Contact us to increase soft limit."
|
||||
value: 3,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.ORGINIZATIONS]: {
|
||||
value: 1,
|
||||
description: "Team limit"
|
||||
}
|
||||
};
|
||||
|
||||
export const tier3LimitSet: LimitSet = {
|
||||
[FeatureId.USERS]: {
|
||||
value: 500,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.SITES]: {
|
||||
value: 250,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.DOMAINS]: {
|
||||
value: 100,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.REMOTE_EXIT_NODES]: {
|
||||
value: 20,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.ORGINIZATIONS]: {
|
||||
value: 5,
|
||||
description: "Business limit"
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import { db, limits } from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { LimitSet } from "./limitSet";
|
||||
import { FeatureId } from "./features";
|
||||
import logger from "@server/logger";
|
||||
|
||||
class LimitService {
|
||||
async applyLimitSetToOrg(orgId: string, limitSet: LimitSet): Promise<void> {
|
||||
@@ -13,6 +14,21 @@ class LimitService {
|
||||
for (const [featureId, entry] of limitEntries) {
|
||||
const limitId = `${orgId}-${featureId}`;
|
||||
const { value, description } = entry;
|
||||
// get the limit first
|
||||
const [limit] = await trx
|
||||
.select()
|
||||
.from(limits)
|
||||
.where(eq(limits.limitId, limitId))
|
||||
.limit(1);
|
||||
|
||||
// check if its overriden
|
||||
if (limit && limit.override) {
|
||||
logger.debug(
|
||||
`Skipping limit ${limitId} for org ${orgId} since it is overridden...`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
await trx
|
||||
.insert(limits)
|
||||
.values({ limitId, orgId, featureId, value, description });
|
||||
|
||||
60
server/lib/billing/tierMatrix.ts
Normal file
60
server/lib/billing/tierMatrix.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Tier } from "@server/types/Tiers";
|
||||
|
||||
export enum TierFeature {
|
||||
OrgOidc = "orgOidc",
|
||||
LoginPageDomain = "loginPageDomain", // handle downgrade by removing custom domain
|
||||
DeviceApprovals = "deviceApprovals", // handle downgrade by disabling device approvals
|
||||
LoginPageBranding = "loginPageBranding", // handle downgrade by setting to default branding
|
||||
LogExport = "logExport",
|
||||
AccessLogs = "accessLogs", // set the retention period to none on downgrade
|
||||
ActionLogs = "actionLogs", // set the retention period to none on downgrade
|
||||
ConnectionLogs = "connectionLogs",
|
||||
RotateCredentials = "rotateCredentials",
|
||||
MaintencePage = "maintencePage", // handle downgrade
|
||||
DevicePosture = "devicePosture",
|
||||
TwoFactorEnforcement = "twoFactorEnforcement", // handle downgrade by setting to optional
|
||||
SessionDurationPolicies = "sessionDurationPolicies", // handle downgrade by setting to default duration
|
||||
PasswordExpirationPolicies = "passwordExpirationPolicies", // handle downgrade by setting to default duration
|
||||
AutoProvisioning = "autoProvisioning", // handle downgrade by disabling auto provisioning
|
||||
SshPam = "sshPam",
|
||||
FullRbac = "fullRbac",
|
||||
SiteProvisioningKeys = "siteProvisioningKeys", // handle downgrade by revoking keys if needed
|
||||
SIEM = "siem" // handle downgrade by disabling SIEM integrations
|
||||
}
|
||||
|
||||
export const tierMatrix: Record<TierFeature, Tier[]> = {
|
||||
[TierFeature.OrgOidc]: ["tier1", "tier2", "tier3", "enterprise"],
|
||||
[TierFeature.LoginPageDomain]: ["tier1", "tier2", "tier3", "enterprise"],
|
||||
[TierFeature.DeviceApprovals]: ["tier1", "tier3", "enterprise"],
|
||||
[TierFeature.LoginPageBranding]: ["tier1", "tier3", "enterprise"],
|
||||
[TierFeature.LogExport]: ["tier3", "enterprise"],
|
||||
[TierFeature.AccessLogs]: ["tier2", "tier3", "enterprise"],
|
||||
[TierFeature.ActionLogs]: ["tier2", "tier3", "enterprise"],
|
||||
[TierFeature.ConnectionLogs]: ["tier2", "tier3", "enterprise"],
|
||||
[TierFeature.RotateCredentials]: ["tier1", "tier2", "tier3", "enterprise"],
|
||||
[TierFeature.MaintencePage]: ["tier1", "tier2", "tier3", "enterprise"],
|
||||
[TierFeature.DevicePosture]: ["tier2", "tier3", "enterprise"],
|
||||
[TierFeature.TwoFactorEnforcement]: [
|
||||
"tier1",
|
||||
"tier2",
|
||||
"tier3",
|
||||
"enterprise"
|
||||
],
|
||||
[TierFeature.SessionDurationPolicies]: [
|
||||
"tier1",
|
||||
"tier2",
|
||||
"tier3",
|
||||
"enterprise"
|
||||
],
|
||||
[TierFeature.PasswordExpirationPolicies]: [
|
||||
"tier1",
|
||||
"tier2",
|
||||
"tier3",
|
||||
"enterprise"
|
||||
],
|
||||
[TierFeature.AutoProvisioning]: ["tier1", "tier3", "enterprise"],
|
||||
[TierFeature.SshPam]: ["tier1", "tier3", "enterprise"],
|
||||
[TierFeature.FullRbac]: ["tier1", "tier2", "tier3", "enterprise"],
|
||||
[TierFeature.SiteProvisioningKeys]: ["tier3", "enterprise"],
|
||||
[TierFeature.SIEM]: ["enterprise"]
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
export enum TierId {
|
||||
STANDARD = "standard"
|
||||
}
|
||||
|
||||
export type TierPriceSet = {
|
||||
[key in TierId]: string;
|
||||
};
|
||||
|
||||
export const tierPriceSet: TierPriceSet = {
|
||||
// Free tier matches the freeLimitSet
|
||||
[TierId.STANDARD]: "price_1RrQ9cD3Ee2Ir7Wmqdy3KBa0"
|
||||
};
|
||||
|
||||
export const tierPriceSetSandbox: TierPriceSet = {
|
||||
// Free tier matches the freeLimitSet
|
||||
// when matching tier the keys closer to 0 index are matched first so list the tiers in descending order of value
|
||||
[TierId.STANDARD]: "price_1RrAYJDCpkOb237By2s1P32m"
|
||||
};
|
||||
|
||||
export function getTierPriceSet(
|
||||
environment?: string,
|
||||
sandbox_mode?: boolean
|
||||
): TierPriceSet {
|
||||
if (
|
||||
(process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true") ||
|
||||
(environment === "prod" && sandbox_mode !== true)
|
||||
) {
|
||||
// THIS GETS LOADED CLIENT SIDE AND SERVER SIDE
|
||||
return tierPriceSet;
|
||||
} else {
|
||||
return tierPriceSetSandbox;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +1,32 @@
|
||||
import { eq, sql, and } from "drizzle-orm";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { PutObjectCommand } from "@aws-sdk/client-s3";
|
||||
import * as fs from "fs/promises";
|
||||
import * as path from "path";
|
||||
import {
|
||||
db,
|
||||
usage,
|
||||
customers,
|
||||
sites,
|
||||
newts,
|
||||
limits,
|
||||
Usage,
|
||||
Limit,
|
||||
Transaction
|
||||
Transaction,
|
||||
orgs
|
||||
} from "@server/db";
|
||||
import { FeatureId, getFeatureMeterId } from "./features";
|
||||
import logger from "@server/logger";
|
||||
import { sendToClient } from "#dynamic/routers/ws";
|
||||
import { build } from "@server/build";
|
||||
import { s3Client } from "@server/lib/s3";
|
||||
import cache from "@server/lib/cache";
|
||||
|
||||
interface StripeEvent {
|
||||
identifier?: string;
|
||||
timestamp: number;
|
||||
event_name: string;
|
||||
payload: {
|
||||
value: number;
|
||||
stripe_customer_id: string;
|
||||
};
|
||||
}
|
||||
import cache from "#dynamic/lib/cache";
|
||||
|
||||
export function noop() {
|
||||
if (
|
||||
build !== "saas" ||
|
||||
!process.env.S3_BUCKET ||
|
||||
!process.env.LOCAL_FILE_PATH
|
||||
) {
|
||||
if (build !== "saas") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export class UsageService {
|
||||
private bucketName: string | undefined;
|
||||
private currentEventFile: string | null = null;
|
||||
private currentFileStartTime: number = 0;
|
||||
private eventsDir: string | undefined;
|
||||
private uploadingFiles: Set<string> = new Set();
|
||||
|
||||
constructor() {
|
||||
if (noop()) {
|
||||
return;
|
||||
}
|
||||
// this.bucketName = privateConfig.getRawPrivateConfig().stripe?.s3Bucket;
|
||||
// this.eventsDir = privateConfig.getRawPrivateConfig().stripe?.localFilePath;
|
||||
this.bucketName = process.env.S3_BUCKET || undefined;
|
||||
this.eventsDir = process.env.LOCAL_FILE_PATH || undefined;
|
||||
|
||||
// Ensure events directory exists
|
||||
this.initializeEventsDirectory().then(() => {
|
||||
this.uploadPendingEventFilesOnStartup();
|
||||
});
|
||||
|
||||
// Periodically check for old event files to upload
|
||||
setInterval(() => {
|
||||
this.uploadOldEventFiles().catch((err) => {
|
||||
logger.error("Error in periodic event file upload:", err);
|
||||
});
|
||||
}, 30000); // every 30 seconds
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,85 +36,6 @@ export class UsageService {
|
||||
return Math.round(value * 100000000000) / 100000000000; // 11 decimal places
|
||||
}
|
||||
|
||||
private async initializeEventsDirectory(): Promise<void> {
|
||||
if (!this.eventsDir) {
|
||||
logger.warn(
|
||||
"Stripe local file path is not configured, skipping events directory initialization."
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await fs.mkdir(this.eventsDir, { recursive: true });
|
||||
} catch (error) {
|
||||
logger.error("Failed to create events directory:", error);
|
||||
}
|
||||
}
|
||||
|
||||
private async uploadPendingEventFilesOnStartup(): Promise<void> {
|
||||
if (!this.eventsDir || !this.bucketName) {
|
||||
logger.warn(
|
||||
"Stripe local file path or bucket name is not configured, skipping leftover event file upload."
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const files = await fs.readdir(this.eventsDir);
|
||||
for (const file of files) {
|
||||
if (file.endsWith(".json")) {
|
||||
const filePath = path.join(this.eventsDir, file);
|
||||
try {
|
||||
const fileContent = await fs.readFile(
|
||||
filePath,
|
||||
"utf-8"
|
||||
);
|
||||
const events = JSON.parse(fileContent);
|
||||
if (Array.isArray(events) && events.length > 0) {
|
||||
// Upload to S3
|
||||
const uploadCommand = new PutObjectCommand({
|
||||
Bucket: this.bucketName,
|
||||
Key: file,
|
||||
Body: fileContent,
|
||||
ContentType: "application/json"
|
||||
});
|
||||
await s3Client.send(uploadCommand);
|
||||
|
||||
// Check if file still exists before unlinking
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
await fs.unlink(filePath);
|
||||
} catch (unlinkError) {
|
||||
logger.debug(
|
||||
`Startup file ${file} was already deleted`
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Uploaded leftover event file ${file} to S3 with ${events.length} events`
|
||||
);
|
||||
} else {
|
||||
// Remove empty file
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
await fs.unlink(filePath);
|
||||
} catch (unlinkError) {
|
||||
logger.debug(
|
||||
`Empty startup file ${file} was already deleted`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`Error processing leftover event file ${file}:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to scan for leftover event files");
|
||||
}
|
||||
}
|
||||
|
||||
public async add(
|
||||
orgId: string,
|
||||
featureId: FeatureId,
|
||||
@@ -176,28 +55,20 @@ export class UsageService {
|
||||
|
||||
while (attempt <= maxRetries) {
|
||||
try {
|
||||
// Get subscription data for this org (with caching)
|
||||
const customerId = await this.getCustomerId(orgId, featureId);
|
||||
|
||||
if (!customerId) {
|
||||
logger.warn(
|
||||
`No subscription data found for org ${orgId} and feature ${featureId}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
let usage;
|
||||
if (transaction) {
|
||||
const orgIdToUse = await this.getBillingOrg(orgId, transaction);
|
||||
usage = await this.internalAddUsage(
|
||||
orgId,
|
||||
orgIdToUse,
|
||||
featureId,
|
||||
value,
|
||||
transaction
|
||||
);
|
||||
} else {
|
||||
await db.transaction(async (trx) => {
|
||||
const orgIdToUse = await this.getBillingOrg(orgId, trx);
|
||||
usage = await this.internalAddUsage(
|
||||
orgId,
|
||||
orgIdToUse,
|
||||
featureId,
|
||||
value,
|
||||
trx
|
||||
@@ -205,9 +76,6 @@ export class UsageService {
|
||||
});
|
||||
}
|
||||
|
||||
// Log event for Stripe
|
||||
await this.logStripeEvent(featureId, value, customerId);
|
||||
|
||||
return usage || null;
|
||||
} catch (error: any) {
|
||||
// Check if this is a deadlock error
|
||||
@@ -243,7 +111,7 @@ export class UsageService {
|
||||
}
|
||||
|
||||
private async internalAddUsage(
|
||||
orgId: string,
|
||||
orgId: string, // here the orgId is the billing org already resolved by getBillingOrg in updateCount
|
||||
featureId: FeatureId,
|
||||
value: number,
|
||||
trx: Transaction
|
||||
@@ -262,17 +130,22 @@ export class UsageService {
|
||||
featureId,
|
||||
orgId,
|
||||
meterId,
|
||||
latestValue: value,
|
||||
instantaneousValue: value || 0,
|
||||
latestValue: value || 0,
|
||||
updatedAt: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: usage.usageId,
|
||||
set: {
|
||||
latestValue: sql`${usage.latestValue} + ${value}`
|
||||
instantaneousValue: sql`COALESCE(${usage.instantaneousValue}, 0) + ${value}`
|
||||
}
|
||||
})
|
||||
.returning();
|
||||
|
||||
logger.debug(
|
||||
`Added usage for org ${orgId} feature ${featureId}: +${value}, new instantaneousValue: ${returnUsage.instantaneousValue}`
|
||||
);
|
||||
|
||||
return returnUsage;
|
||||
}
|
||||
|
||||
@@ -286,7 +159,7 @@ export class UsageService {
|
||||
return new Date(date * 1000).toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
async updateDaily(
|
||||
async updateCount(
|
||||
orgId: string,
|
||||
featureId: FeatureId,
|
||||
value?: number,
|
||||
@@ -295,30 +168,20 @@ export class UsageService {
|
||||
if (noop()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!customerId) {
|
||||
customerId =
|
||||
(await this.getCustomerId(orgId, featureId)) || undefined;
|
||||
if (!customerId) {
|
||||
logger.warn(
|
||||
`No subscription data found for org ${orgId} and feature ${featureId}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const orgIdToUse = await this.getBillingOrg(orgId);
|
||||
|
||||
try {
|
||||
// Truncate value to 11 decimal places if provided
|
||||
if (value !== undefined && value !== null) {
|
||||
value = this.truncateValue(value);
|
||||
}
|
||||
|
||||
const today = this.getTodayDateString();
|
||||
|
||||
let currentUsage: Usage | null = null;
|
||||
|
||||
await db.transaction(async (trx) => {
|
||||
// Get existing meter record
|
||||
const usageId = `${orgId}-${featureId}`;
|
||||
const usageId = `${orgIdToUse}-${featureId}`;
|
||||
// Get current usage record
|
||||
[currentUsage] = await trx
|
||||
.select()
|
||||
@@ -327,66 +190,34 @@ export class UsageService {
|
||||
.limit(1);
|
||||
|
||||
if (currentUsage) {
|
||||
const lastUpdateDate = this.getDateString(
|
||||
currentUsage.updatedAt
|
||||
);
|
||||
const currentRunningTotal = currentUsage.latestValue;
|
||||
const lastDailyValue = currentUsage.instantaneousValue || 0;
|
||||
|
||||
if (value == undefined || value === null) {
|
||||
value = currentUsage.instantaneousValue || 0;
|
||||
}
|
||||
|
||||
if (lastUpdateDate === today) {
|
||||
// Same day update: replace the daily value
|
||||
// Remove old daily value from running total, add new value
|
||||
const newRunningTotal = this.truncateValue(
|
||||
currentRunningTotal - lastDailyValue + value
|
||||
);
|
||||
|
||||
await trx
|
||||
.update(usage)
|
||||
.set({
|
||||
latestValue: newRunningTotal,
|
||||
instantaneousValue: value,
|
||||
updatedAt: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
.where(eq(usage.usageId, usageId));
|
||||
} else {
|
||||
// New day: add to running total
|
||||
const newRunningTotal = this.truncateValue(
|
||||
currentRunningTotal + value
|
||||
);
|
||||
|
||||
await trx
|
||||
.update(usage)
|
||||
.set({
|
||||
latestValue: newRunningTotal,
|
||||
instantaneousValue: value,
|
||||
updatedAt: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
.where(eq(usage.usageId, usageId));
|
||||
}
|
||||
await trx
|
||||
.update(usage)
|
||||
.set({
|
||||
instantaneousValue: value,
|
||||
updatedAt: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
.where(eq(usage.usageId, usageId));
|
||||
} else {
|
||||
// First record for this meter
|
||||
const meterId = getFeatureMeterId(featureId);
|
||||
const truncatedValue = this.truncateValue(value || 0);
|
||||
await trx.insert(usage).values({
|
||||
usageId,
|
||||
featureId,
|
||||
orgId,
|
||||
orgId: orgIdToUse,
|
||||
meterId,
|
||||
instantaneousValue: truncatedValue,
|
||||
latestValue: truncatedValue,
|
||||
instantaneousValue: value || 0,
|
||||
latestValue: value || 0,
|
||||
updatedAt: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await this.logStripeEvent(featureId, value || 0, customerId);
|
||||
// if (privateConfig.getRawPrivateConfig().flags.usage_reporting) {
|
||||
// await this.logStripeEvent(featureId, value || 0, customerId);
|
||||
// }
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to update daily usage for ${orgId}/${featureId}:`,
|
||||
`Failed to update count usage for ${orgIdToUse}/${featureId}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
@@ -396,8 +227,10 @@ export class UsageService {
|
||||
orgId: string,
|
||||
featureId: FeatureId
|
||||
): Promise<string | null> {
|
||||
const cacheKey = `customer_${orgId}_${featureId}`;
|
||||
const cached = cache.get<string>(cacheKey);
|
||||
const orgIdToUse = await this.getBillingOrg(orgId);
|
||||
|
||||
const cacheKey = `customer_${orgIdToUse}_${featureId}`;
|
||||
const cached = await cache.get<string>(cacheKey);
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
@@ -410,7 +243,7 @@ export class UsageService {
|
||||
customerId: customers.customerId
|
||||
})
|
||||
.from(customers)
|
||||
.where(eq(customers.orgId, orgId))
|
||||
.where(eq(customers.orgId, orgIdToUse))
|
||||
.limit(1);
|
||||
|
||||
if (!customer) {
|
||||
@@ -420,194 +253,18 @@ export class UsageService {
|
||||
const customerId = customer.customerId;
|
||||
|
||||
// Cache the result
|
||||
cache.set(cacheKey, customerId, 300); // 5 minute TTL
|
||||
await cache.set(cacheKey, customerId, 300); // 5 minute TTL
|
||||
|
||||
return customerId;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to get subscription data for ${orgId}/${featureId}:`,
|
||||
`Failed to get subscription data for ${orgIdToUse}/${featureId}:`,
|
||||
error
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async logStripeEvent(
|
||||
featureId: FeatureId,
|
||||
value: number,
|
||||
customerId: string
|
||||
): Promise<void> {
|
||||
// Truncate value to 11 decimal places before sending to Stripe
|
||||
const truncatedValue = this.truncateValue(value);
|
||||
|
||||
const event: StripeEvent = {
|
||||
identifier: uuidv4(),
|
||||
timestamp: Math.floor(new Date().getTime() / 1000),
|
||||
event_name: featureId,
|
||||
payload: {
|
||||
value: truncatedValue,
|
||||
stripe_customer_id: customerId
|
||||
}
|
||||
};
|
||||
|
||||
await this.writeEventToFile(event);
|
||||
await this.checkAndUploadFile();
|
||||
}
|
||||
|
||||
private async writeEventToFile(event: StripeEvent): Promise<void> {
|
||||
if (!this.eventsDir || !this.bucketName) {
|
||||
logger.warn(
|
||||
"Stripe local file path or bucket name is not configured, skipping event file write."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.currentEventFile) {
|
||||
this.currentEventFile = this.generateEventFileName();
|
||||
this.currentFileStartTime = Date.now();
|
||||
}
|
||||
|
||||
const filePath = path.join(this.eventsDir, this.currentEventFile);
|
||||
|
||||
try {
|
||||
let events: StripeEvent[] = [];
|
||||
|
||||
// Try to read existing file
|
||||
try {
|
||||
const fileContent = await fs.readFile(filePath, "utf-8");
|
||||
events = JSON.parse(fileContent);
|
||||
} catch (error) {
|
||||
// File doesn't exist or is empty, start with empty array
|
||||
events = [];
|
||||
}
|
||||
|
||||
// Add new event
|
||||
events.push(event);
|
||||
|
||||
// Write back to file
|
||||
await fs.writeFile(filePath, JSON.stringify(events, null, 2));
|
||||
} catch (error) {
|
||||
logger.error("Failed to write event to file:", error);
|
||||
}
|
||||
}
|
||||
|
||||
private async checkAndUploadFile(): Promise<void> {
|
||||
if (!this.currentEventFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const fileAge = now - this.currentFileStartTime;
|
||||
|
||||
// Check if file is at least 1 minute old
|
||||
if (fileAge >= 60000) {
|
||||
// 60 seconds
|
||||
await this.uploadFileToS3();
|
||||
}
|
||||
}
|
||||
|
||||
private async uploadFileToS3(): Promise<void> {
|
||||
if (!this.bucketName || !this.eventsDir) {
|
||||
logger.warn(
|
||||
"Stripe local file path or bucket name is not configured, skipping S3 upload."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.currentEventFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = this.currentEventFile;
|
||||
const filePath = path.join(this.eventsDir, fileName);
|
||||
|
||||
// Check if this file is already being uploaded
|
||||
if (this.uploadingFiles.has(fileName)) {
|
||||
logger.debug(
|
||||
`File ${fileName} is already being uploaded, skipping`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark file as being uploaded
|
||||
this.uploadingFiles.add(fileName);
|
||||
|
||||
try {
|
||||
// Check if file exists before trying to read it
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
} catch (error) {
|
||||
logger.debug(
|
||||
`File ${fileName} does not exist, may have been already processed`
|
||||
);
|
||||
this.uploadingFiles.delete(fileName);
|
||||
// Reset current file if it was this file
|
||||
if (this.currentEventFile === fileName) {
|
||||
this.currentEventFile = null;
|
||||
this.currentFileStartTime = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if file exists and has content
|
||||
const fileContent = await fs.readFile(filePath, "utf-8");
|
||||
const events = JSON.parse(fileContent);
|
||||
|
||||
if (events.length === 0) {
|
||||
// No events to upload, just clean up
|
||||
try {
|
||||
await fs.unlink(filePath);
|
||||
} catch (unlinkError) {
|
||||
// File may have been already deleted
|
||||
logger.debug(
|
||||
`File ${fileName} was already deleted during cleanup`
|
||||
);
|
||||
}
|
||||
this.currentEventFile = null;
|
||||
this.uploadingFiles.delete(fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload to S3
|
||||
const uploadCommand = new PutObjectCommand({
|
||||
Bucket: this.bucketName,
|
||||
Key: fileName,
|
||||
Body: fileContent,
|
||||
ContentType: "application/json"
|
||||
});
|
||||
|
||||
await s3Client.send(uploadCommand);
|
||||
|
||||
// Clean up local file - check if it still exists before unlinking
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
await fs.unlink(filePath);
|
||||
} catch (unlinkError) {
|
||||
// File may have been already deleted by another process
|
||||
logger.debug(
|
||||
`File ${fileName} was already deleted during upload`
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Uploaded ${fileName} to S3 with ${events.length} events`
|
||||
);
|
||||
|
||||
// Reset for next file
|
||||
this.currentEventFile = null;
|
||||
this.currentFileStartTime = 0;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to upload ${fileName} to S3:`, error);
|
||||
} finally {
|
||||
// Always remove from uploading set
|
||||
this.uploadingFiles.delete(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
private generateEventFileName(): string {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const uuid = uuidv4().substring(0, 8);
|
||||
return `events-${timestamp}-${uuid}.json`;
|
||||
}
|
||||
|
||||
public async getUsage(
|
||||
orgId: string,
|
||||
featureId: FeatureId,
|
||||
@@ -617,7 +274,9 @@ export class UsageService {
|
||||
return null;
|
||||
}
|
||||
|
||||
const usageId = `${orgId}-${featureId}`;
|
||||
const orgIdToUse = await this.getBillingOrg(orgId, trx);
|
||||
|
||||
const usageId = `${orgIdToUse}-${featureId}`;
|
||||
|
||||
try {
|
||||
const [result] = await trx
|
||||
@@ -629,7 +288,7 @@ export class UsageService {
|
||||
if (!result) {
|
||||
// Lets create one if it doesn't exist using upsert to handle race conditions
|
||||
logger.info(
|
||||
`Creating new usage record for ${orgId}/${featureId}`
|
||||
`Creating new usage record for ${orgIdToUse}/${featureId}`
|
||||
);
|
||||
const meterId = getFeatureMeterId(featureId);
|
||||
|
||||
@@ -639,7 +298,7 @@ export class UsageService {
|
||||
.values({
|
||||
usageId,
|
||||
featureId,
|
||||
orgId,
|
||||
orgId: orgIdToUse,
|
||||
meterId,
|
||||
latestValue: 0,
|
||||
updatedAt: Math.floor(Date.now() / 1000)
|
||||
@@ -661,7 +320,7 @@ export class UsageService {
|
||||
} catch (insertError) {
|
||||
// Fallback: try to fetch existing record in case of any insert issues
|
||||
logger.warn(
|
||||
`Insert failed for ${orgId}/${featureId}, attempting to fetch existing record:`,
|
||||
`Insert failed for ${orgIdToUse}/${featureId}, attempting to fetch existing record:`,
|
||||
insertError
|
||||
);
|
||||
const [existingUsage] = await trx
|
||||
@@ -676,136 +335,45 @@ export class UsageService {
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to get usage for ${orgId}/${featureId}:`,
|
||||
`Failed to get usage for ${orgIdToUse}/${featureId}:`,
|
||||
error
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async getUsageDaily(
|
||||
public async getBillingOrg(
|
||||
orgId: string,
|
||||
featureId: FeatureId
|
||||
): Promise<Usage | null> {
|
||||
if (noop()) {
|
||||
return null;
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<string> {
|
||||
let orgIdToUse = orgId;
|
||||
|
||||
// get the org
|
||||
const [org] = await trx
|
||||
.select()
|
||||
.from(orgs)
|
||||
.where(eq(orgs.orgId, orgId))
|
||||
.limit(1);
|
||||
|
||||
if (!org) {
|
||||
throw new Error(`Organization with ID ${orgId} not found`);
|
||||
}
|
||||
await this.updateDaily(orgId, featureId); // Ensure daily usage is updated
|
||||
return this.getUsage(orgId, featureId);
|
||||
}
|
||||
|
||||
public async forceUpload(): Promise<void> {
|
||||
await this.uploadFileToS3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the events directory for files older than 1 minute and upload them if not empty.
|
||||
*/
|
||||
private async uploadOldEventFiles(): Promise<void> {
|
||||
if (!this.eventsDir || !this.bucketName) {
|
||||
logger.warn(
|
||||
"Stripe local file path or bucket name is not configured, skipping old event file upload."
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const files = await fs.readdir(this.eventsDir);
|
||||
const now = Date.now();
|
||||
for (const file of files) {
|
||||
if (!file.endsWith(".json")) continue;
|
||||
|
||||
// Skip files that are already being uploaded
|
||||
if (this.uploadingFiles.has(file)) {
|
||||
logger.debug(
|
||||
`Skipping file ${file} as it's already being uploaded`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath = path.join(this.eventsDir, file);
|
||||
|
||||
try {
|
||||
// Check if file still exists before processing
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
} catch (accessError) {
|
||||
logger.debug(`File ${file} does not exist, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const stat = await fs.stat(filePath);
|
||||
const age = now - stat.mtimeMs;
|
||||
if (age >= 90000) {
|
||||
// 1.5 minutes - Mark as being uploaded
|
||||
this.uploadingFiles.add(file);
|
||||
|
||||
try {
|
||||
const fileContent = await fs.readFile(
|
||||
filePath,
|
||||
"utf-8"
|
||||
);
|
||||
const events = JSON.parse(fileContent);
|
||||
if (Array.isArray(events) && events.length > 0) {
|
||||
// Upload to S3
|
||||
const uploadCommand = new PutObjectCommand({
|
||||
Bucket: this.bucketName,
|
||||
Key: file,
|
||||
Body: fileContent,
|
||||
ContentType: "application/json"
|
||||
});
|
||||
await s3Client.send(uploadCommand);
|
||||
|
||||
// Check if file still exists before unlinking
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
await fs.unlink(filePath);
|
||||
} catch (unlinkError) {
|
||||
logger.debug(
|
||||
`File ${file} was already deleted during interval upload`
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Interval: Uploaded event file ${file} to S3 with ${events.length} events`
|
||||
);
|
||||
// If this was the current event file, reset it
|
||||
if (this.currentEventFile === file) {
|
||||
this.currentEventFile = null;
|
||||
this.currentFileStartTime = 0;
|
||||
}
|
||||
} else {
|
||||
// Remove empty file
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
await fs.unlink(filePath);
|
||||
} catch (unlinkError) {
|
||||
logger.debug(
|
||||
`Empty file ${file} was already deleted`
|
||||
);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Always remove from uploading set
|
||||
this.uploadingFiles.delete(file);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`Interval: Error processing event file ${file}:`,
|
||||
err
|
||||
);
|
||||
// Remove from uploading set on error
|
||||
this.uploadingFiles.delete(file);
|
||||
}
|
||||
if (!org.isBillingOrg) {
|
||||
if (org.billingOrgId) {
|
||||
orgIdToUse = org.billingOrgId;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Organization ${orgId} is not a billing org and does not have a billingOrgId set`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Interval: Failed to scan for event files:", err);
|
||||
}
|
||||
|
||||
return orgIdToUse;
|
||||
}
|
||||
|
||||
public async checkLimitSet(
|
||||
orgId: string,
|
||||
kickSites = false,
|
||||
featureId?: FeatureId,
|
||||
usage?: Usage,
|
||||
trx: Transaction | typeof db = db
|
||||
@@ -813,6 +381,9 @@ export class UsageService {
|
||||
if (noop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const orgIdToUse = await this.getBillingOrg(orgId, trx);
|
||||
|
||||
// This method should check the current usage against the limits set for the organization
|
||||
// and kick out all of the sites on the org
|
||||
let hasExceededLimits = false;
|
||||
@@ -826,7 +397,7 @@ export class UsageService {
|
||||
.from(limits)
|
||||
.where(
|
||||
and(
|
||||
eq(limits.orgId, orgId),
|
||||
eq(limits.orgId, orgIdToUse),
|
||||
eq(limits.featureId, featureId)
|
||||
)
|
||||
);
|
||||
@@ -835,11 +406,11 @@ export class UsageService {
|
||||
orgLimits = await trx
|
||||
.select()
|
||||
.from(limits)
|
||||
.where(eq(limits.orgId, orgId));
|
||||
.where(eq(limits.orgId, orgIdToUse));
|
||||
}
|
||||
|
||||
if (orgLimits.length === 0) {
|
||||
logger.debug(`No limits set for org ${orgId}`);
|
||||
logger.debug(`No limits set for org ${orgIdToUse}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -850,7 +421,7 @@ export class UsageService {
|
||||
currentUsage = usage;
|
||||
} else {
|
||||
currentUsage = await this.getUsage(
|
||||
orgId,
|
||||
orgIdToUse,
|
||||
limit.featureId as FeatureId,
|
||||
trx
|
||||
);
|
||||
@@ -861,10 +432,10 @@ export class UsageService {
|
||||
currentUsage?.latestValue ||
|
||||
0;
|
||||
logger.debug(
|
||||
`Current usage for org ${orgId} on feature ${limit.featureId}: ${usageValue}`
|
||||
`Current usage for org ${orgIdToUse} on feature ${limit.featureId}: ${usageValue}`
|
||||
);
|
||||
logger.debug(
|
||||
`Limit for org ${orgId} on feature ${limit.featureId}: ${limit.value}`
|
||||
`Limit for org ${orgIdToUse} on feature ${limit.featureId}: ${limit.value}`
|
||||
);
|
||||
if (
|
||||
currentUsage &&
|
||||
@@ -872,67 +443,15 @@ export class UsageService {
|
||||
usageValue > limit.value
|
||||
) {
|
||||
logger.debug(
|
||||
`Org ${orgId} has exceeded limit for ${limit.featureId}: ` +
|
||||
`Org ${orgIdToUse} has exceeded limit for ${limit.featureId}: ` +
|
||||
`${usageValue} > ${limit.value}`
|
||||
);
|
||||
hasExceededLimits = true;
|
||||
break; // Exit early if any limit is exceeded
|
||||
}
|
||||
}
|
||||
|
||||
// If any limits are exceeded, disconnect all sites for this organization
|
||||
if (hasExceededLimits && kickSites) {
|
||||
logger.warn(
|
||||
`Disconnecting all sites for org ${orgId} due to exceeded limits`
|
||||
);
|
||||
|
||||
// Get all sites for this organization
|
||||
const orgSites = await trx
|
||||
.select()
|
||||
.from(sites)
|
||||
.where(eq(sites.orgId, orgId));
|
||||
|
||||
// Mark all sites as offline and send termination messages
|
||||
const siteUpdates = orgSites.map((site) => site.siteId);
|
||||
|
||||
if (siteUpdates.length > 0) {
|
||||
// Send termination messages to newt sites
|
||||
for (const site of orgSites) {
|
||||
if (site.type === "newt") {
|
||||
const [newt] = await trx
|
||||
.select()
|
||||
.from(newts)
|
||||
.where(eq(newts.siteId, site.siteId))
|
||||
.limit(1);
|
||||
|
||||
if (newt) {
|
||||
const payload = {
|
||||
type: `newt/wg/terminate`,
|
||||
data: {
|
||||
reason: "Usage limits exceeded"
|
||||
}
|
||||
};
|
||||
|
||||
// Don't await to prevent blocking
|
||||
await sendToClient(newt.newtId, payload).catch(
|
||||
(error: any) => {
|
||||
logger.error(
|
||||
`Failed to send termination message to newt ${newt.newtId}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Disconnected ${orgSites.length} sites for org ${orgId} due to exceeded limits`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error checking limits for org ${orgId}:`, error);
|
||||
logger.error(`Error checking limits for org ${orgIdToUse}:`, error);
|
||||
}
|
||||
|
||||
return hasExceededLimits;
|
||||
|
||||
@@ -107,7 +107,7 @@ export async function applyBlueprint({
|
||||
[target],
|
||||
matchingHealthcheck ? [matchingHealthcheck] : [],
|
||||
result.proxyResource.protocol,
|
||||
result.proxyResource.proxyPort
|
||||
site.newt.version
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
userSiteResources
|
||||
} from "@server/db";
|
||||
import { sites } from "@server/db";
|
||||
import { eq, and, ne, inArray } from "drizzle-orm";
|
||||
import { eq, and, ne, inArray, or } from "drizzle-orm";
|
||||
import { Config } from "./types";
|
||||
import logger from "@server/logger";
|
||||
import { getNextAvailableAliasAddress } from "../ip";
|
||||
@@ -142,7 +142,10 @@ export async function updateClientResources(
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
inArray(users.username, resourceData.users),
|
||||
or(
|
||||
inArray(users.username, resourceData.users),
|
||||
inArray(users.email, resourceData.users)
|
||||
),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
);
|
||||
@@ -276,7 +279,10 @@ export async function updateClientResources(
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(
|
||||
and(
|
||||
inArray(users.username, resourceData.users),
|
||||
or(
|
||||
inArray(users.username, resourceData.users),
|
||||
inArray(users.email, resourceData.users)
|
||||
),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -31,8 +31,9 @@ import { pickPort } from "@server/routers/target/helpers";
|
||||
import { resourcePassword } from "@server/db";
|
||||
import { hashPassword } from "@server/auth/password";
|
||||
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
||||
import { isValidRegionId } from "@server/db/regions";
|
||||
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
||||
import { build } from "@server/build";
|
||||
import { tierMatrix } from "../billing/tierMatrix";
|
||||
|
||||
export type ProxyResourcesResults = {
|
||||
proxyResource: Resource;
|
||||
@@ -212,7 +213,10 @@ export async function updateProxyResources(
|
||||
} else {
|
||||
// Update existing resource
|
||||
|
||||
const isLicensed = await isLicensedOrSubscribed(orgId);
|
||||
const isLicensed = await isLicensedOrSubscribed(
|
||||
orgId,
|
||||
tierMatrix.maintencePage
|
||||
);
|
||||
if (!isLicensed) {
|
||||
resourceData.maintenance = undefined;
|
||||
}
|
||||
@@ -590,7 +594,10 @@ export async function updateProxyResources(
|
||||
existingRule.action !== getRuleAction(rule.action) ||
|
||||
existingRule.match !== rule.match.toUpperCase() ||
|
||||
existingRule.value !==
|
||||
getRuleValue(rule.match.toUpperCase(), rule.value) ||
|
||||
getRuleValue(
|
||||
rule.match.toUpperCase(),
|
||||
rule.value
|
||||
) ||
|
||||
existingRule.priority !== intendedPriority
|
||||
) {
|
||||
validateRule(rule);
|
||||
@@ -648,7 +655,10 @@ export async function updateProxyResources(
|
||||
);
|
||||
}
|
||||
|
||||
const isLicensed = await isLicensedOrSubscribed(orgId);
|
||||
const isLicensed = await isLicensedOrSubscribed(
|
||||
orgId,
|
||||
tierMatrix.maintencePage
|
||||
);
|
||||
if (!isLicensed) {
|
||||
resourceData.maintenance = undefined;
|
||||
}
|
||||
@@ -854,6 +864,10 @@ function validateRule(rule: any) {
|
||||
if (!isValidUrlGlobPattern(rule.value)) {
|
||||
throw new Error(`Invalid URL glob pattern: ${rule.value}`);
|
||||
}
|
||||
} else if (rule.match === "region") {
|
||||
if (!isValidRegionId(rule.value)) {
|
||||
throw new Error(`Invalid region ID provided: ${rule.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,7 +949,12 @@ async function syncUserResources(
|
||||
.select()
|
||||
.from(users)
|
||||
.innerJoin(userOrgs, eq(users.userId, userOrgs.userId))
|
||||
.where(and(eq(users.username, username), eq(userOrgs.orgId, orgId)))
|
||||
.where(
|
||||
and(
|
||||
or(eq(users.username, username), eq(users.email, username)),
|
||||
eq(userOrgs.orgId, orgId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { portRangeStringSchema } from "@server/lib/ip";
|
||||
import { MaintenanceSchema } from "#dynamic/lib/blueprints/MaintenanceSchema";
|
||||
import { isValidRegionId } from "@server/db/regions";
|
||||
|
||||
export const SiteSchema = z.object({
|
||||
name: z.string().min(1).max(100),
|
||||
@@ -69,7 +70,7 @@ export const AuthSchema = z.object({
|
||||
.refine((roles) => !roles.includes("Admin"), {
|
||||
error: "Admin role cannot be included in sso-roles"
|
||||
}),
|
||||
"sso-users": z.array(z.email()).optional().default([]),
|
||||
"sso-users": z.array(z.string()).optional().default([]),
|
||||
"whitelist-users": z.array(z.email()).optional().default([]),
|
||||
"auto-login-idp": z.int().positive().optional()
|
||||
});
|
||||
@@ -77,7 +78,7 @@ export const AuthSchema = z.object({
|
||||
export const RuleSchema = z
|
||||
.object({
|
||||
action: z.enum(["allow", "deny", "pass"]),
|
||||
match: z.enum(["cidr", "path", "ip", "country", "asn"]),
|
||||
match: z.enum(["cidr", "path", "ip", "country", "asn", "region"]),
|
||||
value: z.string(),
|
||||
priority: z.int().optional()
|
||||
})
|
||||
@@ -137,6 +138,19 @@ export const RuleSchema = z
|
||||
message:
|
||||
"Value must be 'AS<number>' format or 'ALL' when match is 'asn'"
|
||||
}
|
||||
)
|
||||
.refine(
|
||||
(rule) => {
|
||||
if (rule.match === "region") {
|
||||
return isValidRegionId(rule.value);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
path: ["value"],
|
||||
message:
|
||||
"Value must be a valid UN M.49 region or subregion ID when match is 'region'"
|
||||
}
|
||||
);
|
||||
|
||||
export const HeaderSchema = z.object({
|
||||
@@ -335,7 +349,7 @@ export const ClientResourceSchema = z
|
||||
.refine((roles) => !roles.includes("Admin"), {
|
||||
error: "Admin role cannot be included in roles"
|
||||
}),
|
||||
users: z.array(z.email()).optional().default([]),
|
||||
users: z.array(z.string()).optional().default([]),
|
||||
machines: z.array(z.string()).optional().default([])
|
||||
})
|
||||
.refine(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import NodeCache from "node-cache";
|
||||
import logger from "@server/logger";
|
||||
|
||||
// Create cache with maxKeys limit to prevent memory leaks
|
||||
// Create local cache with maxKeys limit to prevent memory leaks
|
||||
// With ~10k requests/day and 5min TTL, 10k keys should be more than sufficient
|
||||
export const cache = new NodeCache({
|
||||
export const localCache = new NodeCache({
|
||||
stdTTL: 3600,
|
||||
checkperiod: 120,
|
||||
maxKeys: 10000
|
||||
@@ -11,10 +11,151 @@ export const cache = new NodeCache({
|
||||
|
||||
// Log cache statistics periodically for monitoring
|
||||
setInterval(() => {
|
||||
const stats = cache.getStats();
|
||||
const stats = localCache.getStats();
|
||||
logger.debug(
|
||||
`Cache stats - Keys: ${stats.keys}, Hits: ${stats.hits}, Misses: ${stats.misses}, Hit rate: ${stats.hits > 0 ? ((stats.hits / (stats.hits + stats.misses)) * 100).toFixed(2) : 0}%`
|
||||
`Local cache stats - Keys: ${stats.keys}, Hits: ${stats.hits}, Misses: ${stats.misses}, Hit rate: ${stats.hits > 0 ? ((stats.hits / (stats.hits + stats.misses)) * 100).toFixed(2) : 0}%`
|
||||
);
|
||||
}, 300000); // Every 5 minutes
|
||||
|
||||
/**
|
||||
* Adaptive cache that uses Redis when available in multi-node environments,
|
||||
* otherwise falls back to local memory cache for single-node deployments.
|
||||
*/
|
||||
class AdaptiveCache {
|
||||
/**
|
||||
* Set a value in the cache
|
||||
* @param key - Cache key
|
||||
* @param value - Value to cache (will be JSON stringified for Redis)
|
||||
* @param ttl - Time to live in seconds (0 = no expiration)
|
||||
* @returns boolean indicating success
|
||||
*/
|
||||
async set(key: string, value: any, ttl?: number): Promise<boolean> {
|
||||
const effectiveTtl = ttl === 0 ? undefined : ttl;
|
||||
|
||||
// Use local cache as fallback or primary
|
||||
const success = localCache.set(key, value, effectiveTtl || 0);
|
||||
if (success) {
|
||||
logger.debug(`Set key in local cache: ${key}`);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from the cache
|
||||
* @param key - Cache key
|
||||
* @returns The cached value or undefined if not found
|
||||
*/
|
||||
async get<T = any>(key: string): Promise<T | undefined> {
|
||||
// Use local cache as fallback or primary
|
||||
const value = localCache.get<T>(key);
|
||||
if (value !== undefined) {
|
||||
logger.debug(`Cache hit in local cache: ${key}`);
|
||||
} else {
|
||||
logger.debug(`Cache miss in local cache: ${key}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a value from the cache
|
||||
* @param key - Cache key or array of keys
|
||||
* @returns Number of deleted entries
|
||||
*/
|
||||
async del(key: string | string[]): Promise<number> {
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
let deletedCount = 0;
|
||||
|
||||
// Use local cache as fallback or primary
|
||||
for (const k of keys) {
|
||||
const success = localCache.del(k);
|
||||
if (success > 0) {
|
||||
deletedCount++;
|
||||
logger.debug(`Deleted key from local cache: ${k}`);
|
||||
}
|
||||
}
|
||||
|
||||
return deletedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a key exists in the cache
|
||||
* @param key - Cache key
|
||||
* @returns boolean indicating if key exists
|
||||
*/
|
||||
async has(key: string): Promise<boolean> {
|
||||
// Use local cache as fallback or primary
|
||||
return localCache.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple values from the cache
|
||||
* @param keys - Array of cache keys
|
||||
* @returns Array of values (undefined for missing keys)
|
||||
*/
|
||||
async mget<T = any>(keys: string[]): Promise<(T | undefined)[]> {
|
||||
// Use local cache as fallback or primary
|
||||
return keys.map((key) => localCache.get<T>(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all keys from the cache
|
||||
*/
|
||||
async flushAll(): Promise<void> {
|
||||
localCache.flushAll();
|
||||
logger.debug("Flushed local cache");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache statistics
|
||||
* Note: Only returns local cache stats, Redis stats are not included
|
||||
*/
|
||||
getStats() {
|
||||
return localCache.getStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current cache backend being used
|
||||
* @returns "redis" if Redis is available and healthy, "local" otherwise
|
||||
*/
|
||||
getCurrentBackend(): "redis" | "local" {
|
||||
return "local";
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a key from the cache and delete it
|
||||
* @param key - Cache key
|
||||
* @returns The value or undefined if not found
|
||||
*/
|
||||
async take<T = any>(key: string): Promise<T | undefined> {
|
||||
const value = await this.get<T>(key);
|
||||
if (value !== undefined) {
|
||||
await this.del(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TTL (time to live) for a key
|
||||
* @param key - Cache key
|
||||
* @returns TTL in seconds, 0 if no expiration, -1 if key doesn't exist
|
||||
*/
|
||||
getTtl(key: string): number {
|
||||
const ttl = localCache.getTtl(key);
|
||||
if (ttl === undefined) {
|
||||
return -1;
|
||||
}
|
||||
return Math.max(0, Math.floor((ttl - Date.now()) / 1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all keys from the cache
|
||||
* Note: Only returns local cache keys, Redis keys are not included
|
||||
*/
|
||||
keys(): string[] {
|
||||
return localCache.keys();
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const cache = new AdaptiveCache();
|
||||
export default cache;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
roles,
|
||||
Transaction,
|
||||
userClients,
|
||||
userOrgRoles,
|
||||
userOrgs
|
||||
} from "@server/db";
|
||||
import { getUniqueClientName } from "@server/db/names";
|
||||
@@ -20,6 +21,7 @@ import { sendTerminateClient } from "@server/routers/client/terminate";
|
||||
import { and, eq, notInArray, type InferInsertModel } from "drizzle-orm";
|
||||
import { rebuildClientAssociationsFromClient } from "./rebuildClientAssociations";
|
||||
import { OlmErrorCodes } from "@server/routers/olm/error";
|
||||
import { tierMatrix } from "./billing/tierMatrix";
|
||||
|
||||
export async function calculateUserClientsForOrgs(
|
||||
userId: string,
|
||||
@@ -38,20 +40,36 @@ export async function calculateUserClientsForOrgs(
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all user orgs
|
||||
const allUserOrgs = await transaction
|
||||
// Get all user orgs with all roles (for org list and role-based logic)
|
||||
const userOrgRoleRows = await transaction
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.innerJoin(roles, eq(roles.roleId, userOrgs.roleId))
|
||||
.innerJoin(
|
||||
userOrgRoles,
|
||||
and(
|
||||
eq(userOrgs.userId, userOrgRoles.userId),
|
||||
eq(userOrgs.orgId, userOrgRoles.orgId)
|
||||
)
|
||||
)
|
||||
.innerJoin(roles, eq(userOrgRoles.roleId, roles.roleId))
|
||||
.where(eq(userOrgs.userId, userId));
|
||||
|
||||
const userOrgIds = allUserOrgs.map(({ userOrgs: uo }) => uo.orgId);
|
||||
const userOrgIds = [...new Set(userOrgRoleRows.map((r) => r.userOrgs.orgId))];
|
||||
const orgIdToRoleRows = new Map<
|
||||
string,
|
||||
(typeof userOrgRoleRows)[0][]
|
||||
>();
|
||||
for (const r of userOrgRoleRows) {
|
||||
const list = orgIdToRoleRows.get(r.userOrgs.orgId) ?? [];
|
||||
list.push(r);
|
||||
orgIdToRoleRows.set(r.userOrgs.orgId, list);
|
||||
}
|
||||
|
||||
// For each OLM, ensure there's a client in each org the user is in
|
||||
for (const olm of userOlms) {
|
||||
for (const userRoleOrg of allUserOrgs) {
|
||||
const { userOrgs: userOrg, roles: role } = userRoleOrg;
|
||||
const orgId = userOrg.orgId;
|
||||
for (const orgId of orgIdToRoleRows.keys()) {
|
||||
const roleRowsForOrg = orgIdToRoleRows.get(orgId)!;
|
||||
const userOrg = roleRowsForOrg[0].userOrgs;
|
||||
|
||||
const [org] = await transaction
|
||||
.select()
|
||||
@@ -189,12 +207,13 @@ export async function calculateUserClientsForOrgs(
|
||||
const niceId = await getUniqueClientName(orgId);
|
||||
|
||||
const isOrgLicensed = await isLicensedOrSubscribed(
|
||||
userOrg.orgId
|
||||
userOrg.orgId,
|
||||
tierMatrix.deviceApprovals
|
||||
);
|
||||
const requireApproval =
|
||||
build !== "oss" &&
|
||||
isOrgLicensed &&
|
||||
role.requireDeviceApproval;
|
||||
roleRowsForOrg.some((r) => r.roles.requireDeviceApproval);
|
||||
|
||||
const newClientData: InferInsertModel<typeof clients> = {
|
||||
userId,
|
||||
|
||||
@@ -2,10 +2,15 @@ import { db, orgs } from "@server/db";
|
||||
import { cleanUpOldLogs as cleanUpOldAccessLogs } from "#dynamic/lib/logAccessAudit";
|
||||
import { cleanUpOldLogs as cleanUpOldActionLogs } from "#dynamic/middlewares/logActionAudit";
|
||||
import { cleanUpOldLogs as cleanUpOldRequestLogs } from "@server/routers/badger/logRequestAudit";
|
||||
import { cleanUpOldLogs as cleanUpOldConnectionLogs } from "#dynamic/routers/newt";
|
||||
import { gt, or } from "drizzle-orm";
|
||||
import { cleanUpOldFingerprintSnapshots } from "@server/routers/olm/fingerprintingUtils";
|
||||
import { build } from "@server/build";
|
||||
|
||||
export function initLogCleanupInterval() {
|
||||
if (build == "saas") { // skip log cleanup for saas builds
|
||||
return null;
|
||||
}
|
||||
return setInterval(
|
||||
async () => {
|
||||
const orgsToClean = await db
|
||||
@@ -16,14 +21,17 @@ export function initLogCleanupInterval() {
|
||||
settingsLogRetentionDaysAccess:
|
||||
orgs.settingsLogRetentionDaysAccess,
|
||||
settingsLogRetentionDaysRequest:
|
||||
orgs.settingsLogRetentionDaysRequest
|
||||
orgs.settingsLogRetentionDaysRequest,
|
||||
settingsLogRetentionDaysConnection:
|
||||
orgs.settingsLogRetentionDaysConnection
|
||||
})
|
||||
.from(orgs)
|
||||
.where(
|
||||
or(
|
||||
gt(orgs.settingsLogRetentionDaysAction, 0),
|
||||
gt(orgs.settingsLogRetentionDaysAccess, 0),
|
||||
gt(orgs.settingsLogRetentionDaysRequest, 0)
|
||||
gt(orgs.settingsLogRetentionDaysRequest, 0),
|
||||
gt(orgs.settingsLogRetentionDaysConnection, 0)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -33,7 +41,8 @@ export function initLogCleanupInterval() {
|
||||
orgId,
|
||||
settingsLogRetentionDaysAction,
|
||||
settingsLogRetentionDaysAccess,
|
||||
settingsLogRetentionDaysRequest
|
||||
settingsLogRetentionDaysRequest,
|
||||
settingsLogRetentionDaysConnection
|
||||
} = org;
|
||||
|
||||
if (settingsLogRetentionDaysAction > 0) {
|
||||
@@ -56,6 +65,13 @@ export function initLogCleanupInterval() {
|
||||
settingsLogRetentionDaysRequest
|
||||
);
|
||||
}
|
||||
|
||||
if (settingsLogRetentionDaysConnection > 0) {
|
||||
await cleanUpOldConnectionLogs(
|
||||
orgId,
|
||||
settingsLogRetentionDaysConnection
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await cleanUpOldFingerprintSnapshots(365);
|
||||
|
||||
20
server/lib/clientVersionChecks.ts
Normal file
20
server/lib/clientVersionChecks.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import semver from "semver";
|
||||
|
||||
export function canCompress(
|
||||
clientVersion: string | null | undefined,
|
||||
type: "newt" | "olm"
|
||||
): boolean {
|
||||
try {
|
||||
if (!clientVersion) return false;
|
||||
// check if it is a valid semver
|
||||
if (!semver.valid(clientVersion)) return false;
|
||||
if (type === "newt") {
|
||||
return semver.gte(clientVersion, "1.10.3");
|
||||
} else if (type === "olm") {
|
||||
return semver.gte(clientVersion, "1.4.3");
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user