mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-18 19:06:38 +00:00
Compare commits
878 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fa44981cf | ||
|
|
1bacad7854 | ||
|
|
b627e391ac | ||
|
|
40a3eac704 | ||
|
|
2d30b155f2 | ||
|
|
1333e21553 | ||
|
|
4c412528f5 | ||
|
|
a8fce47ba0 | ||
|
|
4447fb8202 | ||
|
|
1c9c4b1802 | ||
|
|
19e15f4ef5 | ||
|
|
c2c29e2cd2 | ||
|
|
7b33dc591d | ||
|
|
a95f2e76f4 | ||
|
|
979860a951 | ||
|
|
9e9a81d9e8 | ||
|
|
8f09561114 | ||
|
|
b167d94ead | ||
|
|
e4c0a157e3 | ||
|
|
956869ab58 | ||
|
|
e5f4da9a99 | ||
|
|
9649d9a46b | ||
|
|
22477b7e81 | ||
|
|
b6c76a2164 | ||
|
|
043834274d | ||
|
|
1e4ca69c89 | ||
|
|
ff2bcfb0e7 | ||
|
|
b47fc9f901 | ||
|
|
165f4023d0 | ||
|
|
d51053ce86 | ||
|
|
229872589c | ||
|
|
e4c47c46a6 | ||
|
|
84fe2fb92e | ||
|
|
076912c648 | ||
|
|
033653e234 | ||
|
|
0624087373 | ||
|
|
346d886f8a | ||
|
|
67ac01b31a | ||
|
|
87f1cf6730 | ||
|
|
0f46651500 | ||
|
|
65bf055e0f | ||
|
|
4c995f786b | ||
|
|
4853c8c872 | ||
|
|
170da08001 | ||
|
|
a39a133ee5 | ||
|
|
1251b1e870 | ||
|
|
418120196f | ||
|
|
759661420e | ||
|
|
bb28f856da | ||
|
|
f90e6bef9e | ||
|
|
cabaa2e6d6 | ||
|
|
c8bddd4289 | ||
|
|
71ba980757 | ||
|
|
38c3c49778 | ||
|
|
ab7ac9cb60 | ||
|
|
e4787924e7 | ||
|
|
3385a92b0f | ||
|
|
e73e6956a5 | ||
|
|
024eb2b157 | ||
|
|
ccff0592ca | ||
|
|
942f7c2bc9 | ||
|
|
b3a6cd0660 | ||
|
|
c5569fccf1 | ||
|
|
cc7c443145 | ||
|
|
8d7e5baf9d | ||
|
|
ed64d4b5ae | ||
|
|
8fe42bc6aa | ||
|
|
a67aa3852d | ||
|
|
c2c907852d | ||
|
|
3123f858bb | ||
|
|
6a18369891 | ||
|
|
0f4ef40600 | ||
|
|
42a7fb949a | ||
|
|
bbfa6e9c82 | ||
|
|
0d8ae0d615 | ||
|
|
7bbbc88c34 | ||
|
|
e2ad197d7e | ||
|
|
ca8f52d304 | ||
|
|
7395a64b26 | ||
|
|
4dd672a590 | ||
|
|
cff3f739db | ||
|
|
7fb35cfebb | ||
|
|
ddfda31924 | ||
|
|
353e085b0e | ||
|
|
989b548ef9 | ||
|
|
8f60e7e200 | ||
|
|
ec74525fde | ||
|
|
a317c50737 | ||
|
|
c62b46268a | ||
|
|
42ef075d4f | ||
|
|
f52605289b | ||
|
|
68e0911866 | ||
|
|
756fcbb590 | ||
|
|
a49d900951 | ||
|
|
38f212d632 | ||
|
|
204fdfd233 | ||
|
|
b5e04e8111 | ||
|
|
21fc829766 | ||
|
|
39851c3412 | ||
|
|
21811465b6 | ||
|
|
7574726815 | ||
|
|
236e0f9ab6 | ||
|
|
8e95f0b73f | ||
|
|
bed45a5fbd | ||
|
|
c50c2e2b01 | ||
|
|
adf982fcd6 | ||
|
|
9b4103be75 | ||
|
|
672eec0c33 | ||
|
|
0d8c06595e | ||
|
|
a5a7ca5fcc | ||
|
|
8767d20c47 | ||
|
|
4cbf3fffb1 | ||
|
|
51fad19d0d | ||
|
|
664aa6ed2a | ||
|
|
574cd2a754 | ||
|
|
1b34ee7369 | ||
|
|
7b2f1dd4c6 | ||
|
|
a97b6efe9c | ||
|
|
3722b67724 | ||
|
|
218a5ec9e4 | ||
|
|
90d3ac07a9 | ||
|
|
149a4b916b | ||
|
|
70914e836f | ||
|
|
a2dae8aa13 | ||
|
|
b6ea0808e4 | ||
|
|
089e43e1ce | ||
|
|
42936ab8dc | ||
|
|
411fa9345f | ||
|
|
336e118096 | ||
|
|
d1707801bf | ||
|
|
71bcf25718 | ||
|
|
288da0ef05 | ||
|
|
fec29eb349 | ||
|
|
032d48e394 | ||
|
|
a433d97573 | ||
|
|
6bd571f1b3 | ||
|
|
1dd89601ad | ||
|
|
a7cf359672 | ||
|
|
baa98952fa | ||
|
|
55afbf4db5 | ||
|
|
dca0fb327b | ||
|
|
e34a31941d | ||
|
|
dbba5002d9 | ||
|
|
4dd9e34a11 | ||
|
|
a30222a13e | ||
|
|
5797144083 | ||
|
|
db513b43e7 | ||
|
|
d387fa3bfb | ||
|
|
1bff9f550e | ||
|
|
0167b30bf1 | ||
|
|
bf993d04f1 | ||
|
|
be2b2c6c77 | ||
|
|
8851156f23 | ||
|
|
1a13694843 | ||
|
|
3872831bd7 | ||
|
|
ef4ce115ff | ||
|
|
516b300731 | ||
|
|
88d97dd49b | ||
|
|
be9494dd54 | ||
|
|
e43fc59634 | ||
|
|
4523a8df0f | ||
|
|
2c8082451f | ||
|
|
7ab498702c | ||
|
|
a06e8c8f83 | ||
|
|
1a01e8d53a | ||
|
|
5ce60cf1cd | ||
|
|
de1a6025d0 | ||
|
|
ca6ae53fe6 | ||
|
|
4eff52ab62 | ||
|
|
e5c5780547 | ||
|
|
f348c9daa7 | ||
|
|
e1dd29dd0b | ||
|
|
558f302342 | ||
|
|
5fee1c3ebd | ||
|
|
248debb7c4 | ||
|
|
8504fd8d9d | ||
|
|
e360a5323d | ||
|
|
1ad5eb010a | ||
|
|
ca7f1e5db8 | ||
|
|
2981e35c75 | ||
|
|
f3e8677ae4 | ||
|
|
d209c8af9d | ||
|
|
26b2233168 | ||
|
|
b2669aaa34 | ||
|
|
1438eef62b | ||
|
|
a92f7dbb7c | ||
|
|
4710bab697 | ||
|
|
52aa27025d | ||
|
|
8e544c056f | ||
|
|
3c2a8b9031 | ||
|
|
fff4883bca | ||
|
|
dc234beab1 | ||
|
|
66d310fcca | ||
|
|
702b5eb3dd | ||
|
|
06477b6e7f | ||
|
|
fc76899384 | ||
|
|
97102b9be9 | ||
|
|
1c0dfa830e | ||
|
|
53bfaac0c0 | ||
|
|
30790fdcb6 | ||
|
|
b8b256da2e | ||
|
|
0472dc1b25 | ||
|
|
1ec3e53e11 | ||
|
|
9f66e09e44 | ||
|
|
a71b0a8924 | ||
|
|
e555d3c496 | ||
|
|
df92e41384 | ||
|
|
d10fdac670 | ||
|
|
b63bffa524 | ||
|
|
957cfdd5d7 | ||
|
|
1352316492 | ||
|
|
73cd82081a | ||
|
|
812820472f | ||
|
|
21f0cd6e3f | ||
|
|
b2ee8ef7de | ||
|
|
1e066cbabd | ||
|
|
4cc38d44e0 | ||
|
|
dcf7393259 | ||
|
|
bab070b09c | ||
|
|
2bd4ad5770 | ||
|
|
61ecebf911 | ||
|
|
33c8663a5b | ||
|
|
76da2ee324 | ||
|
|
31896c9be9 | ||
|
|
f61d722aee | ||
|
|
1f9f3fdede | ||
|
|
a778109214 | ||
|
|
cb7fa9375b | ||
|
|
515ecb09e7 | ||
|
|
a12a620697 | ||
|
|
0c3b2bc2f5 | ||
|
|
78ba27dc63 | ||
|
|
dc20b863ed | ||
|
|
c9a211d5cf | ||
|
|
95f94cffd2 | ||
|
|
0da95cbdb8 | ||
|
|
dadd1e3101 | ||
|
|
d523ae3ffa | ||
|
|
9a41cac6e1 | ||
|
|
5d3c5ab7cc | ||
|
|
08c930e6cf | ||
|
|
e94ded920b | ||
|
|
c882fbd59a | ||
|
|
46b50a042e | ||
|
|
fda9e95786 | ||
|
|
ea1ad23bff | ||
|
|
7ffc5e0212 | ||
|
|
acba9444f4 | ||
|
|
f7e3671801 | ||
|
|
a1b2e36a5d | ||
|
|
44e96942b3 | ||
|
|
f2efa760ff | ||
|
|
256df9042b | ||
|
|
6d7091fb5c | ||
|
|
0d1f88a368 | ||
|
|
2ae601717d | ||
|
|
c7c8b463b4 | ||
|
|
282f839211 | ||
|
|
b2eb846b69 | ||
|
|
62cf925dcf | ||
|
|
e699f84c4d | ||
|
|
c1189dadc5 | ||
|
|
76bc080a6d | ||
|
|
7f989f77ac | ||
|
|
b916f768fe | ||
|
|
e4509c5714 | ||
|
|
ddb6893a64 | ||
|
|
248751ba1d | ||
|
|
b4b74ed53a | ||
|
|
76903cd67f | ||
|
|
e4f2eac703 | ||
|
|
3aa45007a7 | ||
|
|
f452892c88 | ||
|
|
a0fece8a0e | ||
|
|
e3d493209b | ||
|
|
2e8b63553d | ||
|
|
fb8f4b95b7 | ||
|
|
83e107c713 | ||
|
|
e97642a790 | ||
|
|
426d8684bf | ||
|
|
5e7409a4f0 | ||
|
|
c225a4cd48 | ||
|
|
24df9e1ce6 | ||
|
|
eab1fd3722 | ||
|
|
93bd041693 | ||
|
|
665ebe993c | ||
|
|
4086130371 | ||
|
|
29aacf5238 | ||
|
|
497e6a8422 | ||
|
|
af8572add9 | ||
|
|
d6aea96400 | ||
|
|
17e26ff1a6 | ||
|
|
f5f223348d | ||
|
|
e4f90fd7ea | ||
|
|
96dff20760 | ||
|
|
d639f7f6de | ||
|
|
5b35ec2ea2 | ||
|
|
bc78b95265 | ||
|
|
97f22eccbb | ||
|
|
a4fe86e38a | ||
|
|
4bc1e10ecb | ||
|
|
5b840d73bb | ||
|
|
afa9acfb1e | ||
|
|
7b7f65da39 | ||
|
|
806da59f47 | ||
|
|
9a009a4ea3 | ||
|
|
083d890053 | ||
|
|
e693a8aeb8 | ||
|
|
831b46d7b5 | ||
|
|
8dd3022b94 | ||
|
|
b278eb7110 | ||
|
|
7a66163216 | ||
|
|
dda2043401 | ||
|
|
08d6183c9b | ||
|
|
eea0b86d6d | ||
|
|
58c04fd196 | ||
|
|
09de6f6b5f | ||
|
|
d0bbd2b539 | ||
|
|
ee8952de10 | ||
|
|
134595a6b7 | ||
|
|
4ff46f1650 | ||
|
|
4779201d4c | ||
|
|
3a8643d83c | ||
|
|
806a49b822 | ||
|
|
95d74825ee | ||
|
|
e4960909ed | ||
|
|
6cb36aaf13 | ||
|
|
cb06e93650 | ||
|
|
e3a2f7a514 | ||
|
|
01b1e817d8 | ||
|
|
c3a5195575 | ||
|
|
99765c7bd5 | ||
|
|
8929f389f4 | ||
|
|
4141d91f1b | ||
|
|
90272c84d2 | ||
|
|
3006a8e58c | ||
|
|
52a9dbd45d | ||
|
|
b2fb55d2c1 | ||
|
|
a1c16d22d8 | ||
|
|
0e9504ee4d | ||
|
|
e8cad6fc20 | ||
|
|
bc261f7739 | ||
|
|
0b8983a86b | ||
|
|
5a61da3c53 | ||
|
|
800fe6244c | ||
|
|
9e1fec812c | ||
|
|
61632f9c97 | ||
|
|
f5e44129d8 | ||
|
|
3eaca924da | ||
|
|
da1c706334 | ||
|
|
3b726dfb1e | ||
|
|
d51e7f7e40 | ||
|
|
2551e0c291 | ||
|
|
2efd5c31ab | ||
|
|
1eacb8ff36 | ||
|
|
612446c3c9 | ||
|
|
e121e16ad9 | ||
|
|
23616b41be | ||
|
|
1778ba49b2 | ||
|
|
b6f2bd4703 | ||
|
|
5fd67224f6 | ||
|
|
c9d21dde0c | ||
|
|
de2c5aa068 | ||
|
|
ad01cecae6 | ||
|
|
75ef14c75b | ||
|
|
03a5a0eddb | ||
|
|
66befd35eb | ||
|
|
3cbad16c30 | ||
|
|
3bba7c5956 | ||
|
|
0daa84c583 | ||
|
|
92358a52c0 | ||
|
|
faf17e9e86 | ||
|
|
ef6efe94b4 | ||
|
|
819d7ea23e | ||
|
|
61ff192cfd | ||
|
|
ceb1b07ce2 | ||
|
|
90188d4358 | ||
|
|
35aa0ab4e7 | ||
|
|
14dd76db8b | ||
|
|
fb26dfad65 | ||
|
|
bedc5adb75 | ||
|
|
800b1f1520 | ||
|
|
a4571a80ae | ||
|
|
a0a612618e | ||
|
|
db94728a5b | ||
|
|
04352a670a | ||
|
|
fe6e3b013e | ||
|
|
a947a74194 | ||
|
|
06055ff62b | ||
|
|
45cb1562e5 | ||
|
|
2f89a16852 | ||
|
|
86956b8cac | ||
|
|
84fb3add33 | ||
|
|
56ee68d9f3 | ||
|
|
e81fd3bb31 | ||
|
|
938ca29777 | ||
|
|
122902968f | ||
|
|
b55c30065f | ||
|
|
92ac2dbac2 | ||
|
|
d3e6decef9 | ||
|
|
579cd9d338 | ||
|
|
9e2a58dd46 | ||
|
|
64722617c1 | ||
|
|
5845ddbdda | ||
|
|
bf9ce0df9b | ||
|
|
8aee2ec3a1 | ||
|
|
3292eafe4a | ||
|
|
9ad31b2c81 | ||
|
|
374ed79a18 | ||
|
|
3d5f73e344 | ||
|
|
6761428a96 | ||
|
|
0a9b463eaa | ||
|
|
c219256fff | ||
|
|
7e48803dc5 | ||
|
|
d496b8a414 | ||
|
|
4825129560 | ||
|
|
adc54b2582 | ||
|
|
863567c9b6 | ||
|
|
102555023b | ||
|
|
f1a9eef531 | ||
|
|
5f007a5b0f | ||
|
|
9455141262 | ||
|
|
37e1379c88 | ||
|
|
55d597e519 | ||
|
|
da5ee5c951 | ||
|
|
b0bd9279fc | ||
|
|
90456339ca | ||
|
|
a653c8bad7 | ||
|
|
c4fa6cf458 | ||
|
|
268fc7b923 | ||
|
|
02604f5290 | ||
|
|
1dad7e86a0 | ||
|
|
838e3efbca | ||
|
|
3e353717f5 | ||
|
|
0a4b74b91a | ||
|
|
e69fbf3ccf | ||
|
|
0b2349d6bf | ||
|
|
3a8f04cf14 | ||
|
|
e941cf956f | ||
|
|
29f7bcf6f5 | ||
|
|
1cf1e0dc57 | ||
|
|
175283805e | ||
|
|
063c0405e8 | ||
|
|
947cb77753 | ||
|
|
28b3b305ea | ||
|
|
df85f13aea | ||
|
|
042e2c1390 | ||
|
|
e6314bee35 | ||
|
|
4292d3262e | ||
|
|
35d070ad29 | ||
|
|
cd79e77576 | ||
|
|
1f1c20d637 | ||
|
|
e87b3b1b54 | ||
|
|
a6f7b65625 | ||
|
|
722fa47132 | ||
|
|
f83e290b4c | ||
|
|
11b4047283 | ||
|
|
69b2032a86 | ||
|
|
636298569f | ||
|
|
ed8a282d35 | ||
|
|
3bd5e850e0 | ||
|
|
070f1f9159 | ||
|
|
195644cca5 | ||
|
|
8092c86ecd | ||
|
|
28f33702da | ||
|
|
570632b8be | ||
|
|
f2881e1b31 | ||
|
|
dad35e37ef | ||
|
|
39afabd60e | ||
|
|
dc7e14a34b | ||
|
|
1dca71a779 | ||
|
|
e9494efa8e | ||
|
|
8159a0f13d | ||
|
|
ee9101e738 | ||
|
|
b670e6e3dc | ||
|
|
5e5754fa62 | ||
|
|
5fcf76066f | ||
|
|
601645fa72 | ||
|
|
12765ad675 | ||
|
|
ad3383d23d | ||
|
|
7d5961cf50 | ||
|
|
864aa052f1 | ||
|
|
be16196058 | ||
|
|
8a62f12e8b | ||
|
|
78f464f6ca | ||
|
|
f37eda4739 | ||
|
|
4e106e9e5a | ||
|
|
ccf8e5e6f4 | ||
|
|
9455adf61f | ||
|
|
970ab9818a | ||
|
|
7848cf7141 | ||
|
|
8e5aa9c195 | ||
|
|
a03e9ba7dd | ||
|
|
9e646ba385 | ||
|
|
d9a4f20fe6 | ||
|
|
e659f0e75d | ||
|
|
8891d6239f | ||
|
|
e3bd3fb985 | ||
|
|
54764dfacd | ||
|
|
b156b5ff2d | ||
|
|
d8e547c9a0 | ||
|
|
a0b93377a4 | ||
|
|
e8a6efd079 | ||
|
|
18bb6caf8f | ||
|
|
bc335d15c0 | ||
|
|
2a0d440a34 | ||
|
|
b0500fac29 | ||
|
|
af16e6423a | ||
|
|
ff90471f0f | ||
|
|
bcc501c524 | ||
|
|
c4ef211a3e | ||
|
|
592c0eb7ab | ||
|
|
880a000865 | ||
|
|
a9571f6adf | ||
|
|
ca91f313bc | ||
|
|
76b9753916 | ||
|
|
cd8bbe28bf | ||
|
|
9550c11594 | ||
|
|
7ac21cad25 | ||
|
|
2008a3955a | ||
|
|
f1641c9f3e | ||
|
|
bec5bbd033 | ||
|
|
ae11f72e28 | ||
|
|
6b88cb3920 | ||
|
|
38772111e8 | ||
|
|
14f50c3e66 | ||
|
|
b89a2c9e49 | ||
|
|
2d2eda988c | ||
|
|
432033969b | ||
|
|
1fbf74e1f7 | ||
|
|
93648ff00b | ||
|
|
9513136610 | ||
|
|
43a2a39f8d | ||
|
|
218351de9a | ||
|
|
b92b922eee | ||
|
|
91be4937ee | ||
|
|
19b36a5fae | ||
|
|
bb9ee7dfd2 | ||
|
|
ac0351b525 | ||
|
|
405f5ad7cc | ||
|
|
8cc2712da3 | ||
|
|
c02ac8d1bf | ||
|
|
a1802add19 | ||
|
|
218a6642a2 | ||
|
|
21a83a5755 | ||
|
|
bec75e51f6 | ||
|
|
6c9b445be6 | ||
|
|
06b17fa941 | ||
|
|
e1d4c029e7 | ||
|
|
293fd70ccb | ||
|
|
4ee863db5a | ||
|
|
2717be0fed | ||
|
|
1f312e146f | ||
|
|
b91557ebb0 | ||
|
|
465380b5a3 | ||
|
|
60af901feb | ||
|
|
ea78a654ff | ||
|
|
f28b6ad0a5 | ||
|
|
a3bdab1318 | ||
|
|
f8c5d01e3c | ||
|
|
3ebe218b7f | ||
|
|
7d039ab729 | ||
|
|
b2b6c8c268 | ||
|
|
4950f25063 | ||
|
|
524d6b48d9 | ||
|
|
29fb5735e2 | ||
|
|
247fc85440 | ||
|
|
2b4302572c | ||
|
|
9b28780e62 | ||
|
|
8656f68008 | ||
|
|
15651b6919 | ||
|
|
78d3861382 | ||
|
|
72f19274cd | ||
|
|
adbcd1a2e0 | ||
|
|
5b7727fab4 | ||
|
|
9627dfa90c | ||
|
|
50022c9fc8 | ||
|
|
e0b76ffebc | ||
|
|
be5a9a840c | ||
|
|
6e5f429e0a | ||
|
|
e9d9d6e2f4 | ||
|
|
b4a57e630c | ||
|
|
1062e33dc8 | ||
|
|
0e14441f73 | ||
|
|
a6a909ae4f | ||
|
|
2b4a39e64c | ||
|
|
82b4921602 | ||
|
|
4229324a5d | ||
|
|
34d3ca9c51 | ||
|
|
9bd7002917 | ||
|
|
ebed9f7a68 | ||
|
|
5d34bd82c0 | ||
|
|
8bcb2b3b0f | ||
|
|
32ba17cf91 | ||
|
|
704ded4410 | ||
|
|
88277976c6 | ||
|
|
cb95f02912 | ||
|
|
928b406359 | ||
|
|
4757c7db8c | ||
|
|
5df87641a1 | ||
|
|
04077c53fd | ||
|
|
574be52b84 | ||
|
|
a66613c5ca | ||
|
|
01b3b19715 | ||
|
|
fb1481c69c | ||
|
|
9557f755a5 | ||
|
|
60d8831399 | ||
|
|
5ff5660db3 | ||
|
|
d62c359452 | ||
|
|
ec0b6b64fe | ||
|
|
c53eac76f8 | ||
|
|
49cb2ae260 | ||
|
|
77796e8a75 | ||
|
|
49f0f6ec7d | ||
|
|
2c273a85d8 | ||
|
|
8273554a1c | ||
|
|
ad8ab63fd5 | ||
|
|
7de0761329 | ||
|
|
907dab7d05 | ||
|
|
2907f22200 | ||
|
|
7bbe1b2dbe | ||
|
|
099513072c | ||
|
|
7de8bb00e7 | ||
|
|
12d44696e8 | ||
|
|
25cef26251 | ||
|
|
dceb398695 | ||
|
|
f60599abd3 | ||
|
|
44f8098e4a | ||
|
|
747979f939 | ||
|
|
b3083ae779 | ||
|
|
67580a8b69 | ||
|
|
291c7aaf0b | ||
|
|
1a098eecf6 | ||
|
|
0a05bdba1d | ||
|
|
37bfc07ffb | ||
|
|
eae3ab2dc1 | ||
|
|
1665bf6515 | ||
|
|
0383ffb7f3 | ||
|
|
a0d6646e49 | ||
|
|
254b3a0fc8 | ||
|
|
21743e5a23 | ||
|
|
0550924e08 | ||
|
|
7867302be5 | ||
|
|
14815b388d | ||
|
|
92cc82220e | ||
|
|
da1fae6016 | ||
|
|
34002470a5 | ||
|
|
49f84bccad | ||
|
|
4bcb4a1590 | ||
|
|
378de19f41 | ||
|
|
ffe2512734 | ||
|
|
b4be620a5b | ||
|
|
ac8b546393 | ||
|
|
9bdf31ee97 | ||
|
|
c29cd05db8 | ||
|
|
cd34820138 | ||
|
|
d207318494 | ||
|
|
117062f1d1 | ||
|
|
9d561ba94d | ||
|
|
97fcaed9b4 | ||
|
|
5e53ea3607 | ||
|
|
7dc74cb61b | ||
|
|
fbefcfedb9 | ||
|
|
36c0d9aba2 | ||
|
|
8c8a981452 | ||
|
|
7dd586e31d | ||
|
|
366a31b41b | ||
|
|
f09557d73c | ||
|
|
33a2ac402c | ||
|
|
632333c49f | ||
|
|
c8bea4d7de | ||
|
|
c1d75d32c2 | ||
|
|
b805daec51 | ||
|
|
af2088df4e | ||
|
|
3b8d1f40a7 | ||
|
|
8355d3664e | ||
|
|
83a696f743 | ||
|
|
7ca507b1ce | ||
|
|
609435328e | ||
|
|
d771317e3f | ||
|
|
d548563e65 | ||
|
|
f07cd8aee3 | ||
|
|
48963f24df | ||
|
|
7bf98c0c40 | ||
|
|
e73383cc79 | ||
|
|
79ce93d578 | ||
|
|
200a7fcd40 | ||
|
|
e043d0e654 | ||
|
|
21ce678e5b | ||
|
|
5c94887949 | ||
|
|
69a9bcb3da | ||
|
|
2fea091e1f | ||
|
|
24314a103f | ||
|
|
b56db41d0b | ||
|
|
825bff5d60 | ||
|
|
f9184cf489 | ||
|
|
5c04b1e14a | ||
|
|
2c96eb7851 | ||
|
|
67ba225003 | ||
|
|
04ecf41c5a | ||
|
|
6600de7320 | ||
|
|
f7b82f0a7a | ||
|
|
65bdb232f4 | ||
|
|
200e3af384 | ||
|
|
aabfa91f80 | ||
|
|
e5468a7391 | ||
|
|
d5a11edd0c | ||
|
|
fcc86b07ba | ||
|
|
74d2527af5 | ||
|
|
50cf284273 | ||
|
|
aaddde0a9b | ||
|
|
ac87345b7a | ||
|
|
23079d9ac0 | ||
|
|
b573d63648 | ||
|
|
34d705a54e | ||
|
|
eeb1d4954d | ||
|
|
b638adedff | ||
|
|
285e24cdc7 | ||
|
|
396e643b06 | ||
|
|
dc50190dc3 | ||
|
|
2c8bf4f18c | ||
|
|
1f6379a7e6 | ||
|
|
ddd8eb1da0 | ||
|
|
4c463de45f | ||
|
|
1f4a7a7f6f | ||
|
|
e7df29104e | ||
|
|
9987b35b60 | ||
|
|
16e876ab68 | ||
|
|
50fc2fc74e | ||
|
|
c244dc9c0c | ||
|
|
0f50981573 | ||
|
|
0c1cb20936 | ||
|
|
192617a884 | ||
|
|
297991ef5f | ||
|
|
75f97c4a31 | ||
|
|
40f520086c | ||
|
|
c8dda4f90d | ||
|
|
5f09f97032 | ||
|
|
168056d595 | ||
|
|
c70eaa0096 | ||
|
|
5f36b13408 | ||
|
|
9dc73efa3a | ||
|
|
e9c2868998 | ||
|
|
0a13b04c55 | ||
|
|
cf12d3ee56 | ||
|
|
cea7190453 | ||
|
|
c6d78680fb | ||
|
|
0bf302e013 | ||
|
|
1351fb6689 | ||
|
|
af638d666c | ||
|
|
e4fe601d9d | ||
|
|
4f3cd71e1e | ||
|
|
9c0295db9f | ||
|
|
3fc2d1df80 | ||
|
|
4a6747dcc7 | ||
|
|
54b3c92953 | ||
|
|
a4d460e850 | ||
|
|
3d8869066a | ||
|
|
880a123149 | ||
|
|
39e35bc1d6 | ||
|
|
f219f1e36b | ||
|
|
25ed3d65f8 | ||
|
|
30dbabd73d | ||
|
|
ea2e5bf486 | ||
|
|
ae52fcc757 | ||
|
|
b6c2f123e8 | ||
|
|
15f900317a | ||
|
|
22545cac8b | ||
|
|
03c8d82471 | ||
|
|
14d7a138a5 | ||
|
|
a829eb949b | ||
|
|
fd605d9c81 | ||
|
|
55b4a9eddb | ||
|
|
9ccf77b99c | ||
|
|
ea27075bab | ||
|
|
c3723d0fce | ||
|
|
0edb3cd316 | ||
|
|
e9e6b0bc4f | ||
|
|
4701da201d | ||
|
|
d6d2e052dd | ||
|
|
d3d1dcfe1d | ||
|
|
918ebf5e65 | ||
|
|
67184b88a8 | ||
|
|
fb0f4c3939 | ||
|
|
aae5343543 | ||
|
|
51e9762ca8 | ||
|
|
330dafc652 | ||
|
|
7ddf9fa54e | ||
|
|
f2ca09eedd | ||
|
|
f0e2c8416d | ||
|
|
338b7a8c13 | ||
|
|
b4284f82f3 | ||
|
|
75cec731e8 | ||
|
|
0ce430cab5 | ||
|
|
95c0f6c093 | ||
|
|
387dbc360e | ||
|
|
a88be89c2f | ||
|
|
8bc353442f | ||
|
|
b3502bd627 | ||
|
|
56da7c242d | ||
|
|
fa8f49e87d | ||
|
|
6e08a70afc | ||
|
|
bd4be2b05c | ||
|
|
6b6ff0a95e | ||
|
|
4755cae5cb | ||
|
|
b2384ccc06 | ||
|
|
e6589308dd | ||
|
|
16a88281bb | ||
|
|
1574cbc5df | ||
|
|
2cb2a115b0 | ||
|
|
9dce7b2cde | ||
|
|
879b25be9f | ||
|
|
d3ad941b30 | ||
|
|
f077fbc3f5 | ||
|
|
4679ce968b | ||
|
|
101e462649 | ||
|
|
5d93ab9b9e | ||
|
|
d557832509 | ||
|
|
fe5c91db29 | ||
|
|
b2947193ec | ||
|
|
f6440753b6 | ||
|
|
17cf903804 | ||
|
|
dcf530d237 | ||
|
|
6b1808dab1 | ||
|
|
5889efd74a | ||
|
|
1a9de1e5c5 | ||
|
|
d1404a2b07 | ||
|
|
664dbf3f4c | ||
|
|
f32a8e26b6 | ||
|
|
b1a92fd4e0 | ||
|
|
1ea9fd2d49 | ||
|
|
f31e4e3176 | ||
|
|
e3287a7e9f | ||
|
|
ec21153d4b | ||
|
|
917e7a8c1d | ||
|
|
8e0a8dc272 | ||
|
|
91bac29ea3 | ||
|
|
3e333769bb | ||
|
|
b4bde6660a | ||
|
|
917f752081 | ||
|
|
915d561286 | ||
|
|
01ef809fd3 | ||
|
|
19902092ce | ||
|
|
39603b6e53 | ||
|
|
9c85a09d3e | ||
|
|
69baa6785f | ||
|
|
bb84d01e14 | ||
|
|
616dae2d8b | ||
|
|
3fbfe50e09 | ||
|
|
c0c8edb9d1 | ||
|
|
84268e484d | ||
|
|
c473c2fa81 | ||
|
|
7402590f49 | ||
|
|
529d1c9f66 | ||
|
|
e85b772ca5 | ||
|
|
f75169fc26 | ||
|
|
07b86521a5 | ||
|
|
961008bbe1 | ||
|
|
6d359b6bb9 | ||
|
|
ea6f803e78 | ||
|
|
0151f8a6a9 | ||
|
|
39c5101957 | ||
|
|
9b1cd5f79c | ||
|
|
36d0b83ed3 | ||
|
|
f0138fad4f | ||
|
|
69802e78f8 | ||
|
|
92e69f561f | ||
|
|
b351520e92 | ||
|
|
481714f095 | ||
|
|
d38656e026 | ||
|
|
27ac204bb6 | ||
|
|
a2526ea244 | ||
|
|
39c43c0c09 | ||
|
|
350485612e | ||
|
|
df31c13912 | ||
|
|
2259879595 | ||
|
|
4f5091ed7f | ||
|
|
b5afd73024 |
@@ -28,3 +28,5 @@ LICENSE
|
|||||||
CONTRIBUTING.md
|
CONTRIBUTING.md
|
||||||
dist
|
dist
|
||||||
.git
|
.git
|
||||||
|
migrations/
|
||||||
|
config/
|
||||||
47
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
47
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Summary
|
||||||
|
description: A clear and concise summary of the requested feature.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Motivation
|
||||||
|
description: |
|
||||||
|
Why is this feature important?
|
||||||
|
Explain the problem this feature would solve or what use case it would enable.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Proposed Solution
|
||||||
|
description: |
|
||||||
|
How would you like to see this feature implemented?
|
||||||
|
Provide as much detail as possible about the desired behavior, configuration, or changes.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Alternatives Considered
|
||||||
|
description: Describe any alternative solutions or workarounds you've thought about.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context, mockups, or screenshots about the feature request here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before submitting, please:
|
||||||
|
- Check if there is an existing issue for this feature.
|
||||||
|
- Clearly explain the benefit and use case.
|
||||||
|
- Be as specific as possible to help contributors evaluate and implement.
|
||||||
51
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
Normal file
51
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Create a bug report
|
||||||
|
labels: []
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the Bug
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: Please fill out the relevant details below for your environment.
|
||||||
|
value: |
|
||||||
|
- OS Type & Version: (e.g., Ubuntu 22.04)
|
||||||
|
- Pangolin Version:
|
||||||
|
- Gerbil Version:
|
||||||
|
- Traefik Version:
|
||||||
|
- Newt Version:
|
||||||
|
- Olm Version: (if applicable)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: To Reproduce
|
||||||
|
description: |
|
||||||
|
Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below.
|
||||||
|
|
||||||
|
If using code blocks, make sure syntax highlighting is correct and double-check that the rendered preview is not broken.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Need help or have questions?
|
||||||
|
url: https://github.com/orgs/fosrl/discussions
|
||||||
|
about: Ask questions, get help, and discuss with other community members
|
||||||
|
- name: Request a Feature
|
||||||
|
url: https://github.com/orgs/fosrl/discussions/new?category=feature-requests
|
||||||
|
about: Feature requests should be opened as discussions so others can upvote and comment
|
||||||
22
.github/dependabot.yml
vendored
22
.github/dependabot.yml
vendored
@@ -38,3 +38,25 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/install"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
groups:
|
||||||
|
dev-patch-updates:
|
||||||
|
dependency-type: "development"
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
dev-minor-updates:
|
||||||
|
dependency-type: "development"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
prod-patch-updates:
|
||||||
|
dependency-type: "production"
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
prod-minor-updates:
|
||||||
|
dependency-type: "production"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
8
.github/workflows/cicd.yml
vendored
8
.github/workflows/cicd.yml
vendored
@@ -3,7 +3,7 @@ name: CI/CD Pipeline
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "*"
|
- "[0-9]+.[0-9]+.[0-9]+"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -28,9 +28,9 @@ jobs:
|
|||||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.23.0
|
go-version: 1.24
|
||||||
|
|
||||||
- name: Update version in package.json
|
- name: Update version in package.json
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
15
.github/workflows/linting.yml
vendored
15
.github/workflows/linting.yml
vendored
@@ -18,17 +18,18 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: npm ci
|
||||||
npm ci
|
|
||||||
|
- name: Create build file
|
||||||
|
run: npm run set:oss
|
||||||
|
|
||||||
- name: Run ESLint
|
- name: Run ESLint
|
||||||
run: |
|
run: npx eslint . --ext .js,.jsx,.ts,.tsx
|
||||||
npx eslint . --ext .js,.jsx,.ts,.tsx
|
|
||||||
2
.github/workflows/stale-bot.yml
vendored
2
.github/workflows/stale-bot.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
days-before-stale: 14
|
days-before-stale: 14
|
||||||
days-before-close: 14
|
days-before-close: 14
|
||||||
|
|||||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -11,11 +11,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Copy config file
|
- name: Copy config file
|
||||||
run: cp config/config.example.yml config/config.yml
|
run: cp config/config.example.yml config/config.yml
|
||||||
@@ -24,7 +24,10 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Create database index.ts
|
- name: Create database index.ts
|
||||||
run: echo 'export * from "./sqlite";' > server/db/index.ts
|
run: npm run set:sqlite
|
||||||
|
|
||||||
|
- name: Create build file
|
||||||
|
run: npm run set:oss
|
||||||
|
|
||||||
- name: Generate database migrations
|
- name: Generate database migrations
|
||||||
run: npm run db:sqlite:generate
|
run: npm run db:sqlite:generate
|
||||||
@@ -32,6 +35,9 @@ jobs:
|
|||||||
- name: Apply database migrations
|
- name: Apply database migrations
|
||||||
run: npm run db:sqlite:push
|
run: npm run db:sqlite:push
|
||||||
|
|
||||||
|
- name: Test with tsc
|
||||||
|
run: npx tsc --noEmit
|
||||||
|
|
||||||
- name: Start app in background
|
- name: Start app in background
|
||||||
run: nohup npm run dev &
|
run: nohup npm run dev &
|
||||||
|
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -26,6 +26,14 @@ next-env.d.ts
|
|||||||
migrations
|
migrations
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
config/config.yml
|
config/config.yml
|
||||||
|
config/config.saas.yml
|
||||||
|
config/config.oss.yml
|
||||||
|
config/config.enterprise.yml
|
||||||
|
config/privateConfig.yml
|
||||||
|
config/postgres
|
||||||
|
config/postgres*
|
||||||
|
config/openapi.yaml
|
||||||
|
config/key
|
||||||
dist
|
dist
|
||||||
.dist
|
.dist
|
||||||
installer
|
installer
|
||||||
@@ -34,4 +42,9 @@ bin
|
|||||||
.secrets
|
.secrets
|
||||||
test_event.json
|
test_event.json
|
||||||
.idea/
|
.idea/
|
||||||
|
public/branding
|
||||||
server/db/index.ts
|
server/db/index.ts
|
||||||
|
server/build.ts
|
||||||
|
postgres/
|
||||||
|
dynamic/
|
||||||
|
*.mmdb
|
||||||
@@ -4,7 +4,7 @@ Contributions are welcome!
|
|||||||
|
|
||||||
Please see the contribution and local development guide on the docs page before getting started:
|
Please see the contribution and local development guide on the docs page before getting started:
|
||||||
|
|
||||||
https://docs.fossorial.io/development
|
https://docs.digpangolin.com/development/contributing
|
||||||
|
|
||||||
### Licensing Considerations
|
### Licensing Considerations
|
||||||
|
|
||||||
@@ -17,4 +17,4 @@ By creating this pull request, I grant the project maintainers an unlimited,
|
|||||||
perpetual license to use, modify, and redistribute these contributions under any terms they
|
perpetual license to use, modify, and redistribute these contributions under any terms they
|
||||||
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
|
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
|
||||||
represent that I have the right to grant this license for all contributed content.
|
represent that I have the right to grant this license for all contributed content.
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,29 +1,35 @@
|
|||||||
FROM node:20-alpine AS builder
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
ARG BUILD=oss
|
||||||
|
ARG DATABASE=sqlite
|
||||||
|
|
||||||
# COPY package.json package-lock.json ./
|
# COPY package.json package-lock.json ./
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN echo 'export * from "./pg";' > server/db/index.ts
|
RUN echo "export * from \"./$DATABASE\";" > server/db/index.ts
|
||||||
|
|
||||||
RUN npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema.ts --out init
|
RUN echo "export const build = \"$BUILD\" as any;" > server/build.ts
|
||||||
|
|
||||||
RUN npm run build:pg
|
RUN if [ "$DATABASE" = "pg" ]; then npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema.ts --out init; else npx drizzle-kit generate --dialect $DATABASE --schema ./server/db/$DATABASE/schema.ts --out init; fi
|
||||||
|
|
||||||
|
RUN npm run build:$DATABASE
|
||||||
RUN npm run build:cli
|
RUN npm run build:cli
|
||||||
|
|
||||||
FROM node:20-alpine AS runner
|
FROM node:22-alpine AS runner
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Curl used for the health checks
|
# Curl used for the health checks
|
||||||
RUN apk add --no-cache curl
|
RUN apk add --no-cache curl tzdata
|
||||||
|
|
||||||
# COPY package.json package-lock.json ./
|
# COPY package.json package-lock.json ./
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
RUN npm ci --omit=dev && npm cache clean --force
|
RUN npm ci --omit=dev && npm cache clean --force
|
||||||
|
|
||||||
COPY --from=builder /app/.next/standalone ./
|
COPY --from=builder /app/.next/standalone ./
|
||||||
@@ -35,7 +41,6 @@ COPY ./cli/wrapper.sh /usr/local/bin/pangctl
|
|||||||
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs
|
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs
|
||||||
|
|
||||||
COPY server/db/names.json ./dist/names.json
|
COPY server/db/names.json ./dist/names.json
|
||||||
|
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
|
|
||||||
CMD ["npm", "run", "start:pg"]
|
CMD ["npm", "run", "start"]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:20-alpine
|
FROM node:22-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
FROM node:20-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# COPY package.json package-lock.json ./
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm ci
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN echo 'export * from "./sqlite";' > server/db/index.ts
|
|
||||||
|
|
||||||
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/sqlite/schema.ts --out init
|
|
||||||
|
|
||||||
RUN npm run build:sqlite
|
|
||||||
RUN npm run build:cli
|
|
||||||
|
|
||||||
FROM node:20-alpine AS runner
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Curl used for the health checks
|
|
||||||
RUN apk add --no-cache curl
|
|
||||||
|
|
||||||
# COPY package.json package-lock.json ./
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm ci --omit=dev && npm cache clean --force
|
|
||||||
|
|
||||||
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 ./cli/wrapper.sh /usr/local/bin/pangctl
|
|
||||||
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs
|
|
||||||
|
|
||||||
COPY server/db/names.json ./dist/names.json
|
|
||||||
|
|
||||||
COPY public ./public
|
|
||||||
|
|
||||||
CMD ["npm", "run", "start:sqlite"]
|
|
||||||
31
LICENSE
31
LICENSE
@@ -1,3 +1,34 @@
|
|||||||
|
Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
|
||||||
|
Portions of this software are licensed as follows:
|
||||||
|
|
||||||
|
* All files that include a header specifying they are licensed under the
|
||||||
|
"Fossorial Commercial License" are governed by the Fossorial Commercial
|
||||||
|
License terms. The specific terms applicable to each customer depend on the
|
||||||
|
commercial license tier agreed upon in writing with Fossorial, Inc.
|
||||||
|
Unauthorized use, copying, modification, or distribution is strictly
|
||||||
|
prohibited.
|
||||||
|
|
||||||
|
* All files that include a header specifying they are licensed under the GNU
|
||||||
|
Affero General Public License, Version 3 ("AGPL-3"), are governed by the
|
||||||
|
AGPL-3 terms. A full copy of the AGPL-3 license is provided below. However,
|
||||||
|
these files are also available under the Fossorial Commercial License if a
|
||||||
|
separate commercial license agreement has been executed between the customer
|
||||||
|
and Fossorial, Inc.
|
||||||
|
|
||||||
|
* All files without a license header are, by default, licensed under the GNU
|
||||||
|
Affero General Public License, Version 3 (AGPL-3). These files may also be
|
||||||
|
made available under the Fossorial Commercial License upon agreement with
|
||||||
|
Fossorial, Inc.
|
||||||
|
|
||||||
|
* All third-party components included in this repository are licensed under
|
||||||
|
their respective original licenses, as provided by their authors.
|
||||||
|
|
||||||
|
Please consult the header of each individual file to determine the applicable
|
||||||
|
license. For AGPL-3 licensed files, dual-licensing under the Fossorial
|
||||||
|
Commercial License is available subject to written agreement with Fossorial,
|
||||||
|
Inc.
|
||||||
|
|
||||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
Version 3, 19 November 2007
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
|||||||
26
Makefile
26
Makefile
@@ -1,14 +1,28 @@
|
|||||||
.PHONY: build build-pg build-release build-arm build-x86 test clean
|
.PHONY: build build-pg build-release build-arm build-x86 test clean
|
||||||
|
|
||||||
|
major_tag := $(shell echo $(tag) | cut -d. -f1)
|
||||||
|
minor_tag := $(shell echo $(tag) | cut -d. -f1,2)
|
||||||
build-release:
|
build-release:
|
||||||
@if [ -z "$(tag)" ]; then \
|
@if [ -z "$(tag)" ]; then \
|
||||||
echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
|
echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest -f Dockerfile.sqlite --push .
|
docker buildx build \
|
||||||
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(tag) -f Dockerfile.sqlite --push .
|
--build-arg DATABASE=sqlite \
|
||||||
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-latest -f Dockerfile.pg --push .
|
--platform linux/arm64,linux/amd64 \
|
||||||
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(tag) -f Dockerfile.pg --push .
|
--tag fosrl/pangolin:latest \
|
||||||
|
--tag fosrl/pangolin:$(major_tag) \
|
||||||
|
--tag fosrl/pangolin:$(minor_tag) \
|
||||||
|
--tag fosrl/pangolin:$(tag) \
|
||||||
|
--push .
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg DATABASE=pg \
|
||||||
|
--platform linux/arm64,linux/amd64 \
|
||||||
|
--tag fosrl/pangolin:postgresql-latest \
|
||||||
|
--tag fosrl/pangolin:postgresql-$(major_tag) \
|
||||||
|
--tag fosrl/pangolin:postgresql-$(minor_tag) \
|
||||||
|
--tag fosrl/pangolin:postgresql-$(tag) \
|
||||||
|
--push .
|
||||||
|
|
||||||
build-arm:
|
build-arm:
|
||||||
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .
|
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .
|
||||||
@@ -17,10 +31,10 @@ build-x86:
|
|||||||
docker buildx build --platform linux/amd64 -t fosrl/pangolin:latest .
|
docker buildx build --platform linux/amd64 -t fosrl/pangolin:latest .
|
||||||
|
|
||||||
build-sqlite:
|
build-sqlite:
|
||||||
docker build -t fosrl/pangolin:latest -f Dockerfile.sqlite .
|
docker build --build-arg DATABASE=sqlite -t fosrl/pangolin:latest .
|
||||||
|
|
||||||
build-pg:
|
build-pg:
|
||||||
docker build -t fosrl/pangolin:postgresql-latest -f Dockerfile.pg .
|
docker build --build-arg DATABASE=pg -t fosrl/pangolin:postgresql-latest .
|
||||||
|
|
||||||
test:
|
test:
|
||||||
docker run -it -p 3000:3000 -p 3001:3001 -p 3002:3002 -v ./config:/app/config fosrl/pangolin:latest
|
docker run -it -p 3000:3000 -p 3001:3001 -p 3002:3002 -v ./config:/app/config fosrl/pangolin:latest
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -20,19 +20,28 @@ _Pangolin tunnels your services to the internet so you can access anything from
|
|||||||
Website
|
Website
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a href="https://docs.fossorial.io/Getting%20Started/quick-install">
|
<a href="https://docs.digpangolin.com/self-host/quick-install-managed">
|
||||||
Install Guide
|
Quick Install Guide
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a href="mailto:numbat@fossorial.io">
|
<a href="mailto:contact@fossorial.io">
|
||||||
Contact Us
|
Contact Us
|
||||||
</a>
|
</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://digpangolin.com/slack">
|
||||||
|
Slack
|
||||||
|
</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://discord.gg/HCJR8Xhme4">
|
||||||
|
Discord
|
||||||
|
</a>
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
|
[](https://digpangolin.com/slack)
|
||||||
[](https://hub.docker.com/r/fosrl/pangolin)
|
[](https://hub.docker.com/r/fosrl/pangolin)
|
||||||

|

|
||||||
[](https://discord.gg/HCJR8Xhme4)
|
[](https://discord.gg/HCJR8Xhme4)
|
||||||
[](https://www.youtube.com/@fossorial-app)
|
[](https://www.youtube.com/@fossorial-app)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -104,7 +113,7 @@ Pangolin is a self-hosted tunneled reverse proxy server with identity and access
|
|||||||
|
|
||||||
### Fully Self Hosted
|
### Fully Self Hosted
|
||||||
|
|
||||||
Host the full application on your own server or on the cloud with a VPS. Take a look at the [documentation](https://docs.fossorial.io/Getting%20Started/quick-install) to get started.
|
Host the full application on your own server or on the cloud with a VPS. Take a look at the [documentation](https://docs.digpangolin.com/self-host/quick-install) to get started.
|
||||||
|
|
||||||
> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can get a [**VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**](https://my.racknerd.com/aff.php?aff=13788&pid=912). That's a great deal!
|
> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can get a [**VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**](https://my.racknerd.com/aff.php?aff=13788&pid=912). That's a great deal!
|
||||||
|
|
||||||
@@ -114,7 +123,7 @@ Easy to use with simple [pay as you go pricing](https://digpangolin.com/pricing)
|
|||||||
|
|
||||||
- Everything you get with self hosted Pangolin, but fully managed for you.
|
- Everything you get with self hosted Pangolin, but fully managed for you.
|
||||||
|
|
||||||
### Hybrid & High Availability
|
### Managed & High Availability
|
||||||
|
|
||||||
Managed control plane, your infrastructure
|
Managed control plane, your infrastructure
|
||||||
|
|
||||||
@@ -123,7 +132,7 @@ Managed control plane, your infrastructure
|
|||||||
- Traffic flows through your infra.
|
- Traffic flows through your infra.
|
||||||
- We coordinate failover between your nodes or to Cloud when things go bad.
|
- We coordinate failover between your nodes or to Cloud when things go bad.
|
||||||
|
|
||||||
If interested, [contact us](mailto:numbat@fossorial.io).
|
Try it out using [Pangolin Cloud](https://pangolin.fossorial.io)
|
||||||
|
|
||||||
### Full Enterprise On-Premises
|
### Full Enterprise On-Premises
|
||||||
|
|
||||||
@@ -139,8 +148,10 @@ Pangolin is dual licensed under the AGPL-3 and the Fossorial Commercial license.
|
|||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
Looking for something to contribute? Take a look at issues marked with [help wanted](https://github.com/fosrl/pangolin/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22).
|
Looking for something to contribute? Take a look at issues marked with [help wanted](https://github.com/fosrl/pangolin/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22). Also take a look through the freature requests in Discussions - any are available and some are marked as a good first issue.
|
||||||
|
|
||||||
Please see [CONTRIBUTING](./CONTRIBUTING.md) in the repository for guidelines and best practices.
|
Please see [CONTRIBUTING](./CONTRIBUTING.md) in the repository for guidelines and best practices.
|
||||||
|
|
||||||
Please post bug reports and other functional issues in the [Issues](https://github.com/fosrl/pangolin/issues) section of the repository.
|
Please post bug reports and other functional issues in the [Issues](https://github.com/fosrl/pangolin/issues) section of the repository.
|
||||||
|
|
||||||
|
If you are looking to help with translations, please contribute [on Crowdin](https://crowdin.com/project/fossorial-pangolin) or open a PR with changes to the translations files found in `messages/`.
|
||||||
72
blueprint.py
Normal file
72
blueprint.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import requests
|
||||||
|
import yaml
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
|
||||||
|
# The file path for the YAML file to be read
|
||||||
|
# You can change this to the path of your YAML file
|
||||||
|
YAML_FILE_PATH = 'blueprint.yaml'
|
||||||
|
|
||||||
|
# The API endpoint and headers from the curl request
|
||||||
|
API_URL = 'http://api.pangolin.fossorial.io/v1/org/test/blueprint'
|
||||||
|
HEADERS = {
|
||||||
|
'accept': '*/*',
|
||||||
|
'Authorization': 'Bearer <your_token_here>',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
def convert_and_send(file_path, url, headers):
|
||||||
|
"""
|
||||||
|
Reads a YAML file, converts its content to a JSON payload,
|
||||||
|
and sends it via a PUT request to a specified URL.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Read the YAML file content
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
yaml_content = file.read()
|
||||||
|
|
||||||
|
# Parse the YAML string to a Python dictionary
|
||||||
|
# This will be used to ensure the YAML is valid before sending
|
||||||
|
parsed_yaml = yaml.safe_load(yaml_content)
|
||||||
|
|
||||||
|
# convert the parsed YAML to a JSON string
|
||||||
|
json_payload = json.dumps(parsed_yaml)
|
||||||
|
print("Converted JSON payload:")
|
||||||
|
print(json_payload)
|
||||||
|
|
||||||
|
# Encode the JSON string to Base64
|
||||||
|
encoded_json = base64.b64encode(json_payload.encode('utf-8')).decode('utf-8')
|
||||||
|
|
||||||
|
# Create the final payload with the base64 encoded data
|
||||||
|
final_payload = {
|
||||||
|
"blueprint": encoded_json
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Sending the following Base64 encoded JSON payload:")
|
||||||
|
print(final_payload)
|
||||||
|
print("-" * 20)
|
||||||
|
|
||||||
|
# Make the PUT request with the base64 encoded payload
|
||||||
|
response = requests.put(url, headers=headers, json=final_payload)
|
||||||
|
|
||||||
|
# Print the API response for debugging
|
||||||
|
print(f"API Response Status Code: {response.status_code}")
|
||||||
|
print("API Response Content:")
|
||||||
|
print(response.text)
|
||||||
|
|
||||||
|
# Raise an exception for bad status codes (4xx or 5xx)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Error: The file '{file_path}' was not found.")
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
print(f"Error parsing YAML file: {e}")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"An error occurred during the API request: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An unexpected error occurred: {e}")
|
||||||
|
|
||||||
|
# Run the function
|
||||||
|
if __name__ == "__main__":
|
||||||
|
convert_and_send(YAML_FILE_PATH, API_URL, HEADERS)
|
||||||
|
|
||||||
69
blueprint.yaml
Normal file
69
blueprint.yaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
client-resources:
|
||||||
|
client-resource-nice-id-uno:
|
||||||
|
name: this is my resource
|
||||||
|
protocol: tcp
|
||||||
|
proxy-port: 3001
|
||||||
|
hostname: localhost
|
||||||
|
internal-port: 3000
|
||||||
|
site: lively-yosemite-toad
|
||||||
|
client-resource-nice-id-duce:
|
||||||
|
name: this is my resource
|
||||||
|
protocol: udp
|
||||||
|
proxy-port: 3000
|
||||||
|
hostname: localhost
|
||||||
|
internal-port: 3000
|
||||||
|
site: lively-yosemite-toad
|
||||||
|
|
||||||
|
proxy-resources:
|
||||||
|
resource-nice-id-uno:
|
||||||
|
name: this is my resource
|
||||||
|
protocol: http
|
||||||
|
full-domain: duce.test.example.com
|
||||||
|
host-header: example.com
|
||||||
|
tls-server-name: example.com
|
||||||
|
# auth:
|
||||||
|
# pincode: 123456
|
||||||
|
# password: sadfasdfadsf
|
||||||
|
# sso-enabled: true
|
||||||
|
# sso-roles:
|
||||||
|
# - Member
|
||||||
|
# sso-users:
|
||||||
|
# - owen@fossorial.io
|
||||||
|
# whitelist-users:
|
||||||
|
# - owen@fossorial.io
|
||||||
|
headers:
|
||||||
|
- name: X-Example-Header
|
||||||
|
value: example-value
|
||||||
|
- name: X-Another-Header
|
||||||
|
value: another-value
|
||||||
|
rules:
|
||||||
|
- action: allow
|
||||||
|
match: ip
|
||||||
|
value: 1.1.1.1
|
||||||
|
- action: deny
|
||||||
|
match: cidr
|
||||||
|
value: 2.2.2.2/32
|
||||||
|
- action: pass
|
||||||
|
match: path
|
||||||
|
value: /admin
|
||||||
|
targets:
|
||||||
|
- site: lively-yosemite-toad
|
||||||
|
path: /path
|
||||||
|
pathMatchType: prefix
|
||||||
|
hostname: localhost
|
||||||
|
method: http
|
||||||
|
port: 8000
|
||||||
|
- site: slim-alpine-chipmunk
|
||||||
|
hostname: localhost
|
||||||
|
path: /yoman
|
||||||
|
pathMatchType: exact
|
||||||
|
method: http
|
||||||
|
port: 8001
|
||||||
|
resource-nice-id-duce:
|
||||||
|
name: this is other resource
|
||||||
|
protocol: tcp
|
||||||
|
proxy-port: 3000
|
||||||
|
targets:
|
||||||
|
- site: lively-yosemite-toad
|
||||||
|
hostname: localhost
|
||||||
|
port: 3000
|
||||||
17
bruno/API Keys/Create API Key.bru
Normal file
17
bruno/API Keys/Create API Key.bru
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
meta {
|
||||||
|
name: Create API Key
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
url: http://localhost:3000/api/v1/api-key
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"isRoot": true
|
||||||
|
}
|
||||||
|
}
|
||||||
11
bruno/API Keys/Delete API Key.bru
Normal file
11
bruno/API Keys/Delete API Key.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Delete API Key
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
url: http://localhost:3000/api/v1/api-key/dm47aacqxxn3ubj
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
11
bruno/API Keys/List API Key Actions.bru
Normal file
11
bruno/API Keys/List API Key Actions.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: List API Key Actions
|
||||||
|
type: http
|
||||||
|
seq: 6
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/actions
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
11
bruno/API Keys/List Org API Keys.bru
Normal file
11
bruno/API Keys/List Org API Keys.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: List Org API Keys
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1/org/home-lab/api-keys
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
11
bruno/API Keys/List Root API Keys.bru
Normal file
11
bruno/API Keys/List Root API Keys.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: List Root API Keys
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1/root/api-keys
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
17
bruno/API Keys/Set API Key Actions.bru
Normal file
17
bruno/API Keys/Set API Key Actions.bru
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
meta {
|
||||||
|
name: Set API Key Actions
|
||||||
|
type: http
|
||||||
|
seq: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/actions
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"actionIds": ["listSites"]
|
||||||
|
}
|
||||||
|
}
|
||||||
17
bruno/API Keys/Set API Key Orgs.bru
Normal file
17
bruno/API Keys/Set API Key Orgs.bru
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
meta {
|
||||||
|
name: Set API Key Orgs
|
||||||
|
type: http
|
||||||
|
seq: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/orgs
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"orgIds": ["home-lab"]
|
||||||
|
}
|
||||||
|
}
|
||||||
3
bruno/API Keys/folder.bru
Normal file
3
bruno/API Keys/folder.bru
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: API Keys
|
||||||
|
}
|
||||||
@@ -5,14 +5,14 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
url: http://localhost:3000/api/v1/auth/login
|
url: http://localhost:4000/api/v1/auth/login
|
||||||
body: json
|
body: json
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"email": "admin@fosrl.io",
|
"email": "owen@fossorial.io",
|
||||||
"password": "Password123!"
|
"password": "Password123!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
url: http://localhost:3000/api/v1/auth/logout
|
url: http://localhost:4000/api/v1/auth/logout
|
||||||
body: none
|
body: none
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|||||||
22
bruno/IDP/Create OIDC Provider.bru
Normal file
22
bruno/IDP/Create OIDC Provider.bru
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Create OIDC Provider
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
url: http://localhost:3000/api/v1/org/home-lab/idp/oidc
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"clientId": "JJoSvHCZcxnXT2sn6CObj6a21MuKNRXs3kN5wbys",
|
||||||
|
"clientSecret": "2SlGL2wOGgMEWLI9yUuMAeFxre7qSNJVnXMzyepdNzH1qlxYnC4lKhhQ6a157YQEkYH3vm40KK4RCqbYiF8QIweuPGagPX3oGxEj2exwutoXFfOhtq4hHybQKoFq01Z3",
|
||||||
|
"authUrl": "http://localhost:9000/application/o/authorize/",
|
||||||
|
"tokenUrl": "http://localhost:9000/application/o/token/",
|
||||||
|
"scopes": ["email", "openid", "profile"],
|
||||||
|
"userIdentifier": "email"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
bruno/IDP/Generate OIDC URL.bru
Normal file
11
bruno/IDP/Generate OIDC URL.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Generate OIDC URL
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
3
bruno/IDP/folder.bru
Normal file
3
bruno/IDP/folder.bru
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: IDP
|
||||||
|
}
|
||||||
11
bruno/Internal/Traefik Config.bru
Normal file
11
bruno/Internal/Traefik Config.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Traefik Config
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3001/api/v1/traefik-config
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
3
bruno/Internal/folder.bru
Normal file
3
bruno/Internal/folder.bru
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: Internal
|
||||||
|
}
|
||||||
11
bruno/Remote Exit Node/createRemoteExitNode.bru
Normal file
11
bruno/Remote Exit Node/createRemoteExitNode.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: createRemoteExitNode
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
url: http://localhost:4000/api/v1/org/org_i21aifypnlyxur2/remote-exit-node
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
11
bruno/Test.bru
Normal file
11
bruno/Test.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Test
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "1",
|
"version": "1",
|
||||||
"name": "Pangolin",
|
"name": "Pangolin Saas",
|
||||||
"type": "collection",
|
"type": "collection",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
|
|||||||
72
cli/commands/resetUserSecurityKeys.ts
Normal file
72
cli/commands/resetUserSecurityKeys.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { CommandModule } from "yargs";
|
||||||
|
import { db, users, securityKeys } from "@server/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
type ResetUserSecurityKeysArgs = {
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resetUserSecurityKeys: CommandModule<
|
||||||
|
{},
|
||||||
|
ResetUserSecurityKeysArgs
|
||||||
|
> = {
|
||||||
|
command: "reset-user-security-keys",
|
||||||
|
describe:
|
||||||
|
"Reset a user's security keys (passkeys) by deleting all their webauthn credentials",
|
||||||
|
builder: (yargs) => {
|
||||||
|
return yargs.option("email", {
|
||||||
|
type: "string",
|
||||||
|
demandOption: true,
|
||||||
|
describe: "User email address"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (argv: { email: string }) => {
|
||||||
|
try {
|
||||||
|
const { email } = argv;
|
||||||
|
|
||||||
|
console.log(`Looking for user with email: ${email}`);
|
||||||
|
|
||||||
|
// Find the user by email
|
||||||
|
const [user] = await db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.email, email))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
console.error(`User with email '${email}' not found`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Found user: ${user.email} (ID: ${user.userId})`);
|
||||||
|
|
||||||
|
// Check if user has any security keys
|
||||||
|
const userSecurityKeys = await db
|
||||||
|
.select()
|
||||||
|
.from(securityKeys)
|
||||||
|
.where(eq(securityKeys.userId, user.userId));
|
||||||
|
|
||||||
|
if (userSecurityKeys.length === 0) {
|
||||||
|
console.log(`User '${email}' has no security keys to reset`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Found ${userSecurityKeys.length} security key(s) for user '${email}'`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete all security keys for the user
|
||||||
|
await db
|
||||||
|
.delete(securityKeys)
|
||||||
|
.where(eq(securityKeys.userId, user.userId));
|
||||||
|
|
||||||
|
console.log(`Successfully reset security keys for user '${email}'`);
|
||||||
|
console.log(`Deleted ${userSecurityKeys.length} security key(s)`);
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -32,7 +32,9 @@ export const setAdminCredentials: CommandModule<{}, SetAdminCredentialsArgs> = {
|
|||||||
},
|
},
|
||||||
handler: async (argv: { email: string; password: string }) => {
|
handler: async (argv: { email: string; password: string }) => {
|
||||||
try {
|
try {
|
||||||
const { email, password } = argv;
|
const { password } = argv;
|
||||||
|
let { email } = argv;
|
||||||
|
email = email.trim().toLowerCase();
|
||||||
|
|
||||||
const parsed = passwordSchema.safeParse(password);
|
const parsed = passwordSchema.safeParse(password);
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
import { hideBin } from "yargs/helpers";
|
import { hideBin } from "yargs/helpers";
|
||||||
import { setAdminCredentials } from "@cli/commands/setAdminCredentials";
|
import { setAdminCredentials } from "@cli/commands/setAdminCredentials";
|
||||||
|
import { resetUserSecurityKeys } from "@cli/commands/resetUserSecurityKeys";
|
||||||
|
|
||||||
yargs(hideBin(process.argv))
|
yargs(hideBin(process.argv))
|
||||||
.scriptName("pangctl")
|
.scriptName("pangctl")
|
||||||
.command(setAdminCredentials)
|
.command(setAdminCredentials)
|
||||||
|
.command(resetUserSecurityKeys)
|
||||||
.demandCommand()
|
.demandCommand()
|
||||||
.help().argv;
|
.help().argv;
|
||||||
|
|||||||
@@ -1,48 +1,28 @@
|
|||||||
# To see all available options, please visit the docs:
|
# To see all available options, please visit the docs:
|
||||||
# https://docs.fossorial.io/Pangolin/Configuration/config
|
# https://docs.digpangolin.com/self-host/advanced/config-file
|
||||||
|
|
||||||
app:
|
app:
|
||||||
dashboard_url: "http://localhost:3002"
|
dashboard_url: http://localhost:3002
|
||||||
log_level: "info"
|
log_level: debug
|
||||||
save_logs: false
|
|
||||||
|
|
||||||
domains:
|
domains:
|
||||||
domain1:
|
domain1:
|
||||||
base_domain: "example.com"
|
base_domain: example.com
|
||||||
cert_resolver: "letsencrypt"
|
|
||||||
|
|
||||||
server:
|
server:
|
||||||
external_port: 3000
|
secret: my_secret_key
|
||||||
internal_port: 3001
|
|
||||||
next_port: 3002
|
|
||||||
internal_hostname: "pangolin"
|
|
||||||
session_cookie_name: "p_session_token"
|
|
||||||
resource_access_token_param: "p_token"
|
|
||||||
secret: "your_secret_key_here"
|
|
||||||
resource_access_token_headers:
|
|
||||||
id: "P-Access-Token-Id"
|
|
||||||
token: "P-Access-Token"
|
|
||||||
resource_session_request_param: "p_session_request"
|
|
||||||
|
|
||||||
traefik:
|
|
||||||
http_entrypoint: "web"
|
|
||||||
https_entrypoint: "websecure"
|
|
||||||
|
|
||||||
gerbil:
|
gerbil:
|
||||||
start_port: 51820
|
base_endpoint: example.com
|
||||||
base_endpoint: "localhost"
|
|
||||||
block_size: 24
|
|
||||||
site_block_size: 30
|
|
||||||
subnet_group: 100.89.137.0/20
|
|
||||||
use_subdomain: true
|
|
||||||
|
|
||||||
rate_limits:
|
orgs:
|
||||||
global:
|
block_size: 24
|
||||||
window_minutes: 1
|
subnet_group: 100.90.137.0/20
|
||||||
max_requests: 500
|
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
require_email_verification: false
|
require_email_verification: false
|
||||||
disable_signup_without_invite: true
|
disable_signup_without_invite: true
|
||||||
disable_user_create_org: true
|
disable_user_create_org: true
|
||||||
allow_raw_resources: true
|
allow_raw_resources: true
|
||||||
|
enable_integration_api: true
|
||||||
|
enable_clients: true
|
||||||
|
|||||||
Binary file not shown.
46
config/traefik/dynamic_config.yml
Normal file
46
config/traefik/dynamic_config.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
redirect-to-https:
|
||||||
|
redirectScheme:
|
||||||
|
scheme: https
|
||||||
|
|
||||||
|
routers:
|
||||||
|
# HTTP to HTTPS redirect router
|
||||||
|
main-app-router-redirect:
|
||||||
|
rule: "Host(`{{.DashboardDomain}}`)"
|
||||||
|
service: next-service
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
middlewares:
|
||||||
|
- redirect-to-https
|
||||||
|
|
||||||
|
# Next.js router (handles everything except API and WebSocket paths)
|
||||||
|
next-router:
|
||||||
|
rule: "Host(`{{.DashboardDomain}}`)"
|
||||||
|
service: next-service
|
||||||
|
priority: 10
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
# API router (handles /api/v1 paths)
|
||||||
|
api-router:
|
||||||
|
rule: "Host(`{{.DashboardDomain}}`) && PathPrefix(`/api/v1`)"
|
||||||
|
service: api-service
|
||||||
|
priority: 100
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
services:
|
||||||
|
next-service:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://pangolin:3002" # Next.js server
|
||||||
|
|
||||||
|
api-service:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://pangolin:3000" # API/WebSocket server
|
||||||
34
config/traefik/traefik_config.yml
Normal file
34
config/traefik/traefik_config.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
api:
|
||||||
|
insecure: true
|
||||||
|
dashboard: true
|
||||||
|
|
||||||
|
providers:
|
||||||
|
file:
|
||||||
|
directory: "/var/dynamic"
|
||||||
|
watch: true
|
||||||
|
|
||||||
|
experimental:
|
||||||
|
plugins:
|
||||||
|
badger:
|
||||||
|
moduleName: "github.com/fosrl/badger"
|
||||||
|
version: "v1.2.0"
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: "DEBUG"
|
||||||
|
format: "common"
|
||||||
|
maxSize: 100
|
||||||
|
maxBackups: 3
|
||||||
|
maxAge: 3
|
||||||
|
compress: true
|
||||||
|
|
||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
address: ":80"
|
||||||
|
websecure:
|
||||||
|
address: ":9443"
|
||||||
|
transport:
|
||||||
|
respondingTimeouts:
|
||||||
|
readTimeout: "30m"
|
||||||
|
|
||||||
|
serversTransport:
|
||||||
|
insecureSkipVerify: true
|
||||||
@@ -22,8 +22,7 @@ services:
|
|||||||
command:
|
command:
|
||||||
- --reachableAt=http://gerbil:3003
|
- --reachableAt=http://gerbil:3003
|
||||||
- --generateAndSaveKeyTo=/var/config/key
|
- --generateAndSaveKeyTo=/var/config/key
|
||||||
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
|
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||||
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/:/var/config
|
- ./config/:/var/config
|
||||||
cap_add:
|
cap_add:
|
||||||
@@ -36,7 +35,7 @@ services:
|
|||||||
- 80:80 # Port for traefik because of the network_mode
|
- 80:80 # Port for traefik because of the network_mode
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v3.4.0
|
image: traefik:v3.5
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: service:gerbil # Ports appear on the gerbil service
|
network_mode: service:gerbil # Ports appear on the gerbil service
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ services:
|
|||||||
POSTGRES_DB: postgres # Default database name
|
POSTGRES_DB: postgres # Default database name
|
||||||
POSTGRES_USER: postgres # Default user
|
POSTGRES_USER: postgres # Default user
|
||||||
POSTGRES_PASSWORD: password # Default password (change for production!)
|
POSTGRES_PASSWORD: password # Default password (change for production!)
|
||||||
|
volumes:
|
||||||
|
- ./config/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432" # Map host port 5432 to container port 5432
|
- "5432:5432" # Map host port 5432 to container port 5432
|
||||||
restart: no
|
restart: no
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:latest # Use the latest Redis image
|
||||||
|
container_name: dev_redis # Name your Redis container
|
||||||
|
ports:
|
||||||
|
- "6379:6379" # Map host port 6379 to container port 6379
|
||||||
|
restart: no
|
||||||
@@ -9,10 +9,10 @@ services:
|
|||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
- "3001:3001"
|
- "3001:3001"
|
||||||
- "3002:3002"
|
- "3002:3002"
|
||||||
|
- "3003:3003"
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- ENVIRONMENT=dev
|
- ENVIRONMENT=dev
|
||||||
- DB_TYPE=pg
|
|
||||||
volumes:
|
volumes:
|
||||||
# Mount source code for hot reload
|
# Mount source code for hot reload
|
||||||
- ./src:/app/src
|
- ./src:/app/src
|
||||||
@@ -26,4 +26,4 @@ services:
|
|||||||
- ./postcss.config.mjs:/app/postcss.config.mjs
|
- ./postcss.config.mjs:/app/postcss.config.mjs
|
||||||
- ./eslint.config.js:/app/eslint.config.js
|
- ./eslint.config.js:/app/eslint.config.js
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
restart: no
|
restart: no
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
import { defineConfig } from "drizzle-kit";
|
import { defineConfig } from "drizzle-kit";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { build } from "@server/build";
|
||||||
|
|
||||||
|
let schema;
|
||||||
|
if (build === "oss") {
|
||||||
|
schema = [path.join("server", "db", "pg", "schema.ts")];
|
||||||
|
} else {
|
||||||
|
schema = [
|
||||||
|
path.join("server", "db", "pg", "schema.ts"),
|
||||||
|
path.join("server", "db", "pg", "privateSchema.ts")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "postgresql",
|
dialect: "postgresql",
|
||||||
schema: [path.join("server", "db", "pg", "schema.ts")],
|
schema: schema,
|
||||||
out: path.join("server", "migrations"),
|
out: path.join("server", "migrations"),
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
|
import { build } from "@server/build";
|
||||||
import { APP_PATH } from "@server/lib/consts";
|
import { APP_PATH } from "@server/lib/consts";
|
||||||
import { defineConfig } from "drizzle-kit";
|
import { defineConfig } from "drizzle-kit";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
let schema;
|
||||||
|
if (build === "oss") {
|
||||||
|
schema = [path.join("server", "db", "sqlite", "schema.ts")];
|
||||||
|
} else {
|
||||||
|
schema = [
|
||||||
|
path.join("server", "db", "sqlite", "schema.ts"),
|
||||||
|
path.join("server", "db", "sqlite", "privateSchema.ts")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "sqlite",
|
dialect: "sqlite",
|
||||||
schema: path.join("server", "db", "sqlite", "schema.ts"),
|
schema: schema,
|
||||||
out: path.join("server", "migrations"),
|
out: path.join("server", "migrations"),
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ esbuild
|
|||||||
bundle: true,
|
bundle: true,
|
||||||
outfile: argv.out,
|
outfile: argv.out,
|
||||||
format: "esm",
|
format: "esm",
|
||||||
minify: true,
|
minify: false,
|
||||||
banner: {
|
banner: {
|
||||||
js: banner,
|
js: banner,
|
||||||
},
|
},
|
||||||
@@ -63,8 +63,8 @@ esbuild
|
|||||||
packagePath: getPackagePaths(),
|
packagePath: getPackagePaths(),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
sourcemap: true,
|
sourcemap: "inline",
|
||||||
target: "node20",
|
target: "node22",
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("Build completed successfully");
|
console.log("Build completed successfully");
|
||||||
|
|||||||
@@ -37,15 +37,28 @@ type DynamicConfig struct {
|
|||||||
} `yaml:"http"`
|
} `yaml:"http"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigValues holds the extracted configuration values
|
// TraefikConfigValues holds the extracted configuration values
|
||||||
type ConfigValues struct {
|
type TraefikConfigValues struct {
|
||||||
DashboardDomain string
|
DashboardDomain string
|
||||||
LetsEncryptEmail string
|
LetsEncryptEmail string
|
||||||
BadgerVersion string
|
BadgerVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppConfig represents the app section of the config.yml
|
||||||
|
type AppConfig struct {
|
||||||
|
App struct {
|
||||||
|
DashboardURL string `yaml:"dashboard_url"`
|
||||||
|
LogLevel string `yaml:"log_level"`
|
||||||
|
} `yaml:"app"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppConfigValues struct {
|
||||||
|
DashboardURL string
|
||||||
|
LogLevel string
|
||||||
|
}
|
||||||
|
|
||||||
// ReadTraefikConfig reads and extracts values from Traefik configuration files
|
// ReadTraefikConfig reads and extracts values from Traefik configuration files
|
||||||
func ReadTraefikConfig(mainConfigPath, dynamicConfigPath string) (*ConfigValues, error) {
|
func ReadTraefikConfig(mainConfigPath string) (*TraefikConfigValues, error) {
|
||||||
// Read main config file
|
// Read main config file
|
||||||
mainConfigData, err := os.ReadFile(mainConfigPath)
|
mainConfigData, err := os.ReadFile(mainConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,48 +70,33 @@ func ReadTraefikConfig(mainConfigPath, dynamicConfigPath string) (*ConfigValues,
|
|||||||
return nil, fmt.Errorf("error parsing main config file: %w", err)
|
return nil, fmt.Errorf("error parsing main config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read dynamic config file
|
|
||||||
dynamicConfigData, err := os.ReadFile(dynamicConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error reading dynamic config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var dynamicConfig DynamicConfig
|
|
||||||
if err := yaml.Unmarshal(dynamicConfigData, &dynamicConfig); err != nil {
|
|
||||||
return nil, fmt.Errorf("error parsing dynamic config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract values
|
// Extract values
|
||||||
values := &ConfigValues{
|
values := &TraefikConfigValues{
|
||||||
BadgerVersion: mainConfig.Experimental.Plugins.Badger.Version,
|
BadgerVersion: mainConfig.Experimental.Plugins.Badger.Version,
|
||||||
LetsEncryptEmail: mainConfig.CertificatesResolvers.LetsEncrypt.Acme.Email,
|
LetsEncryptEmail: mainConfig.CertificatesResolvers.LetsEncrypt.Acme.Email,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract DashboardDomain from router rules
|
|
||||||
// Look for it in the main router rules
|
|
||||||
for _, router := range dynamicConfig.HTTP.Routers {
|
|
||||||
if router.Rule != "" {
|
|
||||||
// Extract domain from Host(`mydomain.com`)
|
|
||||||
if domain := extractDomainFromRule(router.Rule); domain != "" {
|
|
||||||
values.DashboardDomain = domain
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values, nil
|
return values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractDomainFromRule extracts the domain from a router rule
|
func ReadAppConfig(configPath string) (*AppConfigValues, error) {
|
||||||
func extractDomainFromRule(rule string) string {
|
// Read config file
|
||||||
// Look for the Host(`mydomain.com`) pattern
|
configData, err := os.ReadFile(configPath)
|
||||||
if start := findPattern(rule, "Host(`"); start != -1 {
|
if err != nil {
|
||||||
end := findPattern(rule[start:], "`)")
|
return nil, fmt.Errorf("error reading config file: %w", err)
|
||||||
if end != -1 {
|
|
||||||
return rule[start+6 : start+end]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ""
|
|
||||||
|
var appConfig AppConfig
|
||||||
|
if err := yaml.Unmarshal(configData, &appConfig); err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
values := &AppConfigValues{
|
||||||
|
DashboardURL: appConfig.App.DashboardURL,
|
||||||
|
LogLevel: appConfig.App.LogLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
return values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findPattern finds the start of a pattern in a string
|
// findPattern finds the start of a pattern in a string
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
# To see all available options, please visit the docs:
|
# To see all available options, please visit the docs:
|
||||||
# https://docs.fossorial.io/Pangolin/Configuration/config
|
# https://docs.digpangolin.com/self-host/advanced/config-file
|
||||||
|
|
||||||
|
gerbil:
|
||||||
|
start_port: 51820
|
||||||
|
base_endpoint: "{{.DashboardDomain}}"
|
||||||
|
{{if .HybridMode}}
|
||||||
|
managed:
|
||||||
|
id: "{{.HybridId}}"
|
||||||
|
secret: "{{.HybridSecret}}"
|
||||||
|
|
||||||
|
{{else}}
|
||||||
app:
|
app:
|
||||||
dashboard_url: "https://{{.DashboardDomain}}"
|
dashboard_url: "https://{{.DashboardDomain}}"
|
||||||
log_level: "info"
|
log_level: "info"
|
||||||
|
telemetry:
|
||||||
|
anonymous_usage: true
|
||||||
|
|
||||||
domains:
|
domains:
|
||||||
domain1:
|
domain1:
|
||||||
@@ -17,11 +28,6 @@ server:
|
|||||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||||
credentials: false
|
credentials: false
|
||||||
|
|
||||||
gerbil:
|
|
||||||
start_port: 51820
|
|
||||||
base_endpoint: "{{.DashboardDomain}}"
|
|
||||||
|
|
||||||
{{if .EnableEmail}}
|
{{if .EnableEmail}}
|
||||||
email:
|
email:
|
||||||
smtp_host: "{{.EmailSMTPHost}}"
|
smtp_host: "{{.EmailSMTPHost}}"
|
||||||
@@ -30,9 +36,9 @@ email:
|
|||||||
smtp_pass: "{{.EmailSMTPPass}}"
|
smtp_pass: "{{.EmailSMTPPass}}"
|
||||||
no_reply: "{{.EmailNoReply}}"
|
no_reply: "{{.EmailNoReply}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
require_email_verification: {{.EnableEmail}}
|
require_email_verification: {{.EnableEmail}}
|
||||||
disable_signup_without_invite: true
|
disable_signup_without_invite: true
|
||||||
disable_user_create_org: false
|
disable_user_create_org: false
|
||||||
allow_raw_resources: true
|
allow_raw_resources: true
|
||||||
|
{{end}}
|
||||||
@@ -16,7 +16,7 @@ experimental:
|
|||||||
version: "{{.BadgerVersion}}"
|
version: "{{.BadgerVersion}}"
|
||||||
crowdsec: # CrowdSec plugin configuration added
|
crowdsec: # CrowdSec plugin configuration added
|
||||||
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
version: "v1.4.2"
|
version: "v1.4.4"
|
||||||
|
|
||||||
log:
|
log:
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
|
- pangolin-data:/var/certificates
|
||||||
|
- pangolin-data:/var/dynamic
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||||
interval: "10s"
|
interval: "10s"
|
||||||
@@ -22,8 +24,7 @@ services:
|
|||||||
command:
|
command:
|
||||||
- --reachableAt=http://gerbil:3003
|
- --reachableAt=http://gerbil:3003
|
||||||
- --generateAndSaveKeyTo=/var/config/key
|
- --generateAndSaveKeyTo=/var/config/key
|
||||||
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
|
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||||
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/:/var/config
|
- ./config/:/var/config
|
||||||
cap_add:
|
cap_add:
|
||||||
@@ -32,11 +33,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 51820:51820/udp
|
- 51820:51820/udp
|
||||||
- 21820:21820/udp
|
- 21820:21820/udp
|
||||||
- 443:443 # Port for traefik because of the network_mode
|
- 443:{{if .HybridMode}}8443{{else}}443{{end}}
|
||||||
- 80:80 # Port for traefik because of the network_mode
|
- 80:80
|
||||||
{{end}}
|
{{end}}
|
||||||
traefik:
|
traefik:
|
||||||
image: docker.io/traefik:v3.4.1
|
image: docker.io/traefik:v3.5
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
{{if .InstallGerbil}}
|
{{if .InstallGerbil}}
|
||||||
@@ -55,9 +56,15 @@ services:
|
|||||||
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
|
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
|
||||||
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
||||||
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
|
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
|
||||||
|
# Shared volume for certificates and dynamic config in file mode
|
||||||
|
- pangolin-data:/var/certificates:ro
|
||||||
|
- pangolin-data:/var/dynamic:ro
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
name: pangolin
|
name: pangolin
|
||||||
enable_ipv6: true
|
{{if .EnableIPv6}} enable_ipv6: true{{end}}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pangolin-data:
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ api:
|
|||||||
dashboard: true
|
dashboard: true
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
|
{{if not .HybridMode}}
|
||||||
http:
|
http:
|
||||||
endpoint: "http://pangolin:3001/api/v1/traefik-config"
|
endpoint: "http://pangolin:3001/api/v1/traefik-config"
|
||||||
pollInterval: "5s"
|
pollInterval: "5s"
|
||||||
file:
|
file:
|
||||||
filename: "/etc/traefik/dynamic_config.yml"
|
filename: "/etc/traefik/dynamic_config.yml"
|
||||||
|
{{else}}
|
||||||
|
file:
|
||||||
|
directory: "/var/dynamic"
|
||||||
|
watch: true
|
||||||
|
{{end}}
|
||||||
experimental:
|
experimental:
|
||||||
plugins:
|
plugins:
|
||||||
badger:
|
badger:
|
||||||
@@ -22,7 +27,7 @@ log:
|
|||||||
maxBackups: 3
|
maxBackups: 3
|
||||||
maxAge: 3
|
maxAge: 3
|
||||||
compress: true
|
compress: true
|
||||||
|
{{if not .HybridMode}}
|
||||||
certificatesResolvers:
|
certificatesResolvers:
|
||||||
letsencrypt:
|
letsencrypt:
|
||||||
acme:
|
acme:
|
||||||
@@ -31,18 +36,25 @@ certificatesResolvers:
|
|||||||
email: "{{.LetsEncryptEmail}}"
|
email: "{{.LetsEncryptEmail}}"
|
||||||
storage: "/letsencrypt/acme.json"
|
storage: "/letsencrypt/acme.json"
|
||||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
{{end}}
|
||||||
entryPoints:
|
entryPoints:
|
||||||
web:
|
web:
|
||||||
address: ":80"
|
address: ":80"
|
||||||
websecure:
|
websecure:
|
||||||
address: ":443"
|
address: ":443"
|
||||||
|
{{if .HybridMode}} proxyProtocol:
|
||||||
|
trustedIPs:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
- ::1/128{{end}}
|
||||||
transport:
|
transport:
|
||||||
respondingTimeouts:
|
respondingTimeouts:
|
||||||
readTimeout: "30m"
|
readTimeout: "30m"
|
||||||
http:
|
{{if not .HybridMode}} http:
|
||||||
tls:
|
tls:
|
||||||
certResolver: "letsencrypt"
|
certResolver: "letsencrypt"{{end}}
|
||||||
|
|
||||||
serversTransport:
|
serversTransport:
|
||||||
insecureSkipVerify: true
|
insecureSkipVerify: true
|
||||||
|
|
||||||
|
ping:
|
||||||
|
entryPoint: "web"
|
||||||
332
install/containers.go
Normal file
332
install/containers.go
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func waitForContainer(containerName string, containerType SupportedContainer) error {
|
||||||
|
maxAttempts := 30
|
||||||
|
retryInterval := time.Second * 2
|
||||||
|
|
||||||
|
for attempt := 0; attempt < maxAttempts; attempt++ {
|
||||||
|
// Check if container is running
|
||||||
|
cmd := exec.Command(string(containerType), "container", "inspect", "-f", "{{.State.Running}}", containerName)
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
// If the container doesn't exist or there's another error, wait and retry
|
||||||
|
time.Sleep(retryInterval)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isRunning := strings.TrimSpace(out.String()) == "true"
|
||||||
|
if isRunning {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container exists but isn't running yet, wait and retry
|
||||||
|
time.Sleep(retryInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("container %s did not start within %v seconds", containerName, maxAttempts*int(retryInterval.Seconds()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func installDocker() error {
|
||||||
|
// Detect Linux distribution
|
||||||
|
cmd := exec.Command("cat", "/etc/os-release")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect Linux distribution: %v", err)
|
||||||
|
}
|
||||||
|
osRelease := string(output)
|
||||||
|
|
||||||
|
// Detect system architecture
|
||||||
|
archCmd := exec.Command("uname", "-m")
|
||||||
|
archOutput, err := archCmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect system architecture: %v", err)
|
||||||
|
}
|
||||||
|
arch := strings.TrimSpace(string(archOutput))
|
||||||
|
|
||||||
|
// Map architecture to Docker's architecture naming
|
||||||
|
var dockerArch string
|
||||||
|
switch arch {
|
||||||
|
case "x86_64":
|
||||||
|
dockerArch = "amd64"
|
||||||
|
case "aarch64":
|
||||||
|
dockerArch = "arm64"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported architecture: %s", arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
var installCmd *exec.Cmd
|
||||||
|
switch {
|
||||||
|
case strings.Contains(osRelease, "ID=ubuntu"):
|
||||||
|
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||||
|
apt-get update &&
|
||||||
|
apt-get install -y apt-transport-https ca-certificates curl software-properties-common &&
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg &&
|
||||||
|
echo "deb [arch=%s signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list &&
|
||||||
|
apt-get update &&
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||||
|
`, dockerArch))
|
||||||
|
case strings.Contains(osRelease, "ID=debian"):
|
||||||
|
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||||
|
apt-get update &&
|
||||||
|
apt-get install -y apt-transport-https ca-certificates curl software-properties-common &&
|
||||||
|
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg &&
|
||||||
|
echo "deb [arch=%s signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list &&
|
||||||
|
apt-get update &&
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||||
|
`, dockerArch))
|
||||||
|
case strings.Contains(osRelease, "ID=fedora"):
|
||||||
|
// Detect Fedora version to handle DNF 5 changes
|
||||||
|
versionCmd := exec.Command("bash", "-c", "grep VERSION_ID /etc/os-release | cut -d'=' -f2 | tr -d '\"'")
|
||||||
|
versionOutput, err := versionCmd.Output()
|
||||||
|
var fedoraVersion int
|
||||||
|
if err == nil {
|
||||||
|
if v, parseErr := strconv.Atoi(strings.TrimSpace(string(versionOutput))); parseErr == nil {
|
||||||
|
fedoraVersion = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use appropriate DNF syntax based on version
|
||||||
|
var repoCmd string
|
||||||
|
if fedoraVersion >= 41 {
|
||||||
|
// DNF 5 syntax for Fedora 41+
|
||||||
|
repoCmd = "dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo"
|
||||||
|
} else {
|
||||||
|
// DNF 4 syntax for Fedora < 41
|
||||||
|
repoCmd = "dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo"
|
||||||
|
}
|
||||||
|
|
||||||
|
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
|
||||||
|
dnf -y install dnf-plugins-core &&
|
||||||
|
%s &&
|
||||||
|
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||||
|
`, repoCmd))
|
||||||
|
case strings.Contains(osRelease, "ID=opensuse") || strings.Contains(osRelease, "ID=\"opensuse-"):
|
||||||
|
installCmd = exec.Command("bash", "-c", `
|
||||||
|
zypper install -y docker docker-compose &&
|
||||||
|
systemctl enable docker
|
||||||
|
`)
|
||||||
|
case strings.Contains(osRelease, "ID=rhel") || strings.Contains(osRelease, "ID=\"rhel"):
|
||||||
|
installCmd = exec.Command("bash", "-c", `
|
||||||
|
dnf remove -y runc &&
|
||||||
|
dnf -y install yum-utils &&
|
||||||
|
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo &&
|
||||||
|
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin &&
|
||||||
|
systemctl enable docker
|
||||||
|
`)
|
||||||
|
case strings.Contains(osRelease, "ID=amzn"):
|
||||||
|
installCmd = exec.Command("bash", "-c", `
|
||||||
|
yum update -y &&
|
||||||
|
yum install -y docker &&
|
||||||
|
systemctl enable docker &&
|
||||||
|
usermod -a -G docker ec2-user
|
||||||
|
`)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported Linux distribution")
|
||||||
|
}
|
||||||
|
|
||||||
|
installCmd.Stdout = os.Stdout
|
||||||
|
installCmd.Stderr = os.Stderr
|
||||||
|
return installCmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startDockerService() error {
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
cmd := exec.Command("systemctl", "enable", "--now", "docker")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
} else if runtime.GOOS == "darwin" {
|
||||||
|
// On macOS, Docker is usually started via the Docker Desktop application
|
||||||
|
fmt.Println("Please start Docker Desktop manually on macOS.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unsupported operating system for starting Docker service")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDockerInstalled() bool {
|
||||||
|
return isContainerInstalled("docker")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPodmanInstalled() bool {
|
||||||
|
return isContainerInstalled("podman") && isContainerInstalled("podman-compose")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isContainerInstalled(container string) bool {
|
||||||
|
cmd := exec.Command(container, "--version")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUserInDockerGroup() bool {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// Docker group is not applicable on macOS
|
||||||
|
// So we assume that the user can run Docker commands
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Geteuid() == 0 {
|
||||||
|
return true // Root user can run Docker commands anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the current user is in the docker group
|
||||||
|
if dockerGroup, err := user.LookupGroup("docker"); err == nil {
|
||||||
|
if currentUser, err := user.Current(); err == nil {
|
||||||
|
if currentUserGroupIds, err := currentUser.GroupIds(); err == nil {
|
||||||
|
for _, groupId := range currentUserGroupIds {
|
||||||
|
if groupId == dockerGroup.Gid {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eventually, if any of the checks fail, we assume the user cannot run Docker commands
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDockerRunning checks if the Docker daemon is running by using the `docker info` command.
|
||||||
|
func isDockerRunning() bool {
|
||||||
|
cmd := exec.Command("docker", "info")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeDockerComposeCommandWithArgs executes the appropriate docker command with arguments supplied
|
||||||
|
func executeDockerComposeCommandWithArgs(args ...string) error {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
var useNewStyle bool
|
||||||
|
|
||||||
|
if !isDockerInstalled() {
|
||||||
|
return fmt.Errorf("docker is not installed")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCmd := exec.Command("docker", "compose", "version")
|
||||||
|
if err := checkCmd.Run(); err == nil {
|
||||||
|
useNewStyle = true
|
||||||
|
} else {
|
||||||
|
checkCmd = exec.Command("docker-compose", "version")
|
||||||
|
if err := checkCmd.Run(); err == nil {
|
||||||
|
useNewStyle = false
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("neither 'docker compose' nor 'docker-compose' command is available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if useNewStyle {
|
||||||
|
cmd = exec.Command("docker", append([]string{"compose"}, args...)...)
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("docker-compose", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// pullContainers pulls the containers using the appropriate command.
|
||||||
|
func pullContainers(containerType SupportedContainer) error {
|
||||||
|
fmt.Println("Pulling the container images...")
|
||||||
|
if containerType == Podman {
|
||||||
|
if err := run("podman-compose", "-f", "docker-compose.yml", "pull"); err != nil {
|
||||||
|
return fmt.Errorf("failed to pull the containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerType == Docker {
|
||||||
|
if err := executeDockerComposeCommandWithArgs("-f", "docker-compose.yml", "pull", "--policy", "always"); err != nil {
|
||||||
|
return fmt.Errorf("failed to pull the containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// startContainers starts the containers using the appropriate command.
|
||||||
|
func startContainers(containerType SupportedContainer) error {
|
||||||
|
fmt.Println("Starting containers...")
|
||||||
|
|
||||||
|
if containerType == Podman {
|
||||||
|
if err := run("podman-compose", "-f", "docker-compose.yml", "up", "-d", "--force-recreate"); err != nil {
|
||||||
|
return fmt.Errorf("failed start containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerType == Docker {
|
||||||
|
if err := executeDockerComposeCommandWithArgs("-f", "docker-compose.yml", "up", "-d", "--force-recreate"); err != nil {
|
||||||
|
return fmt.Errorf("failed to start containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stopContainers stops the containers using the appropriate command.
|
||||||
|
func stopContainers(containerType SupportedContainer) error {
|
||||||
|
fmt.Println("Stopping containers...")
|
||||||
|
if containerType == Podman {
|
||||||
|
if err := run("podman-compose", "-f", "docker-compose.yml", "down"); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerType == Docker {
|
||||||
|
if err := executeDockerComposeCommandWithArgs("-f", "docker-compose.yml", "down"); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// restartContainer restarts a specific container using the appropriate command.
|
||||||
|
func restartContainer(container string, containerType SupportedContainer) error {
|
||||||
|
fmt.Println("Restarting containers...")
|
||||||
|
if containerType == Podman {
|
||||||
|
if err := run("podman-compose", "-f", "docker-compose.yml", "restart"); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop the container \"%s\": %v", container, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerType == Docker {
|
||||||
|
if err := executeDockerComposeCommandWithArgs("-f", "docker-compose.yml", "restart", container); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop the container \"%s\": %v", container, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unsupported container type: %s", containerType)
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
module installer
|
module installer
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/term v0.28.0
|
golang.org/x/term v0.35.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/sys v0.29.0 // indirect
|
require golang.org/x/sys v0.36.0 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
74
install/input.go
Normal file
74
install/input.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readString(reader *bufio.Reader, prompt string, defaultValue string) string {
|
||||||
|
if defaultValue != "" {
|
||||||
|
fmt.Printf("%s (default: %s): ", prompt, defaultValue)
|
||||||
|
} else {
|
||||||
|
fmt.Print(prompt + ": ")
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool {
|
||||||
|
defaultStr := "no"
|
||||||
|
if defaultValue {
|
||||||
|
defaultStr = "yes"
|
||||||
|
}
|
||||||
|
input := readString(reader, prompt+" (yes/no)", defaultStr)
|
||||||
|
return strings.ToLower(input) == "yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBoolNoDefault(reader *bufio.Reader, prompt string) bool {
|
||||||
|
input := readStringNoDefault(reader, prompt+" (yes/no)")
|
||||||
|
return strings.ToLower(input) == "yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
func readInt(reader *bufio.Reader, prompt string, defaultValue int) int {
|
||||||
|
input := readString(reader, prompt, fmt.Sprintf("%d", defaultValue))
|
||||||
|
if input == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
value := defaultValue
|
||||||
|
fmt.Sscanf(input, "%d", &value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
docker
|
docker
|
||||||
example.com
|
example.com
|
||||||
pangolin.example.com
|
pangolin.example.com
|
||||||
|
yes
|
||||||
admin@example.com
|
admin@example.com
|
||||||
yes
|
yes
|
||||||
admin@example.com
|
admin@example.com
|
||||||
|
|||||||
912
install/main.go
912
install/main.go
File diff suppressed because it is too large
Load Diff
110
install/quickStart.go
Normal file
110
install/quickStart.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FRONTEND_SECRET_KEY = "af4e4785-7e09-11f0-b93a-74563c4e2a7e"
|
||||||
|
// CLOUD_API_URL = "https://pangolin.fossorial.io/api/v1/remote-exit-node/quick-start"
|
||||||
|
CLOUD_API_URL = "https://pangolin.fossorial.io/api/v1/remote-exit-node/quick-start"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HybridCredentials represents the response from the cloud API
|
||||||
|
type HybridCredentials struct {
|
||||||
|
RemoteExitNodeId string `json:"remoteExitNodeId"`
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIResponse represents the full response structure from the cloud API
|
||||||
|
type APIResponse struct {
|
||||||
|
Data HybridCredentials `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestPayload represents the request body structure
|
||||||
|
type RequestPayload struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateValidationToken() string {
|
||||||
|
timestamp := time.Now().UnixMilli()
|
||||||
|
data := fmt.Sprintf("%s|%d", FRONTEND_SECRET_KEY, timestamp)
|
||||||
|
obfuscated := make([]byte, len(data))
|
||||||
|
for i, char := range []byte(data) {
|
||||||
|
obfuscated[i] = char + 5
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(obfuscated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestHybridCredentials makes an HTTP POST request to the cloud API
|
||||||
|
// to get hybrid credentials (ID and secret)
|
||||||
|
func requestHybridCredentials() (*HybridCredentials, error) {
|
||||||
|
// Generate validation token
|
||||||
|
token := generateValidationToken()
|
||||||
|
|
||||||
|
// Create request payload
|
||||||
|
payload := RequestPayload{
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal payload to JSON
|
||||||
|
jsonData, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal request payload: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create HTTP request
|
||||||
|
req, err := http.NewRequest("POST", CLOUD_API_URL, bytes.NewBuffer(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set headers
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("X-CSRF-Token", "x-csrf-protection")
|
||||||
|
|
||||||
|
// Create HTTP client with timeout
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the request
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to make HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Check response status
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("API request failed with status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read response body for debugging
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the raw JSON response for debugging
|
||||||
|
// fmt.Printf("Raw JSON response: %s\n", string(body))
|
||||||
|
|
||||||
|
// Parse response
|
||||||
|
var apiResponse APIResponse
|
||||||
|
if err := json.Unmarshal(body, &apiResponse); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode API response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate response data
|
||||||
|
if apiResponse.Data.RemoteExitNodeId == "" || apiResponse.Data.Secret == "" {
|
||||||
|
return nil, fmt.Errorf("invalid response: missing remoteExitNodeId or secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiResponse.Data, nil
|
||||||
|
}
|
||||||
1727
messages/bg-BG.json
Normal file
1727
messages/bg-BG.json
Normal file
File diff suppressed because it is too large
Load Diff
2855
messages/cs-CZ.json
2855
messages/cs-CZ.json
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"setupCreate": "Erstelle eine Organisation, Site und Ressourcen",
|
"setupCreate": "Erstelle eine Organisation, einen Standort und Ressourcen",
|
||||||
"setupNewOrg": "Neue Organisation",
|
"setupNewOrg": "Neue Organisation",
|
||||||
"setupCreateOrg": "Organisation erstellen",
|
"setupCreateOrg": "Organisation erstellen",
|
||||||
"setupCreateResources": "Ressource erstellen",
|
"setupCreateResources": "Ressource erstellen",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"componentsMember": "Du bist Mitglied von {count, plural, =0 {keiner Organisation} one {einer Organisation} other {# Organisationen}}.",
|
"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.",
|
"componentsInvalidKey": "Ungültige oder abgelaufene Lizenzschlüssel erkannt. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
|
||||||
"dismiss": "Verwerfen",
|
"dismiss": "Verwerfen",
|
||||||
"componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Sites, die das Lizenzlimit der {maxSites} Sites überschreiten. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
|
"componentsLicenseViolation": "Lizenzverstoß: Dieser Server benutzt {usedSites} Standorte, was das Lizenzlimit von {maxSites} Standorten überschreitet. Beachte die Lizenzbedingungen, um alle Funktionen weiterhin zu nutzen.",
|
||||||
"componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!",
|
"componentsSupporterMessage": "Vielen Dank für die Unterstützung von Pangolin als {tier}!",
|
||||||
"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.",
|
"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.",
|
||||||
"inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.",
|
"inviteErrorUser": "Es tut uns leid, aber es scheint, als sei die Einladung, auf die du zugreifen möchtest, nicht für diesen Benutzer bestimmt.",
|
||||||
@@ -38,25 +38,25 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
"offline": "Offline",
|
"offline": "Offline",
|
||||||
"site": "Seite",
|
"site": "Standort",
|
||||||
"dataIn": "Daten eingehend",
|
"dataIn": "Daten eingehend",
|
||||||
"dataOut": "Daten ausgehend",
|
"dataOut": "Daten ausgehend",
|
||||||
"connectionType": "Verbindungstyp",
|
"connectionType": "Verbindungstyp",
|
||||||
"tunnelType": "Tunneltyp",
|
"tunnelType": "Tunneltyp",
|
||||||
"local": "Lokal",
|
"local": "Lokal",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"siteConfirmDelete": "Site löschen bestätigen",
|
"siteConfirmDelete": "Standort löschen bestätigen",
|
||||||
"siteDelete": "Site löschen",
|
"siteDelete": "Standort löschen",
|
||||||
"siteMessageRemove": "Sobald diese Seite entfernt ist, wird sie nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit der Site verbunden sind, werden ebenfalls entfernt.",
|
"siteMessageRemove": "Sobald dieser Standort entfernt ist, wird er nicht mehr zugänglich sein. Alle Ressourcen und Ziele, die mit diesem Standort verbunden sind, werden ebenfalls entfernt.",
|
||||||
"siteMessageConfirm": "Um zu bestätigen, gib den Namen der Site ein.",
|
"siteMessageConfirm": "Um zu bestätigen, gib den Namen des Standortes unten ein.",
|
||||||
"siteQuestionRemove": "Bist du sicher, dass Sie die Site {selectedSite} aus der Organisation entfernt werden soll?",
|
"siteQuestionRemove": "Bist du sicher, dass der Standort {selectedSite} aus der Organisation entfernt werden soll?",
|
||||||
"siteManageSites": "Sites verwalten",
|
"siteManageSites": "Standorte verwalten",
|
||||||
"siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben",
|
"siteDescription": "Verbindung zum Netzwerk durch sichere Tunnel erlauben",
|
||||||
"siteCreate": "Site erstellen",
|
"siteCreate": "Standort erstellen",
|
||||||
"siteCreateDescription2": "Folge den nachfolgenden Schritten, um eine neue Site zu erstellen und zu verbinden",
|
"siteCreateDescription2": "Folge den nachfolgenden Schritten, um einen neuen Standort zu erstellen und zu verbinden",
|
||||||
"siteCreateDescription": "Erstelle eine neue Site, um Ressourcen zu verbinden",
|
"siteCreateDescription": "Erstelle einen neuen Standort, um Ressourcen zu verbinden",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"siteErrorCreate": "Fehler beim Erstellen der Site",
|
"siteErrorCreate": "Fehler beim Erstellen des Standortes",
|
||||||
"siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden",
|
"siteErrorCreateKeyPair": "Schlüsselpaar oder Standardwerte nicht gefunden",
|
||||||
"siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden",
|
"siteErrorCreateDefaults": "Standardwerte der Site nicht gefunden",
|
||||||
"method": "Methode",
|
"method": "Methode",
|
||||||
@@ -70,8 +70,8 @@
|
|||||||
"dockerRun": "Docker Run",
|
"dockerRun": "Docker Run",
|
||||||
"siteLearnLocal": "Mehr Infos zu lokalen Sites",
|
"siteLearnLocal": "Mehr Infos zu lokalen Sites",
|
||||||
"siteConfirmCopy": "Ich habe die Konfiguration kopiert",
|
"siteConfirmCopy": "Ich habe die Konfiguration kopiert",
|
||||||
"searchSitesProgress": "Sites durchsuchen...",
|
"searchSitesProgress": "Standorte durchsuchen...",
|
||||||
"siteAdd": "Site hinzufügen",
|
"siteAdd": "Standort hinzufügen",
|
||||||
"siteInstallNewt": "Newt installieren",
|
"siteInstallNewt": "Newt installieren",
|
||||||
"siteInstallNewtDescription": "Installiere Newt auf deinem System.",
|
"siteInstallNewtDescription": "Installiere Newt auf deinem System.",
|
||||||
"WgConfiguration": "WireGuard Konfiguration",
|
"WgConfiguration": "WireGuard Konfiguration",
|
||||||
@@ -82,26 +82,28 @@
|
|||||||
"siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.",
|
"siteNewtDescription": "Nutze Newt für die beste Benutzererfahrung. Newt verwendet WireGuard as Basis und erlaubt Ihnen, Ihre privaten Ressourcen über ihre LAN-Adresse in Ihrem privaten Netzwerk aus dem Pangolin-Dashboard heraus zu adressieren.",
|
||||||
"siteRunsInDocker": "Läuft in Docker",
|
"siteRunsInDocker": "Läuft in Docker",
|
||||||
"siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows",
|
"siteRunsInShell": "Läuft in der Konsole auf macOS, Linux und Windows",
|
||||||
"siteErrorDelete": "Fehler beim Löschen der Site",
|
"siteErrorDelete": "Fehler beim Löschen des Standortes",
|
||||||
"siteErrorUpdate": "Fehler beim Aktualisieren der Site",
|
"siteErrorUpdate": "Fehler beim Aktualisieren des Standortes",
|
||||||
"siteErrorUpdateDescription": "Beim Aktualisieren der Site ist ein Fehler aufgetreten.",
|
"siteErrorUpdateDescription": "Beim Aktualisieren des Standortes ist ein Fehler aufgetreten.",
|
||||||
"siteUpdated": "Site aktualisiert",
|
"siteUpdated": "Standort aktualisiert",
|
||||||
"siteUpdatedDescription": "Die Site wurde aktualisiert.",
|
"siteUpdatedDescription": "Der Standort wurde aktualisiert.",
|
||||||
"siteGeneralDescription": "Allgemeine Einstellungen für diese Site konfigurieren",
|
"siteGeneralDescription": "Allgemeine Einstellungen für diesen Standort konfigurieren",
|
||||||
"siteSettingDescription": "Konfigurieren der Site Einstellungen",
|
"siteSettingDescription": "Konfigurieren der Standort Einstellungen",
|
||||||
"siteSetting": "{siteName} Einstellungen",
|
"siteSetting": "{siteName} Einstellungen",
|
||||||
"siteNewtTunnel": "Newt-Tunnel (empfohlen)",
|
"siteNewtTunnel": "Newt-Tunnel (empfohlen)",
|
||||||
"siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.",
|
"siteNewtTunnelDescription": "Einfachster Weg, einen Zugriffspunkt zu deinem Netzwerk zu erstellen. Keine zusätzliche Einrichtung erforderlich.",
|
||||||
"siteWg": "Einfacher WireGuard Tunnel",
|
"siteWg": "Einfacher WireGuard Tunnel",
|
||||||
"siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.",
|
"siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.",
|
||||||
|
"siteWgDescriptionSaas": "Verwenden Sie jeden WireGuard-Client, um einen Tunnel zu erstellen. Manuelles NAT-Setup erforderlich. FUNKTIONIERT NUR BEI SELBSTGEHOSTETEN KNOTEN",
|
||||||
"siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.",
|
"siteLocalDescription": "Nur lokale Ressourcen. Kein Tunneling.",
|
||||||
"siteSeeAll": "Alle Sites anzeigen",
|
"siteLocalDescriptionSaas": "Nur lokale Ressourcen. Keine Tunneldurchführung. FUNKTIONIERT NUR BEI SELBSTGEHOSTETEN KNOTEN",
|
||||||
"siteTunnelDescription": "Lege fest, wie du dich mit deiner Site verbinden möchtest",
|
"siteSeeAll": "Alle Standorte anzeigen",
|
||||||
|
"siteTunnelDescription": "Lege fest, wie du dich mit deinem Standort verbinden möchtest",
|
||||||
"siteNewtCredentials": "Neue Newt Zugangsdaten",
|
"siteNewtCredentials": "Neue Newt Zugangsdaten",
|
||||||
"siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren",
|
"siteNewtCredentialsDescription": "So wird sich Newt mit dem Server authentifizieren",
|
||||||
"siteCredentialsSave": "Ihre Zugangsdaten speichern",
|
"siteCredentialsSave": "Ihre Zugangsdaten speichern",
|
||||||
"siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.",
|
"siteCredentialsSaveDescription": "Du kannst das nur einmal sehen. Stelle sicher, dass du es an einen sicheren Ort kopierst.",
|
||||||
"siteInfo": "Site-Informationen",
|
"siteInfo": "Standort-Informationen",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"shareTitle": "Links zum Teilen verwalten",
|
"shareTitle": "Links zum Teilen verwalten",
|
||||||
"shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren",
|
"shareDescription": "Erstellen Sie teilbare Links, um temporären oder permanenten Zugriff auf Ihre Ressourcen zu gewähren",
|
||||||
@@ -163,10 +165,13 @@
|
|||||||
"resourceSeeAll": "Alle Ressourcen anzeigen",
|
"resourceSeeAll": "Alle Ressourcen anzeigen",
|
||||||
"resourceInfo": "Ressourcen-Informationen",
|
"resourceInfo": "Ressourcen-Informationen",
|
||||||
"resourceNameDescription": "Dies ist der Anzeigename für die Ressource.",
|
"resourceNameDescription": "Dies ist der Anzeigename für die Ressource.",
|
||||||
"siteSelect": "Site auswählen",
|
"siteSelect": "Standort auswählen",
|
||||||
"siteSearch": "Website durchsuchen",
|
"siteSearch": "Standorte durchsuchen",
|
||||||
"siteNotFound": "Keine Site gefunden.",
|
"siteNotFound": "Keinen Standort gefunden.",
|
||||||
"siteSelectionDescription": "Diese Seite wird die Verbindung zu der Ressource herstellen.",
|
"selectCountry": "Land auswählen",
|
||||||
|
"searchCountries": "Länder suchen...",
|
||||||
|
"noCountryFound": "Kein Land gefunden.",
|
||||||
|
"siteSelectionDescription": "Dieser Standort wird die Verbindung zum Ziel herstellen.",
|
||||||
"resourceType": "Ressourcentyp",
|
"resourceType": "Ressourcentyp",
|
||||||
"resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten",
|
"resourceTypeDescription": "Legen Sie fest, wie Sie auf Ihre Ressource zugreifen möchten",
|
||||||
"resourceHTTPSSettings": "HTTPS-Einstellungen",
|
"resourceHTTPSSettings": "HTTPS-Einstellungen",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "Allgemein",
|
"general": "Allgemein",
|
||||||
"generalSettings": "Allgemeine Einstellungen",
|
"generalSettings": "Allgemeine Einstellungen",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
|
"internal": "Intern",
|
||||||
"rules": "Regeln",
|
"rules": "Regeln",
|
||||||
"resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource",
|
"resourceSettingDescription": "Konfigurieren Sie die Einstellungen Ihrer Ressource",
|
||||||
"resourceSetting": "{resourceName} Einstellungen",
|
"resourceSetting": "{resourceName} Einstellungen",
|
||||||
"alwaysAllow": "Immer erlauben",
|
"alwaysAllow": "Immer erlauben",
|
||||||
"alwaysDeny": "Immer ablehnen",
|
"alwaysDeny": "Immer ablehnen",
|
||||||
|
"passToAuth": "Weiterleiten zur Authentifizierung",
|
||||||
"orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation",
|
"orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation",
|
||||||
"orgGeneralSettings": "Organisations-Einstellungen",
|
"orgGeneralSettings": "Organisations-Einstellungen",
|
||||||
"orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten",
|
"orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten",
|
||||||
@@ -302,7 +309,7 @@
|
|||||||
"userQuestionRemove": "Sind Sie sicher, dass Sie {selectedUser} dauerhaft vom Server löschen möchten?",
|
"userQuestionRemove": "Sind Sie sicher, dass Sie {selectedUser} dauerhaft vom Server löschen möchten?",
|
||||||
"licenseKey": "Lizenzschlüssel",
|
"licenseKey": "Lizenzschlüssel",
|
||||||
"valid": "Gültig",
|
"valid": "Gültig",
|
||||||
"numberOfSites": "Anzahl der Sites",
|
"numberOfSites": "Anzahl der Standorte",
|
||||||
"licenseKeySearch": "Lizenzschlüssel suchen...",
|
"licenseKeySearch": "Lizenzschlüssel suchen...",
|
||||||
"licenseKeyAdd": "Lizenzschlüssel hinzufügen",
|
"licenseKeyAdd": "Lizenzschlüssel hinzufügen",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
@@ -342,16 +349,16 @@
|
|||||||
"licensedNot": "Nicht lizenziert",
|
"licensedNot": "Nicht lizenziert",
|
||||||
"hostId": "Host-ID",
|
"hostId": "Host-ID",
|
||||||
"licenseReckeckAll": "Überprüfe alle Schlüssel",
|
"licenseReckeckAll": "Überprüfe alle Schlüssel",
|
||||||
"licenseSiteUsage": "Website-Nutzung",
|
"licenseSiteUsage": "Standort-Nutzung",
|
||||||
"licenseSiteUsageDecsription": "Sehen Sie sich die Anzahl der Sites an, die diese Lizenz verwenden.",
|
"licenseSiteUsageDecsription": "Sehen Sie sich die Anzahl der Standorte an, die diese Lizenz verwenden.",
|
||||||
"licenseNoSiteLimit": "Die Anzahl der Sites, die einen nicht lizenzierten Host verwenden, ist unbegrenzt.",
|
"licenseNoSiteLimit": "Die Anzahl der Standorte, die einen nicht lizenzierten Host verwenden, ist unbegrenzt.",
|
||||||
"licensePurchase": "Lizenz kaufen",
|
"licensePurchase": "Lizenz kaufen",
|
||||||
"licensePurchaseSites": "Zusätzliche Seiten kaufen",
|
"licensePurchaseSites": "Zusätzliche Standorte kaufen\n",
|
||||||
"licenseSitesUsedMax": "{usedSites} der {maxSites} Seiten verwendet",
|
"licenseSitesUsedMax": "{usedSites} von {maxSites} Standorten verwendet",
|
||||||
"licenseSitesUsed": "{count, plural, =0 {# Seiten} one {# Seite} other {# Seiten}} im System.",
|
"licenseSitesUsed": "{count, plural, =0 {# Standorte} one {# Standort} other {# Standorte}} im System.",
|
||||||
"licensePurchaseDescription": "Wähle aus, für wieviele Seiten du möchtest {selectedMode, select, license {kaufe eine Lizenz. Du kannst später immer weitere Seiten hinzufügen.} other {Füge zu deiner bestehenden Lizenz hinzu.}}",
|
"licensePurchaseDescription": "Wähle aus, für wieviele Seiten du möchtest {selectedMode, select, license {kaufe eine Lizenz. Du kannst später immer weitere Seiten hinzufügen.} other {Füge zu deiner bestehenden Lizenz hinzu.}}",
|
||||||
"licenseFee": "Lizenzgebühr",
|
"licenseFee": "Lizenzgebühr",
|
||||||
"licensePriceSite": "Preis pro Seite",
|
"licensePriceSite": "Preis pro Standort",
|
||||||
"total": "Gesamt",
|
"total": "Gesamt",
|
||||||
"licenseContinuePayment": "Weiter zur Zahlung",
|
"licenseContinuePayment": "Weiter zur Zahlung",
|
||||||
"pricingPage": "Preisseite",
|
"pricingPage": "Preisseite",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Beim Hinzufügen des Benutzers zur Rolle ist ein Fehler aufgetreten.",
|
"accessRoleErrorAddDescription": "Beim Hinzufügen des Benutzers zur Rolle ist ein Fehler aufgetreten.",
|
||||||
"userSaved": "Benutzer gespeichert",
|
"userSaved": "Benutzer gespeichert",
|
||||||
"userSavedDescription": "Der Benutzer wurde aktualisiert.",
|
"userSavedDescription": "Der Benutzer wurde aktualisiert.",
|
||||||
|
"autoProvisioned": "Automatisch bereitgestellt",
|
||||||
|
"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",
|
"accessControlsDescription": "Verwalten Sie, worauf dieser Benutzer in der Organisation zugreifen und was er tun kann",
|
||||||
"accessControlsSubmit": "Zugriffskontrollen speichern",
|
"accessControlsSubmit": "Zugriffskontrollen speichern",
|
||||||
"roles": "Rollen",
|
"roles": "Rollen",
|
||||||
@@ -467,7 +476,7 @@
|
|||||||
"targetErrorDuplicate": "Doppeltes Ziel",
|
"targetErrorDuplicate": "Doppeltes Ziel",
|
||||||
"targetErrorDuplicateDescription": "Ein Ziel mit diesen Einstellungen existiert bereits",
|
"targetErrorDuplicateDescription": "Ein Ziel mit diesen Einstellungen existiert bereits",
|
||||||
"targetWireGuardErrorInvalidIp": "Ungültige Ziel-IP",
|
"targetWireGuardErrorInvalidIp": "Ungültige Ziel-IP",
|
||||||
"targetWireGuardErrorInvalidIpDescription": "Die Ziel-IP muss innerhalb des Site-Subnets liegen",
|
"targetWireGuardErrorInvalidIpDescription": "Die Ziel-IP muss innerhalb des Standort-Subnets liegen",
|
||||||
"targetsUpdated": "Ziele aktualisiert",
|
"targetsUpdated": "Ziele aktualisiert",
|
||||||
"targetsUpdatedDescription": "Ziele und Einstellungen erfolgreich aktualisiert",
|
"targetsUpdatedDescription": "Ziele und Einstellungen erfolgreich aktualisiert",
|
||||||
"targetsErrorUpdate": "Fehler beim Aktualisieren der Ziele",
|
"targetsErrorUpdate": "Fehler beim Aktualisieren der Ziele",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "Der zu verwendende TLS-Servername für SNI. Leer lassen, um den Standard zu verwenden.",
|
"targetTlsSniDescription": "Der zu verwendende TLS-Servername für SNI. Leer lassen, um den Standard zu verwenden.",
|
||||||
"targetTlsSubmit": "Einstellungen speichern",
|
"targetTlsSubmit": "Einstellungen speichern",
|
||||||
"targets": "Ziel-Konfiguration",
|
"targets": "Ziel-Konfiguration",
|
||||||
"targetsDescription": "Richten Sie Ziele ein, um Datenverkehr zu Ihren Diensten zu leiten",
|
"targetsDescription": "Richten Sie Ziele ein, um Datenverkehr zu Ihren Backend-Diensten zu leiten",
|
||||||
"targetStickySessions": "Sticky Sessions aktivieren",
|
"targetStickySessions": "Sticky Sessions aktivieren",
|
||||||
"targetStickySessionsDescription": "Verbindungen für die gesamte Sitzung auf demselben Backend-Ziel halten.",
|
"targetStickySessionsDescription": "Verbindungen für die gesamte Sitzung auf demselben Backend-Ziel halten.",
|
||||||
"methodSelect": "Methode auswählen",
|
"methodSelect": "Methode auswählen",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Ungültiges IP-Adressformat",
|
"ipAddressErrorInvalidFormat": "Ungültiges IP-Adressformat",
|
||||||
"ipAddressErrorInvalidOctet": "Ungültiges IP-Adress-Oktett",
|
"ipAddressErrorInvalidOctet": "Ungültiges IP-Adress-Oktett",
|
||||||
"path": "Pfad",
|
"path": "Pfad",
|
||||||
|
"matchPath": "Spielpfad",
|
||||||
"ipAddressRange": "IP-Bereich",
|
"ipAddressRange": "IP-Bereich",
|
||||||
"rulesErrorFetch": "Fehler beim Abrufen der Regeln",
|
"rulesErrorFetch": "Fehler beim Abrufen der Regeln",
|
||||||
"rulesErrorFetchDescription": "Beim Abrufen der Regeln ist ein Fehler aufgetreten",
|
"rulesErrorFetchDescription": "Beim Abrufen der Regeln ist ein Fehler aufgetreten",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Aktionen",
|
"rulesActions": "Aktionen",
|
||||||
"rulesActionAlwaysAllow": "Immer erlauben: Alle Authentifizierungsmethoden umgehen",
|
"rulesActionAlwaysAllow": "Immer erlauben: Alle Authentifizierungsmethoden umgehen",
|
||||||
"rulesActionAlwaysDeny": "Immer verweigern: Alle Anfragen blockieren; keine Authentifizierung möglich",
|
"rulesActionAlwaysDeny": "Immer verweigern: Alle Anfragen blockieren; keine Authentifizierung möglich",
|
||||||
|
"rulesActionPassToAuth": "Weiterleiten zur Authentifizierung: Erlaubt das Versuchen von Authentifizierungsmethoden",
|
||||||
"rulesMatchCriteria": "Übereinstimmungskriterien",
|
"rulesMatchCriteria": "Übereinstimmungskriterien",
|
||||||
"rulesMatchCriteriaIpAddress": "Mit einer bestimmten IP-Adresse übereinstimmen",
|
"rulesMatchCriteriaIpAddress": "Mit einer bestimmten IP-Adresse übereinstimmen",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Mit einem IP-Adressbereich in CIDR-Notation übereinstimmen",
|
"rulesMatchCriteriaIpAddressRange": "Mit einem IP-Adressbereich in CIDR-Notation übereinstimmen",
|
||||||
@@ -558,8 +569,8 @@
|
|||||||
"resourceErrorCreateDescription": "Beim Erstellen der Ressource ist ein Fehler aufgetreten",
|
"resourceErrorCreateDescription": "Beim Erstellen der Ressource ist ein Fehler aufgetreten",
|
||||||
"resourceErrorCreateMessage": "Fehler beim Erstellen der Ressource:",
|
"resourceErrorCreateMessage": "Fehler beim Erstellen der Ressource:",
|
||||||
"resourceErrorCreateMessageDescription": "Ein unerwarteter Fehler ist aufgetreten",
|
"resourceErrorCreateMessageDescription": "Ein unerwarteter Fehler ist aufgetreten",
|
||||||
"sitesErrorFetch": "Fehler beim Abrufen der Sites",
|
"sitesErrorFetch": "Fehler beim Abrufen der Standorte",
|
||||||
"sitesErrorFetchDescription": "Beim Abrufen der Sites ist ein Fehler aufgetreten",
|
"sitesErrorFetchDescription": "Beim Abrufen der Standorte ist ein Fehler aufgetreten",
|
||||||
"domainsErrorFetch": "Fehler beim Abrufen der Domains",
|
"domainsErrorFetch": "Fehler beim Abrufen der Domains",
|
||||||
"domainsErrorFetchDescription": "Beim Abrufen der Domains ist ein Fehler aufgetreten",
|
"domainsErrorFetchDescription": "Beim Abrufen der Domains ist ein Fehler aufgetreten",
|
||||||
"none": "Keine",
|
"none": "Keine",
|
||||||
@@ -677,10 +688,10 @@
|
|||||||
"resourceGeneralDescription": "Konfigurieren Sie die allgemeinen Einstellungen für diese Ressource",
|
"resourceGeneralDescription": "Konfigurieren Sie die allgemeinen Einstellungen für diese Ressource",
|
||||||
"resourceEnable": "Ressource aktivieren",
|
"resourceEnable": "Ressource aktivieren",
|
||||||
"resourceTransfer": "Ressource übertragen",
|
"resourceTransfer": "Ressource übertragen",
|
||||||
"resourceTransferDescription": "Diese Ressource auf eine andere Site übertragen",
|
"resourceTransferDescription": "Diese Ressource auf einen anderen Standort übertragen",
|
||||||
"resourceTransferSubmit": "Ressource übertragen",
|
"resourceTransferSubmit": "Ressource übertragen",
|
||||||
"siteDestination": "Zielsite",
|
"siteDestination": "Zielort",
|
||||||
"searchSites": "Sites durchsuchen",
|
"searchSites": "Standorte durchsuchen",
|
||||||
"accessRoleCreate": "Rolle erstellen",
|
"accessRoleCreate": "Rolle erstellen",
|
||||||
"accessRoleCreateDescription": "Erstellen Sie eine neue Rolle, um Benutzer zu gruppieren und ihre Berechtigungen zu verwalten.",
|
"accessRoleCreateDescription": "Erstellen Sie eine neue Rolle, um Benutzer zu gruppieren und ihre Berechtigungen zu verwalten.",
|
||||||
"accessRoleCreateSubmit": "Rolle erstellen",
|
"accessRoleCreateSubmit": "Rolle erstellen",
|
||||||
@@ -700,7 +711,7 @@
|
|||||||
"accessRoleRemovedDescription": "Die Rolle wurde erfolgreich 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.",
|
"accessRoleRequiredRemove": "Bevor Sie diese Rolle löschen, wählen Sie bitte eine neue Rolle aus, zu der die bestehenden Mitglieder übertragen werden sollen.",
|
||||||
"manage": "Verwalten",
|
"manage": "Verwalten",
|
||||||
"sitesNotFound": "Keine Sites gefunden.",
|
"sitesNotFound": "Keine Standorte gefunden.",
|
||||||
"pangolinServerAdmin": "Server-Admin - Pangolin",
|
"pangolinServerAdmin": "Server-Admin - Pangolin",
|
||||||
"licenseTierProfessional": "Professional Lizenz",
|
"licenseTierProfessional": "Professional Lizenz",
|
||||||
"licenseTierEnterprise": "Enterprise Lizenz",
|
"licenseTierEnterprise": "Enterprise Lizenz",
|
||||||
@@ -708,10 +719,10 @@
|
|||||||
"licensed": "Lizenziert",
|
"licensed": "Lizenziert",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"no": "Nein",
|
"no": "Nein",
|
||||||
"sitesAdditional": "Zusätzliche Sites",
|
"sitesAdditional": "Zusätzliche Standorte",
|
||||||
"licenseKeys": "Lizenzschlüssel",
|
"licenseKeys": "Lizenzschlüssel",
|
||||||
"sitestCountDecrease": "Anzahl der Sites verringern",
|
"sitestCountDecrease": "Anzahl der Standorte verringern",
|
||||||
"sitestCountIncrease": "Anzahl der Sites erhöhen",
|
"sitestCountIncrease": "Anzahl der Standorte erhöhen",
|
||||||
"idpManage": "Identitätsanbieter verwalten",
|
"idpManage": "Identitätsanbieter verwalten",
|
||||||
"idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten",
|
"idpManageDescription": "Identitätsanbieter im System anzeigen und verwalten",
|
||||||
"idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht",
|
"idpDeletedDescription": "Identitätsanbieter erfolgreich gelöscht",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN muss genau 6 Ziffern lang sein",
|
"pincodeRequirementsLength": "PIN muss genau 6 Ziffern lang sein",
|
||||||
"pincodeRequirementsChars": "PIN darf nur Zahlen enthalten",
|
"pincodeRequirementsChars": "PIN darf nur Zahlen enthalten",
|
||||||
"passwordRequirementsLength": "Passwort muss mindestens 1 Zeichen lang sein",
|
"passwordRequirementsLength": "Passwort muss mindestens 1 Zeichen lang sein",
|
||||||
|
"passwordRequirementsTitle": "Passwortanforderungen:",
|
||||||
|
"passwordRequirementLength": "Mindestens 8 Zeichen lang",
|
||||||
|
"passwordRequirementUppercase": "Mindestens ein Großbuchstabe",
|
||||||
|
"passwordRequirementLowercase": "Mindestens ein Kleinbuchstabe",
|
||||||
|
"passwordRequirementNumber": "Mindestens eine Zahl",
|
||||||
|
"passwordRequirementSpecial": "Mindestens ein Sonderzeichen",
|
||||||
|
"passwordRequirementsMet": "✓ Passwort erfüllt alle Anforderungen",
|
||||||
|
"passwordStrength": "Passwortstärke",
|
||||||
|
"passwordStrengthWeak": "Schwach",
|
||||||
|
"passwordStrengthMedium": "Mittel",
|
||||||
|
"passwordStrengthStrong": "Stark",
|
||||||
|
"passwordRequirements": "Anforderungen:",
|
||||||
|
"passwordRequirementLengthText": "8+ Zeichen",
|
||||||
|
"passwordRequirementUppercaseText": "Großbuchstabe (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Kleinbuchstabe (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Zahl (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Sonderzeichen (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Passwörter stimmen nicht überein",
|
||||||
"otpEmailRequirementsLength": "OTP muss mindestens 1 Zeichen lang sein",
|
"otpEmailRequirementsLength": "OTP muss mindestens 1 Zeichen lang sein",
|
||||||
"otpEmailSent": "OTP gesendet",
|
"otpEmailSent": "OTP gesendet",
|
||||||
"otpEmailSentDescription": "Ein OTP wurde an Ihre E-Mail gesendet",
|
"otpEmailSentDescription": "Ein OTP wurde an Ihre E-Mail gesendet",
|
||||||
@@ -952,23 +981,30 @@
|
|||||||
"logoutError": "Fehler beim Abmelden",
|
"logoutError": "Fehler beim Abmelden",
|
||||||
"signingAs": "Angemeldet als",
|
"signingAs": "Angemeldet als",
|
||||||
"serverAdmin": "Server-Administrator",
|
"serverAdmin": "Server-Administrator",
|
||||||
|
"managedSelfhosted": "Verwaltetes Selbsthosted",
|
||||||
"otpEnable": "Zwei-Faktor aktivieren",
|
"otpEnable": "Zwei-Faktor aktivieren",
|
||||||
"otpDisable": "Zwei-Faktor deaktivieren",
|
"otpDisable": "Zwei-Faktor deaktivieren",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
"licenseTierProfessionalRequired": "Professional Edition erforderlich",
|
"licenseTierProfessionalRequired": "Professional Edition erforderlich",
|
||||||
"licenseTierProfessionalRequiredDescription": "Diese Funktion ist nur in der Professional Edition verfügbar.",
|
"licenseTierProfessionalRequiredDescription": "Diese Funktion ist nur in der Professional Edition verfügbar.",
|
||||||
"actionGetOrg": "Organisation abrufen",
|
"actionGetOrg": "Organisation abrufen",
|
||||||
|
"updateOrgUser": "Org Benutzer aktualisieren",
|
||||||
|
"createOrgUser": "Org Benutzer erstellen",
|
||||||
"actionUpdateOrg": "Organisation aktualisieren",
|
"actionUpdateOrg": "Organisation aktualisieren",
|
||||||
"actionUpdateUser": "Benutzer aktualisieren",
|
"actionUpdateUser": "Benutzer aktualisieren",
|
||||||
"actionGetUser": "Benutzer abrufen",
|
"actionGetUser": "Benutzer abrufen",
|
||||||
"actionGetOrgUser": "Organisationsbenutzer abrufen",
|
"actionGetOrgUser": "Organisationsbenutzer abrufen",
|
||||||
"actionListOrgDomains": "Organisationsdomänen auflisten",
|
"actionListOrgDomains": "Organisationsdomänen auflisten",
|
||||||
"actionCreateSite": "Site erstellen",
|
"actionCreateSite": "Standort erstellen",
|
||||||
"actionDeleteSite": "Site löschen",
|
"actionDeleteSite": "Standort löschen",
|
||||||
"actionGetSite": "Site abrufen",
|
"actionGetSite": "Standort abrufen",
|
||||||
"actionListSites": "Sites auflisten",
|
"actionListSites": "Standorte auflisten",
|
||||||
"actionUpdateSite": "Site aktualisieren",
|
"actionApplyBlueprint": "Blaupause anwenden",
|
||||||
"actionListSiteRoles": "Erlaubte Site-Rollen auflisten",
|
"setupToken": "Setup-Token",
|
||||||
|
"setupTokenDescription": "Geben Sie das Setup-Token von der Serverkonsole ein.",
|
||||||
|
"setupTokenRequired": "Setup-Token ist erforderlich",
|
||||||
|
"actionUpdateSite": "Standorte aktualisieren",
|
||||||
|
"actionListSiteRoles": "Erlaubte Standort-Rollen auflisten",
|
||||||
"actionCreateResource": "Ressource erstellen",
|
"actionCreateResource": "Ressource erstellen",
|
||||||
"actionDeleteResource": "Ressource löschen",
|
"actionDeleteResource": "Ressource löschen",
|
||||||
"actionGetResource": "Ressource abrufen",
|
"actionGetResource": "Ressource abrufen",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
|
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
|
||||||
"actionListIdpOrgs": "IDP-Organisationen auflisten",
|
"actionListIdpOrgs": "IDP-Organisationen auflisten",
|
||||||
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
|
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
|
||||||
|
"actionCreateClient": "Kunde erstellen",
|
||||||
|
"actionDeleteClient": "Kunde löschen",
|
||||||
|
"actionUpdateClient": "Kunde aktualisieren",
|
||||||
|
"actionListClients": "Kunden auflisten",
|
||||||
|
"actionGetClient": "Kunde holen",
|
||||||
|
"actionCreateSiteResource": "Site-Ressource erstellen",
|
||||||
|
"actionDeleteSiteResource": "Site-Ressource löschen",
|
||||||
|
"actionGetSiteResource": "Site-Ressource abrufen",
|
||||||
|
"actionListSiteResources": "Site-Ressourcen auflisten",
|
||||||
|
"actionUpdateSiteResource": "Site-Ressource aktualisieren",
|
||||||
|
"actionListInvitations": "Einladungen auflisten",
|
||||||
"noneSelected": "Keine ausgewählt",
|
"noneSelected": "Keine ausgewählt",
|
||||||
"orgNotFound2": "Keine Organisationen gefunden.",
|
"orgNotFound2": "Keine Organisationen gefunden.",
|
||||||
"searchProgress": "Suche...",
|
"searchProgress": "Suche...",
|
||||||
@@ -1073,7 +1120,7 @@
|
|||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
"verificationCodeRequired": "Code ist erforderlich",
|
"verificationCodeRequired": "Code ist erforderlich",
|
||||||
"userErrorNoUpdate": "Kein Benutzer zum Aktualisieren",
|
"userErrorNoUpdate": "Kein Benutzer zum Aktualisieren",
|
||||||
"siteErrorNoUpdate": "Keine Site zum Aktualisieren",
|
"siteErrorNoUpdate": "Keine Standorte zum Aktualisieren",
|
||||||
"resourceErrorNoUpdate": "Keine Ressource zum Aktualisieren",
|
"resourceErrorNoUpdate": "Keine Ressource zum Aktualisieren",
|
||||||
"authErrorNoUpdate": "Keine Auth-Informationen zum Aktualisieren",
|
"authErrorNoUpdate": "Keine Auth-Informationen zum Aktualisieren",
|
||||||
"orgErrorNoUpdate": "Keine Organisation zum Aktualisieren",
|
"orgErrorNoUpdate": "Keine Organisation zum Aktualisieren",
|
||||||
@@ -1081,7 +1128,7 @@
|
|||||||
"apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren",
|
"apiKeysErrorNoUpdate": "Kein API-Schlüssel zum Aktualisieren",
|
||||||
"sidebarOverview": "Übersicht",
|
"sidebarOverview": "Übersicht",
|
||||||
"sidebarHome": "Zuhause",
|
"sidebarHome": "Zuhause",
|
||||||
"sidebarSites": "Seiten",
|
"sidebarSites": "Standorte",
|
||||||
"sidebarResources": "Ressourcen",
|
"sidebarResources": "Ressourcen",
|
||||||
"sidebarAccessControl": "Zugriffskontrolle",
|
"sidebarAccessControl": "Zugriffskontrolle",
|
||||||
"sidebarUsers": "Benutzer",
|
"sidebarUsers": "Benutzer",
|
||||||
@@ -1093,10 +1140,10 @@
|
|||||||
"sidebarAllUsers": "Alle Benutzer",
|
"sidebarAllUsers": "Alle Benutzer",
|
||||||
"sidebarIdentityProviders": "Identitätsanbieter",
|
"sidebarIdentityProviders": "Identitätsanbieter",
|
||||||
"sidebarLicense": "Lizenz",
|
"sidebarLicense": "Lizenz",
|
||||||
"sidebarClients": "Clients (Beta)",
|
"sidebarClients": "Kunden (Beta)",
|
||||||
"sidebarDomains": "Domains",
|
"sidebarDomains": "Domänen",
|
||||||
"enableDockerSocket": "Docker Socket aktivieren",
|
"enableDockerSocket": "Docker Blaupause aktivieren",
|
||||||
"enableDockerSocketDescription": "Docker Socket-Erkennung aktivieren, um Container-Informationen zu befüllen. Socket-Pfad muss Newt bereitgestellt werden.",
|
"enableDockerSocketDescription": "Aktiviere Docker-Socket-Label-Scraping für Blaupausenbeschriftungen. Der Socket-Pfad muss neu angegeben werden.",
|
||||||
"enableDockerSocketLink": "Mehr erfahren",
|
"enableDockerSocketLink": "Mehr erfahren",
|
||||||
"viewDockerContainers": "Docker Container anzeigen",
|
"viewDockerContainers": "Docker Container anzeigen",
|
||||||
"containersIn": "Container in {siteName}",
|
"containersIn": "Container in {siteName}",
|
||||||
@@ -1142,7 +1189,7 @@
|
|||||||
"certificateStatus": "Zertifikatsstatus",
|
"certificateStatus": "Zertifikatsstatus",
|
||||||
"loading": "Laden",
|
"loading": "Laden",
|
||||||
"restart": "Neustart",
|
"restart": "Neustart",
|
||||||
"domains": "Domains",
|
"domains": "Domänen",
|
||||||
"domainsDescription": "Domains für Ihre Organisation verwalten",
|
"domainsDescription": "Domains für Ihre Organisation verwalten",
|
||||||
"domainsSearch": "Domains durchsuchen...",
|
"domainsSearch": "Domains durchsuchen...",
|
||||||
"domainAdd": "Domain hinzufügen",
|
"domainAdd": "Domain hinzufügen",
|
||||||
@@ -1155,7 +1202,7 @@
|
|||||||
"domainMessageConfirm": "Um zu bestätigen, geben Sie bitte den Domainnamen unten ein.",
|
"domainMessageConfirm": "Um zu bestätigen, geben Sie bitte den Domainnamen unten ein.",
|
||||||
"domainConfirmDelete": "Domain-Löschung bestätigen",
|
"domainConfirmDelete": "Domain-Löschung bestätigen",
|
||||||
"domainDelete": "Domain löschen",
|
"domainDelete": "Domain löschen",
|
||||||
"domain": "Domain",
|
"domain": "Domäne",
|
||||||
"selectDomainTypeNsName": "Domain-Delegation (NS)",
|
"selectDomainTypeNsName": "Domain-Delegation (NS)",
|
||||||
"selectDomainTypeNsDescription": "Diese Domain und alle ihre Subdomains. Verwenden Sie dies, wenn Sie eine gesamte Domainzone kontrollieren möchten.",
|
"selectDomainTypeNsDescription": "Diese Domain und alle ihre Subdomains. Verwenden Sie dies, wenn Sie eine gesamte Domainzone kontrollieren möchten.",
|
||||||
"selectDomainTypeCnameName": "Einzelne Domain (CNAME)",
|
"selectDomainTypeCnameName": "Einzelne Domain (CNAME)",
|
||||||
@@ -1195,8 +1242,8 @@
|
|||||||
"sidebarExpand": "Erweitern",
|
"sidebarExpand": "Erweitern",
|
||||||
"newtUpdateAvailable": "Update verfügbar",
|
"newtUpdateAvailable": "Update verfügbar",
|
||||||
"newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.",
|
"newtUpdateAvailableInfo": "Eine neue Version von Newt ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für das beste Erlebnis.",
|
||||||
"domainPickerEnterDomain": "Domain",
|
"domainPickerEnterDomain": "Domäne",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com, oder einfach myapp",
|
"domainPickerPlaceholder": "myapp.example.com",
|
||||||
"domainPickerDescription": "Geben Sie die vollständige Domäne der Ressource ein, um verfügbare Optionen zu sehen.",
|
"domainPickerDescription": "Geben Sie die vollständige Domäne der Ressource ein, um verfügbare Optionen zu sehen.",
|
||||||
"domainPickerDescriptionSaas": "Geben Sie eine vollständige Domäne, Subdomäne oder einfach einen Namen ein, um verfügbare Optionen zu sehen",
|
"domainPickerDescriptionSaas": "Geben Sie eine vollständige Domäne, Subdomäne oder einfach einen Namen ein, um verfügbare Optionen zu sehen",
|
||||||
"domainPickerTabAll": "Alle",
|
"domainPickerTabAll": "Alle",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "Subdomain: {subdomain}",
|
"domainPickerSubdomain": "Subdomain: {subdomain}",
|
||||||
"domainPickerNamespace": "Namespace: {namespace}",
|
"domainPickerNamespace": "Namespace: {namespace}",
|
||||||
"domainPickerShowMore": "Mehr anzeigen",
|
"domainPickerShowMore": "Mehr anzeigen",
|
||||||
|
"regionSelectorTitle": "Region auswählen",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Abonnement wird geladen...",
|
||||||
|
"billingFreeTier": "Kostenlose Stufe",
|
||||||
|
"billingWarningOverLimit": "Warnung: Sie haben ein oder mehrere Nutzungslimits überschritten. Ihre Webseiten werden nicht verbunden, bis Sie Ihr Abonnement ändern oder Ihren Verbrauch anpassen.",
|
||||||
|
"billingUsageLimitsOverview": "Übersicht über Nutzungsgrenzen",
|
||||||
|
"billingMonitorUsage": "Überwachen Sie Ihren Verbrauch im Vergleich zu konfigurierten Grenzwerten. Wenn Sie eine Erhöhung der Limits benötigen, kontaktieren Sie uns bitte support@fossorial.io.",
|
||||||
|
"billingDataUsage": "Datenverbrauch",
|
||||||
|
"billingOnlineTime": "Online-Zeit der Seite",
|
||||||
|
"billingUsers": "Aktive Benutzer",
|
||||||
|
"billingDomains": "Aktive Domänen",
|
||||||
|
"billingRemoteExitNodes": "Aktive selbstgehostete Nodes",
|
||||||
|
"billingNoLimitConfigured": "Kein Limit konfiguriert",
|
||||||
|
"billingEstimatedPeriod": "Geschätzter Abrechnungszeitraum",
|
||||||
|
"billingIncludedUsage": "Inklusive Nutzung",
|
||||||
|
"billingIncludedUsageDescription": "Nutzung, die in Ihrem aktuellen Abonnementplan enthalten ist",
|
||||||
|
"billingFreeTierIncludedUsage": "Nutzungskontingente der kostenlosen Stufe",
|
||||||
|
"billingIncluded": "inbegriffen",
|
||||||
|
"billingEstimatedTotal": "Geschätzte Gesamtsumme:",
|
||||||
|
"billingNotes": "Notizen",
|
||||||
|
"billingEstimateNote": "Dies ist eine Schätzung basierend auf Ihrem aktuellen Verbrauch.",
|
||||||
|
"billingActualChargesMayVary": "Tatsächliche Kosten können variieren.",
|
||||||
|
"billingBilledAtEnd": "Sie werden am Ende des Abrechnungszeitraums in Rechnung gestellt.",
|
||||||
|
"billingModifySubscription": "Abonnement ändern",
|
||||||
|
"billingStartSubscription": "Abonnement starten",
|
||||||
|
"billingRecurringCharge": "Wiederkehrende Kosten",
|
||||||
|
"billingManageSubscriptionSettings": "Verwalten Sie Ihre Abonnement-Einstellungen und Präferenzen",
|
||||||
|
"billingNoActiveSubscription": "Sie haben kein aktives Abonnement. Starten Sie Ihr Abonnement, um Nutzungslimits zu erhöhen.",
|
||||||
|
"billingFailedToLoadSubscription": "Fehler beim Laden des Abonnements",
|
||||||
|
"billingFailedToLoadUsage": "Fehler beim Laden der Nutzung",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Fehler beim Abrufen der Checkout-URL",
|
||||||
|
"billingPleaseTryAgainLater": "Bitte versuchen Sie es später noch einmal.",
|
||||||
|
"billingCheckoutError": "Checkout-Fehler",
|
||||||
|
"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": "Ihnen wird für jeden Benutzer in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Benutzerkonten in Ihrer Organisation.",
|
||||||
|
"billingDomainInfo": "Ihnen wird für jede Domäne in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven Domänenkonten in Ihrer Organisation.",
|
||||||
|
"billingRemoteExitNodesInfo": "Ihnen wird für jeden verwalteten Node in Ihrer Organisation berechnet. Die Abrechnung erfolgt täglich basierend auf der Anzahl der aktiven verwalteten Nodes in Ihrer Organisation.",
|
||||||
"domainNotFound": "Domain nicht gefunden",
|
"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.",
|
"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",
|
"failed": "Fehlgeschlagen",
|
||||||
@@ -1274,27 +1363,28 @@
|
|||||||
"createDomainDnsPropagationDescription": "Es kann einige Zeit dauern, bis DNS-Änderungen im Internet verbreitet werden. Dies kann je nach Ihrem DNS-Provider und den TTL-Einstellungen von einigen Minuten bis zu 48 Stunden dauern.",
|
"createDomainDnsPropagationDescription": "Es kann einige Zeit dauern, bis DNS-Änderungen im Internet verbreitet werden. Dies kann je nach Ihrem DNS-Provider und den TTL-Einstellungen von einigen Minuten bis zu 48 Stunden dauern.",
|
||||||
"resourcePortRequired": "Portnummer ist für nicht-HTTP-Ressourcen erforderlich",
|
"resourcePortRequired": "Portnummer ist für nicht-HTTP-Ressourcen erforderlich",
|
||||||
"resourcePortNotAllowed": "Portnummer sollte für HTTP-Ressourcen nicht gesetzt werden",
|
"resourcePortNotAllowed": "Portnummer sollte für HTTP-Ressourcen nicht gesetzt werden",
|
||||||
|
"billingPricingCalculatorLink": "Preisrechner",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Ich stimme den",
|
"IAgreeToThe": "Ich stimme den",
|
||||||
"termsOfService": "Nutzungsbedingungen zu",
|
"termsOfService": "Nutzungsbedingungen zu",
|
||||||
"and": "und",
|
"and": "und",
|
||||||
"privacyPolicy": "Datenschutzrichtlinie"
|
"privacyPolicy": "Datenschutzrichtlinie"
|
||||||
},
|
},
|
||||||
"siteRequired": "Site ist erforderlich.",
|
"siteRequired": "Standort ist erforderlich.",
|
||||||
"olmTunnel": "Olm Tunnel",
|
"olmTunnel": "Olm-Tunnel",
|
||||||
"olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung",
|
"olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung",
|
||||||
"errorCreatingClient": "Fehler beim Erstellen des Clients",
|
"errorCreatingClient": "Fehler beim Erstellen des Clients",
|
||||||
"clientDefaultsNotFound": "Kundenvorgaben nicht gefunden",
|
"clientDefaultsNotFound": "Kundenvorgaben nicht gefunden",
|
||||||
"createClient": "Client erstellen",
|
"createClient": "Client erstellen",
|
||||||
"createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Sites.",
|
"createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Standorten.",
|
||||||
"seeAllClients": "Alle Clients anzeigen",
|
"seeAllClients": "Alle Clients anzeigen",
|
||||||
"clientInformation": "Kundeninformationen",
|
"clientInformation": "Kundeninformationen",
|
||||||
"clientNamePlaceholder": "Kundenname",
|
"clientNamePlaceholder": "Kundenname",
|
||||||
"address": "Adresse",
|
"address": "Adresse",
|
||||||
"subnetPlaceholder": "Subnetz",
|
"subnetPlaceholder": "Subnetz",
|
||||||
"addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.",
|
"addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.",
|
||||||
"selectSites": "Sites auswählen",
|
"selectSites": "Standorte auswählen",
|
||||||
"sitesDescription": "Der Client wird zu den ausgewählten Sites eine Verbindung haben.",
|
"sitesDescription": "Der Client wird zu den ausgewählten Standorten eine Verbindung haben.",
|
||||||
"clientInstallOlm": "Olm installieren",
|
"clientInstallOlm": "Olm installieren",
|
||||||
"clientInstallOlmDescription": "Olm auf Ihrem System zum Laufen bringen",
|
"clientInstallOlmDescription": "Olm auf Ihrem System zum Laufen bringen",
|
||||||
"clientOlmCredentials": "Olm-Zugangsdaten",
|
"clientOlmCredentials": "Olm-Zugangsdaten",
|
||||||
@@ -1309,14 +1399,329 @@
|
|||||||
"clientUpdatedDescription": "Der Client wurde aktualisiert.",
|
"clientUpdatedDescription": "Der Client wurde aktualisiert.",
|
||||||
"clientUpdateFailed": "Fehler beim Aktualisieren des Clients",
|
"clientUpdateFailed": "Fehler beim Aktualisieren des Clients",
|
||||||
"clientUpdateError": "Beim Aktualisieren des Clients ist ein Fehler aufgetreten.",
|
"clientUpdateError": "Beim Aktualisieren des Clients ist ein Fehler aufgetreten.",
|
||||||
"sitesFetchFailed": "Fehler beim Abrufen von Sites",
|
"sitesFetchFailed": "Fehler beim Abrufen von Standorten",
|
||||||
"sitesFetchError": "Beim Abrufen von Sites ist ein Fehler aufgetreten.",
|
"sitesFetchError": "Beim Abrufen von Standorten ist ein Fehler aufgetreten.",
|
||||||
"olmErrorFetchReleases": "Beim Abrufen von Olm-Veröffentlichungen ist ein Fehler aufgetreten.",
|
"olmErrorFetchReleases": "Beim Abrufen von Olm-Veröffentlichungen ist ein Fehler aufgetreten.",
|
||||||
"olmErrorFetchLatest": "Beim Abrufen der neuesten Olm-Veröffentlichung ist ein Fehler aufgetreten.",
|
"olmErrorFetchLatest": "Beim Abrufen der neuesten Olm-Veröffentlichung ist ein Fehler aufgetreten.",
|
||||||
"remoteSubnets": "Remote-Subnetze",
|
"remoteSubnets": "Remote-Subnetze",
|
||||||
"enterCidrRange": "Geben Sie den CIDR-Bereich ein",
|
"enterCidrRange": "Geben Sie den CIDR-Bereich ein",
|
||||||
"remoteSubnetsDescription": "Fügen Sie CIDR-Bereiche hinzu, die aus der Ferne auf diese Site zugreifen können. Verwenden Sie das Format wie 10.0.0.0/24 oder 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Fügen Sie CIDR-Bereiche hinzu, die über Clients von dieser Site aus remote zugänglich sind. Verwenden Sie ein Format wie 10.0.0.0/24. Dies gilt NUR für die VPN-Client-Konnektivität.",
|
||||||
"resourceEnableProxy": "Öffentlichen Proxy aktivieren",
|
"resourceEnableProxy": "Öffentlichen Proxy aktivieren",
|
||||||
"resourceEnableProxyDescription": "Ermöglichen Sie öffentliches Proxieren zu dieser Ressource. Dies ermöglicht den Zugriff auf die Ressource von außerhalb des Netzwerks durch die Cloud über einen offenen Port. Erfordert Traefik-Config.",
|
"resourceEnableProxyDescription": "Ermöglichen Sie öffentliches Proxieren zu dieser Ressource. Dies ermöglicht den Zugriff auf die Ressource von außerhalb des Netzwerks durch die Cloud über einen offenen Port. Erfordert Traefik-Config.",
|
||||||
"externalProxyEnabled": "Externer Proxy aktiviert"
|
"externalProxyEnabled": "Externer Proxy aktiviert",
|
||||||
|
"addNewTarget": "Neues Ziel hinzufügen",
|
||||||
|
"targetsList": "Ziel-Liste",
|
||||||
|
"targetErrorDuplicateTargetFound": "Doppeltes Ziel gefunden",
|
||||||
|
"healthCheckHealthy": "Gesund",
|
||||||
|
"healthCheckUnhealthy": "Ungesund",
|
||||||
|
"healthCheckUnknown": "Unbekannt",
|
||||||
|
"healthCheck": "Gesundheits-Check",
|
||||||
|
"configureHealthCheck": "Gesundheits-Check konfigurieren",
|
||||||
|
"configureHealthCheckDescription": "Richten Sie die Gesundheitsüberwachung für {target} ein",
|
||||||
|
"enableHealthChecks": "Gesundheits-Checks aktivieren",
|
||||||
|
"enableHealthChecksDescription": "Überwachen Sie die Gesundheit dieses Ziels. Bei Bedarf können Sie einen anderen Endpunkt als das Ziel überwachen.",
|
||||||
|
"healthScheme": "Methode",
|
||||||
|
"healthSelectScheme": "Methode auswählen",
|
||||||
|
"healthCheckPath": "Pfad",
|
||||||
|
"healthHostname": "IP / Host",
|
||||||
|
"healthPort": "Port",
|
||||||
|
"healthCheckPathDescription": "Der Pfad zum Überprüfen des Gesundheitszustands.",
|
||||||
|
"healthyIntervalSeconds": "Gesunder Intervall",
|
||||||
|
"unhealthyIntervalSeconds": "Ungesunder Intervall",
|
||||||
|
"IntervalSeconds": "Gesunder Intervall",
|
||||||
|
"timeoutSeconds": "Timeout",
|
||||||
|
"timeIsInSeconds": "Zeit ist in Sekunden",
|
||||||
|
"retryAttempts": "Wiederholungsversuche",
|
||||||
|
"expectedResponseCodes": "Erwartete Antwortcodes",
|
||||||
|
"expectedResponseCodesDescription": "HTTP-Statuscode, der einen gesunden Zustand anzeigt. Wenn leer gelassen, wird 200-300 als gesund angesehen.",
|
||||||
|
"customHeaders": "Eigene Kopfzeilen",
|
||||||
|
"customHeadersDescription": "Header neue Zeile getrennt: Header-Name: Wert",
|
||||||
|
"headersValidationError": "Header müssen im Format Header-Name: Wert sein.",
|
||||||
|
"saveHealthCheck": "Gesundheits-Check speichern",
|
||||||
|
"healthCheckSaved": "Gesundheits-Check gespeichert",
|
||||||
|
"healthCheckSavedDescription": "Die Konfiguration des Gesundheits-Checks wurde erfolgreich gespeichert",
|
||||||
|
"healthCheckError": "Fehler beim Gesundheits-Check",
|
||||||
|
"healthCheckErrorDescription": "Beim Speichern der Gesundheits-Check-Konfiguration ist ein Fehler aufgetreten",
|
||||||
|
"healthCheckPathRequired": "Gesundheits-Check-Pfad ist erforderlich",
|
||||||
|
"healthCheckMethodRequired": "HTTP-Methode ist erforderlich",
|
||||||
|
"healthCheckIntervalMin": "Prüfintervall muss mindestens 5 Sekunden betragen",
|
||||||
|
"healthCheckTimeoutMin": "Timeout muss mindestens 1 Sekunde betragen",
|
||||||
|
"healthCheckRetryMin": "Wiederholungsversuche müssen mindestens 1 betragen",
|
||||||
|
"httpMethod": "HTTP-Methode",
|
||||||
|
"selectHttpMethod": "HTTP-Methode auswählen",
|
||||||
|
"domainPickerSubdomainLabel": "Subdomain",
|
||||||
|
"domainPickerBaseDomainLabel": "Basisdomäne",
|
||||||
|
"domainPickerSearchDomains": "Domains suchen...",
|
||||||
|
"domainPickerNoDomainsFound": "Keine Domains gefunden",
|
||||||
|
"domainPickerLoadingDomains": "Domains werden geladen...",
|
||||||
|
"domainPickerSelectBaseDomain": "Basisdomäne auswählen...",
|
||||||
|
"domainPickerNotAvailableForCname": "Für CNAME-Domains nicht verfügbar",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Geben Sie eine Subdomain ein oder lassen Sie das Feld leer, um die Basisdomäne zu verwenden.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Geben Sie eine Subdomain ein, um verfügbare freie Domains zu suchen und auszuwählen.",
|
||||||
|
"domainPickerFreeDomains": "Freie Domains",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Verfügbare Domains suchen",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Hinweis: Kostenlose bereitgestellte Domains sind derzeit nicht für selbstgehostete Instanzen verfügbar.",
|
||||||
|
"resourceDomain": "Domäne",
|
||||||
|
"resourceEditDomain": "Domain bearbeiten",
|
||||||
|
"siteName": "Site-Name",
|
||||||
|
"proxyPort": "Port",
|
||||||
|
"resourcesTableProxyResources": "Proxy-Ressourcen",
|
||||||
|
"resourcesTableClientResources": "Client-Ressourcen",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Keine Proxy-Ressourcen gefunden.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Keine internen Ressourcen gefunden.",
|
||||||
|
"resourcesTableDestination": "Ziel",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Diese Ressourcen sind zur Verwendung mit",
|
||||||
|
"resourcesTableClients": "Kunden",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "und sind nur intern zugänglich, wenn mit einem Client verbunden.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Client-Ressource bearbeiten",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Aktualisieren Sie die Ressourceneigenschaften und die Zielkonfiguration für {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Ressourceneigenschaften",
|
||||||
|
"editInternalResourceDialogName": "Name",
|
||||||
|
"editInternalResourceDialogProtocol": "Protokoll",
|
||||||
|
"editInternalResourceDialogSitePort": "Site-Port",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Zielkonfiguration",
|
||||||
|
"editInternalResourceDialogCancel": "Abbrechen",
|
||||||
|
"editInternalResourceDialogSaveResource": "Ressource speichern",
|
||||||
|
"editInternalResourceDialogSuccess": "Erfolg",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Interne Ressource erfolgreich aktualisiert",
|
||||||
|
"editInternalResourceDialogError": "Fehler",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Interne Ressource konnte nicht aktualisiert werden",
|
||||||
|
"editInternalResourceDialogNameRequired": "Name ist erforderlich",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Der Name darf nicht länger als 255 Zeichen sein",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Proxy-Port muss mindestens 1 sein",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Proxy-Port muss kleiner als 65536 sein",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Ungültiges IP-Adressformat",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Ziel-Port muss mindestens 1 sein",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Ziel-Port muss kleiner als 65536 sein",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Keine Sites verfügbar",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Sie müssen mindestens eine Newt-Site mit einem konfigurierten Subnetz haben, um interne Ressourcen zu erstellen.",
|
||||||
|
"createInternalResourceDialogClose": "Schließen",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Ressource erstellen",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Erstellen Sie eine neue Ressource, die für Clients zugänglich ist, die mit der ausgewählten Site verbunden sind.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Ressourceneigenschaften",
|
||||||
|
"createInternalResourceDialogName": "Name",
|
||||||
|
"createInternalResourceDialogSite": "Standort",
|
||||||
|
"createInternalResourceDialogSelectSite": "Standort auswählen...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Sites durchsuchen...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Keine Standorte gefunden.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protokoll",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Site-Port",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Verwenden Sie diesen Port, um bei Verbindung mit einem Client auf die Ressource an der Site zuzugreifen.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Zielkonfiguration",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "Die IP-Adresse oder Hostname Adresse der Ressource im Netzwerk der Website.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "Der Port auf der Ziel-IP, unter dem die Ressource zugänglich ist.",
|
||||||
|
"createInternalResourceDialogCancel": "Abbrechen",
|
||||||
|
"createInternalResourceDialogCreateResource": "Ressource erstellen",
|
||||||
|
"createInternalResourceDialogSuccess": "Erfolg",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Interne Ressource erfolgreich erstellt",
|
||||||
|
"createInternalResourceDialogError": "Fehler",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Interne Ressource konnte nicht erstellt werden",
|
||||||
|
"createInternalResourceDialogNameRequired": "Name ist erforderlich",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Der Name darf nicht länger als 255 Zeichen sein",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Bitte wählen Sie eine Site aus",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Proxy-Port muss mindestens 1 sein",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Proxy-Port muss kleiner als 65536 sein",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Ungültiges IP-Adressformat",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Ziel-Port muss mindestens 1 sein",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Ziel-Port muss kleiner als 65536 sein",
|
||||||
|
"siteConfiguration": "Konfiguration",
|
||||||
|
"siteAcceptClientConnections": "Clientverbindungen akzeptieren",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Erlauben Sie anderen Geräten, über diese Newt-Instanz mit Clients als Gateway zu verbinden.",
|
||||||
|
"siteAddress": "Site-Adresse",
|
||||||
|
"siteAddressDescription": "Geben Sie die IP-Adresse des Hosts an, mit dem sich die Clients verbinden sollen. Dies ist die interne Adresse der Site im Pangolin-Netzwerk, die von Clients angesprochen werden muss. Muss innerhalb des Unternehmens-Subnetzes liegen.",
|
||||||
|
"autoLoginExternalIdp": "Automatische Anmeldung mit externem IDP",
|
||||||
|
"autoLoginExternalIdpDescription": "Leiten Sie den Benutzer sofort zur Authentifizierung an den externen IDP weiter.",
|
||||||
|
"selectIdp": "IDP auswählen",
|
||||||
|
"selectIdpPlaceholder": "Wählen Sie einen IDP...",
|
||||||
|
"selectIdpRequired": "Bitte wählen Sie einen IDP aus, wenn automatische Anmeldung aktiviert ist.",
|
||||||
|
"autoLoginTitle": "Weiterleitung",
|
||||||
|
"autoLoginDescription": "Sie werden zum externen Identitätsanbieter zur Authentifizierung weitergeleitet.",
|
||||||
|
"autoLoginProcessing": "Authentifizierung vorbereiten...",
|
||||||
|
"autoLoginRedirecting": "Weiterleitung zur Anmeldung...",
|
||||||
|
"autoLoginError": "Fehler bei der automatischen Anmeldung",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "Keine Weiterleitungs-URL vom Identitätsanbieter erhalten.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Fehler beim Generieren der Authentifizierungs-URL.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Selbst-Hosted verwalten",
|
||||||
|
"remoteExitNodeDescription": "Knoten verwalten, um die Netzwerkverbindung zu erweitern",
|
||||||
|
"remoteExitNodes": "Knoten",
|
||||||
|
"searchRemoteExitNodes": "Knoten suchen...",
|
||||||
|
"remoteExitNodeAdd": "Knoten hinzufügen",
|
||||||
|
"remoteExitNodeErrorDelete": "Fehler beim Löschen des Knotens",
|
||||||
|
"remoteExitNodeQuestionRemove": "Sind Sie sicher, dass Sie den Knoten {selectedNode} aus der Organisation entfernen möchten?",
|
||||||
|
"remoteExitNodeMessageRemove": "Einmal entfernt, wird der Knoten nicht mehr zugänglich sein.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Um zu bestätigen, geben Sie bitte den Namen des Knotens unten ein.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Löschknoten bestätigen",
|
||||||
|
"remoteExitNodeDelete": "Knoten löschen",
|
||||||
|
"sidebarRemoteExitNodes": "Knoten",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Knoten erstellen",
|
||||||
|
"description": "Erstellen Sie einen neuen Knoten, um Ihre Netzwerkverbindung zu erweitern",
|
||||||
|
"viewAllButton": "Alle Knoten anzeigen",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Erstellungsstrategie",
|
||||||
|
"description": "Wählen Sie diese Option, um Ihren Knoten manuell zu konfigurieren oder neue Zugangsdaten zu generieren.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Node übernehmen",
|
||||||
|
"description": "Wählen Sie dies, wenn Sie bereits die Anmeldedaten für den Knoten haben."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Schlüssel generieren",
|
||||||
|
"description": "Wählen Sie dies, wenn Sie neue Schlüssel für den Knoten generieren möchten"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Vorhandenen Node übernehmen",
|
||||||
|
"description": "Geben Sie die Zugangsdaten des vorhandenen Knotens ein, den Sie übernehmen möchten",
|
||||||
|
"nodeIdLabel": "Knoten-ID",
|
||||||
|
"nodeIdDescription": "Die ID des vorhandenen Knotens, den Sie übernehmen möchten",
|
||||||
|
"secretLabel": "Geheimnis",
|
||||||
|
"secretDescription": "Der geheime Schlüssel des vorhandenen Knotens",
|
||||||
|
"submitButton": "Node übernehmen"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Generierte Anmeldedaten",
|
||||||
|
"description": "Verwenden Sie diese generierten Anmeldeinformationen, um Ihren Knoten zu konfigurieren",
|
||||||
|
"nodeIdTitle": "Knoten-ID",
|
||||||
|
"secretTitle": "Geheimnis",
|
||||||
|
"saveCredentialsTitle": "Anmeldedaten zur Konfiguration hinzufügen",
|
||||||
|
"saveCredentialsDescription": "Fügen Sie diese Anmeldedaten zu Ihrer selbst-gehosteten Pangolin Node-Konfigurationsdatei hinzu, um die Verbindung abzuschließen.",
|
||||||
|
"submitButton": "Knoten erstellen"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "Knoten-ID und Geheimnis sind erforderlich, wenn ein existierender Knoten angenommen wird"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Fehler beim Laden der Standardeinstellungen",
|
||||||
|
"defaultsNotLoaded": "Standardeinstellungen nicht geladen",
|
||||||
|
"createFailed": "Knoten konnte nicht erstellt werden"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Knoten erfolgreich erstellt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Knotenauswahl",
|
||||||
|
"remoteExitNodeSelectionDescription": "Wählen Sie einen Knoten aus, durch den Traffic für diese lokale Seite geleitet werden soll",
|
||||||
|
"remoteExitNodeRequired": "Ein Knoten muss für lokale Seiten ausgewählt sein",
|
||||||
|
"noRemoteExitNodesAvailable": "Keine Knoten verfügbar",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Für diese Organisation sind keine Knoten verfügbar. Erstellen Sie zuerst einen Knoten, um lokale Sites zu verwenden.",
|
||||||
|
"exitNode": "Exit-Node",
|
||||||
|
"country": "Land",
|
||||||
|
"rulesMatchCountry": "Derzeit basierend auf der Quell-IP",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Verwaltetes Selbsthosted",
|
||||||
|
"description": "Zuverlässiger und wartungsarmer Pangolin Server mit zusätzlichen Glocken und Pfeifen",
|
||||||
|
"introTitle": "Verwalteter selbstgehosteter Pangolin",
|
||||||
|
"introDescription": "ist eine Deployment-Option, die für Personen konzipiert wurde, die Einfachheit und zusätzliche Zuverlässigkeit wünschen, während sie ihre Daten privat und selbstgehostet halten.",
|
||||||
|
"introDetail": "Mit dieser Option haben Sie immer noch Ihren eigenen Pangolin-Knoten – Ihre Tunnel, SSL-Terminierung und Traffic bleiben auf Ihrem Server. Der Unterschied besteht darin, dass Verwaltung und Überwachung über unser Cloud-Dashboard abgewickelt werden, das eine Reihe von Vorteilen freischaltet:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Einfachere Operationen",
|
||||||
|
"description": "Sie brauchen keinen eigenen Mail-Server auszuführen oder komplexe Warnungen einzurichten. Sie erhalten Gesundheitschecks und Ausfallwarnungen aus dem Box."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Automatische Updates",
|
||||||
|
"description": "Das Cloud-Dashboard entwickelt sich schnell, so dass Sie neue Funktionen und Fehlerbehebungen erhalten, ohne jedes Mal neue Container manuell ziehen zu müssen."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Weniger Wartung",
|
||||||
|
"description": "Keine Datenbankmigrationen, Sicherungen oder zusätzliche Infrastruktur zum Verwalten. Wir kümmern uns um das in der Cloud."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Cloud-Ausfall",
|
||||||
|
"description": "Wenn Ihr Knoten runtergeht, können Ihre Tunnel vorübergehend an unsere Cloud-Punkte scheitern, bis Sie ihn wieder online bringen."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Hohe Verfügbarkeit (PoPs)",
|
||||||
|
"description": "Sie können auch mehrere Knoten an Ihr Konto anhängen, um Redundanz und bessere Leistung zu erzielen."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Zukünftige Verbesserungen",
|
||||||
|
"description": "Wir planen weitere Analyse-, Alarm- und Management-Tools hinzuzufügen, um Ihren Einsatz noch robuster zu machen."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Erfahren Sie mehr über die Managed Self-Hosted Option in unserer",
|
||||||
|
"documentation": "dokumentation"
|
||||||
|
},
|
||||||
|
"convertButton": "Diesen Knoten in Managed Self-Hosted umwandeln"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Internationale Domain erkannt",
|
||||||
|
"willbestoredas": "Wird gespeichert als:",
|
||||||
|
"roleMappingDescription": "Legen Sie fest, wie den Benutzern Rollen zugewiesen werden, wenn sie sich anmelden, wenn Auto Provision aktiviert ist.",
|
||||||
|
"selectRole": "Wählen Sie eine Rolle",
|
||||||
|
"roleMappingExpression": "Ausdruck",
|
||||||
|
"selectRolePlaceholder": "Rolle auswählen",
|
||||||
|
"selectRoleDescription": "Wählen Sie eine Rolle aus, die allen Benutzern von diesem Identitätsprovider zugewiesen werden soll",
|
||||||
|
"roleMappingExpressionDescription": "Geben Sie einen JMESPath-Ausdruck ein, um Rolleninformationen aus dem ID-Token zu extrahieren",
|
||||||
|
"idpTenantIdRequired": "Mandant ID ist erforderlich",
|
||||||
|
"invalidValue": "Ungültiger Wert",
|
||||||
|
"idpTypeLabel": "Identitätsanbietertyp",
|
||||||
|
"roleMappingExpressionPlaceholder": "z. B. enthalten(Gruppen, 'admin') && 'Admin' || 'Mitglied'",
|
||||||
|
"idpGoogleConfiguration": "Google-Konfiguration",
|
||||||
|
"idpGoogleConfigurationDescription": "Konfigurieren Sie Ihre Google OAuth2 Zugangsdaten",
|
||||||
|
"idpGoogleClientIdDescription": "Ihre Google OAuth2 Client-ID",
|
||||||
|
"idpGoogleClientSecretDescription": "Ihr Google OAuth2 Client Secret",
|
||||||
|
"idpAzureConfiguration": "Azure Entra ID Konfiguration",
|
||||||
|
"idpAzureConfigurationDescription": "Konfigurieren Sie Ihre Azure Entra ID OAuth2 Zugangsdaten",
|
||||||
|
"idpTenantId": "Mandanten-ID",
|
||||||
|
"idpTenantIdPlaceholder": "deine Mandant-ID",
|
||||||
|
"idpAzureTenantIdDescription": "Ihre Azure Mieter-ID (gefunden in Azure Active Directory Übersicht)",
|
||||||
|
"idpAzureClientIdDescription": "Ihre Azure App Registration Client ID",
|
||||||
|
"idpAzureClientSecretDescription": "Ihr Azure App Registration Client Secret",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Google-Konfiguration",
|
||||||
|
"idpAzureConfigurationTitle": "Azure Entra ID Konfiguration",
|
||||||
|
"idpTenantIdLabel": "Mandanten-ID",
|
||||||
|
"idpAzureClientIdDescription2": "Ihre Azure App Registration Client ID",
|
||||||
|
"idpAzureClientSecretDescription2": "Ihr Azure App Registration Client Secret",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC Provider",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Subnetz",
|
||||||
|
"subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.",
|
||||||
|
"authPage": "Auth Seite",
|
||||||
|
"authPageDescription": "Konfigurieren Sie die Auth-Seite für Ihre Organisation",
|
||||||
|
"authPageDomain": "Domain der Auth Seite",
|
||||||
|
"noDomainSet": "Keine Domäne gesetzt",
|
||||||
|
"changeDomain": "Domain ändern",
|
||||||
|
"selectDomain": "Domain auswählen",
|
||||||
|
"restartCertificate": "Zertifikat neu starten",
|
||||||
|
"editAuthPageDomain": "Auth Page Domain bearbeiten",
|
||||||
|
"setAuthPageDomain": "Domain der Auth Seite festlegen",
|
||||||
|
"failedToFetchCertificate": "Zertifikat konnte nicht abgerufen werden",
|
||||||
|
"failedToRestartCertificate": "Zertifikat konnte nicht neu gestartet werden",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Fügen Sie eine Domain hinzu, um benutzerdefinierte Authentifizierungsseiten für Ihre Organisation zu aktivieren",
|
||||||
|
"selectDomainForOrgAuthPage": "Wählen Sie eine Domain für die Authentifizierungsseite der Organisation",
|
||||||
|
"domainPickerProvidedDomain": "Angegebene Domain",
|
||||||
|
"domainPickerFreeProvidedDomain": "Kostenlose Domain",
|
||||||
|
"domainPickerVerified": "Verifiziert",
|
||||||
|
"domainPickerUnverified": "Nicht verifiziert",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Diese Subdomain enthält ungültige Zeichen oder Struktur. Sie wird beim Speichern automatisch bereinigt.",
|
||||||
|
"domainPickerError": "Fehler",
|
||||||
|
"domainPickerErrorLoadDomains": "Fehler beim Laden der Organisations-Domänen",
|
||||||
|
"domainPickerErrorCheckAvailability": "Fehler beim Prüfen der Domain-Verfügbarkeit",
|
||||||
|
"domainPickerInvalidSubdomain": "Ungültige Subdomain",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "Die Eingabe \"{sub}\" wurde entfernt, weil sie nicht gültig ist.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" konnte nicht für {domain} gültig gemacht werden.",
|
||||||
|
"domainPickerSubdomainSanitized": "Subdomain bereinigt",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" wurde korrigiert zu \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Bei Ihrer Organisation anmelden",
|
||||||
|
"orgAuthChooseIdpDescription": "Wähle deinen Identitätsanbieter um fortzufahren",
|
||||||
|
"orgAuthNoIdpConfigured": "Diese Organisation hat keine Identitätsanbieter konfiguriert. Sie können sich stattdessen mit Ihrer Pangolin-Identität anmelden.",
|
||||||
|
"orgAuthSignInWithPangolin": "Mit Pangolin anmelden",
|
||||||
|
"subscriptionRequiredToUse": "Um diese Funktion nutzen zu können, ist ein Abonnement erforderlich.",
|
||||||
|
"idpDisabled": "Identitätsanbieter sind deaktiviert.",
|
||||||
|
"orgAuthPageDisabled": "Organisations-Authentifizierungsseite ist deaktiviert.",
|
||||||
|
"domainRestartedDescription": "Domain-Verifizierung erfolgreich neu gestartet",
|
||||||
|
"resourceAddEntrypointsEditFile": "Datei bearbeiten: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Datei bearbeiten: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "E-Mail-Verifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Kommen Sie dann wieder hierher.",
|
||||||
|
"twoFactorSetupRequired": "Die Zwei-Faktor-Authentifizierung ist erforderlich. Bitte melden Sie sich erneut über {dashboardUrl}/auth/login an. Dann kommen Sie hierher zurück.",
|
||||||
|
"authPageErrorUpdateMessage": "Beim Aktualisieren der Auth-Seiten-Einstellungen ist ein Fehler aufgetreten",
|
||||||
|
"authPageUpdated": "Auth-Seite erfolgreich aktualisiert",
|
||||||
|
"healthCheckNotAvailable": "Lokal",
|
||||||
|
"rewritePath": "Pfad neu schreiben",
|
||||||
|
"rewritePathDescription": "Optional den Pfad umschreiben, bevor er an das Ziel weitergeleitet wird."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"setupCreate": "Create your organization, site, and resources",
|
"setupCreate": "Create your organization, site, and resources",
|
||||||
"setupNewOrg": "New Organization",
|
"setupNewOrg": "New Organization",
|
||||||
@@ -94,7 +95,9 @@
|
|||||||
"siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.",
|
"siteNewtTunnelDescription": "Easiest way to create an entrypoint into your network. No extra setup.",
|
||||||
"siteWg": "Basic WireGuard",
|
"siteWg": "Basic WireGuard",
|
||||||
"siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
"siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
||||||
|
"siteWgDescriptionSaas": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
||||||
"siteLocalDescription": "Local resources only. No tunneling.",
|
"siteLocalDescription": "Local resources only. No tunneling.",
|
||||||
|
"siteLocalDescriptionSaas": "Local resources only. No tunneling.",
|
||||||
"siteSeeAll": "See All Sites",
|
"siteSeeAll": "See All Sites",
|
||||||
"siteTunnelDescription": "Determine how you want to connect to your site",
|
"siteTunnelDescription": "Determine how you want to connect to your site",
|
||||||
"siteNewtCredentials": "Newt Credentials",
|
"siteNewtCredentials": "Newt Credentials",
|
||||||
@@ -157,7 +160,7 @@
|
|||||||
"resourceHTTP": "HTTPS Resource",
|
"resourceHTTP": "HTTPS Resource",
|
||||||
"resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.",
|
"resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.",
|
||||||
"resourceRaw": "Raw TCP/UDP Resource",
|
"resourceRaw": "Raw TCP/UDP Resource",
|
||||||
"resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.",
|
"resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number. This only works when sites are connected to nodes.",
|
||||||
"resourceCreate": "Create Resource",
|
"resourceCreate": "Create Resource",
|
||||||
"resourceCreateDescription": "Follow the steps below to create a new resource",
|
"resourceCreateDescription": "Follow the steps below to create a new resource",
|
||||||
"resourceSeeAll": "See All Resources",
|
"resourceSeeAll": "See All Resources",
|
||||||
@@ -166,7 +169,10 @@
|
|||||||
"siteSelect": "Select site",
|
"siteSelect": "Select site",
|
||||||
"siteSearch": "Search site",
|
"siteSearch": "Search site",
|
||||||
"siteNotFound": "No site found.",
|
"siteNotFound": "No site found.",
|
||||||
"siteSelectionDescription": "This site will provide connectivity to the resource.",
|
"selectCountry": "Select country",
|
||||||
|
"searchCountries": "Search countries...",
|
||||||
|
"noCountryFound": "No country found.",
|
||||||
|
"siteSelectionDescription": "This site will provide connectivity to the target.",
|
||||||
"resourceType": "Resource Type",
|
"resourceType": "Resource Type",
|
||||||
"resourceTypeDescription": "Determine how you want to access your resource",
|
"resourceTypeDescription": "Determine how you want to access your resource",
|
||||||
"resourceHTTPSSettings": "HTTPS Settings",
|
"resourceHTTPSSettings": "HTTPS Settings",
|
||||||
@@ -197,11 +203,13 @@
|
|||||||
"general": "General",
|
"general": "General",
|
||||||
"generalSettings": "General Settings",
|
"generalSettings": "General Settings",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
|
"internal": "Internal",
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"resourceSettingDescription": "Configure the settings on your resource",
|
"resourceSettingDescription": "Configure the settings on your resource",
|
||||||
"resourceSetting": "{resourceName} Settings",
|
"resourceSetting": "{resourceName} Settings",
|
||||||
"alwaysAllow": "Always Allow",
|
"alwaysAllow": "Always Allow",
|
||||||
"alwaysDeny": "Always Deny",
|
"alwaysDeny": "Always Deny",
|
||||||
|
"passToAuth": "Pass to Auth",
|
||||||
"orgSettingsDescription": "Configure your organization's general settings",
|
"orgSettingsDescription": "Configure your organization's general settings",
|
||||||
"orgGeneralSettings": "Organization Settings",
|
"orgGeneralSettings": "Organization Settings",
|
||||||
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
||||||
@@ -450,6 +458,8 @@
|
|||||||
"accessRoleErrorAddDescription": "An error occurred while adding user to the role.",
|
"accessRoleErrorAddDescription": "An error occurred while adding user to the role.",
|
||||||
"userSaved": "User saved",
|
"userSaved": "User saved",
|
||||||
"userSavedDescription": "The user has been updated.",
|
"userSavedDescription": "The user has been updated.",
|
||||||
|
"autoProvisioned": "Auto Provisioned",
|
||||||
|
"autoProvisionedDescription": "Allow this user to be automatically managed by identity provider",
|
||||||
"accessControlsDescription": "Manage what this user can access and do in the organization",
|
"accessControlsDescription": "Manage what this user can access and do in the organization",
|
||||||
"accessControlsSubmit": "Save Access Controls",
|
"accessControlsSubmit": "Save Access Controls",
|
||||||
"roles": "Roles",
|
"roles": "Roles",
|
||||||
@@ -490,7 +500,7 @@
|
|||||||
"targetTlsSniDescription": "The TLS Server Name to use for SNI. Leave empty to use the default.",
|
"targetTlsSniDescription": "The TLS Server Name to use for SNI. Leave empty to use the default.",
|
||||||
"targetTlsSubmit": "Save Settings",
|
"targetTlsSubmit": "Save Settings",
|
||||||
"targets": "Targets Configuration",
|
"targets": "Targets Configuration",
|
||||||
"targetsDescription": "Set up targets to route traffic to your services",
|
"targetsDescription": "Set up targets to route traffic to your backend services",
|
||||||
"targetStickySessions": "Enable Sticky Sessions",
|
"targetStickySessions": "Enable Sticky Sessions",
|
||||||
"targetStickySessionsDescription": "Keep connections on the same backend target for their entire session.",
|
"targetStickySessionsDescription": "Keep connections on the same backend target for their entire session.",
|
||||||
"methodSelect": "Select method",
|
"methodSelect": "Select method",
|
||||||
@@ -507,6 +517,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Invalid IP address format",
|
"ipAddressErrorInvalidFormat": "Invalid IP address format",
|
||||||
"ipAddressErrorInvalidOctet": "Invalid IP address octet",
|
"ipAddressErrorInvalidOctet": "Invalid IP address octet",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
|
"matchPath": "Match Path",
|
||||||
"ipAddressRange": "IP Range",
|
"ipAddressRange": "IP Range",
|
||||||
"rulesErrorFetch": "Failed to fetch rules",
|
"rulesErrorFetch": "Failed to fetch rules",
|
||||||
"rulesErrorFetchDescription": "An error occurred while fetching rules",
|
"rulesErrorFetchDescription": "An error occurred while fetching rules",
|
||||||
@@ -542,6 +553,7 @@
|
|||||||
"rulesActions": "Actions",
|
"rulesActions": "Actions",
|
||||||
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
||||||
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
||||||
|
"rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted",
|
||||||
"rulesMatchCriteria": "Matching Criteria",
|
"rulesMatchCriteria": "Matching Criteria",
|
||||||
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
||||||
@@ -833,6 +845,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN must be exactly 6 digits",
|
"pincodeRequirementsLength": "PIN must be exactly 6 digits",
|
||||||
"pincodeRequirementsChars": "PIN must only contain numbers",
|
"pincodeRequirementsChars": "PIN must only contain numbers",
|
||||||
"passwordRequirementsLength": "Password must be at least 1 character long",
|
"passwordRequirementsLength": "Password must be at least 1 character long",
|
||||||
|
"passwordRequirementsTitle": "Password requirements:",
|
||||||
|
"passwordRequirementLength": "At least 8 characters long",
|
||||||
|
"passwordRequirementUppercase": "At least one uppercase letter",
|
||||||
|
"passwordRequirementLowercase": "At least one lowercase letter",
|
||||||
|
"passwordRequirementNumber": "At least one number",
|
||||||
|
"passwordRequirementSpecial": "At least one special character",
|
||||||
|
"passwordRequirementsMet": "✓ Password meets all requirements",
|
||||||
|
"passwordStrength": "Password strength",
|
||||||
|
"passwordStrengthWeak": "Weak",
|
||||||
|
"passwordStrengthMedium": "Medium",
|
||||||
|
"passwordStrengthStrong": "Strong",
|
||||||
|
"passwordRequirements": "Requirements:",
|
||||||
|
"passwordRequirementLengthText": "8+ characters",
|
||||||
|
"passwordRequirementUppercaseText": "Uppercase letter (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Lowercase letter (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Number (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Special character (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Passwords do not match",
|
||||||
"otpEmailRequirementsLength": "OTP must be at least 1 character long",
|
"otpEmailRequirementsLength": "OTP must be at least 1 character long",
|
||||||
"otpEmailSent": "OTP Sent",
|
"otpEmailSent": "OTP Sent",
|
||||||
"otpEmailSentDescription": "An OTP has been sent to your email",
|
"otpEmailSentDescription": "An OTP has been sent to your email",
|
||||||
@@ -952,12 +982,15 @@
|
|||||||
"logoutError": "Error logging out",
|
"logoutError": "Error logging out",
|
||||||
"signingAs": "Signed in as",
|
"signingAs": "Signed in as",
|
||||||
"serverAdmin": "Server Admin",
|
"serverAdmin": "Server Admin",
|
||||||
|
"managedSelfhosted": "Managed Self-Hosted",
|
||||||
"otpEnable": "Enable Two-factor",
|
"otpEnable": "Enable Two-factor",
|
||||||
"otpDisable": "Disable Two-factor",
|
"otpDisable": "Disable Two-factor",
|
||||||
"logout": "Log Out",
|
"logout": "Log Out",
|
||||||
"licenseTierProfessionalRequired": "Professional Edition Required",
|
"licenseTierProfessionalRequired": "Professional Edition Required",
|
||||||
"licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.",
|
"licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.",
|
||||||
"actionGetOrg": "Get Organization",
|
"actionGetOrg": "Get Organization",
|
||||||
|
"updateOrgUser": "Update Org User",
|
||||||
|
"createOrgUser": "Create Org User",
|
||||||
"actionUpdateOrg": "Update Organization",
|
"actionUpdateOrg": "Update Organization",
|
||||||
"actionUpdateUser": "Update User",
|
"actionUpdateUser": "Update User",
|
||||||
"actionGetUser": "Get User",
|
"actionGetUser": "Get User",
|
||||||
@@ -967,6 +1000,10 @@
|
|||||||
"actionDeleteSite": "Delete Site",
|
"actionDeleteSite": "Delete Site",
|
||||||
"actionGetSite": "Get Site",
|
"actionGetSite": "Get Site",
|
||||||
"actionListSites": "List Sites",
|
"actionListSites": "List Sites",
|
||||||
|
"actionApplyBlueprint": "Apply Blueprint",
|
||||||
|
"setupToken": "Setup Token",
|
||||||
|
"setupTokenDescription": "Enter the setup token from the server console.",
|
||||||
|
"setupTokenRequired": "Setup token is required",
|
||||||
"actionUpdateSite": "Update Site",
|
"actionUpdateSite": "Update Site",
|
||||||
"actionListSiteRoles": "List Allowed Site Roles",
|
"actionListSiteRoles": "List Allowed Site Roles",
|
||||||
"actionCreateResource": "Create Resource",
|
"actionCreateResource": "Create Resource",
|
||||||
@@ -1004,6 +1041,26 @@
|
|||||||
"actionDeleteResourceRule": "Delete Resource Rule",
|
"actionDeleteResourceRule": "Delete Resource Rule",
|
||||||
"actionListResourceRules": "List Resource Rules",
|
"actionListResourceRules": "List Resource Rules",
|
||||||
"actionUpdateResourceRule": "Update Resource Rule",
|
"actionUpdateResourceRule": "Update Resource Rule",
|
||||||
|
"ruleTemplates": "Rule Templates",
|
||||||
|
"ruleTemplatesDescription": "Assign rule templates to automatically apply consistent rules across multiple resources",
|
||||||
|
"ruleTemplatesSearch": "Search templates...",
|
||||||
|
"ruleTemplateAdd": "Create Template",
|
||||||
|
"ruleTemplateErrorDelete": "Failed to delete template",
|
||||||
|
"ruleTemplateCreated": "Template created",
|
||||||
|
"ruleTemplateCreatedDescription": "Rule template created successfully",
|
||||||
|
"ruleTemplateErrorCreate": "Failed to create template",
|
||||||
|
"ruleTemplateErrorCreateDescription": "An error occurred while creating the template",
|
||||||
|
"ruleTemplateSetting": "Rule Template Settings",
|
||||||
|
"ruleTemplateSettingDescription": "Manage template details and rules",
|
||||||
|
"ruleTemplateErrorLoad": "Failed to load template",
|
||||||
|
"ruleTemplateErrorLoadDescription": "An error occurred while loading the template",
|
||||||
|
"ruleTemplateUpdated": "Template updated",
|
||||||
|
"ruleTemplateUpdatedDescription": "Template updated successfully",
|
||||||
|
"ruleTemplateErrorUpdate": "Failed to update template",
|
||||||
|
"ruleTemplateErrorUpdateDescription": "An error occurred while updating the template",
|
||||||
|
"save": "Save",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"templateDetails": "Template Details",
|
||||||
"actionListOrgs": "List Organizations",
|
"actionListOrgs": "List Organizations",
|
||||||
"actionCheckOrgId": "Check ID",
|
"actionCheckOrgId": "Check ID",
|
||||||
"actionCreateOrg": "Create Organization",
|
"actionCreateOrg": "Create Organization",
|
||||||
@@ -1022,6 +1079,17 @@
|
|||||||
"actionDeleteIdpOrg": "Delete IDP Org Policy",
|
"actionDeleteIdpOrg": "Delete IDP Org Policy",
|
||||||
"actionListIdpOrgs": "List IDP Orgs",
|
"actionListIdpOrgs": "List IDP Orgs",
|
||||||
"actionUpdateIdpOrg": "Update IDP Org",
|
"actionUpdateIdpOrg": "Update IDP Org",
|
||||||
|
"actionCreateClient": "Create Client",
|
||||||
|
"actionDeleteClient": "Delete Client",
|
||||||
|
"actionUpdateClient": "Update Client",
|
||||||
|
"actionListClients": "List Clients",
|
||||||
|
"actionGetClient": "Get Client",
|
||||||
|
"actionCreateSiteResource": "Create Site Resource",
|
||||||
|
"actionDeleteSiteResource": "Delete Site Resource",
|
||||||
|
"actionGetSiteResource": "Get Site Resource",
|
||||||
|
"actionListSiteResources": "List Site Resources",
|
||||||
|
"actionUpdateSiteResource": "Update Site Resource",
|
||||||
|
"actionListInvitations": "List Invitations",
|
||||||
"noneSelected": "None selected",
|
"noneSelected": "None selected",
|
||||||
"orgNotFound2": "No organizations found.",
|
"orgNotFound2": "No organizations found.",
|
||||||
"searchProgress": "Search...",
|
"searchProgress": "Search...",
|
||||||
@@ -1088,6 +1156,7 @@
|
|||||||
"sidebarInvitations": "Invitations",
|
"sidebarInvitations": "Invitations",
|
||||||
"sidebarRoles": "Roles",
|
"sidebarRoles": "Roles",
|
||||||
"sidebarShareableLinks": "Shareable Links",
|
"sidebarShareableLinks": "Shareable Links",
|
||||||
|
"sidebarRuleTemplates": "Rule Templates",
|
||||||
"sidebarApiKeys": "API Keys",
|
"sidebarApiKeys": "API Keys",
|
||||||
"sidebarSettings": "Settings",
|
"sidebarSettings": "Settings",
|
||||||
"sidebarAllUsers": "All Users",
|
"sidebarAllUsers": "All Users",
|
||||||
@@ -1095,8 +1164,8 @@
|
|||||||
"sidebarLicense": "License",
|
"sidebarLicense": "License",
|
||||||
"sidebarClients": "Clients (Beta)",
|
"sidebarClients": "Clients (Beta)",
|
||||||
"sidebarDomains": "Domains",
|
"sidebarDomains": "Domains",
|
||||||
"enableDockerSocket": "Enable Docker Socket",
|
"enableDockerSocket": "Enable Docker Blueprint",
|
||||||
"enableDockerSocketDescription": "Enable Docker Socket discovery for populating container information. Socket path must be provided to Newt.",
|
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
||||||
"enableDockerSocketLink": "Learn More",
|
"enableDockerSocketLink": "Learn More",
|
||||||
"viewDockerContainers": "View Docker Containers",
|
"viewDockerContainers": "View Docker Containers",
|
||||||
"containersIn": "Containers in {siteName}",
|
"containersIn": "Containers in {siteName}",
|
||||||
@@ -1196,7 +1265,7 @@
|
|||||||
"newtUpdateAvailable": "Update Available",
|
"newtUpdateAvailable": "Update Available",
|
||||||
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.",
|
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.",
|
||||||
"domainPickerEnterDomain": "Domain",
|
"domainPickerEnterDomain": "Domain",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com, or just myapp",
|
"domainPickerPlaceholder": "myapp.example.com",
|
||||||
"domainPickerDescription": "Enter the full domain of the resource to see available options.",
|
"domainPickerDescription": "Enter the full domain of the resource to see available options.",
|
||||||
"domainPickerDescriptionSaas": "Enter a full domain, subdomain, or just a name to see available options",
|
"domainPickerDescriptionSaas": "Enter a full domain, subdomain, or just a name to see available options",
|
||||||
"domainPickerTabAll": "All",
|
"domainPickerTabAll": "All",
|
||||||
@@ -1211,6 +1280,48 @@
|
|||||||
"domainPickerSubdomain": "Subdomain: {subdomain}",
|
"domainPickerSubdomain": "Subdomain: {subdomain}",
|
||||||
"domainPickerNamespace": "Namespace: {namespace}",
|
"domainPickerNamespace": "Namespace: {namespace}",
|
||||||
"domainPickerShowMore": "Show More",
|
"domainPickerShowMore": "Show More",
|
||||||
|
"regionSelectorTitle": "Select Region",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Loading subscription...",
|
||||||
|
"billingFreeTier": "Free Tier",
|
||||||
|
"billingWarningOverLimit": "Warning: You have exceeded one or more usage limits. Your sites will not connect until you modify your subscription or adjust your usage.",
|
||||||
|
"billingUsageLimitsOverview": "Usage Limits Overview",
|
||||||
|
"billingMonitorUsage": "Monitor your usage against configured limits. If you need limits increased please contact us support@fossorial.io.",
|
||||||
|
"billingDataUsage": "Data Usage",
|
||||||
|
"billingOnlineTime": "Site Online Time",
|
||||||
|
"billingUsers": "Active Users",
|
||||||
|
"billingDomains": "Active Domains",
|
||||||
|
"billingRemoteExitNodes": "Active Self-hosted Nodes",
|
||||||
|
"billingNoLimitConfigured": "No limit configured",
|
||||||
|
"billingEstimatedPeriod": "Estimated Billing Period",
|
||||||
|
"billingIncludedUsage": "Included Usage",
|
||||||
|
"billingIncludedUsageDescription": "Usage included with your current subscription plan",
|
||||||
|
"billingFreeTierIncludedUsage": "Free tier usage allowances",
|
||||||
|
"billingIncluded": "included",
|
||||||
|
"billingEstimatedTotal": "Estimated Total:",
|
||||||
|
"billingNotes": "Notes",
|
||||||
|
"billingEstimateNote": "This is an estimate based on your current usage.",
|
||||||
|
"billingActualChargesMayVary": "Actual charges may vary.",
|
||||||
|
"billingBilledAtEnd": "You will be billed at the end of the billing period.",
|
||||||
|
"billingModifySubscription": "Modify Subscription",
|
||||||
|
"billingStartSubscription": "Start Subscription",
|
||||||
|
"billingRecurringCharge": "Recurring Charge",
|
||||||
|
"billingManageSubscriptionSettings": "Manage your subscription settings and preferences",
|
||||||
|
"billingNoActiveSubscription": "You don't have an active subscription. Start your subscription to increase usage limits.",
|
||||||
|
"billingFailedToLoadSubscription": "Failed to load subscription",
|
||||||
|
"billingFailedToLoadUsage": "Failed to load usage",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Failed to get checkout URL",
|
||||||
|
"billingPleaseTryAgainLater": "Please try again later.",
|
||||||
|
"billingCheckoutError": "Checkout Error",
|
||||||
|
"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 your organization. Billing is calculated daily based on the number of active user accounts in your org.",
|
||||||
|
"billingDomainInfo": "You're charged for each domain in your 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 your organization. Billing is calculated daily based on the number of active managed Nodes in your org.",
|
||||||
"domainNotFound": "Domain Not Found",
|
"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.",
|
"domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.",
|
||||||
"failed": "Failed",
|
"failed": "Failed",
|
||||||
@@ -1274,6 +1385,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.",
|
"createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.",
|
||||||
"resourcePortRequired": "Port number is required for non-HTTP resources",
|
"resourcePortRequired": "Port number is required for non-HTTP resources",
|
||||||
"resourcePortNotAllowed": "Port number should not be set for HTTP resources",
|
"resourcePortNotAllowed": "Port number should not be set for HTTP resources",
|
||||||
|
"billingPricingCalculatorLink": "Pricing Calculator",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "I agree to the",
|
"IAgreeToThe": "I agree to the",
|
||||||
"termsOfService": "terms of service",
|
"termsOfService": "terms of service",
|
||||||
@@ -1315,8 +1427,324 @@
|
|||||||
"olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.",
|
"olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.",
|
||||||
"remoteSubnets": "Remote Subnets",
|
"remoteSubnets": "Remote Subnets",
|
||||||
"enterCidrRange": "Enter CIDR range",
|
"enterCidrRange": "Enter CIDR range",
|
||||||
"remoteSubnetsDescription": "Add CIDR ranges that can access this site remotely. Use format like 10.0.0.0/24 or 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Add CIDR ranges that can be accessed from this site remotely using clients. Use format like 10.0.0.0/24. This ONLY applies to VPN client connectivity.",
|
||||||
"resourceEnableProxy": "Enable Public Proxy",
|
"resourceEnableProxy": "Enable Public Proxy",
|
||||||
"resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.",
|
"resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.",
|
||||||
"externalProxyEnabled": "External Proxy Enabled"
|
"externalProxyEnabled": "External Proxy Enabled",
|
||||||
|
"addNewTarget": "Add New Target",
|
||||||
|
"targetsList": "Targets List",
|
||||||
|
"targetErrorDuplicateTargetFound": "Duplicate target found",
|
||||||
|
"healthCheckHealthy": "Healthy",
|
||||||
|
"healthCheckUnhealthy": "Unhealthy",
|
||||||
|
"healthCheckUnknown": "Unknown",
|
||||||
|
"healthCheck": "Health Check",
|
||||||
|
"configureHealthCheck": "Configure Health Check",
|
||||||
|
"configureHealthCheckDescription": "Set up health monitoring for {target}",
|
||||||
|
"enableHealthChecks": "Enable Health Checks",
|
||||||
|
"enableHealthChecksDescription": "Monitor the health of this target. You can monitor a different endpoint than the target if required.",
|
||||||
|
"healthScheme": "Method",
|
||||||
|
"healthSelectScheme": "Select Method",
|
||||||
|
"healthCheckPath": "Path",
|
||||||
|
"healthHostname": "IP / Host",
|
||||||
|
"healthPort": "Port",
|
||||||
|
"healthCheckPathDescription": "The path to check for health status.",
|
||||||
|
"healthyIntervalSeconds": "Healthy Interval",
|
||||||
|
"unhealthyIntervalSeconds": "Unhealthy Interval",
|
||||||
|
"IntervalSeconds": "Healthy Interval",
|
||||||
|
"timeoutSeconds": "Timeout",
|
||||||
|
"timeIsInSeconds": "Time is in seconds",
|
||||||
|
"retryAttempts": "Retry Attempts",
|
||||||
|
"expectedResponseCodes": "Expected Response Codes",
|
||||||
|
"expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.",
|
||||||
|
"customHeaders": "Custom Headers",
|
||||||
|
"customHeadersDescription": "Headers new line separated: Header-Name: value",
|
||||||
|
"headersValidationError": "Headers must be in the format: Header-Name: value",
|
||||||
|
"saveHealthCheck": "Save Health Check",
|
||||||
|
"healthCheckSaved": "Health Check Saved",
|
||||||
|
"healthCheckSavedDescription": "Health check configuration has been saved successfully",
|
||||||
|
"healthCheckError": "Health Check Error",
|
||||||
|
"healthCheckErrorDescription": "An error occurred while saving the health check configuration",
|
||||||
|
"healthCheckPathRequired": "Health check path is required",
|
||||||
|
"healthCheckMethodRequired": "HTTP method is required",
|
||||||
|
"healthCheckIntervalMin": "Check interval must be at least 5 seconds",
|
||||||
|
"healthCheckTimeoutMin": "Timeout must be at least 1 second",
|
||||||
|
"healthCheckRetryMin": "Retry attempts must be at least 1",
|
||||||
|
"httpMethod": "HTTP Method",
|
||||||
|
"selectHttpMethod": "Select HTTP method",
|
||||||
|
"domainPickerSubdomainLabel": "Subdomain",
|
||||||
|
"domainPickerBaseDomainLabel": "Base Domain",
|
||||||
|
"domainPickerSearchDomains": "Search domains...",
|
||||||
|
"domainPickerNoDomainsFound": "No domains found",
|
||||||
|
"domainPickerLoadingDomains": "Loading domains...",
|
||||||
|
"domainPickerSelectBaseDomain": "Select base domain...",
|
||||||
|
"domainPickerNotAvailableForCname": "Not available for CNAME domains",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Enter subdomain or leave blank to use base domain.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Enter a subdomain to search and select from available free domains.",
|
||||||
|
"domainPickerFreeDomains": "Free Domains",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Search for available domains",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Note: Free provided domains are not available for self-hosted instances right now.",
|
||||||
|
"resourceDomain": "Domain",
|
||||||
|
"resourceEditDomain": "Edit Domain",
|
||||||
|
"siteName": "Site Name",
|
||||||
|
"proxyPort": "Port",
|
||||||
|
"resourcesTableProxyResources": "Proxy Resources",
|
||||||
|
"resourcesTableClientResources": "Client Resources",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "No proxy resources found.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "No internal resources found.",
|
||||||
|
"resourcesTableDestination": "Destination",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "These resources are for use with",
|
||||||
|
"resourcesTableClients": "Clients",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "and are only accessible internally when connected with a client.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Edit Client Resource",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Update the resource properties and target configuration for {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Resource Properties",
|
||||||
|
"editInternalResourceDialogName": "Name",
|
||||||
|
"editInternalResourceDialogProtocol": "Protocol",
|
||||||
|
"editInternalResourceDialogSitePort": "Site Port",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Target Configuration",
|
||||||
|
"editInternalResourceDialogCancel": "Cancel",
|
||||||
|
"editInternalResourceDialogSaveResource": "Save Resource",
|
||||||
|
"editInternalResourceDialogSuccess": "Success",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Internal resource updated successfully",
|
||||||
|
"editInternalResourceDialogError": "Error",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Failed to update internal resource",
|
||||||
|
"editInternalResourceDialogNameRequired": "Name is required",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Name must be less than 255 characters",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Proxy port must be at least 1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Proxy port must be less than 65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Invalid IP address format",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Destination port must be at least 1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Destination port must be less than 65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "No Sites Available",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "You need to have at least one Newt site with a subnet configured to create internal resources.",
|
||||||
|
"createInternalResourceDialogClose": "Close",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Create Client Resource",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Create a new resource that will be accessible to clients connected to the selected site.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Resource Properties",
|
||||||
|
"createInternalResourceDialogName": "Name",
|
||||||
|
"createInternalResourceDialogSite": "Site",
|
||||||
|
"createInternalResourceDialogSelectSite": "Select site...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Search sites...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "No sites found.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protocol",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Site Port",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Use this port to access the resource on the site when connected with a client.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Target Configuration",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "The IP or hostname address of the resource on the site's network.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "The port on the destination IP where the resource is accessible.",
|
||||||
|
"createInternalResourceDialogCancel": "Cancel",
|
||||||
|
"createInternalResourceDialogCreateResource": "Create Resource",
|
||||||
|
"createInternalResourceDialogSuccess": "Success",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Internal resource created successfully",
|
||||||
|
"createInternalResourceDialogError": "Error",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Failed to create internal resource",
|
||||||
|
"createInternalResourceDialogNameRequired": "Name is required",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Name must be less than 255 characters",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Please select a site",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Proxy port must be at least 1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Proxy port must be less than 65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Invalid IP address format",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Destination port must be at least 1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Destination port must be less than 65536",
|
||||||
|
"siteConfiguration": "Configuration",
|
||||||
|
"siteAcceptClientConnections": "Accept Client Connections",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Allow other devices to connect through this Newt instance as a gateway using clients.",
|
||||||
|
"siteAddress": "Site Address",
|
||||||
|
"siteAddressDescription": "Specify the IP address of the host for clients to connect to. This is the internal address of the site in the Pangolin network for clients to address. Must fall within the Org subnet.",
|
||||||
|
"autoLoginExternalIdp": "Auto Login with External IDP",
|
||||||
|
"autoLoginExternalIdpDescription": "Immediately redirect the user to the external IDP for authentication.",
|
||||||
|
"selectIdp": "Select IDP",
|
||||||
|
"selectIdpPlaceholder": "Choose an IDP...",
|
||||||
|
"selectIdpRequired": "Please select an IDP when auto login is enabled.",
|
||||||
|
"autoLoginTitle": "Redirecting",
|
||||||
|
"autoLoginDescription": "Redirecting you to the external identity provider for authentication.",
|
||||||
|
"autoLoginProcessing": "Preparing authentication...",
|
||||||
|
"autoLoginRedirecting": "Redirecting to login...",
|
||||||
|
"autoLoginError": "Auto Login Error",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "No redirect URL received from the identity provider.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Failed to generate authentication URL.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Manage Self-Hosted",
|
||||||
|
"remoteExitNodeDescription": "Manage nodes to extend your network connectivity",
|
||||||
|
"remoteExitNodes": "Nodes",
|
||||||
|
"searchRemoteExitNodes": "Search nodes...",
|
||||||
|
"remoteExitNodeAdd": "Add Node",
|
||||||
|
"remoteExitNodeErrorDelete": "Error deleting node",
|
||||||
|
"remoteExitNodeQuestionRemove": "Are you sure you want to remove the node {selectedNode} from the organization?",
|
||||||
|
"remoteExitNodeMessageRemove": "Once removed, the node will no longer be accessible.",
|
||||||
|
"remoteExitNodeMessageConfirm": "To confirm, please type the name of the node below.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Confirm Delete Node",
|
||||||
|
"remoteExitNodeDelete": "Delete Node",
|
||||||
|
"sidebarRemoteExitNodes": "Nodes",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Create Node",
|
||||||
|
"description": "Create a new node to extend your network connectivity",
|
||||||
|
"viewAllButton": "View All Nodes",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Creation Strategy",
|
||||||
|
"description": "Choose this to manually configure your node or generate new credentials.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adopt Node",
|
||||||
|
"description": "Choose this if you already have the credentials for the node."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Generate Keys",
|
||||||
|
"description": "Choose this if you want to generate new keys for the node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adopt Existing Node",
|
||||||
|
"description": "Enter the credentials of the existing node you want to adopt",
|
||||||
|
"nodeIdLabel": "Node ID",
|
||||||
|
"nodeIdDescription": "The ID of the existing node you want to adopt",
|
||||||
|
"secretLabel": "Secret",
|
||||||
|
"secretDescription": "The secret key of the existing node",
|
||||||
|
"submitButton": "Adopt Node"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Generated Credentials",
|
||||||
|
"description": "Use these generated credentials to configure your node",
|
||||||
|
"nodeIdTitle": "Node ID",
|
||||||
|
"secretTitle": "Secret",
|
||||||
|
"saveCredentialsTitle": "Add Credentials to Config",
|
||||||
|
"saveCredentialsDescription": "Add these credentials to your self-hosted Pangolin node configuration file to complete the connection.",
|
||||||
|
"submitButton": "Create Node"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "Node ID and Secret are required when adopting an existing node"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Failed to load defaults",
|
||||||
|
"defaultsNotLoaded": "Defaults not loaded",
|
||||||
|
"createFailed": "Failed to create node"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Node created successfully"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Node Selection",
|
||||||
|
"remoteExitNodeSelectionDescription": "Select a node to route traffic through for this local site",
|
||||||
|
"remoteExitNodeRequired": "A node must be selected for local sites",
|
||||||
|
"noRemoteExitNodesAvailable": "No Nodes Available",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "No nodes are available for this organization. Create a node first to use local sites.",
|
||||||
|
"exitNode": "Exit Node",
|
||||||
|
"country": "Country",
|
||||||
|
"rulesMatchCountry": "Currently based on source IP",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Managed Self-Hosted",
|
||||||
|
"description": "More reliable and low-maintenance self-hosted Pangolin server with extra bells and whistles",
|
||||||
|
"introTitle": "Managed Self-Hosted Pangolin",
|
||||||
|
"introDescription": "is a deployment option designed for people who want simplicity and extra reliability while still keeping their data private and self-hosted.",
|
||||||
|
"introDetail": "With this option, you still run your own Pangolin node — your tunnels, SSL termination, and traffic all stay on your server. The difference is that management and monitoring are handled through our cloud dashboard, which unlocks a number of benefits:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Simpler operations",
|
||||||
|
"description": "No need to run your own mail server or set up complex alerting. You'll get health checks and downtime alerts out of the box."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Automatic updates",
|
||||||
|
"description": "The cloud dashboard evolves quickly, so you get new features and bug fixes without having to manually pull new containers every time."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Less maintenance",
|
||||||
|
"description": "No database migrations, backups, or extra infrastructure to manage. We handle that in the cloud."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Cloud failover",
|
||||||
|
"description": "If your node goes down, your tunnels can temporarily fail over to our cloud points of presence until you bring it back online."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "High availability (PoPs)",
|
||||||
|
"description": "You can also attach multiple nodes to your account for redundancy and better performance."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Future enhancements",
|
||||||
|
"description": "We're planning to add more analytics, alerting, and management tools to make your deployment even more robust."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Learn more about the Managed Self-Hosted option in our",
|
||||||
|
"documentation": "documentation"
|
||||||
|
},
|
||||||
|
"convertButton": "Convert This Node to Managed Self-Hosted"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "International Domain Detected",
|
||||||
|
"willbestoredas": "Will be stored as:",
|
||||||
|
"roleMappingDescription": "Determine how roles are assigned to users when they sign in when Auto Provision is enabled.",
|
||||||
|
"selectRole": "Select a Role",
|
||||||
|
"roleMappingExpression": "Expression",
|
||||||
|
"selectRolePlaceholder": "Choose a role",
|
||||||
|
"selectRoleDescription": "Select a role to assign to all users from this identity provider",
|
||||||
|
"roleMappingExpressionDescription": "Enter a JMESPath expression to extract role information from the ID token",
|
||||||
|
"idpTenantIdRequired": "Tenant ID is required",
|
||||||
|
"invalidValue": "Invalid value",
|
||||||
|
"idpTypeLabel": "Identity Provider Type",
|
||||||
|
"roleMappingExpressionPlaceholder": "e.g., contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||||
|
"idpGoogleConfiguration": "Google Configuration",
|
||||||
|
"idpGoogleConfigurationDescription": "Configure your Google OAuth2 credentials",
|
||||||
|
"idpGoogleClientIdDescription": "Your Google OAuth2 Client ID",
|
||||||
|
"idpGoogleClientSecretDescription": "Your Google OAuth2 Client Secret",
|
||||||
|
"idpAzureConfiguration": "Azure Entra ID Configuration",
|
||||||
|
"idpAzureConfigurationDescription": "Configure your Azure Entra ID OAuth2 credentials",
|
||||||
|
"idpTenantId": "Tenant ID",
|
||||||
|
"idpTenantIdPlaceholder": "your-tenant-id",
|
||||||
|
"idpAzureTenantIdDescription": "Your Azure tenant ID (found in Azure Active Directory overview)",
|
||||||
|
"idpAzureClientIdDescription": "Your Azure App Registration Client ID",
|
||||||
|
"idpAzureClientSecretDescription": "Your Azure App Registration Client Secret",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Google Configuration",
|
||||||
|
"idpAzureConfigurationTitle": "Azure Entra ID Configuration",
|
||||||
|
"idpTenantIdLabel": "Tenant ID",
|
||||||
|
"idpAzureClientIdDescription2": "Your Azure App Registration Client ID",
|
||||||
|
"idpAzureClientSecretDescription2": "Your Azure App Registration Client Secret",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Subnet",
|
||||||
|
"subnetDescription": "The subnet for this organization's network configuration.",
|
||||||
|
"authPage": "Auth Page",
|
||||||
|
"authPageDescription": "Configure the auth page for your organization",
|
||||||
|
"authPageDomain": "Auth Page Domain",
|
||||||
|
"noDomainSet": "No domain set",
|
||||||
|
"changeDomain": "Change Domain",
|
||||||
|
"selectDomain": "Select Domain",
|
||||||
|
"restartCertificate": "Restart Certificate",
|
||||||
|
"editAuthPageDomain": "Edit Auth Page Domain",
|
||||||
|
"setAuthPageDomain": "Set Auth Page Domain",
|
||||||
|
"failedToFetchCertificate": "Failed to fetch certificate",
|
||||||
|
"failedToRestartCertificate": "Failed to restart certificate",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Add a domain to enable custom authentication pages for your organization",
|
||||||
|
"selectDomainForOrgAuthPage": "Select a domain for the organization's authentication page",
|
||||||
|
"domainPickerProvidedDomain": "Provided Domain",
|
||||||
|
"domainPickerFreeProvidedDomain": "Free Provided Domain",
|
||||||
|
"domainPickerVerified": "Verified",
|
||||||
|
"domainPickerUnverified": "Unverified",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "This subdomain contains invalid characters or structure. It will be sanitized automatically when you save.",
|
||||||
|
"domainPickerError": "Error",
|
||||||
|
"domainPickerErrorLoadDomains": "Failed to load organization domains",
|
||||||
|
"domainPickerErrorCheckAvailability": "Failed to check domain availability",
|
||||||
|
"domainPickerInvalidSubdomain": "Invalid subdomain",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "The input \"{sub}\" was removed because it's not valid.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" could not be made valid for {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Subdomain sanitized",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Sign in to your organization",
|
||||||
|
"orgAuthChooseIdpDescription": "Choose your identity provider to continue",
|
||||||
|
"orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.",
|
||||||
|
"orgAuthSignInWithPangolin": "Sign in with Pangolin",
|
||||||
|
"subscriptionRequiredToUse": "A subscription is required to use this feature.",
|
||||||
|
"idpDisabled": "Identity providers are disabled.",
|
||||||
|
"orgAuthPageDisabled": "Organization auth page is disabled.",
|
||||||
|
"domainRestartedDescription": "Domain verification restarted successfully",
|
||||||
|
"resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Edit file: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "Email verification is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.",
|
||||||
|
"twoFactorSetupRequired": "Two-factor authentication setup is required. Please log in again via {dashboardUrl}/auth/login complete this step. Then, come back here.",
|
||||||
|
"authPageErrorUpdateMessage": "An error occurred while updating the auth page settings",
|
||||||
|
"authPageUpdated": "Auth page updated successfully",
|
||||||
|
"healthCheckNotAvailable": "Local",
|
||||||
|
"rewritePath": "Rewrite Path",
|
||||||
|
"rewritePathDescription": "Optionally rewrite the path before forwarding to the target.",
|
||||||
|
"continueToApplication": "Continue to application"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
"siteDocker": "Expandir para detalles de despliegue de Docker",
|
"siteDocker": "Expandir para detalles de despliegue de Docker",
|
||||||
"toggle": "Cambiar",
|
"toggle": "Cambiar",
|
||||||
"dockerCompose": "Componer Docker",
|
"dockerCompose": "Componer Docker",
|
||||||
"dockerRun": "Docker Run",
|
"dockerRun": "Ejecutar Docker",
|
||||||
"siteLearnLocal": "Los sitios locales no tienen túnel, aprender más",
|
"siteLearnLocal": "Los sitios locales no tienen túnel, aprender más",
|
||||||
"siteConfirmCopy": "He copiado la configuración",
|
"siteConfirmCopy": "He copiado la configuración",
|
||||||
"searchSitesProgress": "Buscar sitios...",
|
"searchSitesProgress": "Buscar sitios...",
|
||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en tu red. Sin configuración adicional.",
|
"siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en tu red. Sin configuración adicional.",
|
||||||
"siteWg": "Wirex Guardia Básica",
|
"siteWg": "Wirex Guardia Básica",
|
||||||
"siteWgDescription": "Utilice cualquier cliente Wirex Guard para establecer un túnel. Se requiere una configuración manual de NAT.",
|
"siteWgDescription": "Utilice cualquier cliente Wirex Guard para establecer un túnel. Se requiere una configuración manual de NAT.",
|
||||||
|
"siteWgDescriptionSaas": "Utilice cualquier cliente de WireGuard para establecer un túnel. Se requiere configuración manual de NAT. SOLO FUNCIONA EN NODOS AUTOGESTIONADOS",
|
||||||
"siteLocalDescription": "Solo recursos locales. Sin túneles.",
|
"siteLocalDescription": "Solo recursos locales. Sin túneles.",
|
||||||
|
"siteLocalDescriptionSaas": "Solo recursos locales. Sin túneles. SOLO FUNCIONA EN NODOS AUTOGESTIONADOS",
|
||||||
"siteSeeAll": "Ver todos los sitios",
|
"siteSeeAll": "Ver todos los sitios",
|
||||||
"siteTunnelDescription": "Determina cómo quieres conectarte a tu sitio",
|
"siteTunnelDescription": "Determina cómo quieres conectarte a tu sitio",
|
||||||
"siteNewtCredentials": "Credenciales nuevas",
|
"siteNewtCredentials": "Credenciales nuevas",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Seleccionar sitio",
|
"siteSelect": "Seleccionar sitio",
|
||||||
"siteSearch": "Buscar sitio",
|
"siteSearch": "Buscar sitio",
|
||||||
"siteNotFound": "Sitio no encontrado.",
|
"siteNotFound": "Sitio no encontrado.",
|
||||||
"siteSelectionDescription": "Este sitio proporcionará conectividad al recurso.",
|
"selectCountry": "Seleccionar país",
|
||||||
|
"searchCountries": "Buscar países...",
|
||||||
|
"noCountryFound": "Ningún país encontrado.",
|
||||||
|
"siteSelectionDescription": "Este sitio proporcionará conectividad al objetivo.",
|
||||||
"resourceType": "Tipo de recurso",
|
"resourceType": "Tipo de recurso",
|
||||||
"resourceTypeDescription": "Determina cómo quieres acceder a tu recurso",
|
"resourceTypeDescription": "Determina cómo quieres acceder a tu recurso",
|
||||||
"resourceHTTPSSettings": "Configuración HTTPS",
|
"resourceHTTPSSettings": "Configuración HTTPS",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "General",
|
"general": "General",
|
||||||
"generalSettings": "Configuración General",
|
"generalSettings": "Configuración General",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
|
"internal": "Interno",
|
||||||
"rules": "Reglas",
|
"rules": "Reglas",
|
||||||
"resourceSettingDescription": "Configure la configuración de su recurso",
|
"resourceSettingDescription": "Configure la configuración de su recurso",
|
||||||
"resourceSetting": "Ajustes {resourceName}",
|
"resourceSetting": "Ajustes {resourceName}",
|
||||||
"alwaysAllow": "Permitir siempre",
|
"alwaysAllow": "Permitir siempre",
|
||||||
"alwaysDeny": "Denegar siempre",
|
"alwaysDeny": "Denegar siempre",
|
||||||
|
"passToAuth": "Pasar a Autenticación",
|
||||||
"orgSettingsDescription": "Configurar la configuración general de su organización",
|
"orgSettingsDescription": "Configurar la configuración general de su organización",
|
||||||
"orgGeneralSettings": "Configuración de la organización",
|
"orgGeneralSettings": "Configuración de la organización",
|
||||||
"orgGeneralSettingsDescription": "Administra los detalles y la configuración de tu organización",
|
"orgGeneralSettingsDescription": "Administra los detalles y la configuración de tu organización",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Ocurrió un error mientras se añadía el usuario al rol.",
|
"accessRoleErrorAddDescription": "Ocurrió un error mientras se añadía el usuario al rol.",
|
||||||
"userSaved": "Usuario guardado",
|
"userSaved": "Usuario guardado",
|
||||||
"userSavedDescription": "El usuario ha sido actualizado.",
|
"userSavedDescription": "El usuario ha sido actualizado.",
|
||||||
|
"autoProvisioned": "Auto asegurado",
|
||||||
|
"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",
|
"accessControlsDescription": "Administrar lo que este usuario puede acceder y hacer en la organización",
|
||||||
"accessControlsSubmit": "Guardar controles de acceso",
|
"accessControlsSubmit": "Guardar controles de acceso",
|
||||||
"roles": "Roles",
|
"roles": "Roles",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Formato de dirección IP inválido",
|
"ipAddressErrorInvalidFormat": "Formato de dirección IP inválido",
|
||||||
"ipAddressErrorInvalidOctet": "Octet de dirección IP no válido",
|
"ipAddressErrorInvalidOctet": "Octet de dirección IP no válido",
|
||||||
"path": "Ruta",
|
"path": "Ruta",
|
||||||
|
"matchPath": "Coincidir ruta",
|
||||||
"ipAddressRange": "Rango IP",
|
"ipAddressRange": "Rango IP",
|
||||||
"rulesErrorFetch": "Error al obtener las reglas",
|
"rulesErrorFetch": "Error al obtener las reglas",
|
||||||
"rulesErrorFetchDescription": "Se ha producido un error al recuperar las reglas",
|
"rulesErrorFetchDescription": "Se ha producido un error al recuperar las reglas",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Acciones",
|
"rulesActions": "Acciones",
|
||||||
"rulesActionAlwaysAllow": "Permitir siempre: pasar todos los métodos de autenticación",
|
"rulesActionAlwaysAllow": "Permitir siempre: pasar todos los métodos de autenticación",
|
||||||
"rulesActionAlwaysDeny": "Denegar siempre: Bloquear todas las peticiones; no se puede intentar autenticación",
|
"rulesActionAlwaysDeny": "Denegar siempre: Bloquear todas las peticiones; no se puede intentar autenticación",
|
||||||
|
"rulesActionPassToAuth": "Pasar a Autenticación: Permitir que se intenten los métodos de autenticación",
|
||||||
"rulesMatchCriteria": "Criterios coincidentes",
|
"rulesMatchCriteria": "Criterios coincidentes",
|
||||||
"rulesMatchCriteriaIpAddress": "Coincidir con una dirección IP específica",
|
"rulesMatchCriteriaIpAddress": "Coincidir con una dirección IP específica",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Coincide con un rango de direcciones IP en notación CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Coincide con un rango de direcciones IP en notación CIDR",
|
||||||
@@ -806,7 +817,7 @@
|
|||||||
"redirectUrl": "URL de redirección",
|
"redirectUrl": "URL de redirección",
|
||||||
"redirectUrlAbout": "Acerca de la URL de redirección",
|
"redirectUrlAbout": "Acerca de la URL de redirección",
|
||||||
"redirectUrlAboutDescription": "Esta es la URL a la que los usuarios serán redireccionados después de la autenticación. Necesitas configurar esta URL en la configuración de tu proveedor de identidad.",
|
"redirectUrlAboutDescription": "Esta es la URL a la que los usuarios serán redireccionados después de la autenticación. Necesitas configurar esta URL en la configuración de tu proveedor de identidad.",
|
||||||
"pangolinAuth": "Auth - Pangolin",
|
"pangolinAuth": "Autenticación - Pangolin",
|
||||||
"verificationCodeLengthRequirements": "Tu código de verificación debe tener 8 caracteres.",
|
"verificationCodeLengthRequirements": "Tu código de verificación debe tener 8 caracteres.",
|
||||||
"errorOccurred": "Se ha producido un error",
|
"errorOccurred": "Se ha producido un error",
|
||||||
"emailErrorVerify": "No se pudo verificar el email:",
|
"emailErrorVerify": "No se pudo verificar el email:",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "El PIN debe tener exactamente 6 dígitos",
|
"pincodeRequirementsLength": "El PIN debe tener exactamente 6 dígitos",
|
||||||
"pincodeRequirementsChars": "El PIN sólo debe contener números",
|
"pincodeRequirementsChars": "El PIN sólo debe contener números",
|
||||||
"passwordRequirementsLength": "La contraseña debe tener al menos 1 carácter",
|
"passwordRequirementsLength": "La contraseña debe tener al menos 1 carácter",
|
||||||
|
"passwordRequirementsTitle": "Requisitos de la contraseña:",
|
||||||
|
"passwordRequirementLength": "Al menos 8 caracteres de largo",
|
||||||
|
"passwordRequirementUppercase": "Al menos una letra mayúscula",
|
||||||
|
"passwordRequirementLowercase": "Al menos una letra minúscula",
|
||||||
|
"passwordRequirementNumber": "Al menos un número",
|
||||||
|
"passwordRequirementSpecial": "Al menos un carácter especial",
|
||||||
|
"passwordRequirementsMet": "✓ La contraseña cumple con todos los requisitos",
|
||||||
|
"passwordStrength": "Seguridad de la contraseña",
|
||||||
|
"passwordStrengthWeak": "Débil",
|
||||||
|
"passwordStrengthMedium": "Media",
|
||||||
|
"passwordStrengthStrong": "Fuerte",
|
||||||
|
"passwordRequirements": "Requisitos:",
|
||||||
|
"passwordRequirementLengthText": "8+ caracteres",
|
||||||
|
"passwordRequirementUppercaseText": "Letra mayúscula (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Letra minúscula (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Número (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Caracter especial (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Las contraseñas no coinciden",
|
||||||
"otpEmailRequirementsLength": "OTP debe tener al menos 1 carácter",
|
"otpEmailRequirementsLength": "OTP debe tener al menos 1 carácter",
|
||||||
"otpEmailSent": "OTP enviado",
|
"otpEmailSent": "OTP enviado",
|
||||||
"otpEmailSentDescription": "Un OTP ha sido enviado a tu correo electrónico",
|
"otpEmailSentDescription": "Un OTP ha sido enviado a tu correo electrónico",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "Error al cerrar sesión",
|
"logoutError": "Error al cerrar sesión",
|
||||||
"signingAs": "Conectado como",
|
"signingAs": "Conectado como",
|
||||||
"serverAdmin": "Admin Servidor",
|
"serverAdmin": "Admin Servidor",
|
||||||
|
"managedSelfhosted": "Autogestionado",
|
||||||
"otpEnable": "Activar doble factor",
|
"otpEnable": "Activar doble factor",
|
||||||
"otpDisable": "Desactivar doble factor",
|
"otpDisable": "Desactivar doble factor",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"licenseTierProfessionalRequired": "Edición Profesional requerida",
|
"licenseTierProfessionalRequired": "Edición Profesional requerida",
|
||||||
"licenseTierProfessionalRequiredDescription": "Esta característica sólo está disponible en la Edición Profesional.",
|
"licenseTierProfessionalRequiredDescription": "Esta característica sólo está disponible en la Edición Profesional.",
|
||||||
"actionGetOrg": "Obtener organización",
|
"actionGetOrg": "Obtener organización",
|
||||||
|
"updateOrgUser": "Actualizar usuario Org",
|
||||||
|
"createOrgUser": "Crear usuario Org",
|
||||||
"actionUpdateOrg": "Actualizar organización",
|
"actionUpdateOrg": "Actualizar organización",
|
||||||
"actionUpdateUser": "Actualizar usuario",
|
"actionUpdateUser": "Actualizar usuario",
|
||||||
"actionGetUser": "Obtener usuario",
|
"actionGetUser": "Obtener usuario",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "Eliminar sitio",
|
"actionDeleteSite": "Eliminar sitio",
|
||||||
"actionGetSite": "Obtener sitio",
|
"actionGetSite": "Obtener sitio",
|
||||||
"actionListSites": "Listar sitios",
|
"actionListSites": "Listar sitios",
|
||||||
|
"actionApplyBlueprint": "Aplicar plano",
|
||||||
|
"setupToken": "Configuración de token",
|
||||||
|
"setupTokenDescription": "Ingrese el token de configuración desde la consola del servidor.",
|
||||||
|
"setupTokenRequired": "Se requiere el token de configuración",
|
||||||
"actionUpdateSite": "Actualizar sitio",
|
"actionUpdateSite": "Actualizar sitio",
|
||||||
"actionListSiteRoles": "Lista de roles permitidos del sitio",
|
"actionListSiteRoles": "Lista de roles permitidos del sitio",
|
||||||
"actionCreateResource": "Crear Recurso",
|
"actionCreateResource": "Crear Recurso",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Eliminar política de IDP Org",
|
"actionDeleteIdpOrg": "Eliminar política de IDP Org",
|
||||||
"actionListIdpOrgs": "Listar Orgs IDP",
|
"actionListIdpOrgs": "Listar Orgs IDP",
|
||||||
"actionUpdateIdpOrg": "Actualizar IDP Org",
|
"actionUpdateIdpOrg": "Actualizar IDP Org",
|
||||||
|
"actionCreateClient": "Crear cliente",
|
||||||
|
"actionDeleteClient": "Eliminar cliente",
|
||||||
|
"actionUpdateClient": "Actualizar cliente",
|
||||||
|
"actionListClients": "Listar clientes",
|
||||||
|
"actionGetClient": "Obtener cliente",
|
||||||
|
"actionCreateSiteResource": "Crear Recurso del Sitio",
|
||||||
|
"actionDeleteSiteResource": "Eliminar recurso del sitio",
|
||||||
|
"actionGetSiteResource": "Obtener recurso del sitio",
|
||||||
|
"actionListSiteResources": "Listar recursos del sitio",
|
||||||
|
"actionUpdateSiteResource": "Actualizar recurso del sitio",
|
||||||
|
"actionListInvitations": "Listar invitaciones",
|
||||||
"noneSelected": "Ninguno seleccionado",
|
"noneSelected": "Ninguno seleccionado",
|
||||||
"orgNotFound2": "No se encontraron organizaciones.",
|
"orgNotFound2": "No se encontraron organizaciones.",
|
||||||
"searchProgress": "Buscar...",
|
"searchProgress": "Buscar...",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "Licencia",
|
"sidebarLicense": "Licencia",
|
||||||
"sidebarClients": "Clientes (Beta)",
|
"sidebarClients": "Clientes (Beta)",
|
||||||
"sidebarDomains": "Dominios",
|
"sidebarDomains": "Dominios",
|
||||||
"enableDockerSocket": "Habilitar conector Docker",
|
"enableDockerSocket": "Habilitar Plano Docker",
|
||||||
"enableDockerSocketDescription": "Habilitar el descubrimiento de Docker Socket para completar la información del contenedor. La ruta del socket debe proporcionarse a Newt.",
|
"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",
|
"enableDockerSocketLink": "Saber más",
|
||||||
"viewDockerContainers": "Ver contenedores Docker",
|
"viewDockerContainers": "Ver contenedores Docker",
|
||||||
"containersIn": "Contenedores en {siteName}",
|
"containersIn": "Contenedores en {siteName}",
|
||||||
@@ -1173,7 +1220,7 @@
|
|||||||
"billing": "Facturación",
|
"billing": "Facturación",
|
||||||
"orgBillingDescription": "Gestiona tu información de facturación y suscripciones",
|
"orgBillingDescription": "Gestiona tu información de facturación y suscripciones",
|
||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
"pangolinHosted": "Pangolin Hosted",
|
"pangolinHosted": "Pangolin Alojado",
|
||||||
"fossorial": "Fossorial",
|
"fossorial": "Fossorial",
|
||||||
"completeAccountSetup": "Completar configuración de cuenta",
|
"completeAccountSetup": "Completar configuración de cuenta",
|
||||||
"completeAccountSetupDescription": "Establece tu contraseña para comenzar",
|
"completeAccountSetupDescription": "Establece tu contraseña para comenzar",
|
||||||
@@ -1196,7 +1243,7 @@
|
|||||||
"newtUpdateAvailable": "Nueva actualización disponible",
|
"newtUpdateAvailable": "Nueva actualización disponible",
|
||||||
"newtUpdateAvailableInfo": "Hay una nueva versión de Newt disponible. Actualice a la última versión para la mejor experiencia.",
|
"newtUpdateAvailableInfo": "Hay una nueva versión de Newt disponible. Actualice a la última versión para la mejor experiencia.",
|
||||||
"domainPickerEnterDomain": "Dominio",
|
"domainPickerEnterDomain": "Dominio",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.miDominio.com, o solo myapp",
|
"domainPickerPlaceholder": "miapp.ejemplo.com",
|
||||||
"domainPickerDescription": "Ingresa el dominio completo del recurso para ver las opciones disponibles.",
|
"domainPickerDescription": "Ingresa el dominio completo del recurso para ver las opciones disponibles.",
|
||||||
"domainPickerDescriptionSaas": "Ingresa un dominio completo, subdominio o simplemente un nombre para ver las opciones disponibles",
|
"domainPickerDescriptionSaas": "Ingresa un dominio completo, subdominio o simplemente un nombre para ver las opciones disponibles",
|
||||||
"domainPickerTabAll": "Todo",
|
"domainPickerTabAll": "Todo",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "Subdominio: {subdomain}",
|
"domainPickerSubdomain": "Subdominio: {subdomain}",
|
||||||
"domainPickerNamespace": "Espacio de nombres: {namespace}",
|
"domainPickerNamespace": "Espacio de nombres: {namespace}",
|
||||||
"domainPickerShowMore": "Mostrar más",
|
"domainPickerShowMore": "Mostrar más",
|
||||||
|
"regionSelectorTitle": "Seleccionar Región",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Cargando suscripción...",
|
||||||
|
"billingFreeTier": "Nivel Gratis",
|
||||||
|
"billingWarningOverLimit": "Advertencia: Has excedido uno o más límites de uso. Tus sitios no se conectarán hasta que modifiques tu suscripción o ajustes tu uso.",
|
||||||
|
"billingUsageLimitsOverview": "Descripción general de los límites de uso",
|
||||||
|
"billingMonitorUsage": "Monitorea tu uso comparado con los límites configurados. Si necesitas que aumenten los límites, contáctanos a soporte@fossorial.io.",
|
||||||
|
"billingDataUsage": "Uso de datos",
|
||||||
|
"billingOnlineTime": "Tiempo en línea del sitio",
|
||||||
|
"billingUsers": "Usuarios activos",
|
||||||
|
"billingDomains": "Dominios activos",
|
||||||
|
"billingRemoteExitNodes": "Nodos autogestionados activos",
|
||||||
|
"billingNoLimitConfigured": "No se ha configurado ningún límite",
|
||||||
|
"billingEstimatedPeriod": "Período de facturación estimado",
|
||||||
|
"billingIncludedUsage": "Uso incluido",
|
||||||
|
"billingIncludedUsageDescription": "Uso incluido con su plan de suscripción actual",
|
||||||
|
"billingFreeTierIncludedUsage": "Permisos de uso del nivel gratuito",
|
||||||
|
"billingIncluded": "incluido",
|
||||||
|
"billingEstimatedTotal": "Total Estimado:",
|
||||||
|
"billingNotes": "Notas",
|
||||||
|
"billingEstimateNote": "Esta es una estimación basada en tu uso actual.",
|
||||||
|
"billingActualChargesMayVary": "Los cargos reales pueden variar.",
|
||||||
|
"billingBilledAtEnd": "Se te facturará al final del período de facturación.",
|
||||||
|
"billingModifySubscription": "Modificar Suscripción",
|
||||||
|
"billingStartSubscription": "Iniciar Suscripción",
|
||||||
|
"billingRecurringCharge": "Cargo Recurrente",
|
||||||
|
"billingManageSubscriptionSettings": "Administra la configuración y preferencias de tu suscripción",
|
||||||
|
"billingNoActiveSubscription": "No tienes una suscripción activa. Inicia tu suscripción para aumentar los límites de uso.",
|
||||||
|
"billingFailedToLoadSubscription": "Error al cargar la suscripción",
|
||||||
|
"billingFailedToLoadUsage": "Error al cargar el uso",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Error al obtener la URL de pago",
|
||||||
|
"billingPleaseTryAgainLater": "Por favor, inténtelo de nuevo más tarde.",
|
||||||
|
"billingCheckoutError": "Error de pago",
|
||||||
|
"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 te cobra por cada usuario en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de usuario activas en tu organización.",
|
||||||
|
"billingDomainInfo": "Se te cobra por cada dominio en tu organización. La facturación se calcula diariamente según la cantidad de cuentas de dominio activas en tu organización.",
|
||||||
|
"billingRemoteExitNodesInfo": "Se te cobra por cada nodo gestionado en tu organización. La facturación se calcula diariamente según la cantidad de nodos gestionados activos en tu organización.",
|
||||||
"domainNotFound": "Dominio no encontrado",
|
"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.",
|
"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",
|
"failed": "Fallido",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "Los cambios de DNS pueden tardar un tiempo en propagarse a través de internet. Esto puede tardar desde unos pocos minutos hasta 48 horas, dependiendo de tu proveedor de DNS y la configuración de TTL.",
|
"createDomainDnsPropagationDescription": "Los cambios de DNS pueden tardar un tiempo en propagarse a través de internet. Esto puede tardar desde unos pocos minutos hasta 48 horas, dependiendo de tu proveedor de DNS y la configuración de TTL.",
|
||||||
"resourcePortRequired": "Se requiere número de puerto para recursos no HTTP",
|
"resourcePortRequired": "Se requiere número de puerto para recursos no HTTP",
|
||||||
"resourcePortNotAllowed": "El número de puerto no debe establecerse para recursos HTTP",
|
"resourcePortNotAllowed": "El número de puerto no debe establecerse para recursos HTTP",
|
||||||
|
"billingPricingCalculatorLink": "Calculadora de Precios",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Estoy de acuerdo con los",
|
"IAgreeToThe": "Estoy de acuerdo con los",
|
||||||
"termsOfService": "términos del servicio",
|
"termsOfService": "términos del servicio",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "Se ha producido un error al recuperar la última versión de Olm.",
|
"olmErrorFetchLatest": "Se ha producido un error al recuperar la última versión de Olm.",
|
||||||
"remoteSubnets": "Subredes remotas",
|
"remoteSubnets": "Subredes remotas",
|
||||||
"enterCidrRange": "Ingresa el rango CIDR",
|
"enterCidrRange": "Ingresa el rango CIDR",
|
||||||
"remoteSubnetsDescription": "Agregue rangos CIDR que puedan acceder a este sitio de forma remota. Use un formato como 10.0.0.0/24 o 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Agregue rangos CIDR que se puedan acceder desde este sitio de forma remota usando clientes. Utilice el formato como 10.0.0.0/24. Esto SOLO se aplica a la conectividad del cliente VPN.",
|
||||||
"resourceEnableProxy": "Habilitar proxy público",
|
"resourceEnableProxy": "Habilitar proxy público",
|
||||||
"resourceEnableProxyDescription": "Habilite el proxy público para este recurso. Esto permite el acceso al recurso desde fuera de la red a través de la nube en un puerto abierto. Requiere configuración de Traefik.",
|
"resourceEnableProxyDescription": "Habilite el proxy público para este recurso. Esto permite el acceso al recurso desde fuera de la red a través de la nube en un puerto abierto. Requiere configuración de Traefik.",
|
||||||
"externalProxyEnabled": "Proxy externo habilitado"
|
"externalProxyEnabled": "Proxy externo habilitado",
|
||||||
|
"addNewTarget": "Agregar nuevo destino",
|
||||||
|
"targetsList": "Lista de destinos",
|
||||||
|
"targetErrorDuplicateTargetFound": "Se encontró un destino duplicado",
|
||||||
|
"healthCheckHealthy": "Saludable",
|
||||||
|
"healthCheckUnhealthy": "No saludable",
|
||||||
|
"healthCheckUnknown": "Desconocido",
|
||||||
|
"healthCheck": "Chequeo de salud",
|
||||||
|
"configureHealthCheck": "Configurar Chequeo de Salud",
|
||||||
|
"configureHealthCheckDescription": "Configura la monitorización de salud para {target}",
|
||||||
|
"enableHealthChecks": "Activar Chequeos de Salud",
|
||||||
|
"enableHealthChecksDescription": "Controlar la salud de este objetivo. Puedes supervisar un punto final diferente al objetivo si es necesario.",
|
||||||
|
"healthScheme": "Método",
|
||||||
|
"healthSelectScheme": "Seleccionar método",
|
||||||
|
"healthCheckPath": "Ruta",
|
||||||
|
"healthHostname": "IP / Nombre del host",
|
||||||
|
"healthPort": "Puerto",
|
||||||
|
"healthCheckPathDescription": "La ruta para comprobar el estado de salud.",
|
||||||
|
"healthyIntervalSeconds": "Intervalo Saludable",
|
||||||
|
"unhealthyIntervalSeconds": "Intervalo No Saludable",
|
||||||
|
"IntervalSeconds": "Intervalo Saludable",
|
||||||
|
"timeoutSeconds": "Tiempo de Espera",
|
||||||
|
"timeIsInSeconds": "El tiempo está en segundos",
|
||||||
|
"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.",
|
||||||
|
"customHeaders": "Cabeceras personalizadas",
|
||||||
|
"customHeadersDescription": "Nueva línea de cabeceras separada: Nombre de cabecera: valor",
|
||||||
|
"headersValidationError": "Los encabezados deben estar en el formato: Nombre de cabecera: valor.",
|
||||||
|
"saveHealthCheck": "Guardar Chequeo de Salud",
|
||||||
|
"healthCheckSaved": "Chequeo de Salud Guardado",
|
||||||
|
"healthCheckSavedDescription": "La configuración del chequeo de salud se ha guardado correctamente",
|
||||||
|
"healthCheckError": "Error en el Chequeo de Salud",
|
||||||
|
"healthCheckErrorDescription": "Ocurrió un error al guardar la configuración del chequeo de salud",
|
||||||
|
"healthCheckPathRequired": "Se requiere la ruta del chequeo de salud",
|
||||||
|
"healthCheckMethodRequired": "Se requiere el método HTTP",
|
||||||
|
"healthCheckIntervalMin": "El intervalo de comprobación debe ser de al menos 5 segundos",
|
||||||
|
"healthCheckTimeoutMin": "El tiempo de espera debe ser de al menos 1 segundo",
|
||||||
|
"healthCheckRetryMin": "Los intentos de reintento deben ser de al menos 1",
|
||||||
|
"httpMethod": "Método HTTP",
|
||||||
|
"selectHttpMethod": "Seleccionar método HTTP",
|
||||||
|
"domainPickerSubdomainLabel": "Subdominio",
|
||||||
|
"domainPickerBaseDomainLabel": "Dominio base",
|
||||||
|
"domainPickerSearchDomains": "Buscar dominios...",
|
||||||
|
"domainPickerNoDomainsFound": "No se encontraron dominios",
|
||||||
|
"domainPickerLoadingDomains": "Cargando dominios...",
|
||||||
|
"domainPickerSelectBaseDomain": "Seleccionar dominio base...",
|
||||||
|
"domainPickerNotAvailableForCname": "No disponible para dominios CNAME",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Ingrese subdominio o deje en blanco para usar dominio base.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Ingrese un subdominio para buscar y seleccionar entre dominios gratuitos disponibles.",
|
||||||
|
"domainPickerFreeDomains": "Dominios gratuitos",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Buscar dominios disponibles",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Nota: Los dominios gratuitos proporcionados no están disponibles para instancias autogestionadas por ahora.",
|
||||||
|
"resourceDomain": "Dominio",
|
||||||
|
"resourceEditDomain": "Editar dominio",
|
||||||
|
"siteName": "Nombre del sitio",
|
||||||
|
"proxyPort": "Puerto",
|
||||||
|
"resourcesTableProxyResources": "Recursos de proxy",
|
||||||
|
"resourcesTableClientResources": "Recursos del cliente",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "No se encontraron recursos de proxy.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "No se encontraron recursos internos.",
|
||||||
|
"resourcesTableDestination": "Destino",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Estos recursos son para uso con",
|
||||||
|
"resourcesTableClients": "Clientes",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "y solo son accesibles internamente cuando se conectan con un cliente.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Editar recurso del cliente",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Actualizar las propiedades del recurso y la configuración del objetivo para {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Propiedades del recurso",
|
||||||
|
"editInternalResourceDialogName": "Nombre",
|
||||||
|
"editInternalResourceDialogProtocol": "Protocolo",
|
||||||
|
"editInternalResourceDialogSitePort": "Puerto del sitio",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Configuración de objetivos",
|
||||||
|
"editInternalResourceDialogCancel": "Cancelar",
|
||||||
|
"editInternalResourceDialogSaveResource": "Guardar recurso",
|
||||||
|
"editInternalResourceDialogSuccess": "Éxito",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Recurso interno actualizado con éxito",
|
||||||
|
"editInternalResourceDialogError": "Error",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Error al actualizar el recurso interno",
|
||||||
|
"editInternalResourceDialogNameRequired": "El nombre es requerido",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "El nombre no debe tener más de 255 caracteres",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "El puerto del proxy debe ser al menos 1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "El puerto del proxy debe ser menor de 65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Formato de dirección IP inválido",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "El puerto de destino debe ser al menos 1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "El puerto de destino debe ser menor de 65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "No hay sitios disponibles",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Necesita tener al menos un sitio de Newt con una subred configurada para crear recursos internos.",
|
||||||
|
"createInternalResourceDialogClose": "Cerrar",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Crear recurso del cliente",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Crear un nuevo recurso que será accesible para los clientes conectados al sitio seleccionado.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Propiedades del recurso",
|
||||||
|
"createInternalResourceDialogName": "Nombre",
|
||||||
|
"createInternalResourceDialogSite": "Sitio",
|
||||||
|
"createInternalResourceDialogSelectSite": "Seleccionar sitio...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Buscar sitios...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Sitios no encontrados.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protocolo",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Puerto del sitio",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Use este puerto para acceder al recurso en el sitio cuando se conecta con un cliente.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Configuración de objetivos",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "La dirección IP o nombre de host del recurso en la red del sitio.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "El puerto en la IP de destino donde el recurso es accesible.",
|
||||||
|
"createInternalResourceDialogCancel": "Cancelar",
|
||||||
|
"createInternalResourceDialogCreateResource": "Crear recurso",
|
||||||
|
"createInternalResourceDialogSuccess": "Éxito",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Recurso interno creado con éxito",
|
||||||
|
"createInternalResourceDialogError": "Error",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Error al crear recurso interno",
|
||||||
|
"createInternalResourceDialogNameRequired": "El nombre es requerido",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "El nombre debe ser menor de 255 caracteres",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Por favor seleccione un sitio",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "El puerto del proxy debe ser al menos 1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "El puerto del proxy debe ser menor de 65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Formato de dirección IP inválido",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "El puerto de destino debe ser al menos 1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "El puerto de destino debe ser menor de 65536",
|
||||||
|
"siteConfiguration": "Configuración",
|
||||||
|
"siteAcceptClientConnections": "Aceptar conexiones de clientes",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Permitir que otros dispositivos se conecten a través de esta instancia Newt como una puerta de enlace utilizando clientes.",
|
||||||
|
"siteAddress": "Dirección del sitio",
|
||||||
|
"siteAddressDescription": "Especifique la dirección IP del host que los clientes deben usar para conectarse. Esta es la dirección interna del sitio en la red de Pangolín para que los clientes dirijan. Debe estar dentro de la subred de la organización.",
|
||||||
|
"autoLoginExternalIdp": "Inicio de sesión automático con IDP externo",
|
||||||
|
"autoLoginExternalIdpDescription": "Redirigir inmediatamente al usuario al IDP externo para autenticación.",
|
||||||
|
"selectIdp": "Seleccionar IDP",
|
||||||
|
"selectIdpPlaceholder": "Elegir un IDP...",
|
||||||
|
"selectIdpRequired": "Por favor seleccione un IDP cuando el inicio de sesión automático esté habilitado.",
|
||||||
|
"autoLoginTitle": "Redirigiendo",
|
||||||
|
"autoLoginDescription": "Te estamos redirigiendo al proveedor de identidad externo para autenticación.",
|
||||||
|
"autoLoginProcessing": "Preparando autenticación...",
|
||||||
|
"autoLoginRedirecting": "Redirigiendo al inicio de sesión...",
|
||||||
|
"autoLoginError": "Error de inicio de sesión automático",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "No se recibió URL de redirección del proveedor de identidad.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Error al generar URL de autenticación.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Administrar Nodos Autogestionados",
|
||||||
|
"remoteExitNodeDescription": "Administrar nodos para extender la conectividad de red",
|
||||||
|
"remoteExitNodes": "Nodos",
|
||||||
|
"searchRemoteExitNodes": "Buscar nodos...",
|
||||||
|
"remoteExitNodeAdd": "Añadir Nodo",
|
||||||
|
"remoteExitNodeErrorDelete": "Error al eliminar el nodo",
|
||||||
|
"remoteExitNodeQuestionRemove": "¿Está seguro de que desea eliminar el nodo {selectedNode} de la organización?",
|
||||||
|
"remoteExitNodeMessageRemove": "Una vez eliminado, el nodo ya no será accesible.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Para confirmar, por favor escriba el nombre del nodo a continuación.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Confirmar eliminar nodo",
|
||||||
|
"remoteExitNodeDelete": "Eliminar Nodo",
|
||||||
|
"sidebarRemoteExitNodes": "Nodos",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Crear Nodo",
|
||||||
|
"description": "Crear un nuevo nodo para extender la conectividad de red",
|
||||||
|
"viewAllButton": "Ver todos los nodos",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Estrategia de Creación",
|
||||||
|
"description": "Elija esto para configurar manualmente su nodo o generar nuevas credenciales.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adoptar Nodo",
|
||||||
|
"description": "Elija esto si ya tiene las credenciales para el nodo."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Generar Claves",
|
||||||
|
"description": "Elija esto si desea generar nuevas claves para el nodo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adoptar Nodo Existente",
|
||||||
|
"description": "Introduzca las credenciales del nodo existente que desea adoptar",
|
||||||
|
"nodeIdLabel": "ID del nodo",
|
||||||
|
"nodeIdDescription": "El ID del nodo existente que desea adoptar",
|
||||||
|
"secretLabel": "Secreto",
|
||||||
|
"secretDescription": "La clave secreta del nodo existente",
|
||||||
|
"submitButton": "Adoptar Nodo"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Credenciales Generadas",
|
||||||
|
"description": "Utilice estas credenciales generadas para configurar su nodo",
|
||||||
|
"nodeIdTitle": "ID del nodo",
|
||||||
|
"secretTitle": "Secreto",
|
||||||
|
"saveCredentialsTitle": "Agregar Credenciales a la Configuración",
|
||||||
|
"saveCredentialsDescription": "Agrega estas credenciales a tu archivo de configuración del nodo Pangolin autogestionado para completar la conexión.",
|
||||||
|
"submitButton": "Crear Nodo"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "El ID del nodo y el secreto son necesarios al adoptar un nodo existente"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Falló al cargar los valores predeterminados",
|
||||||
|
"defaultsNotLoaded": "Valores predeterminados no cargados",
|
||||||
|
"createFailed": "Error al crear el nodo"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Nodo creado correctamente"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Selección de nodo",
|
||||||
|
"remoteExitNodeSelectionDescription": "Seleccione un nodo a través del cual enrutar el tráfico para este sitio local",
|
||||||
|
"remoteExitNodeRequired": "Un nodo debe ser seleccionado para sitios locales",
|
||||||
|
"noRemoteExitNodesAvailable": "No hay nodos disponibles",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "No hay nodos disponibles para esta organización. Crea un nodo primero para usar sitios locales.",
|
||||||
|
"exitNode": "Nodo de Salida",
|
||||||
|
"country": "País",
|
||||||
|
"rulesMatchCountry": "Actualmente basado en IP de origen",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Autogestionado",
|
||||||
|
"description": "Servidor Pangolin autoalojado más fiable y de bajo mantenimiento con campanas y silbidos extra",
|
||||||
|
"introTitle": "Pangolin autogestionado",
|
||||||
|
"introDescription": "es una opción de despliegue diseñada para personas que quieren simplicidad y fiabilidad extra mientras mantienen sus datos privados y autoalojados.",
|
||||||
|
"introDetail": "Con esta opción, todavía ejecuta su propio nodo Pangolin, sus túneles, terminación SSL y tráfico permanecen en su servidor. La diferencia es que la gestión y el control se gestionan a través de nuestro panel de control en la nube, que desbloquea una serie de ventajas:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Operaciones simples",
|
||||||
|
"description": "No necesitas ejecutar tu propio servidor de correo o configurar alertas complejas. Recibirás cheques de salud y alertas de tiempo de inactividad."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Actualizaciones automáticas",
|
||||||
|
"description": "El tablero de la nube evolucionará rápidamente, por lo que obtendrá nuevas características y correcciones de errores sin tener que extraer manualmente nuevos contenedores cada vez."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Menos mantenimiento",
|
||||||
|
"description": "No hay migraciones de base de datos, copias de seguridad o infraestructura extra para administrar. Lo manejamos en la nube."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Fallo en la nube",
|
||||||
|
"description": "Si tu nodo cae, tus túneles pueden fallar temporalmente a nuestros puntos de presencia en la nube hasta que lo vuelvas a conectar."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Alta disponibilidad (PoPs)",
|
||||||
|
"description": "También puede adjuntar múltiples nodos a su cuenta para redundancia y mejor rendimiento."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Mejoras futuras",
|
||||||
|
"description": "Estamos planeando añadir más herramientas analíticas, alertas y de administración para hacer su despliegue aún más robusto."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Aprenda más acerca de la opción de autoalojamiento administrado en nuestra",
|
||||||
|
"documentation": "documentación"
|
||||||
|
},
|
||||||
|
"convertButton": "Convierte este nodo a autoalojado administrado"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Dominio Internacional detectado",
|
||||||
|
"willbestoredas": "Se almacenará como:",
|
||||||
|
"roleMappingDescription": "Determinar cómo se asignan los roles a los usuarios cuando se registran cuando está habilitada la provisión automática.",
|
||||||
|
"selectRole": "Seleccione un rol",
|
||||||
|
"roleMappingExpression": "Expresión",
|
||||||
|
"selectRolePlaceholder": "Elija un rol",
|
||||||
|
"selectRoleDescription": "Seleccione un rol para asignar a todos los usuarios de este proveedor de identidad",
|
||||||
|
"roleMappingExpressionDescription": "Introduzca una expresión JMESPath para extraer información de rol del token de ID",
|
||||||
|
"idpTenantIdRequired": "El ID del cliente es obligatorio",
|
||||||
|
"invalidValue": "Valor inválido",
|
||||||
|
"idpTypeLabel": "Tipo de proveedor de identidad",
|
||||||
|
"roleMappingExpressionPlaceholder": "e.g., contiene(grupos, 'administrador') && 'administrador' || 'miembro'",
|
||||||
|
"idpGoogleConfiguration": "Configuración de Google",
|
||||||
|
"idpGoogleConfigurationDescription": "Configura tus credenciales de Google OAuth2",
|
||||||
|
"idpGoogleClientIdDescription": "Tu ID de cliente de Google OAuth2",
|
||||||
|
"idpGoogleClientSecretDescription": "Tu secreto de cliente de Google OAuth2",
|
||||||
|
"idpAzureConfiguration": "Configuración de Azure Entra ID",
|
||||||
|
"idpAzureConfigurationDescription": "Configure sus credenciales de Azure Entra ID OAuth2",
|
||||||
|
"idpTenantId": "ID del inquilino",
|
||||||
|
"idpTenantIdPlaceholder": "su-inquilino-id",
|
||||||
|
"idpAzureTenantIdDescription": "Su ID de inquilino de Azure (encontrado en el resumen de Azure Active Directory)",
|
||||||
|
"idpAzureClientIdDescription": "Tu ID de Cliente de Registro de Azure App",
|
||||||
|
"idpAzureClientSecretDescription": "Tu Azure App Registro Cliente secreto",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Configuración de Google",
|
||||||
|
"idpAzureConfigurationTitle": "Configuración de Azure Entra ID",
|
||||||
|
"idpTenantIdLabel": "ID del inquilino",
|
||||||
|
"idpAzureClientIdDescription2": "Tu ID de Cliente de Registro de Azure App",
|
||||||
|
"idpAzureClientSecretDescription2": "Tu Azure App Registro Cliente secreto",
|
||||||
|
"idpGoogleDescription": "Proveedor OAuth2/OIDC de Google",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Subred",
|
||||||
|
"subnetDescription": "La subred para la configuración de red de esta organización.",
|
||||||
|
"authPage": "Página Auth",
|
||||||
|
"authPageDescription": "Configurar la página de autenticación de su organización",
|
||||||
|
"authPageDomain": "Dominio de la página Auth",
|
||||||
|
"noDomainSet": "Ningún dominio establecido",
|
||||||
|
"changeDomain": "Cambiar dominio",
|
||||||
|
"selectDomain": "Seleccionar dominio",
|
||||||
|
"restartCertificate": "Reiniciar certificado",
|
||||||
|
"editAuthPageDomain": "Editar dominio Auth Page",
|
||||||
|
"setAuthPageDomain": "Establecer dominio Auth Page",
|
||||||
|
"failedToFetchCertificate": "Error al obtener el certificado",
|
||||||
|
"failedToRestartCertificate": "Error al reiniciar el certificado",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Añadir un dominio para habilitar páginas de autenticación personalizadas para su organización",
|
||||||
|
"selectDomainForOrgAuthPage": "Seleccione un dominio para la página de autenticación de la organización",
|
||||||
|
"domainPickerProvidedDomain": "Dominio proporcionado",
|
||||||
|
"domainPickerFreeProvidedDomain": "Dominio proporcionado gratis",
|
||||||
|
"domainPickerVerified": "Verificado",
|
||||||
|
"domainPickerUnverified": "Sin verificar",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Este subdominio contiene caracteres o estructura no válidos. Se limpiará automáticamente al guardar.",
|
||||||
|
"domainPickerError": "Error",
|
||||||
|
"domainPickerErrorLoadDomains": "Error al cargar los dominios de la organización",
|
||||||
|
"domainPickerErrorCheckAvailability": "No se pudo comprobar la disponibilidad del dominio",
|
||||||
|
"domainPickerInvalidSubdomain": "Subdominio inválido",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "La entrada \"{sub}\" fue eliminada porque no es válida.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "No se ha podido hacer válido \"{sub}\" para {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Subdominio saneado",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" fue corregido a \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Inicia sesión en tu organización",
|
||||||
|
"orgAuthChooseIdpDescription": "Elige tu proveedor de identidad para continuar",
|
||||||
|
"orgAuthNoIdpConfigured": "Esta organización no tiene ningún proveedor de identidad configurado. En su lugar puedes iniciar sesión con tu identidad de Pangolin.",
|
||||||
|
"orgAuthSignInWithPangolin": "Iniciar sesión con Pangolin",
|
||||||
|
"subscriptionRequiredToUse": "Se requiere una suscripción para utilizar esta función.",
|
||||||
|
"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",
|
||||||
|
"resourceAddEntrypointsEditFile": "Editar archivo: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Editar archivo: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "Se requiere verificación de correo electrónico. Por favor, inicie sesión de nuevo a través de {dashboardUrl}/auth/login complete este paso. Luego, vuelva aquí.",
|
||||||
|
"twoFactorSetupRequired": "La configuración de autenticación de doble factor es requerida. Por favor, inicia sesión de nuevo a través de {dashboardUrl}/auth/login completa este paso. Luego, vuelve aquí.",
|
||||||
|
"authPageErrorUpdateMessage": "Ocurrió un error mientras se actualizaban los ajustes de la página auth",
|
||||||
|
"authPageUpdated": "Página auth actualizada correctamente",
|
||||||
|
"healthCheckNotAvailable": "Local",
|
||||||
|
"rewritePath": "Reescribir Ruta",
|
||||||
|
"rewritePathDescription": "Opcionalmente reescribe la ruta antes de reenviar al destino."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.",
|
"siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans votre réseau. Pas de configuration supplémentaire.",
|
||||||
"siteWg": "WireGuard basique",
|
"siteWg": "WireGuard basique",
|
||||||
"siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.",
|
"siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.",
|
||||||
|
"siteWgDescriptionSaas": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES",
|
||||||
"siteLocalDescription": "Ressources locales seulement. Pas de tunneling.",
|
"siteLocalDescription": "Ressources locales seulement. Pas de tunneling.",
|
||||||
|
"siteLocalDescriptionSaas": "Ressources locales uniquement. Pas de tunneling. FONCTIONNE UNIQUEMENT SUR DES NŒUDS AUTONOMES",
|
||||||
"siteSeeAll": "Voir tous les sites",
|
"siteSeeAll": "Voir tous les sites",
|
||||||
"siteTunnelDescription": "Déterminez comment vous voulez vous connecter à votre site",
|
"siteTunnelDescription": "Déterminez comment vous voulez vous connecter à votre site",
|
||||||
"siteNewtCredentials": "Identifiants Newt",
|
"siteNewtCredentials": "Identifiants Newt",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Sélectionner un site",
|
"siteSelect": "Sélectionner un site",
|
||||||
"siteSearch": "Chercher un site",
|
"siteSearch": "Chercher un site",
|
||||||
"siteNotFound": "Aucun site trouvé.",
|
"siteNotFound": "Aucun site trouvé.",
|
||||||
"siteSelectionDescription": "Ce site fournira la connectivité à la ressource.",
|
"selectCountry": "Sélectionnez un pays",
|
||||||
|
"searchCountries": "Recherchez des pays...",
|
||||||
|
"noCountryFound": "Aucun pays trouvé.",
|
||||||
|
"siteSelectionDescription": "Ce site fournira la connectivité à la cible.",
|
||||||
"resourceType": "Type de ressource",
|
"resourceType": "Type de ressource",
|
||||||
"resourceTypeDescription": "Déterminer comment vous voulez accéder à votre ressource",
|
"resourceTypeDescription": "Déterminer comment vous voulez accéder à votre ressource",
|
||||||
"resourceHTTPSSettings": "Paramètres HTTPS",
|
"resourceHTTPSSettings": "Paramètres HTTPS",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "Généraux",
|
"general": "Généraux",
|
||||||
"generalSettings": "Paramètres généraux",
|
"generalSettings": "Paramètres généraux",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
|
"internal": "Interne",
|
||||||
"rules": "Règles",
|
"rules": "Règles",
|
||||||
"resourceSettingDescription": "Configurer les paramètres de votre ressource",
|
"resourceSettingDescription": "Configurer les paramètres de votre ressource",
|
||||||
"resourceSetting": "Réglages {resourceName}",
|
"resourceSetting": "Réglages {resourceName}",
|
||||||
"alwaysAllow": "Toujours autoriser",
|
"alwaysAllow": "Toujours autoriser",
|
||||||
"alwaysDeny": "Toujours refuser",
|
"alwaysDeny": "Toujours refuser",
|
||||||
|
"passToAuth": "Paser à l'authentification",
|
||||||
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
||||||
"orgGeneralSettings": "Paramètres de l'organisation",
|
"orgGeneralSettings": "Paramètres de l'organisation",
|
||||||
"orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation",
|
"orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Une erreur s'est produite lors de l'ajout de l'utilisateur au rôle.",
|
"accessRoleErrorAddDescription": "Une erreur s'est produite lors de l'ajout de l'utilisateur au rôle.",
|
||||||
"userSaved": "Utilisateur enregistré",
|
"userSaved": "Utilisateur enregistré",
|
||||||
"userSavedDescription": "L'utilisateur a été mis à jour.",
|
"userSavedDescription": "L'utilisateur a été mis à jour.",
|
||||||
|
"autoProvisioned": "Auto-provisionné",
|
||||||
|
"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",
|
"accessControlsDescription": "Gérer ce que cet utilisateur peut accéder et faire dans l'organisation",
|
||||||
"accessControlsSubmit": "Enregistrer les contrôles d'accès",
|
"accessControlsSubmit": "Enregistrer les contrôles d'accès",
|
||||||
"roles": "Rôles",
|
"roles": "Rôles",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "Le nom de serveur TLS à utiliser pour SNI. Laissez vide pour utiliser la valeur par défaut.",
|
"targetTlsSniDescription": "Le nom de serveur TLS à utiliser pour SNI. Laissez vide pour utiliser la valeur par défaut.",
|
||||||
"targetTlsSubmit": "Enregistrer les paramètres",
|
"targetTlsSubmit": "Enregistrer les paramètres",
|
||||||
"targets": "Configuration des cibles",
|
"targets": "Configuration des cibles",
|
||||||
"targetsDescription": "Configurez les cibles pour router le trafic vers vos services",
|
"targetsDescription": "Configurez les cibles pour router le trafic vers vos services.",
|
||||||
"targetStickySessions": "Activer les sessions persistantes",
|
"targetStickySessions": "Activer les sessions persistantes",
|
||||||
"targetStickySessionsDescription": "Maintenir les connexions sur la même cible backend pendant toute leur session.",
|
"targetStickySessionsDescription": "Maintenir les connexions sur la même cible backend pendant toute leur session.",
|
||||||
"methodSelect": "Sélectionner la méthode",
|
"methodSelect": "Sélectionner la méthode",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Format d'adresse IP invalide",
|
"ipAddressErrorInvalidFormat": "Format d'adresse IP invalide",
|
||||||
"ipAddressErrorInvalidOctet": "Octet d'adresse IP invalide",
|
"ipAddressErrorInvalidOctet": "Octet d'adresse IP invalide",
|
||||||
"path": "Chemin",
|
"path": "Chemin",
|
||||||
|
"matchPath": "Chemin de correspondance",
|
||||||
"ipAddressRange": "Plage IP",
|
"ipAddressRange": "Plage IP",
|
||||||
"rulesErrorFetch": "Échec de la récupération des règles",
|
"rulesErrorFetch": "Échec de la récupération des règles",
|
||||||
"rulesErrorFetchDescription": "Une erreur s'est produite lors de la récupération des règles",
|
"rulesErrorFetchDescription": "Une erreur s'est produite lors de la récupération des règles",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Actions",
|
"rulesActions": "Actions",
|
||||||
"rulesActionAlwaysAllow": "Toujours autoriser : Contourner toutes les méthodes d'authentification",
|
"rulesActionAlwaysAllow": "Toujours autoriser : Contourner toutes les méthodes d'authentification",
|
||||||
"rulesActionAlwaysDeny": "Toujours refuser : Bloquer toutes les requêtes ; aucune authentification ne peut être tentée",
|
"rulesActionAlwaysDeny": "Toujours refuser : Bloquer toutes les requêtes ; aucune authentification ne peut être tentée",
|
||||||
|
"rulesActionPassToAuth": "Passer à l'authentification : Autoriser les méthodes d'authentification à être tentées",
|
||||||
"rulesMatchCriteria": "Critères de correspondance",
|
"rulesMatchCriteria": "Critères de correspondance",
|
||||||
"rulesMatchCriteriaIpAddress": "Correspondre à une adresse IP spécifique",
|
"rulesMatchCriteriaIpAddress": "Correspondre à une adresse IP spécifique",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Correspondre à une plage d'adresses IP en notation CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Correspondre à une plage d'adresses IP en notation CIDR",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "Le code PIN doit comporter exactement 6 chiffres",
|
"pincodeRequirementsLength": "Le code PIN doit comporter exactement 6 chiffres",
|
||||||
"pincodeRequirementsChars": "Le code PIN ne doit contenir que des chiffres",
|
"pincodeRequirementsChars": "Le code PIN ne doit contenir que des chiffres",
|
||||||
"passwordRequirementsLength": "Le mot de passe doit comporter au moins 1 caractère",
|
"passwordRequirementsLength": "Le mot de passe doit comporter au moins 1 caractère",
|
||||||
|
"passwordRequirementsTitle": "Exigences relatives au mot de passe :",
|
||||||
|
"passwordRequirementLength": "Au moins 8 caractères",
|
||||||
|
"passwordRequirementUppercase": "Au moins une lettre majuscule",
|
||||||
|
"passwordRequirementLowercase": "Au moins une lettre minuscule",
|
||||||
|
"passwordRequirementNumber": "Au moins un chiffre",
|
||||||
|
"passwordRequirementSpecial": "Au moins un caractère spécial",
|
||||||
|
"passwordRequirementsMet": "✓ Le mot de passe répond à toutes les exigences",
|
||||||
|
"passwordStrength": "Solidité du mot de passe",
|
||||||
|
"passwordStrengthWeak": "Faible",
|
||||||
|
"passwordStrengthMedium": "Moyen",
|
||||||
|
"passwordStrengthStrong": "Fort",
|
||||||
|
"passwordRequirements": "Exigences :",
|
||||||
|
"passwordRequirementLengthText": "8+ caractères",
|
||||||
|
"passwordRequirementUppercaseText": "Lettre majuscule (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Lettre minuscule (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Nombre (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Caractère spécial (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas",
|
||||||
"otpEmailRequirementsLength": "L'OTP doit comporter au moins 1 caractère",
|
"otpEmailRequirementsLength": "L'OTP doit comporter au moins 1 caractère",
|
||||||
"otpEmailSent": "OTP envoyé",
|
"otpEmailSent": "OTP envoyé",
|
||||||
"otpEmailSentDescription": "Un OTP a été envoyé à votre e-mail",
|
"otpEmailSentDescription": "Un OTP a été envoyé à votre e-mail",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "Erreur lors de la déconnexion",
|
"logoutError": "Erreur lors de la déconnexion",
|
||||||
"signingAs": "Connecté en tant que",
|
"signingAs": "Connecté en tant que",
|
||||||
"serverAdmin": "Admin Serveur",
|
"serverAdmin": "Admin Serveur",
|
||||||
|
"managedSelfhosted": "Gestion autonome",
|
||||||
"otpEnable": "Activer l'authentification à deux facteurs",
|
"otpEnable": "Activer l'authentification à deux facteurs",
|
||||||
"otpDisable": "Désactiver l'authentification à deux facteurs",
|
"otpDisable": "Désactiver l'authentification à deux facteurs",
|
||||||
"logout": "Déconnexion",
|
"logout": "Déconnexion",
|
||||||
"licenseTierProfessionalRequired": "Édition Professionnelle Requise",
|
"licenseTierProfessionalRequired": "Édition Professionnelle Requise",
|
||||||
"licenseTierProfessionalRequiredDescription": "Cette fonctionnalité n'est disponible que dans l'Édition Professionnelle.",
|
"licenseTierProfessionalRequiredDescription": "Cette fonctionnalité n'est disponible que dans l'Édition Professionnelle.",
|
||||||
"actionGetOrg": "Obtenir l'organisation",
|
"actionGetOrg": "Obtenir l'organisation",
|
||||||
|
"updateOrgUser": "Mise à jour de l'utilisateur Org",
|
||||||
|
"createOrgUser": "Créer un utilisateur Org",
|
||||||
"actionUpdateOrg": "Mettre à jour l'organisation",
|
"actionUpdateOrg": "Mettre à jour l'organisation",
|
||||||
"actionUpdateUser": "Mettre à jour l'utilisateur",
|
"actionUpdateUser": "Mettre à jour l'utilisateur",
|
||||||
"actionGetUser": "Obtenir l'utilisateur",
|
"actionGetUser": "Obtenir l'utilisateur",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "Supprimer un site",
|
"actionDeleteSite": "Supprimer un site",
|
||||||
"actionGetSite": "Obtenir un site",
|
"actionGetSite": "Obtenir un site",
|
||||||
"actionListSites": "Lister les sites",
|
"actionListSites": "Lister les sites",
|
||||||
|
"actionApplyBlueprint": "Appliquer le Plan",
|
||||||
|
"setupToken": "Jeton de configuration",
|
||||||
|
"setupTokenDescription": "Entrez le jeton de configuration depuis la console du serveur.",
|
||||||
|
"setupTokenRequired": "Le jeton de configuration est requis.",
|
||||||
"actionUpdateSite": "Mettre à jour un site",
|
"actionUpdateSite": "Mettre à jour un site",
|
||||||
"actionListSiteRoles": "Lister les rôles autorisés du site",
|
"actionListSiteRoles": "Lister les rôles autorisés du site",
|
||||||
"actionCreateResource": "Créer une ressource",
|
"actionCreateResource": "Créer une ressource",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Supprimer une politique d'organisation IDP",
|
"actionDeleteIdpOrg": "Supprimer une politique d'organisation IDP",
|
||||||
"actionListIdpOrgs": "Lister les organisations IDP",
|
"actionListIdpOrgs": "Lister les organisations IDP",
|
||||||
"actionUpdateIdpOrg": "Mettre à jour une organisation IDP",
|
"actionUpdateIdpOrg": "Mettre à jour une organisation IDP",
|
||||||
|
"actionCreateClient": "Créer un client",
|
||||||
|
"actionDeleteClient": "Supprimer le client",
|
||||||
|
"actionUpdateClient": "Mettre à jour le client",
|
||||||
|
"actionListClients": "Liste des clients",
|
||||||
|
"actionGetClient": "Obtenir le client",
|
||||||
|
"actionCreateSiteResource": "Créer une ressource de site",
|
||||||
|
"actionDeleteSiteResource": "Supprimer une ressource de site",
|
||||||
|
"actionGetSiteResource": "Obtenir une ressource de site",
|
||||||
|
"actionListSiteResources": "Lister les ressources de site",
|
||||||
|
"actionUpdateSiteResource": "Mettre à jour une ressource de site",
|
||||||
|
"actionListInvitations": "Lister les invitations",
|
||||||
"noneSelected": "Aucune sélection",
|
"noneSelected": "Aucune sélection",
|
||||||
"orgNotFound2": "Aucune organisation trouvée.",
|
"orgNotFound2": "Aucune organisation trouvée.",
|
||||||
"searchProgress": "Rechercher...",
|
"searchProgress": "Rechercher...",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "Licence",
|
"sidebarLicense": "Licence",
|
||||||
"sidebarClients": "Clients (Bêta)",
|
"sidebarClients": "Clients (Bêta)",
|
||||||
"sidebarDomains": "Domaines",
|
"sidebarDomains": "Domaines",
|
||||||
"enableDockerSocket": "Activer Docker Socket",
|
"enableDockerSocket": "Activer le Plan Docker",
|
||||||
"enableDockerSocketDescription": "Activer la découverte Docker Socket pour remplir les informations du conteneur. Le chemin du socket doit être fourni à Newt.",
|
"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",
|
"enableDockerSocketLink": "En savoir plus",
|
||||||
"viewDockerContainers": "Voir les conteneurs Docker",
|
"viewDockerContainers": "Voir les conteneurs Docker",
|
||||||
"containersIn": "Conteneurs en {siteName}",
|
"containersIn": "Conteneurs en {siteName}",
|
||||||
@@ -1196,7 +1243,7 @@
|
|||||||
"newtUpdateAvailable": "Mise à jour disponible",
|
"newtUpdateAvailable": "Mise à jour disponible",
|
||||||
"newtUpdateAvailableInfo": "Une nouvelle version de Newt est disponible. Veuillez mettre à jour vers la dernière version pour une meilleure expérience.",
|
"newtUpdateAvailableInfo": "Une nouvelle version de Newt est disponible. Veuillez mettre à jour vers la dernière version pour une meilleure expérience.",
|
||||||
"domainPickerEnterDomain": "Domaine",
|
"domainPickerEnterDomain": "Domaine",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com, ou simplement myapp",
|
"domainPickerPlaceholder": "monapp.exemple.com",
|
||||||
"domainPickerDescription": "Entrez le domaine complet de la ressource pour voir les options disponibles.",
|
"domainPickerDescription": "Entrez le domaine complet de la ressource pour voir les options disponibles.",
|
||||||
"domainPickerDescriptionSaas": "Entrez un domaine complet, un sous-domaine ou juste un nom pour voir les options disponibles",
|
"domainPickerDescriptionSaas": "Entrez un domaine complet, un sous-domaine ou juste un nom pour voir les options disponibles",
|
||||||
"domainPickerTabAll": "Tous",
|
"domainPickerTabAll": "Tous",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "Sous-domaine : {subdomain}",
|
"domainPickerSubdomain": "Sous-domaine : {subdomain}",
|
||||||
"domainPickerNamespace": "Espace de noms : {namespace}",
|
"domainPickerNamespace": "Espace de noms : {namespace}",
|
||||||
"domainPickerShowMore": "Afficher plus",
|
"domainPickerShowMore": "Afficher plus",
|
||||||
|
"regionSelectorTitle": "Sélectionner Région",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Chargement de l'abonnement...",
|
||||||
|
"billingFreeTier": "Niveau gratuit",
|
||||||
|
"billingWarningOverLimit": "Attention : Vous avez dépassé une ou plusieurs limites d'utilisation. Vos sites ne se connecteront pas tant que vous n'avez pas modifié votre abonnement ou ajusté votre utilisation.",
|
||||||
|
"billingUsageLimitsOverview": "Vue d'ensemble des limites d'utilisation",
|
||||||
|
"billingMonitorUsage": "Surveillez votre consommation par rapport aux limites configurées. Si vous avez besoin d'une augmentation des limites, veuillez nous contacter à support@fossorial.io.",
|
||||||
|
"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",
|
||||||
|
"billingNoLimitConfigured": "Aucune limite configurée",
|
||||||
|
"billingEstimatedPeriod": "Période de facturation estimée",
|
||||||
|
"billingIncludedUsage": "Utilisation incluse",
|
||||||
|
"billingIncludedUsageDescription": "Utilisation incluse dans votre plan d'abonnement actuel",
|
||||||
|
"billingFreeTierIncludedUsage": "Tolérances d'utilisation du niveau gratuit",
|
||||||
|
"billingIncluded": "inclus",
|
||||||
|
"billingEstimatedTotal": "Total estimé :",
|
||||||
|
"billingNotes": "Notes",
|
||||||
|
"billingEstimateNote": "Ceci est une estimation basée sur votre utilisation actuelle.",
|
||||||
|
"billingActualChargesMayVary": "Les frais réels peuvent varier.",
|
||||||
|
"billingBilledAtEnd": "Vous serez facturé à la fin de la période de facturation.",
|
||||||
|
"billingModifySubscription": "Modifier l'abonnement",
|
||||||
|
"billingStartSubscription": "Démarrer l'abonnement",
|
||||||
|
"billingRecurringCharge": "Frais récurrents",
|
||||||
|
"billingManageSubscriptionSettings": "Gérez les paramètres et préférences de votre abonnement",
|
||||||
|
"billingNoActiveSubscription": "Vous n'avez pas d'abonnement actif. Commencez votre abonnement pour augmenter les limites d'utilisation.",
|
||||||
|
"billingFailedToLoadSubscription": "Échec du chargement de l'abonnement",
|
||||||
|
"billingFailedToLoadUsage": "Échec du chargement de l'utilisation",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Échec pour obtenir l'URL de paiement",
|
||||||
|
"billingPleaseTryAgainLater": "Veuillez réessayer plus tard.",
|
||||||
|
"billingCheckoutError": "Erreur de paiement",
|
||||||
|
"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 dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de comptes utilisateurs actifs dans votre organisation.",
|
||||||
|
"billingDomainInfo": "Vous êtes facturé pour chaque domaine dans votre 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 nœud géré dans votre organisation. La facturation est calculée quotidiennement en fonction du nombre de nœuds gérés actifs dans votre organisation.",
|
||||||
"domainNotFound": "Domaine introuvable",
|
"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.",
|
"domainNotFoundDescription": "Cette ressource est désactivée car le domaine n'existe plus dans notre système. Veuillez définir un nouveau domaine pour cette ressource.",
|
||||||
"failed": "Échec",
|
"failed": "Échec",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "Les modifications DNS peuvent mettre du temps à se propager sur internet. Cela peut prendre de quelques minutes à 48 heures selon votre fournisseur DNS et les réglages TTL.",
|
"createDomainDnsPropagationDescription": "Les modifications DNS peuvent mettre du temps à se propager sur internet. Cela peut prendre de quelques minutes à 48 heures selon votre fournisseur DNS et les réglages TTL.",
|
||||||
"resourcePortRequired": "Le numéro de port est requis pour les ressources non-HTTP",
|
"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",
|
"resourcePortNotAllowed": "Le numéro de port ne doit pas être défini pour les ressources HTTP",
|
||||||
|
"billingPricingCalculatorLink": "Calculateur de prix",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Je suis d'accord avec",
|
"IAgreeToThe": "Je suis d'accord avec",
|
||||||
"termsOfService": "les conditions d'utilisation",
|
"termsOfService": "les conditions d'utilisation",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "Une erreur s'est produite lors de la récupération de la dernière version d'Olm.",
|
"olmErrorFetchLatest": "Une erreur s'est produite lors de la récupération de la dernière version d'Olm.",
|
||||||
"remoteSubnets": "Sous-réseaux distants",
|
"remoteSubnets": "Sous-réseaux distants",
|
||||||
"enterCidrRange": "Entrez la plage CIDR",
|
"enterCidrRange": "Entrez la plage CIDR",
|
||||||
"remoteSubnetsDescription": "Ajoutez des plages CIDR pouvant accéder à ce site à distance. Utilisez le format comme 10.0.0.0/24 ou 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Ajoutez des plages CIDR accessibles à distance depuis ce site à l'aide de clients. Utilisez le format comme 10.0.0.0/24. Cela s'applique UNIQUEMENT à la connectivité des clients VPN.",
|
||||||
"resourceEnableProxy": "Activer le proxy public",
|
"resourceEnableProxy": "Activer le proxy public",
|
||||||
"resourceEnableProxyDescription": "Activez le proxy public vers cette ressource. Cela permet d'accéder à la ressource depuis l'extérieur du réseau via le cloud sur un port ouvert. Nécessite la configuration de Traefik.",
|
"resourceEnableProxyDescription": "Activez le proxy public vers cette ressource. Cela permet d'accéder à la ressource depuis l'extérieur du réseau via le cloud sur un port ouvert. Nécessite la configuration de Traefik.",
|
||||||
"externalProxyEnabled": "Proxy externe activé"
|
"externalProxyEnabled": "Proxy externe activé",
|
||||||
|
"addNewTarget": "Ajouter une nouvelle cible",
|
||||||
|
"targetsList": "Liste des cibles",
|
||||||
|
"targetErrorDuplicateTargetFound": "Cible en double trouvée",
|
||||||
|
"healthCheckHealthy": "Sain",
|
||||||
|
"healthCheckUnhealthy": "En mauvaise santé",
|
||||||
|
"healthCheckUnknown": "Inconnu",
|
||||||
|
"healthCheck": "Vérification de l'état de santé",
|
||||||
|
"configureHealthCheck": "Configurer la vérification de l'état de santé",
|
||||||
|
"configureHealthCheckDescription": "Configurer la surveillance de la santé pour {target}",
|
||||||
|
"enableHealthChecks": "Activer les vérifications de santé",
|
||||||
|
"enableHealthChecksDescription": "Surveiller la vie de cette cible. Vous pouvez surveiller un point de terminaison différent de la cible si nécessaire.",
|
||||||
|
"healthScheme": "Méthode",
|
||||||
|
"healthSelectScheme": "Sélectionnez la méthode",
|
||||||
|
"healthCheckPath": "Chemin d'accès",
|
||||||
|
"healthHostname": "IP / Hôte",
|
||||||
|
"healthPort": "Port",
|
||||||
|
"healthCheckPathDescription": "Le chemin à vérifier pour le statut de santé.",
|
||||||
|
"healthyIntervalSeconds": "Intervalle sain",
|
||||||
|
"unhealthyIntervalSeconds": "Intervalle en mauvaise santé",
|
||||||
|
"IntervalSeconds": "Intervalle sain",
|
||||||
|
"timeoutSeconds": "Délai",
|
||||||
|
"timeIsInSeconds": "Le temps est exprimé en secondes",
|
||||||
|
"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.",
|
||||||
|
"customHeaders": "En-têtes personnalisés",
|
||||||
|
"customHeadersDescription": "En-têtes séparés par une nouvelle ligne: En-nom: valeur",
|
||||||
|
"headersValidationError": "Les entêtes doivent être au format : Header-Name: valeur.",
|
||||||
|
"saveHealthCheck": "Sauvegarder la vérification de l'état de santé",
|
||||||
|
"healthCheckSaved": "Vérification de l'état de santé enregistrée",
|
||||||
|
"healthCheckSavedDescription": "La configuration de la vérification de l'état de santé a été enregistrée avec succès",
|
||||||
|
"healthCheckError": "Erreur de vérification de l'état de santé",
|
||||||
|
"healthCheckErrorDescription": "Une erreur s'est produite lors de l'enregistrement de la configuration de la vérification de l'état de santé",
|
||||||
|
"healthCheckPathRequired": "Le chemin de vérification de l'état de santé est requis",
|
||||||
|
"healthCheckMethodRequired": "La méthode HTTP est requise",
|
||||||
|
"healthCheckIntervalMin": "L'intervalle de vérification doit être d'au moins 5 secondes",
|
||||||
|
"healthCheckTimeoutMin": "Le délai doit être d'au moins 1 seconde",
|
||||||
|
"healthCheckRetryMin": "Les tentatives de réessai doivent être d'au moins 1",
|
||||||
|
"httpMethod": "Méthode HTTP",
|
||||||
|
"selectHttpMethod": "Sélectionnez la méthode HTTP",
|
||||||
|
"domainPickerSubdomainLabel": "Sous-domaine",
|
||||||
|
"domainPickerBaseDomainLabel": "Domaine de base",
|
||||||
|
"domainPickerSearchDomains": "Rechercher des domaines...",
|
||||||
|
"domainPickerNoDomainsFound": "Aucun domaine trouvé",
|
||||||
|
"domainPickerLoadingDomains": "Chargement des domaines...",
|
||||||
|
"domainPickerSelectBaseDomain": "Sélectionnez le domaine de base...",
|
||||||
|
"domainPickerNotAvailableForCname": "Non disponible pour les domaines CNAME",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Entrez un sous-domaine ou laissez vide pour utiliser le domaine de base.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Entrez un sous-domaine pour rechercher et sélectionner parmi les domaines gratuits disponibles.",
|
||||||
|
"domainPickerFreeDomains": "Domaines gratuits",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Rechercher des domaines disponibles",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Remarque : Les domaines fournis gratuitement ne sont pas disponibles pour les instances auto-hébergées pour le moment.",
|
||||||
|
"resourceDomain": "Domaine",
|
||||||
|
"resourceEditDomain": "Modifier le domaine",
|
||||||
|
"siteName": "Nom du site",
|
||||||
|
"proxyPort": "Port",
|
||||||
|
"resourcesTableProxyResources": "Ressources proxy",
|
||||||
|
"resourcesTableClientResources": "Ressources client",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Aucune ressource proxy trouvée.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Aucune ressource interne trouvée.",
|
||||||
|
"resourcesTableDestination": "Destination",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Ces ressources sont à utiliser avec",
|
||||||
|
"resourcesTableClients": "Clients",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "et sont uniquement accessibles en interne lorsqu'elles sont connectées avec un client.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Modifier la ressource client",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Mettez à jour les propriétés de la ressource et la configuration de la cible pour {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Propriétés de la ressource",
|
||||||
|
"editInternalResourceDialogName": "Nom",
|
||||||
|
"editInternalResourceDialogProtocol": "Protocole",
|
||||||
|
"editInternalResourceDialogSitePort": "Port du site",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Configuration de la cible",
|
||||||
|
"editInternalResourceDialogCancel": "Abandonner",
|
||||||
|
"editInternalResourceDialogSaveResource": "Enregistrer la ressource",
|
||||||
|
"editInternalResourceDialogSuccess": "Succès",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Ressource interne mise à jour avec succès",
|
||||||
|
"editInternalResourceDialogError": "Erreur",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Échec de la mise à jour de la ressource interne",
|
||||||
|
"editInternalResourceDialogNameRequired": "Le nom est requis",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Le nom doit être inférieur à 255 caractères",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Le port proxy doit être d'au moins 1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Le port proxy doit être inférieur à 65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Format d'adresse IP invalide",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Le port de destination doit être d'au moins 1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Le port de destination doit être inférieur à 65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Aucun site disponible",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Vous devez avoir au moins un site Newt avec un sous-réseau configuré pour créer des ressources internes.",
|
||||||
|
"createInternalResourceDialogClose": "Fermer",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Créer une ressource client",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Créez une ressource accessible aux clients connectés au site sélectionné.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Propriétés de la ressource",
|
||||||
|
"createInternalResourceDialogName": "Nom",
|
||||||
|
"createInternalResourceDialogSite": "Site",
|
||||||
|
"createInternalResourceDialogSelectSite": "Sélectionner un site...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Rechercher des sites...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Aucun site trouvé.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protocole",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Port du site",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Utilisez ce port pour accéder à la ressource sur le site lors de la connexion avec un client.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Configuration de la cible",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "L'adresse IP ou le nom d'hôte de la ressource sur le réseau du site.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "Le port sur l'IP de destination où la ressource est accessible.",
|
||||||
|
"createInternalResourceDialogCancel": "Abandonner",
|
||||||
|
"createInternalResourceDialogCreateResource": "Créer une ressource",
|
||||||
|
"createInternalResourceDialogSuccess": "Succès",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Ressource interne créée avec succès",
|
||||||
|
"createInternalResourceDialogError": "Erreur",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Échec de la création de la ressource interne",
|
||||||
|
"createInternalResourceDialogNameRequired": "Le nom est requis",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Le nom doit être inférieur à 255 caractères",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Veuillez sélectionner un site",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Le port proxy doit être d'au moins 1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Le port proxy doit être inférieur à 65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Format d'adresse IP invalide",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Le port de destination doit être d'au moins 1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Le port de destination doit être inférieur à 65536",
|
||||||
|
"siteConfiguration": "Configuration",
|
||||||
|
"siteAcceptClientConnections": "Accepter les connexions client",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Permet à d'autres appareils de se connecter via cette instance de Newt en tant que passerelle utilisant des clients.",
|
||||||
|
"siteAddress": "Adresse du site",
|
||||||
|
"siteAddressDescription": "Spécifiez l'adresse IP de l'hôte pour que les clients puissent s'y connecter. C'est l'adresse interne du site dans le réseau Pangolin pour que les clients puissent s'adresser. Doit être dans le sous-réseau de l'organisation.",
|
||||||
|
"autoLoginExternalIdp": "Connexion automatique avec IDP externe",
|
||||||
|
"autoLoginExternalIdpDescription": "Rediriger immédiatement l'utilisateur vers l'IDP externe pour l'authentification.",
|
||||||
|
"selectIdp": "Sélectionner l'IDP",
|
||||||
|
"selectIdpPlaceholder": "Choisissez un IDP...",
|
||||||
|
"selectIdpRequired": "Veuillez sélectionner un IDP lorsque la connexion automatique est activée.",
|
||||||
|
"autoLoginTitle": "Redirection",
|
||||||
|
"autoLoginDescription": "Redirection vers le fournisseur d'identité externe pour l'authentification.",
|
||||||
|
"autoLoginProcessing": "Préparation de l'authentification...",
|
||||||
|
"autoLoginRedirecting": "Redirection vers la connexion...",
|
||||||
|
"autoLoginError": "Erreur de connexion automatique",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "Aucune URL de redirection reçue du fournisseur d'identité.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Échec de la génération de l'URL d'authentification.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Gérer auto-hébergé",
|
||||||
|
"remoteExitNodeDescription": "Gérer les nœuds pour étendre votre connectivité réseau",
|
||||||
|
"remoteExitNodes": "Nœuds",
|
||||||
|
"searchRemoteExitNodes": "Rechercher des nœuds...",
|
||||||
|
"remoteExitNodeAdd": "Ajouter un noeud",
|
||||||
|
"remoteExitNodeErrorDelete": "Erreur lors de la suppression du noeud",
|
||||||
|
"remoteExitNodeQuestionRemove": "Êtes-vous sûr de vouloir supprimer le noeud {selectedNode} de l'organisation ?",
|
||||||
|
"remoteExitNodeMessageRemove": "Une fois supprimé, le noeud ne sera plus accessible.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Pour confirmer, veuillez saisir le nom du noeud ci-dessous.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Confirmer la suppression du noeud",
|
||||||
|
"remoteExitNodeDelete": "Supprimer le noeud",
|
||||||
|
"sidebarRemoteExitNodes": "Nœuds",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Créer un noeud",
|
||||||
|
"description": "Créer un nouveau nœud pour étendre votre connectivité réseau",
|
||||||
|
"viewAllButton": "Voir tous les nœuds",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Stratégie de création",
|
||||||
|
"description": "Choisissez ceci pour configurer manuellement votre nœud ou générer de nouveaux identifiants.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adopter un nœud",
|
||||||
|
"description": "Choisissez ceci si vous avez déjà les identifiants pour le noeud."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Générer des clés",
|
||||||
|
"description": "Choisissez ceci si vous voulez générer de nouvelles clés pour le noeud"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adopter un nœud existant",
|
||||||
|
"description": "Entrez les identifiants du noeud existant que vous souhaitez adopter",
|
||||||
|
"nodeIdLabel": "Nœud ID",
|
||||||
|
"nodeIdDescription": "L'ID du noeud existant que vous voulez adopter",
|
||||||
|
"secretLabel": "Secret",
|
||||||
|
"secretDescription": "La clé secrète du noeud existant",
|
||||||
|
"submitButton": "Noeud d'Adopt"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Informations d'identification générées",
|
||||||
|
"description": "Utilisez ces identifiants générés pour configurer votre noeud",
|
||||||
|
"nodeIdTitle": "Nœud ID",
|
||||||
|
"secretTitle": "Secret",
|
||||||
|
"saveCredentialsTitle": "Ajouter des identifiants à la config",
|
||||||
|
"saveCredentialsDescription": "Ajoutez ces informations d'identification à votre fichier de configuration du nœud Pangolin auto-hébergé pour compléter la connexion.",
|
||||||
|
"submitButton": "Créer un noeud"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "ID de nœud et secret sont requis lors de l'adoption d'un noeud existant"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Échec du chargement des valeurs par défaut",
|
||||||
|
"defaultsNotLoaded": "Valeurs par défaut non chargées",
|
||||||
|
"createFailed": "Impossible de créer le noeud"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Noeud créé avec succès"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Sélection du noeud",
|
||||||
|
"remoteExitNodeSelectionDescription": "Sélectionnez un nœud pour acheminer le trafic pour ce site local",
|
||||||
|
"remoteExitNodeRequired": "Un noeud doit être sélectionné pour les sites locaux",
|
||||||
|
"noRemoteExitNodesAvailable": "Aucun noeud disponible",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Aucun noeud n'est disponible pour cette organisation. Créez d'abord un noeud pour utiliser des sites locaux.",
|
||||||
|
"exitNode": "Nœud de sortie",
|
||||||
|
"country": "Pays",
|
||||||
|
"rulesMatchCountry": "Actuellement basé sur l'IP source",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Gestion autonome",
|
||||||
|
"description": "Serveur Pangolin auto-hébergé avec des cloches et des sifflets supplémentaires",
|
||||||
|
"introTitle": "Pangolin auto-hébergé géré",
|
||||||
|
"introDescription": "est une option de déploiement conçue pour les personnes qui veulent de la simplicité et de la fiabilité tout en gardant leurs données privées et auto-hébergées.",
|
||||||
|
"introDetail": "Avec cette option, vous exécutez toujours votre propre nœud Pangolin — vos tunnels, la terminaison SSL et le trafic restent sur votre serveur. La différence est que la gestion et la surveillance sont gérées via notre tableau de bord du cloud, qui déverrouille un certain nombre d'avantages :",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Opérations plus simples",
|
||||||
|
"description": "Pas besoin de faire tourner votre propre serveur de messagerie ou de configurer des alertes complexes. Vous obtiendrez des contrôles de santé et des alertes de temps d'arrêt par la suite."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Mises à jour automatiques",
|
||||||
|
"description": "Le tableau de bord du cloud évolue rapidement, de sorte que vous obtenez de nouvelles fonctionnalités et des corrections de bugs sans avoir à extraire manuellement de nouveaux conteneurs à chaque fois."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Moins de maintenance",
|
||||||
|
"description": "Aucune migration de base de données, sauvegarde ou infrastructure supplémentaire à gérer. Nous gérons cela dans le cloud."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Basculement du Cloud",
|
||||||
|
"description": "Si votre nœud descend, vos tunnels peuvent temporairement échouer jusqu'à ce que vous le rapatriez en ligne."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Haute disponibilité (PoPs)",
|
||||||
|
"description": "Vous pouvez également attacher plusieurs nœuds à votre compte pour une redondance et de meilleures performances."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Améliorations futures",
|
||||||
|
"description": "Nous prévoyons d'ajouter plus d'outils d'analyse, d'alerte et de gestion pour rendre votre déploiement encore plus robuste."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "En savoir plus sur l'option Auto-Hébergement géré dans notre",
|
||||||
|
"documentation": "documentation"
|
||||||
|
},
|
||||||
|
"convertButton": "Convertir ce noeud en auto-hébergé géré"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Domaine international détecté",
|
||||||
|
"willbestoredas": "Sera stocké comme :",
|
||||||
|
"roleMappingDescription": "Détermine comment les rôles sont assignés aux utilisateurs lorsqu'ils se connectent lorsque la fourniture automatique est activée.",
|
||||||
|
"selectRole": "Sélectionnez un rôle",
|
||||||
|
"roleMappingExpression": "Expression",
|
||||||
|
"selectRolePlaceholder": "Choisir un rôle",
|
||||||
|
"selectRoleDescription": "Sélectionnez un rôle à assigner à tous les utilisateurs de ce fournisseur d'identité",
|
||||||
|
"roleMappingExpressionDescription": "Entrez une expression JMESPath pour extraire les informations du rôle du jeton ID",
|
||||||
|
"idpTenantIdRequired": "L'ID du locataire est requis",
|
||||||
|
"invalidValue": "Valeur non valide",
|
||||||
|
"idpTypeLabel": "Type de fournisseur d'identité",
|
||||||
|
"roleMappingExpressionPlaceholder": "ex: contenu(groupes) && 'admin' || 'membre'",
|
||||||
|
"idpGoogleConfiguration": "Configuration Google",
|
||||||
|
"idpGoogleConfigurationDescription": "Configurer vos identifiants Google OAuth2",
|
||||||
|
"idpGoogleClientIdDescription": "Votre identifiant client Google OAuth2",
|
||||||
|
"idpGoogleClientSecretDescription": "Votre secret client Google OAuth2",
|
||||||
|
"idpAzureConfiguration": "Configuration de l'entra ID Azure",
|
||||||
|
"idpAzureConfigurationDescription": "Configurer vos identifiants OAuth2 Azure Entra",
|
||||||
|
"idpTenantId": "ID du locataire",
|
||||||
|
"idpTenantIdPlaceholder": "votre-locataire-id",
|
||||||
|
"idpAzureTenantIdDescription": "Votre ID de locataire Azure (trouvé dans l'aperçu Azure Active Directory)",
|
||||||
|
"idpAzureClientIdDescription": "Votre ID client d'enregistrement de l'application Azure",
|
||||||
|
"idpAzureClientSecretDescription": "Le secret de votre client d'enregistrement Azure App",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Configuration Google",
|
||||||
|
"idpAzureConfigurationTitle": "Configuration de l'entra ID Azure",
|
||||||
|
"idpTenantIdLabel": "ID du locataire",
|
||||||
|
"idpAzureClientIdDescription2": "Votre ID client d'enregistrement de l'application Azure",
|
||||||
|
"idpAzureClientSecretDescription2": "Le secret de votre client d'enregistrement Azure App",
|
||||||
|
"idpGoogleDescription": "Fournisseur Google OAuth2/OIDC",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Sous-réseau",
|
||||||
|
"subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.",
|
||||||
|
"authPage": "Page d'authentification",
|
||||||
|
"authPageDescription": "Configurer la page d'authentification de votre organisation",
|
||||||
|
"authPageDomain": "Domaine de la page d'authentification",
|
||||||
|
"noDomainSet": "Aucun domaine défini",
|
||||||
|
"changeDomain": "Changer de domaine",
|
||||||
|
"selectDomain": "Sélectionner un domaine",
|
||||||
|
"restartCertificate": "Redémarrer le certificat",
|
||||||
|
"editAuthPageDomain": "Modifier le domaine de la page d'authentification",
|
||||||
|
"setAuthPageDomain": "Définir le domaine de la page d'authentification",
|
||||||
|
"failedToFetchCertificate": "Impossible de récupérer le certificat",
|
||||||
|
"failedToRestartCertificate": "Échec du redémarrage du certificat",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Ajouter un domaine pour activer les pages d'authentification personnalisées pour votre organisation",
|
||||||
|
"selectDomainForOrgAuthPage": "Sélectionnez un domaine pour la page d'authentification de l'organisation",
|
||||||
|
"domainPickerProvidedDomain": "Domaine fourni",
|
||||||
|
"domainPickerFreeProvidedDomain": "Domaine fourni gratuitement",
|
||||||
|
"domainPickerVerified": "Vérifié",
|
||||||
|
"domainPickerUnverified": "Non vérifié",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Ce sous-domaine contient des caractères ou une structure non valide. Il sera automatiquement nettoyé lorsque vous enregistrez.",
|
||||||
|
"domainPickerError": "Erreur",
|
||||||
|
"domainPickerErrorLoadDomains": "Impossible de charger les domaines de l'organisation",
|
||||||
|
"domainPickerErrorCheckAvailability": "Impossible de vérifier la disponibilité du domaine",
|
||||||
|
"domainPickerInvalidSubdomain": "Sous-domaine invalide",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "L'entrée \"{sub}\" a été supprimée car elle n'est pas valide.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "La «{sub}» n'a pas pu être validée pour {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Sous-domaine nettoyé",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" a été corrigé à \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Connectez-vous à votre organisation",
|
||||||
|
"orgAuthChooseIdpDescription": "Choisissez votre fournisseur d'identité pour continuer",
|
||||||
|
"orgAuthNoIdpConfigured": "Cette organisation n'a aucun fournisseur d'identité configuré. Vous pouvez vous connecter avec votre identité Pangolin à la place.",
|
||||||
|
"orgAuthSignInWithPangolin": "Se connecter avec Pangolin",
|
||||||
|
"subscriptionRequiredToUse": "Un abonnement est requis pour utiliser cette fonctionnalité.",
|
||||||
|
"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",
|
||||||
|
"resourceAddEntrypointsEditFile": "Modifier le fichier : config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Modifier le fichier : docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "La vérification de l'e-mail est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.",
|
||||||
|
"twoFactorSetupRequired": "La configuration d'authentification à deux facteurs est requise. Veuillez vous reconnecter via {dashboardUrl}/auth/login terminé cette étape. Puis revenez ici.",
|
||||||
|
"authPageErrorUpdateMessage": "Une erreur s'est produite lors de la mise à jour de la page d\u000027authentification",
|
||||||
|
"authPageUpdated": "Page d\u000027authentification mise à jour avec succès",
|
||||||
|
"healthCheckNotAvailable": "Locale",
|
||||||
|
"rewritePath": "Réécrire le chemin",
|
||||||
|
"rewritePathDescription": "Réécrivez éventuellement le chemin avant de le transmettre à la cible."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@
|
|||||||
"viewSettings": "Visualizza impostazioni",
|
"viewSettings": "Visualizza impostazioni",
|
||||||
"delete": "Elimina",
|
"delete": "Elimina",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"online": "Online",
|
"online": "In linea",
|
||||||
"offline": "Offline",
|
"offline": "Non in linea",
|
||||||
"site": "Sito",
|
"site": "Sito",
|
||||||
"dataIn": "Dati In",
|
"dataIn": "Dati In",
|
||||||
"dataOut": "Dati Fuori",
|
"dataOut": "Dati Fuori",
|
||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint nella rete. Nessuna configurazione aggiuntiva.",
|
"siteNewtTunnelDescription": "Modo più semplice per creare un entrypoint nella rete. Nessuna configurazione aggiuntiva.",
|
||||||
"siteWg": "WireGuard Base",
|
"siteWg": "WireGuard Base",
|
||||||
"siteWgDescription": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta.",
|
"siteWgDescription": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta.",
|
||||||
|
"siteWgDescriptionSaas": "Usa qualsiasi client WireGuard per stabilire un tunnel. Impostazione NAT manuale richiesta. FUNZIONA SOLO SU NODI AUTO-OSPITATI",
|
||||||
"siteLocalDescription": "Solo risorse locali. Nessun tunneling.",
|
"siteLocalDescription": "Solo risorse locali. Nessun tunneling.",
|
||||||
|
"siteLocalDescriptionSaas": "Solo risorse locali. Nessun tunneling. FUNZIONA SOLO SU NODI AUTO-OSPITATI",
|
||||||
"siteSeeAll": "Vedi Tutti I Siti",
|
"siteSeeAll": "Vedi Tutti I Siti",
|
||||||
"siteTunnelDescription": "Determina come vuoi connetterti al tuo sito",
|
"siteTunnelDescription": "Determina come vuoi connetterti al tuo sito",
|
||||||
"siteNewtCredentials": "Credenziali Newt",
|
"siteNewtCredentials": "Credenziali Newt",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Seleziona sito",
|
"siteSelect": "Seleziona sito",
|
||||||
"siteSearch": "Cerca sito",
|
"siteSearch": "Cerca sito",
|
||||||
"siteNotFound": "Nessun sito trovato.",
|
"siteNotFound": "Nessun sito trovato.",
|
||||||
"siteSelectionDescription": "Questo sito fornirà connettività alla risorsa.",
|
"selectCountry": "Seleziona paese",
|
||||||
|
"searchCountries": "Cerca paesi...",
|
||||||
|
"noCountryFound": "Nessun paese trovato.",
|
||||||
|
"siteSelectionDescription": "Questo sito fornirà connettività all'obiettivo.",
|
||||||
"resourceType": "Tipo Di Risorsa",
|
"resourceType": "Tipo Di Risorsa",
|
||||||
"resourceTypeDescription": "Determina come vuoi accedere alla tua risorsa",
|
"resourceTypeDescription": "Determina come vuoi accedere alla tua risorsa",
|
||||||
"resourceHTTPSSettings": "Impostazioni HTTPS",
|
"resourceHTTPSSettings": "Impostazioni HTTPS",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "Generale",
|
"general": "Generale",
|
||||||
"generalSettings": "Impostazioni Generali",
|
"generalSettings": "Impostazioni Generali",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
|
"internal": "Interno",
|
||||||
"rules": "Regole",
|
"rules": "Regole",
|
||||||
"resourceSettingDescription": "Configura le impostazioni sulla tua risorsa",
|
"resourceSettingDescription": "Configura le impostazioni sulla tua risorsa",
|
||||||
"resourceSetting": "Impostazioni {resourceName}",
|
"resourceSetting": "Impostazioni {resourceName}",
|
||||||
"alwaysAllow": "Consenti Sempre",
|
"alwaysAllow": "Consenti Sempre",
|
||||||
"alwaysDeny": "Nega Sempre",
|
"alwaysDeny": "Nega Sempre",
|
||||||
|
"passToAuth": "Passa all'autenticazione",
|
||||||
"orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione",
|
"orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione",
|
||||||
"orgGeneralSettings": "Impostazioni Organizzazione",
|
"orgGeneralSettings": "Impostazioni Organizzazione",
|
||||||
"orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione",
|
"orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Si è verificato un errore durante l'aggiunta dell'utente al ruolo.",
|
"accessRoleErrorAddDescription": "Si è verificato un errore durante l'aggiunta dell'utente al ruolo.",
|
||||||
"userSaved": "Utente salvato",
|
"userSaved": "Utente salvato",
|
||||||
"userSavedDescription": "L'utente è stato aggiornato.",
|
"userSavedDescription": "L'utente è stato aggiornato.",
|
||||||
|
"autoProvisioned": "Auto Provisioned",
|
||||||
|
"autoProvisionedDescription": "Permetti a questo utente di essere gestito automaticamente dal provider di identità",
|
||||||
"accessControlsDescription": "Gestisci cosa questo utente può accedere e fare nell'organizzazione",
|
"accessControlsDescription": "Gestisci cosa questo utente può accedere e fare nell'organizzazione",
|
||||||
"accessControlsSubmit": "Salva Controlli di Accesso",
|
"accessControlsSubmit": "Salva Controlli di Accesso",
|
||||||
"roles": "Ruoli",
|
"roles": "Ruoli",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "Il Nome Server TLS da usare per SNI. Lascia vuoto per usare quello predefinito.",
|
"targetTlsSniDescription": "Il Nome Server TLS da usare per SNI. Lascia vuoto per usare quello predefinito.",
|
||||||
"targetTlsSubmit": "Salva Impostazioni",
|
"targetTlsSubmit": "Salva Impostazioni",
|
||||||
"targets": "Configurazione Target",
|
"targets": "Configurazione Target",
|
||||||
"targetsDescription": "Configura i target per instradare il traffico ai tuoi servizi",
|
"targetsDescription": "Configura i target per instradare il traffico ai tuoi servizi backend",
|
||||||
"targetStickySessions": "Abilita Sessioni Persistenti",
|
"targetStickySessions": "Abilita Sessioni Persistenti",
|
||||||
"targetStickySessionsDescription": "Mantieni le connessioni sullo stesso target backend per l'intera sessione.",
|
"targetStickySessionsDescription": "Mantieni le connessioni sullo stesso target backend per l'intera sessione.",
|
||||||
"methodSelect": "Seleziona metodo",
|
"methodSelect": "Seleziona metodo",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Formato indirizzo IP non valido",
|
"ipAddressErrorInvalidFormat": "Formato indirizzo IP non valido",
|
||||||
"ipAddressErrorInvalidOctet": "Ottetto indirizzo IP non valido",
|
"ipAddressErrorInvalidOctet": "Ottetto indirizzo IP non valido",
|
||||||
"path": "Percorso",
|
"path": "Percorso",
|
||||||
|
"matchPath": "Corrispondenza Tracciato",
|
||||||
"ipAddressRange": "Intervallo IP",
|
"ipAddressRange": "Intervallo IP",
|
||||||
"rulesErrorFetch": "Impossibile recuperare le regole",
|
"rulesErrorFetch": "Impossibile recuperare le regole",
|
||||||
"rulesErrorFetchDescription": "Si è verificato un errore durante il recupero delle regole",
|
"rulesErrorFetchDescription": "Si è verificato un errore durante il recupero delle regole",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Azioni",
|
"rulesActions": "Azioni",
|
||||||
"rulesActionAlwaysAllow": "Consenti Sempre: Ignora tutti i metodi di autenticazione",
|
"rulesActionAlwaysAllow": "Consenti Sempre: Ignora tutti i metodi di autenticazione",
|
||||||
"rulesActionAlwaysDeny": "Nega Sempre: Blocca tutte le richieste; nessuna autenticazione può essere tentata",
|
"rulesActionAlwaysDeny": "Nega Sempre: Blocca tutte le richieste; nessuna autenticazione può essere tentata",
|
||||||
|
"rulesActionPassToAuth": "Passa all'autenticazione: Consenti di tentare i metodi di autenticazione",
|
||||||
"rulesMatchCriteria": "Criteri di Corrispondenza",
|
"rulesMatchCriteria": "Criteri di Corrispondenza",
|
||||||
"rulesMatchCriteriaIpAddress": "Corrisponde a un indirizzo IP specifico",
|
"rulesMatchCriteriaIpAddress": "Corrisponde a un indirizzo IP specifico",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Corrisponde a un intervallo di indirizzi IP in notazione CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Corrisponde a un intervallo di indirizzi IP in notazione CIDR",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "Il PIN deve essere esattamente di 6 cifre",
|
"pincodeRequirementsLength": "Il PIN deve essere esattamente di 6 cifre",
|
||||||
"pincodeRequirementsChars": "Il PIN deve contenere solo numeri",
|
"pincodeRequirementsChars": "Il PIN deve contenere solo numeri",
|
||||||
"passwordRequirementsLength": "La password deve essere lunga almeno 1 carattere",
|
"passwordRequirementsLength": "La password deve essere lunga almeno 1 carattere",
|
||||||
|
"passwordRequirementsTitle": "Requisiti della password:",
|
||||||
|
"passwordRequirementLength": "Almeno 8 caratteri",
|
||||||
|
"passwordRequirementUppercase": "Almeno una lettera maiuscola",
|
||||||
|
"passwordRequirementLowercase": "Almeno una lettera minuscola",
|
||||||
|
"passwordRequirementNumber": "Almeno un numero",
|
||||||
|
"passwordRequirementSpecial": "Almeno un carattere speciale",
|
||||||
|
"passwordRequirementsMet": "✓ La password soddisfa tutti i requisiti",
|
||||||
|
"passwordStrength": "Forza della password",
|
||||||
|
"passwordStrengthWeak": "Debole",
|
||||||
|
"passwordStrengthMedium": "Media",
|
||||||
|
"passwordStrengthStrong": "Forte",
|
||||||
|
"passwordRequirements": "Requisiti:",
|
||||||
|
"passwordRequirementLengthText": "8+ caratteri",
|
||||||
|
"passwordRequirementUppercaseText": "Lettera maiuscola (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Lettera minuscola (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Numero (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Carattere speciale (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Le password non coincidono",
|
||||||
"otpEmailRequirementsLength": "L'OTP deve essere lungo almeno 1 carattere",
|
"otpEmailRequirementsLength": "L'OTP deve essere lungo almeno 1 carattere",
|
||||||
"otpEmailSent": "OTP Inviato",
|
"otpEmailSent": "OTP Inviato",
|
||||||
"otpEmailSentDescription": "Un OTP è stato inviato alla tua email",
|
"otpEmailSentDescription": "Un OTP è stato inviato alla tua email",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "Errore durante il logout",
|
"logoutError": "Errore durante il logout",
|
||||||
"signingAs": "Accesso come",
|
"signingAs": "Accesso come",
|
||||||
"serverAdmin": "Amministratore Server",
|
"serverAdmin": "Amministratore Server",
|
||||||
|
"managedSelfhosted": "Gestito Auto-Ospitato",
|
||||||
"otpEnable": "Abilita Autenticazione a Due Fattori",
|
"otpEnable": "Abilita Autenticazione a Due Fattori",
|
||||||
"otpDisable": "Disabilita Autenticazione a Due Fattori",
|
"otpDisable": "Disabilita Autenticazione a Due Fattori",
|
||||||
"logout": "Disconnetti",
|
"logout": "Disconnetti",
|
||||||
"licenseTierProfessionalRequired": "Edizione Professional Richiesta",
|
"licenseTierProfessionalRequired": "Edizione Professional Richiesta",
|
||||||
"licenseTierProfessionalRequiredDescription": "Questa funzionalità è disponibile solo nell'Edizione Professional.",
|
"licenseTierProfessionalRequiredDescription": "Questa funzionalità è disponibile solo nell'Edizione Professional.",
|
||||||
"actionGetOrg": "Ottieni Organizzazione",
|
"actionGetOrg": "Ottieni Organizzazione",
|
||||||
|
"updateOrgUser": "Aggiorna Utente Org",
|
||||||
|
"createOrgUser": "Crea Utente Org",
|
||||||
"actionUpdateOrg": "Aggiorna Organizzazione",
|
"actionUpdateOrg": "Aggiorna Organizzazione",
|
||||||
"actionUpdateUser": "Aggiorna Utente",
|
"actionUpdateUser": "Aggiorna Utente",
|
||||||
"actionGetUser": "Ottieni Utente",
|
"actionGetUser": "Ottieni Utente",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "Elimina Sito",
|
"actionDeleteSite": "Elimina Sito",
|
||||||
"actionGetSite": "Ottieni Sito",
|
"actionGetSite": "Ottieni Sito",
|
||||||
"actionListSites": "Elenca Siti",
|
"actionListSites": "Elenca Siti",
|
||||||
|
"actionApplyBlueprint": "Applica Progetto",
|
||||||
|
"setupToken": "Configura Token",
|
||||||
|
"setupTokenDescription": "Inserisci il token di configurazione dalla console del server.",
|
||||||
|
"setupTokenRequired": "Il token di configurazione è richiesto",
|
||||||
"actionUpdateSite": "Aggiorna Sito",
|
"actionUpdateSite": "Aggiorna Sito",
|
||||||
"actionListSiteRoles": "Elenca Ruoli Sito Consentiti",
|
"actionListSiteRoles": "Elenca Ruoli Sito Consentiti",
|
||||||
"actionCreateResource": "Crea Risorsa",
|
"actionCreateResource": "Crea Risorsa",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Elimina Politica Org IDP",
|
"actionDeleteIdpOrg": "Elimina Politica Org IDP",
|
||||||
"actionListIdpOrgs": "Elenca Org IDP",
|
"actionListIdpOrgs": "Elenca Org IDP",
|
||||||
"actionUpdateIdpOrg": "Aggiorna Org IDP",
|
"actionUpdateIdpOrg": "Aggiorna Org IDP",
|
||||||
|
"actionCreateClient": "Crea Client",
|
||||||
|
"actionDeleteClient": "Elimina Client",
|
||||||
|
"actionUpdateClient": "Aggiorna Client",
|
||||||
|
"actionListClients": "Elenco Clienti",
|
||||||
|
"actionGetClient": "Ottieni Client",
|
||||||
|
"actionCreateSiteResource": "Crea Risorsa del Sito",
|
||||||
|
"actionDeleteSiteResource": "Elimina Risorsa del Sito",
|
||||||
|
"actionGetSiteResource": "Ottieni Risorsa del Sito",
|
||||||
|
"actionListSiteResources": "Elenca Risorse del Sito",
|
||||||
|
"actionUpdateSiteResource": "Aggiorna Risorsa del Sito",
|
||||||
|
"actionListInvitations": "Elenco Inviti",
|
||||||
"noneSelected": "Nessuna selezione",
|
"noneSelected": "Nessuna selezione",
|
||||||
"orgNotFound2": "Nessuna organizzazione trovata.",
|
"orgNotFound2": "Nessuna organizzazione trovata.",
|
||||||
"searchProgress": "Ricerca...",
|
"searchProgress": "Ricerca...",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "Licenza",
|
"sidebarLicense": "Licenza",
|
||||||
"sidebarClients": "Clienti (Beta)",
|
"sidebarClients": "Clienti (Beta)",
|
||||||
"sidebarDomains": "Domini",
|
"sidebarDomains": "Domini",
|
||||||
"enableDockerSocket": "Abilita Docker Socket",
|
"enableDockerSocket": "Abilita Progetto Docker",
|
||||||
"enableDockerSocketDescription": "Abilita il rilevamento Docker Socket per popolare le informazioni del contenitore. Il percorso del socket deve essere fornito a Newt.",
|
"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ù",
|
"enableDockerSocketLink": "Scopri di più",
|
||||||
"viewDockerContainers": "Visualizza Contenitori Docker",
|
"viewDockerContainers": "Visualizza Contenitori Docker",
|
||||||
"containersIn": "Contenitori in {siteName}",
|
"containersIn": "Contenitori in {siteName}",
|
||||||
@@ -1174,7 +1221,7 @@
|
|||||||
"orgBillingDescription": "Gestisci le tue informazioni di fatturazione e abbonamenti",
|
"orgBillingDescription": "Gestisci le tue informazioni di fatturazione e abbonamenti",
|
||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
"pangolinHosted": "Pangolin Hosted",
|
"pangolinHosted": "Pangolin Hosted",
|
||||||
"fossorial": "Fossorial",
|
"fossorial": "Fossoriale",
|
||||||
"completeAccountSetup": "Completa la Configurazione dell'Account",
|
"completeAccountSetup": "Completa la Configurazione dell'Account",
|
||||||
"completeAccountSetupDescription": "Imposta la tua password per iniziare",
|
"completeAccountSetupDescription": "Imposta la tua password per iniziare",
|
||||||
"accountSetupSent": "Invieremo un codice di configurazione dell'account a questo indirizzo email.",
|
"accountSetupSent": "Invieremo un codice di configurazione dell'account a questo indirizzo email.",
|
||||||
@@ -1196,7 +1243,7 @@
|
|||||||
"newtUpdateAvailable": "Aggiornamento Disponibile",
|
"newtUpdateAvailable": "Aggiornamento Disponibile",
|
||||||
"newtUpdateAvailableInfo": "È disponibile una nuova versione di Newt. Si prega di aggiornare all'ultima versione per la migliore esperienza.",
|
"newtUpdateAvailableInfo": "È disponibile una nuova versione di Newt. Si prega di aggiornare all'ultima versione per la migliore esperienza.",
|
||||||
"domainPickerEnterDomain": "Dominio",
|
"domainPickerEnterDomain": "Dominio",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com, o semplicemente myapp",
|
"domainPickerPlaceholder": "myapp.example.com",
|
||||||
"domainPickerDescription": "Inserisci il dominio completo della risorsa per vedere le opzioni disponibili.",
|
"domainPickerDescription": "Inserisci il dominio completo della risorsa per vedere le opzioni disponibili.",
|
||||||
"domainPickerDescriptionSaas": "Inserisci un dominio completo, un sottodominio o semplicemente un nome per vedere le opzioni disponibili",
|
"domainPickerDescriptionSaas": "Inserisci un dominio completo, un sottodominio o semplicemente un nome per vedere le opzioni disponibili",
|
||||||
"domainPickerTabAll": "Tutti",
|
"domainPickerTabAll": "Tutti",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "Sottodominio: {subdomain}",
|
"domainPickerSubdomain": "Sottodominio: {subdomain}",
|
||||||
"domainPickerNamespace": "Namespace: {namespace}",
|
"domainPickerNamespace": "Namespace: {namespace}",
|
||||||
"domainPickerShowMore": "Mostra Altro",
|
"domainPickerShowMore": "Mostra Altro",
|
||||||
|
"regionSelectorTitle": "Seleziona regione",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Caricamento abbonamento...",
|
||||||
|
"billingFreeTier": "Piano Gratuito",
|
||||||
|
"billingWarningOverLimit": "Avviso: Hai superato uno o più limiti di utilizzo. I tuoi siti non si connetteranno finché non modifichi il tuo abbonamento o non adegui il tuo utilizzo.",
|
||||||
|
"billingUsageLimitsOverview": "Panoramica dei Limiti di Utilizzo",
|
||||||
|
"billingMonitorUsage": "Monitora il tuo utilizzo rispetto ai limiti configurati. Se hai bisogno di aumentare i limiti, contattaci all'indirizzo support@fossorial.io.",
|
||||||
|
"billingDataUsage": "Utilizzo dei Dati",
|
||||||
|
"billingOnlineTime": "Tempo Online del Sito",
|
||||||
|
"billingUsers": "Utenti Attivi",
|
||||||
|
"billingDomains": "Domini Attivi",
|
||||||
|
"billingRemoteExitNodes": "Nodi Self-hosted Attivi",
|
||||||
|
"billingNoLimitConfigured": "Nessun limite configurato",
|
||||||
|
"billingEstimatedPeriod": "Periodo di Fatturazione Stimato",
|
||||||
|
"billingIncludedUsage": "Utilizzo Incluso",
|
||||||
|
"billingIncludedUsageDescription": "Utilizzo incluso nel tuo piano di abbonamento corrente",
|
||||||
|
"billingFreeTierIncludedUsage": "Elenchi di utilizzi inclusi nel piano gratuito",
|
||||||
|
"billingIncluded": "incluso",
|
||||||
|
"billingEstimatedTotal": "Totale Stimato:",
|
||||||
|
"billingNotes": "Note",
|
||||||
|
"billingEstimateNote": "Questa è una stima basata sul tuo utilizzo attuale.",
|
||||||
|
"billingActualChargesMayVary": "I costi effettivi possono variare.",
|
||||||
|
"billingBilledAtEnd": "Sarai fatturato alla fine del periodo di fatturazione.",
|
||||||
|
"billingModifySubscription": "Modifica Abbonamento",
|
||||||
|
"billingStartSubscription": "Inizia Abbonamento",
|
||||||
|
"billingRecurringCharge": "Addebito Ricorrente",
|
||||||
|
"billingManageSubscriptionSettings": "Gestisci impostazioni e preferenze dell'abbonamento",
|
||||||
|
"billingNoActiveSubscription": "Non hai un abbonamento attivo. Avvia il tuo abbonamento per aumentare i limiti di utilizzo.",
|
||||||
|
"billingFailedToLoadSubscription": "Caricamento abbonamento fallito",
|
||||||
|
"billingFailedToLoadUsage": "Caricamento utilizzo fallito",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Errore durante l'ottenimento dell'URL di pagamento",
|
||||||
|
"billingPleaseTryAgainLater": "Per favore, riprova più tardi.",
|
||||||
|
"billingCheckoutError": "Errore di Pagamento",
|
||||||
|
"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 nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account utente attivi nella tua organizzazione.",
|
||||||
|
"billingDomainInfo": "Sei addebitato per ogni dominio nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di account dominio attivi nella tua organizzazione.",
|
||||||
|
"billingRemoteExitNodesInfo": "Sei addebitato per ogni nodo gestito nella tua organizzazione. La fatturazione viene calcolata giornalmente in base al numero di nodi gestiti attivi nella tua organizzazione.",
|
||||||
"domainNotFound": "Domini Non Trovati",
|
"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.",
|
"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",
|
"failed": "Fallito",
|
||||||
@@ -1244,7 +1333,7 @@
|
|||||||
"twoFactorRequired": "È richiesta l'autenticazione a due fattori per registrare una chiave di sicurezza.",
|
"twoFactorRequired": "È richiesta l'autenticazione a due fattori per registrare una chiave di sicurezza.",
|
||||||
"twoFactor": "Autenticazione a Due Fattori",
|
"twoFactor": "Autenticazione a Due Fattori",
|
||||||
"adminEnabled2FaOnYourAccount": "Il tuo amministratore ha abilitato l'autenticazione a due fattori per {email}. Completa il processo di configurazione per continuare.",
|
"adminEnabled2FaOnYourAccount": "Il tuo amministratore ha abilitato l'autenticazione a due fattori per {email}. Completa il processo di configurazione per continuare.",
|
||||||
"continueToApplication": "Continua all'Applicazione",
|
"continueToApplication": "Continua con l'applicazione",
|
||||||
"securityKeyAdd": "Aggiungi Chiave di Sicurezza",
|
"securityKeyAdd": "Aggiungi Chiave di Sicurezza",
|
||||||
"securityKeyRegisterTitle": "Registra Nuova Chiave di Sicurezza",
|
"securityKeyRegisterTitle": "Registra Nuova Chiave di Sicurezza",
|
||||||
"securityKeyRegisterDescription": "Collega la tua chiave di sicurezza e inserisci un nome per identificarla",
|
"securityKeyRegisterDescription": "Collega la tua chiave di sicurezza e inserisci un nome per identificarla",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "Le modifiche DNS possono richiedere del tempo per propagarsi in Internet. Questo può richiedere da pochi minuti a 48 ore, a seconda del tuo provider DNS e delle impostazioni TTL.",
|
"createDomainDnsPropagationDescription": "Le modifiche DNS possono richiedere del tempo per propagarsi in Internet. Questo può richiedere da pochi minuti a 48 ore, a seconda del tuo provider DNS e delle impostazioni TTL.",
|
||||||
"resourcePortRequired": "Numero di porta richiesto per risorse non-HTTP",
|
"resourcePortRequired": "Numero di porta richiesto per risorse non-HTTP",
|
||||||
"resourcePortNotAllowed": "Il numero di porta non deve essere impostato per risorse HTTP",
|
"resourcePortNotAllowed": "Il numero di porta non deve essere impostato per risorse HTTP",
|
||||||
|
"billingPricingCalculatorLink": "Calcolatore di Prezzi",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Accetto i",
|
"IAgreeToThe": "Accetto i",
|
||||||
"termsOfService": "termini di servizio",
|
"termsOfService": "termini di servizio",
|
||||||
@@ -1281,7 +1371,7 @@
|
|||||||
"privacyPolicy": "informativa sulla privacy"
|
"privacyPolicy": "informativa sulla privacy"
|
||||||
},
|
},
|
||||||
"siteRequired": "Il sito è richiesto.",
|
"siteRequired": "Il sito è richiesto.",
|
||||||
"olmTunnel": "Olm Tunnel",
|
"olmTunnel": "Tunnel Olm",
|
||||||
"olmTunnelDescription": "Usa Olm per la connettività client",
|
"olmTunnelDescription": "Usa Olm per la connettività client",
|
||||||
"errorCreatingClient": "Errore nella creazione del client",
|
"errorCreatingClient": "Errore nella creazione del client",
|
||||||
"clientDefaultsNotFound": "Impostazioni predefinite del client non trovate",
|
"clientDefaultsNotFound": "Impostazioni predefinite del client non trovate",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "Si è verificato un errore durante il recupero dell'ultima versione di Olm.",
|
"olmErrorFetchLatest": "Si è verificato un errore durante il recupero dell'ultima versione di Olm.",
|
||||||
"remoteSubnets": "Sottoreti Remote",
|
"remoteSubnets": "Sottoreti Remote",
|
||||||
"enterCidrRange": "Inserisci l'intervallo CIDR",
|
"enterCidrRange": "Inserisci l'intervallo CIDR",
|
||||||
"remoteSubnetsDescription": "Aggiungi intervalli CIDR che possono accedere a questo sito da remoto. Usa il formato come 10.0.0.0/24 o 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Aggiungi intervalli CIDR che possono essere accessibili da questo sito in remoto utilizzando i client. Usa il formato come 10.0.0.0/24. Questo si applica SOLO alla connettività del client VPN.",
|
||||||
"resourceEnableProxy": "Abilita Proxy Pubblico",
|
"resourceEnableProxy": "Abilita Proxy Pubblico",
|
||||||
"resourceEnableProxyDescription": "Abilita il proxy pubblico a questa risorsa. Consente l'accesso alla risorsa dall'esterno della rete tramite il cloud su una porta aperta. Richiede la configurazione di Traefik.",
|
"resourceEnableProxyDescription": "Abilita il proxy pubblico a questa risorsa. Consente l'accesso alla risorsa dall'esterno della rete tramite il cloud su una porta aperta. Richiede la configurazione di Traefik.",
|
||||||
"externalProxyEnabled": "Proxy Esterno Abilitato"
|
"externalProxyEnabled": "Proxy Esterno Abilitato",
|
||||||
|
"addNewTarget": "Aggiungi Nuovo Target",
|
||||||
|
"targetsList": "Elenco dei Target",
|
||||||
|
"targetErrorDuplicateTargetFound": "Target duplicato trovato",
|
||||||
|
"healthCheckHealthy": "Sano",
|
||||||
|
"healthCheckUnhealthy": "Non Sano",
|
||||||
|
"healthCheckUnknown": "Sconosciuto",
|
||||||
|
"healthCheck": "Controllo Salute",
|
||||||
|
"configureHealthCheck": "Configura Controllo Salute",
|
||||||
|
"configureHealthCheckDescription": "Imposta il monitoraggio della salute per {target}",
|
||||||
|
"enableHealthChecks": "Abilita i Controlli di Salute",
|
||||||
|
"enableHealthChecksDescription": "Monitorare lo stato di salute di questo obiettivo. Se necessario, è possibile monitorare un endpoint diverso da quello del bersaglio.",
|
||||||
|
"healthScheme": "Metodo",
|
||||||
|
"healthSelectScheme": "Seleziona Metodo",
|
||||||
|
"healthCheckPath": "Percorso",
|
||||||
|
"healthHostname": "IP / Nome host",
|
||||||
|
"healthPort": "Porta",
|
||||||
|
"healthCheckPathDescription": "Percorso per verificare lo stato di salute.",
|
||||||
|
"healthyIntervalSeconds": "Intervallo Sano",
|
||||||
|
"unhealthyIntervalSeconds": "Intervallo Non Sano",
|
||||||
|
"IntervalSeconds": "Intervallo Sano",
|
||||||
|
"timeoutSeconds": "Timeout",
|
||||||
|
"timeIsInSeconds": "Il tempo è in secondi",
|
||||||
|
"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.",
|
||||||
|
"customHeaders": "Intestazioni Personalizzate",
|
||||||
|
"customHeadersDescription": "Intestazioni nuova riga separate: Intestazione-Nome: valore",
|
||||||
|
"headersValidationError": "Le intestazioni devono essere nel formato: Intestazione-Nome: valore.",
|
||||||
|
"saveHealthCheck": "Salva Controllo Salute",
|
||||||
|
"healthCheckSaved": "Controllo Salute Salvato",
|
||||||
|
"healthCheckSavedDescription": "La configurazione del controllo salute è stata salvata con successo",
|
||||||
|
"healthCheckError": "Errore Controllo Salute",
|
||||||
|
"healthCheckErrorDescription": "Si è verificato un errore durante il salvataggio della configurazione del controllo salute.",
|
||||||
|
"healthCheckPathRequired": "Il percorso del controllo salute è richiesto",
|
||||||
|
"healthCheckMethodRequired": "Metodo HTTP richiesto",
|
||||||
|
"healthCheckIntervalMin": "L'intervallo del controllo deve essere almeno di 5 secondi",
|
||||||
|
"healthCheckTimeoutMin": "Il timeout deve essere di almeno 1 secondo",
|
||||||
|
"healthCheckRetryMin": "I tentativi di riprova devono essere almeno 1",
|
||||||
|
"httpMethod": "Metodo HTTP",
|
||||||
|
"selectHttpMethod": "Seleziona metodo HTTP",
|
||||||
|
"domainPickerSubdomainLabel": "Sottodominio",
|
||||||
|
"domainPickerBaseDomainLabel": "Dominio Base",
|
||||||
|
"domainPickerSearchDomains": "Cerca domini...",
|
||||||
|
"domainPickerNoDomainsFound": "Nessun dominio trovato",
|
||||||
|
"domainPickerLoadingDomains": "Caricamento domini...",
|
||||||
|
"domainPickerSelectBaseDomain": "Seleziona dominio base...",
|
||||||
|
"domainPickerNotAvailableForCname": "Non disponibile per i domini CNAME",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Inserisci un sottodominio o lascia vuoto per utilizzare il dominio base.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Inserisci un sottodominio per cercare e selezionare dai domini gratuiti disponibili.",
|
||||||
|
"domainPickerFreeDomains": "Domini Gratuiti",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Cerca domini disponibili",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Nota: I domini forniti gratuitamente non sono disponibili per le istanze self-hosted al momento.",
|
||||||
|
"resourceDomain": "Dominio",
|
||||||
|
"resourceEditDomain": "Modifica Dominio",
|
||||||
|
"siteName": "Nome del Sito",
|
||||||
|
"proxyPort": "Porta",
|
||||||
|
"resourcesTableProxyResources": "Risorse Proxy",
|
||||||
|
"resourcesTableClientResources": "Risorse Client",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Nessuna risorsa proxy trovata.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Nessuna risorsa interna trovata.",
|
||||||
|
"resourcesTableDestination": "Destinazione",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Queste risorse sono per uso con",
|
||||||
|
"resourcesTableClients": "Client",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "e sono accessibili solo internamente quando connessi con un client.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Modifica Risorsa Client",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Aggiorna le proprietà della risorsa e la configurazione del target per {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Proprietà della Risorsa",
|
||||||
|
"editInternalResourceDialogName": "Nome",
|
||||||
|
"editInternalResourceDialogProtocol": "Protocollo",
|
||||||
|
"editInternalResourceDialogSitePort": "Porta del Sito",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Configurazione Target",
|
||||||
|
"editInternalResourceDialogCancel": "Annulla",
|
||||||
|
"editInternalResourceDialogSaveResource": "Salva Risorsa",
|
||||||
|
"editInternalResourceDialogSuccess": "Successo",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Risorsa interna aggiornata con successo",
|
||||||
|
"editInternalResourceDialogError": "Errore",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Impossibile aggiornare la risorsa interna",
|
||||||
|
"editInternalResourceDialogNameRequired": "Il nome è obbligatorio",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Il nome deve essere inferiore a 255 caratteri",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "La porta proxy deve essere almeno 1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "La porta proxy deve essere inferiore a 65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Formato dell'indirizzo IP non valido",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "La porta di destinazione deve essere almeno 1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "La porta di destinazione deve essere inferiore a 65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Nessun Sito Disponibile",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Devi avere almeno un sito Newt con una subnet configurata per creare risorse interne.",
|
||||||
|
"createInternalResourceDialogClose": "Chiudi",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Crea Risorsa Client",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Crea una nuova risorsa che sarà accessibile ai client connessi al sito selezionato.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Proprietà della Risorsa",
|
||||||
|
"createInternalResourceDialogName": "Nome",
|
||||||
|
"createInternalResourceDialogSite": "Sito",
|
||||||
|
"createInternalResourceDialogSelectSite": "Seleziona sito...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Cerca siti...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Nessun sito trovato.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protocollo",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Porta del Sito",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Usa questa porta per accedere alla risorsa nel sito quando sei connesso con un client.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Configurazione Target",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "L'indirizzo IP o hostname della risorsa nella rete del sito.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "La porta sull'IP di destinazione dove la risorsa è accessibile.",
|
||||||
|
"createInternalResourceDialogCancel": "Annulla",
|
||||||
|
"createInternalResourceDialogCreateResource": "Crea Risorsa",
|
||||||
|
"createInternalResourceDialogSuccess": "Successo",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Risorsa interna creata con successo",
|
||||||
|
"createInternalResourceDialogError": "Errore",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Impossibile creare la risorsa interna",
|
||||||
|
"createInternalResourceDialogNameRequired": "Il nome è obbligatorio",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Il nome non deve superare i 255 caratteri",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Si prega di selezionare un sito",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "La porta proxy deve essere almeno 1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "La porta proxy deve essere inferiore a 65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Formato dell'indirizzo IP non valido",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "La porta di destinazione deve essere almeno 1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "La porta di destinazione deve essere inferiore a 65536",
|
||||||
|
"siteConfiguration": "Configurazione",
|
||||||
|
"siteAcceptClientConnections": "Accetta Connessioni Client",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Permetti ad altri dispositivi di connettersi attraverso questa istanza Newt come gateway utilizzando i client.",
|
||||||
|
"siteAddress": "Indirizzo del Sito",
|
||||||
|
"siteAddressDescription": "Specifica l'indirizzo IP dell'host a cui i client si collegano. Questo è l'indirizzo interno del sito nella rete Pangolin per indirizzare i client. Deve rientrare nella subnet dell'Organizzazione.",
|
||||||
|
"autoLoginExternalIdp": "Accesso Automatico con IDP Esterno",
|
||||||
|
"autoLoginExternalIdpDescription": "Reindirizzare immediatamente l'utente all'IDP esterno per l'autenticazione.",
|
||||||
|
"selectIdp": "Seleziona IDP",
|
||||||
|
"selectIdpPlaceholder": "Scegli un IDP...",
|
||||||
|
"selectIdpRequired": "Si prega di selezionare un IDP quando l'accesso automatico è abilitato.",
|
||||||
|
"autoLoginTitle": "Reindirizzamento",
|
||||||
|
"autoLoginDescription": "Reindirizzandoti al provider di identità esterno per l'autenticazione.",
|
||||||
|
"autoLoginProcessing": "Preparazione dell'autenticazione...",
|
||||||
|
"autoLoginRedirecting": "Reindirizzamento al login...",
|
||||||
|
"autoLoginError": "Errore di Accesso Automatico",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "Nessun URL di reindirizzamento ricevuto dal provider di identità.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Impossibile generare l'URL di autenticazione.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Gestisci Self-Hosted",
|
||||||
|
"remoteExitNodeDescription": "Gestisci i nodi per estendere la connettività di rete",
|
||||||
|
"remoteExitNodes": "Nodi",
|
||||||
|
"searchRemoteExitNodes": "Cerca nodi...",
|
||||||
|
"remoteExitNodeAdd": "Aggiungi Nodo",
|
||||||
|
"remoteExitNodeErrorDelete": "Errore nell'eliminare il nodo",
|
||||||
|
"remoteExitNodeQuestionRemove": "Sei sicuro di voler rimuovere il nodo {selectedNode} dall'organizzazione?",
|
||||||
|
"remoteExitNodeMessageRemove": "Una volta rimosso, il nodo non sarà più accessibile.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Per confermare, digita il nome del nodo qui sotto.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Conferma Eliminazione Nodo",
|
||||||
|
"remoteExitNodeDelete": "Elimina Nodo",
|
||||||
|
"sidebarRemoteExitNodes": "Nodi",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Crea Nodo",
|
||||||
|
"description": "Crea un nuovo nodo per estendere la connettività di rete",
|
||||||
|
"viewAllButton": "Visualizza Tutti I Nodi",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Strategia di Creazione",
|
||||||
|
"description": "Scegli questa opzione per configurare manualmente il nodo o generare nuove credenziali.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adotta Nodo",
|
||||||
|
"description": "Scegli questo se hai già le credenziali per il nodo."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Genera Chiavi",
|
||||||
|
"description": "Scegli questa opzione se vuoi generare nuove chiavi per il nodo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adotta Nodo Esistente",
|
||||||
|
"description": "Inserisci le credenziali del nodo esistente che vuoi adottare",
|
||||||
|
"nodeIdLabel": "ID Nodo",
|
||||||
|
"nodeIdDescription": "L'ID del nodo esistente che si desidera adottare",
|
||||||
|
"secretLabel": "Segreto",
|
||||||
|
"secretDescription": "La chiave segreta del nodo esistente",
|
||||||
|
"submitButton": "Adotta Nodo"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Credenziali Generate",
|
||||||
|
"description": "Usa queste credenziali generate per configurare il nodo",
|
||||||
|
"nodeIdTitle": "ID Nodo",
|
||||||
|
"secretTitle": "Segreto",
|
||||||
|
"saveCredentialsTitle": "Aggiungi Credenziali alla Configurazione",
|
||||||
|
"saveCredentialsDescription": "Aggiungi queste credenziali al tuo file di configurazione del nodo self-hosted Pangolin per completare la connessione.",
|
||||||
|
"submitButton": "Crea Nodo"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "L'ID del nodo e il segreto sono necessari quando si adotta un nodo esistente"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Caricamento impostazioni predefinite fallito",
|
||||||
|
"defaultsNotLoaded": "Impostazioni predefinite non caricate",
|
||||||
|
"createFailed": "Impossibile creare il nodo"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Nodo creato con successo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Selezione Nodo",
|
||||||
|
"remoteExitNodeSelectionDescription": "Seleziona un nodo per instradare il traffico per questo sito locale",
|
||||||
|
"remoteExitNodeRequired": "Un nodo deve essere selezionato per i siti locali",
|
||||||
|
"noRemoteExitNodesAvailable": "Nessun Nodo Disponibile",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Non ci sono nodi disponibili per questa organizzazione. Crea un nodo prima per usare i siti locali.",
|
||||||
|
"exitNode": "Nodo di Uscita",
|
||||||
|
"country": "Paese",
|
||||||
|
"rulesMatchCountry": "Attualmente basato sull'IP di origine",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Gestito Auto-Ospitato",
|
||||||
|
"description": "Server Pangolin self-hosted più affidabile e a bassa manutenzione con campanelli e fischietti extra",
|
||||||
|
"introTitle": "Managed Self-Hosted Pangolin",
|
||||||
|
"introDescription": "è un'opzione di distribuzione progettata per le persone che vogliono la semplicità e l'affidabilità extra mantenendo i loro dati privati e self-hosted.",
|
||||||
|
"introDetail": "Con questa opzione, esegui ancora il tuo nodo Pangolin — i tunnel, la terminazione SSL e il traffico rimangono tutti sul tuo server. La differenza è che la gestione e il monitoraggio sono gestiti attraverso il nostro cruscotto cloud, che sblocca una serie di vantaggi:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Operazioni più semplici",
|
||||||
|
"description": "Non è necessario eseguire il proprio server di posta o impostare un avviso complesso. Otterrai controlli di salute e avvisi di inattività fuori dalla casella."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Aggiornamenti automatici",
|
||||||
|
"description": "Il cruscotto cloud si evolve rapidamente, in modo da ottenere nuove funzionalità e correzioni di bug senza dover tirare manualmente nuovi contenitori ogni volta."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Meno manutenzione",
|
||||||
|
"description": "Nessuna migrazione di database, backup o infrastruttura extra da gestire. Gestiamo questo problema nel cloud."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "failover del cloud",
|
||||||
|
"description": "Se il tuo nodo scende, i tuoi tunnel possono temporaneamente fallire nei nostri punti di presenza cloud fino a quando non lo riporti online."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Alta disponibilità (PoPs)",
|
||||||
|
"description": "Puoi anche allegare più nodi al tuo account per ridondanza e prestazioni migliori."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Miglioramenti futuri",
|
||||||
|
"description": "Stiamo pianificando di aggiungere più strumenti di analisi, allerta e gestione per rendere la tua distribuzione ancora più robusta."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Scopri di più sull'opzione Managed Self-Hosted nella nostra",
|
||||||
|
"documentation": "documentazione"
|
||||||
|
},
|
||||||
|
"convertButton": "Converti questo nodo in auto-ospitato gestito"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Dominio Internazionale Rilevato",
|
||||||
|
"willbestoredas": "Verrà conservato come:",
|
||||||
|
"roleMappingDescription": "Determinare come i ruoli sono assegnati agli utenti quando accedono quando è abilitata la fornitura automatica.",
|
||||||
|
"selectRole": "Seleziona un ruolo",
|
||||||
|
"roleMappingExpression": "Espressione",
|
||||||
|
"selectRolePlaceholder": "Scegli un ruolo",
|
||||||
|
"selectRoleDescription": "Seleziona un ruolo da assegnare a tutti gli utenti da questo provider di identità",
|
||||||
|
"roleMappingExpressionDescription": "Inserire un'espressione JMESPath per estrarre le informazioni sul ruolo dal token ID",
|
||||||
|
"idpTenantIdRequired": "L'ID dell'inquilino è obbligatorio",
|
||||||
|
"invalidValue": "Valore non valido",
|
||||||
|
"idpTypeLabel": "Tipo Provider Identità",
|
||||||
|
"roleMappingExpressionPlaceholder": "es. contiene(gruppi, 'admin') && 'Admin' <unk> <unk> 'Membro'",
|
||||||
|
"idpGoogleConfiguration": "Configurazione Google",
|
||||||
|
"idpGoogleConfigurationDescription": "Configura le tue credenziali di Google OAuth2",
|
||||||
|
"idpGoogleClientIdDescription": "Il Tuo Client Id Google OAuth2",
|
||||||
|
"idpGoogleClientSecretDescription": "Il Tuo Client Google OAuth2 Secret",
|
||||||
|
"idpAzureConfiguration": "Configurazione Azure Entra ID",
|
||||||
|
"idpAzureConfigurationDescription": "Configura le credenziali OAuth2 di Azure Entra ID",
|
||||||
|
"idpTenantId": "ID Tenant",
|
||||||
|
"idpTenantIdPlaceholder": "iltuo-inquilino-id",
|
||||||
|
"idpAzureTenantIdDescription": "Il tuo ID del tenant Azure (trovato nella panoramica di Azure Active Directory)",
|
||||||
|
"idpAzureClientIdDescription": "Il Tuo Id Client Registrazione App Azure",
|
||||||
|
"idpAzureClientSecretDescription": "Il Tuo Client Di Registrazione App Azure Secret",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Configurazione Google",
|
||||||
|
"idpAzureConfigurationTitle": "Configurazione Azure Entra ID",
|
||||||
|
"idpTenantIdLabel": "ID Tenant",
|
||||||
|
"idpAzureClientIdDescription2": "Il Tuo Id Client Registrazione App Azure",
|
||||||
|
"idpAzureClientSecretDescription2": "Il Tuo Client Di Registrazione App Azure Secret",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Sottorete",
|
||||||
|
"subnetDescription": "La sottorete per la configurazione di rete di questa organizzazione.",
|
||||||
|
"authPage": "Pagina Autenticazione",
|
||||||
|
"authPageDescription": "Configura la pagina di autenticazione per la tua organizzazione",
|
||||||
|
"authPageDomain": "Dominio Pagina Auth",
|
||||||
|
"noDomainSet": "Nessun dominio impostato",
|
||||||
|
"changeDomain": "Cambia Dominio",
|
||||||
|
"selectDomain": "Seleziona Dominio",
|
||||||
|
"restartCertificate": "Riavvia Certificato",
|
||||||
|
"editAuthPageDomain": "Modifica Dominio Pagina Auth",
|
||||||
|
"setAuthPageDomain": "Imposta Dominio Pagina Autenticazione",
|
||||||
|
"failedToFetchCertificate": "Recupero del certificato non riuscito",
|
||||||
|
"failedToRestartCertificate": "Riavvio del certificato non riuscito",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Aggiungi un dominio per abilitare le pagine di autenticazione personalizzate per la tua organizzazione",
|
||||||
|
"selectDomainForOrgAuthPage": "Seleziona un dominio per la pagina di autenticazione dell'organizzazione",
|
||||||
|
"domainPickerProvidedDomain": "Dominio Fornito",
|
||||||
|
"domainPickerFreeProvidedDomain": "Dominio Fornito Gratuito",
|
||||||
|
"domainPickerVerified": "Verificato",
|
||||||
|
"domainPickerUnverified": "Non Verificato",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Questo sottodominio contiene caratteri o struttura non validi. Sarà sanificato automaticamente quando si salva.",
|
||||||
|
"domainPickerError": "Errore",
|
||||||
|
"domainPickerErrorLoadDomains": "Impossibile caricare i domini dell'organizzazione",
|
||||||
|
"domainPickerErrorCheckAvailability": "Impossibile verificare la disponibilità del dominio",
|
||||||
|
"domainPickerInvalidSubdomain": "Sottodominio non valido",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "L'input \"{sub}\" è stato rimosso perché non è valido.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" non può essere reso valido per {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Sottodominio igienizzato",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" è stato corretto in \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Accedi alla tua organizzazione",
|
||||||
|
"orgAuthChooseIdpDescription": "Scegli il tuo provider di identità per continuare",
|
||||||
|
"orgAuthNoIdpConfigured": "Questa organizzazione non ha nessun provider di identità configurato. Puoi accedere con la tua identità Pangolin.",
|
||||||
|
"orgAuthSignInWithPangolin": "Accedi con Pangolino",
|
||||||
|
"subscriptionRequiredToUse": "Per utilizzare questa funzionalità è necessario un abbonamento.",
|
||||||
|
"idpDisabled": "I provider di identità sono disabilitati.",
|
||||||
|
"orgAuthPageDisabled": "La pagina di autenticazione dell'organizzazione è disabilitata.",
|
||||||
|
"domainRestartedDescription": "Verifica del dominio riavviata con successo",
|
||||||
|
"resourceAddEntrypointsEditFile": "Modifica file: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Modifica file: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "Verifica via email. Effettua nuovamente il login via {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.",
|
||||||
|
"twoFactorSetupRequired": "È richiesta la configurazione di autenticazione a due fattori. Effettua nuovamente l'accesso tramite {dashboardUrl}/auth/login completa questo passaggio. Quindi, torna qui.",
|
||||||
|
"authPageErrorUpdateMessage": "Si è verificato un errore durante l'aggiornamento delle impostazioni della pagina di autenticazione",
|
||||||
|
"authPageUpdated": "Pagina di autenticazione aggiornata con successo",
|
||||||
|
"healthCheckNotAvailable": "Locale",
|
||||||
|
"rewritePath": "Riscrivi percorso",
|
||||||
|
"rewritePathDescription": "Riscrivi eventualmente il percorso prima di inoltrarlo al target."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "네트워크에 대한 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.",
|
"siteNewtTunnelDescription": "네트워크에 대한 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.",
|
||||||
"siteWg": "기본 WireGuard",
|
"siteWg": "기본 WireGuard",
|
||||||
"siteWgDescription": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다.",
|
"siteWgDescription": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다.",
|
||||||
|
"siteWgDescriptionSaas": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다. 자체 호스팅 노드에서만 작동합니다.",
|
||||||
"siteLocalDescription": "로컬 리소스만 사용 가능합니다. 터널링이 없습니다.",
|
"siteLocalDescription": "로컬 리소스만 사용 가능합니다. 터널링이 없습니다.",
|
||||||
|
"siteLocalDescriptionSaas": "로컬 리소스만. 터널링 없음. 자체 호스팅 노드에서만 작동합니다.",
|
||||||
"siteSeeAll": "모든 사이트 보기",
|
"siteSeeAll": "모든 사이트 보기",
|
||||||
"siteTunnelDescription": "사이트에 연결하는 방법을 결정하세요",
|
"siteTunnelDescription": "사이트에 연결하는 방법을 결정하세요",
|
||||||
"siteNewtCredentials": "Newt 자격 증명",
|
"siteNewtCredentials": "Newt 자격 증명",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "사이트 선택",
|
"siteSelect": "사이트 선택",
|
||||||
"siteSearch": "사이트 검색",
|
"siteSearch": "사이트 검색",
|
||||||
"siteNotFound": "사이트를 찾을 수 없습니다.",
|
"siteNotFound": "사이트를 찾을 수 없습니다.",
|
||||||
"siteSelectionDescription": "이 사이트는 리소스에 대한 연결을 제공합니다.",
|
"selectCountry": "국가 선택하기",
|
||||||
|
"searchCountries": "국가 검색...",
|
||||||
|
"noCountryFound": "국가를 찾을 수 없습니다.",
|
||||||
|
"siteSelectionDescription": "이 사이트는 대상에 대한 연결을 제공합니다.",
|
||||||
"resourceType": "리소스 유형",
|
"resourceType": "리소스 유형",
|
||||||
"resourceTypeDescription": "리소스에 접근하는 방법을 결정하세요",
|
"resourceTypeDescription": "리소스에 접근하는 방법을 결정하세요",
|
||||||
"resourceHTTPSSettings": "HTTPS 설정",
|
"resourceHTTPSSettings": "HTTPS 설정",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "일반",
|
"general": "일반",
|
||||||
"generalSettings": "일반 설정",
|
"generalSettings": "일반 설정",
|
||||||
"proxy": "프록시",
|
"proxy": "프록시",
|
||||||
|
"internal": "내부",
|
||||||
"rules": "규칙",
|
"rules": "규칙",
|
||||||
"resourceSettingDescription": "리소스의 설정을 구성하세요.",
|
"resourceSettingDescription": "리소스의 설정을 구성하세요.",
|
||||||
"resourceSetting": "{resourceName} 설정",
|
"resourceSetting": "{resourceName} 설정",
|
||||||
"alwaysAllow": "항상 허용",
|
"alwaysAllow": "항상 허용",
|
||||||
"alwaysDeny": "항상 거부",
|
"alwaysDeny": "항상 거부",
|
||||||
|
"passToAuth": "인증으로 전달",
|
||||||
"orgSettingsDescription": "조직의 일반 설정을 구성하세요",
|
"orgSettingsDescription": "조직의 일반 설정을 구성하세요",
|
||||||
"orgGeneralSettings": "조직 설정",
|
"orgGeneralSettings": "조직 설정",
|
||||||
"orgGeneralSettingsDescription": "조직 세부정보 및 구성을 관리하세요.",
|
"orgGeneralSettingsDescription": "조직 세부정보 및 구성을 관리하세요.",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "사용자를 역할에 추가하는 동안 오류가 발생했습니다.",
|
"accessRoleErrorAddDescription": "사용자를 역할에 추가하는 동안 오류가 발생했습니다.",
|
||||||
"userSaved": "사용자 저장됨",
|
"userSaved": "사용자 저장됨",
|
||||||
"userSavedDescription": "사용자가 업데이트되었습니다.",
|
"userSavedDescription": "사용자가 업데이트되었습니다.",
|
||||||
|
"autoProvisioned": "자동 프로비저닝됨",
|
||||||
|
"autoProvisionedDescription": "이 사용자가 ID 공급자에 의해 자동으로 관리될 수 있도록 허용합니다",
|
||||||
"accessControlsDescription": "이 사용자가 조직에서 접근하고 수행할 수 있는 작업을 관리하세요",
|
"accessControlsDescription": "이 사용자가 조직에서 접근하고 수행할 수 있는 작업을 관리하세요",
|
||||||
"accessControlsSubmit": "접근 제어 저장",
|
"accessControlsSubmit": "접근 제어 저장",
|
||||||
"roles": "역할",
|
"roles": "역할",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "SNI에 사용할 TLS 서버 이름. 기본값을 사용하려면 비워 두십시오.",
|
"targetTlsSniDescription": "SNI에 사용할 TLS 서버 이름. 기본값을 사용하려면 비워 두십시오.",
|
||||||
"targetTlsSubmit": "설정 저장",
|
"targetTlsSubmit": "설정 저장",
|
||||||
"targets": "대상 구성",
|
"targets": "대상 구성",
|
||||||
"targetsDescription": "서비스로 트래픽을 라우팅할 대상을 설정하십시오",
|
"targetsDescription": "사용자 백엔드 서비스로 트래픽을 라우팅할 대상을 설정하십시오.",
|
||||||
"targetStickySessions": "스티키 세션 활성화",
|
"targetStickySessions": "스티키 세션 활성화",
|
||||||
"targetStickySessionsDescription": "세션 전체 동안 동일한 백엔드 대상을 유지합니다.",
|
"targetStickySessionsDescription": "세션 전체 동안 동일한 백엔드 대상을 유지합니다.",
|
||||||
"methodSelect": "선택 방법",
|
"methodSelect": "선택 방법",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "잘못된 IP 주소 형식",
|
"ipAddressErrorInvalidFormat": "잘못된 IP 주소 형식",
|
||||||
"ipAddressErrorInvalidOctet": "유효하지 않은 IP 주소 옥텟",
|
"ipAddressErrorInvalidOctet": "유효하지 않은 IP 주소 옥텟",
|
||||||
"path": "경로",
|
"path": "경로",
|
||||||
|
"matchPath": "경로 맞춤",
|
||||||
"ipAddressRange": "IP 범위",
|
"ipAddressRange": "IP 범위",
|
||||||
"rulesErrorFetch": "규칙을 가져오는 데 실패했습니다.",
|
"rulesErrorFetch": "규칙을 가져오는 데 실패했습니다.",
|
||||||
"rulesErrorFetchDescription": "규칙을 가져오는 중 오류가 발생했습니다",
|
"rulesErrorFetchDescription": "규칙을 가져오는 중 오류가 발생했습니다",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "작업",
|
"rulesActions": "작업",
|
||||||
"rulesActionAlwaysAllow": "항상 허용: 모든 인증 방법 우회",
|
"rulesActionAlwaysAllow": "항상 허용: 모든 인증 방법 우회",
|
||||||
"rulesActionAlwaysDeny": "항상 거부: 모든 요청을 차단합니다. 인증을 시도할 수 없습니다.",
|
"rulesActionAlwaysDeny": "항상 거부: 모든 요청을 차단합니다. 인증을 시도할 수 없습니다.",
|
||||||
|
"rulesActionPassToAuth": "인증으로 전달: 인증 방법 시도를 허용합니다",
|
||||||
"rulesMatchCriteria": "일치 기준",
|
"rulesMatchCriteria": "일치 기준",
|
||||||
"rulesMatchCriteriaIpAddress": "특정 IP 주소와 일치",
|
"rulesMatchCriteriaIpAddress": "특정 IP 주소와 일치",
|
||||||
"rulesMatchCriteriaIpAddressRange": "CIDR 표기법으로 IP 주소 범위를 일치시킵니다",
|
"rulesMatchCriteriaIpAddressRange": "CIDR 표기법으로 IP 주소 범위를 일치시킵니다",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN은 정확히 6자리여야 합니다",
|
"pincodeRequirementsLength": "PIN은 정확히 6자리여야 합니다",
|
||||||
"pincodeRequirementsChars": "PIN은 숫자만 포함해야 합니다.",
|
"pincodeRequirementsChars": "PIN은 숫자만 포함해야 합니다.",
|
||||||
"passwordRequirementsLength": "비밀번호는 최소 1자 이상이어야 합니다",
|
"passwordRequirementsLength": "비밀번호는 최소 1자 이상이어야 합니다",
|
||||||
|
"passwordRequirementsTitle": "비밀번호 요구사항:",
|
||||||
|
"passwordRequirementLength": "최소 8자 이상",
|
||||||
|
"passwordRequirementUppercase": "최소 대문자 하나",
|
||||||
|
"passwordRequirementLowercase": "최소 소문자 하나",
|
||||||
|
"passwordRequirementNumber": "최소 숫자 하나",
|
||||||
|
"passwordRequirementSpecial": "최소 특수 문자 하나",
|
||||||
|
"passwordRequirementsMet": "✓ 비밀번호가 모든 요구사항을 충족합니다.",
|
||||||
|
"passwordStrength": "비밀번호 강도",
|
||||||
|
"passwordStrengthWeak": "약함",
|
||||||
|
"passwordStrengthMedium": "보통",
|
||||||
|
"passwordStrengthStrong": "강함",
|
||||||
|
"passwordRequirements": "요구 사항:",
|
||||||
|
"passwordRequirementLengthText": "8자 이상",
|
||||||
|
"passwordRequirementUppercaseText": "대문자 (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "소문자 (a-z)",
|
||||||
|
"passwordRequirementNumberText": "숫자 (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "특수 문자 (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "비밀번호가 일치하지 않습니다.",
|
||||||
"otpEmailRequirementsLength": "OTP는 최소 1자 이상이어야 합니다",
|
"otpEmailRequirementsLength": "OTP는 최소 1자 이상이어야 합니다",
|
||||||
"otpEmailSent": "OTP 전송됨",
|
"otpEmailSent": "OTP 전송됨",
|
||||||
"otpEmailSentDescription": "OTP가 귀하의 이메일로 전송되었습니다.",
|
"otpEmailSentDescription": "OTP가 귀하의 이메일로 전송되었습니다.",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "로그아웃 중 오류 발생",
|
"logoutError": "로그아웃 중 오류 발생",
|
||||||
"signingAs": "로그인한 사용자",
|
"signingAs": "로그인한 사용자",
|
||||||
"serverAdmin": "서버 관리자",
|
"serverAdmin": "서버 관리자",
|
||||||
|
"managedSelfhosted": "관리 자체 호스팅",
|
||||||
"otpEnable": "이중 인증 활성화",
|
"otpEnable": "이중 인증 활성화",
|
||||||
"otpDisable": "이중 인증 비활성화",
|
"otpDisable": "이중 인증 비활성화",
|
||||||
"logout": "로그 아웃",
|
"logout": "로그 아웃",
|
||||||
"licenseTierProfessionalRequired": "전문 에디션이 필요합니다.",
|
"licenseTierProfessionalRequired": "전문 에디션이 필요합니다.",
|
||||||
"licenseTierProfessionalRequiredDescription": "이 기능은 Professional Edition에서만 사용할 수 있습니다.",
|
"licenseTierProfessionalRequiredDescription": "이 기능은 Professional Edition에서만 사용할 수 있습니다.",
|
||||||
"actionGetOrg": "조직 가져오기",
|
"actionGetOrg": "조직 가져오기",
|
||||||
|
"updateOrgUser": "조직 사용자 업데이트",
|
||||||
|
"createOrgUser": "조직 사용자 생성",
|
||||||
"actionUpdateOrg": "조직 업데이트",
|
"actionUpdateOrg": "조직 업데이트",
|
||||||
"actionUpdateUser": "사용자 업데이트",
|
"actionUpdateUser": "사용자 업데이트",
|
||||||
"actionGetUser": "사용자 조회",
|
"actionGetUser": "사용자 조회",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "사이트 삭제",
|
"actionDeleteSite": "사이트 삭제",
|
||||||
"actionGetSite": "사이트 가져오기",
|
"actionGetSite": "사이트 가져오기",
|
||||||
"actionListSites": "사이트 목록",
|
"actionListSites": "사이트 목록",
|
||||||
|
"actionApplyBlueprint": "청사진 적용",
|
||||||
|
"setupToken": "설정 토큰",
|
||||||
|
"setupTokenDescription": "서버 콘솔에서 설정 토큰 입력.",
|
||||||
|
"setupTokenRequired": "설정 토큰이 필요합니다",
|
||||||
"actionUpdateSite": "사이트 업데이트",
|
"actionUpdateSite": "사이트 업데이트",
|
||||||
"actionListSiteRoles": "허용된 사이트 역할 목록",
|
"actionListSiteRoles": "허용된 사이트 역할 목록",
|
||||||
"actionCreateResource": "리소스 생성",
|
"actionCreateResource": "리소스 생성",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "IDP 조직 정책 삭제",
|
"actionDeleteIdpOrg": "IDP 조직 정책 삭제",
|
||||||
"actionListIdpOrgs": "IDP 조직 목록",
|
"actionListIdpOrgs": "IDP 조직 목록",
|
||||||
"actionUpdateIdpOrg": "IDP 조직 업데이트",
|
"actionUpdateIdpOrg": "IDP 조직 업데이트",
|
||||||
|
"actionCreateClient": "클라이언트 생성",
|
||||||
|
"actionDeleteClient": "클라이언트 삭제",
|
||||||
|
"actionUpdateClient": "클라이언트 업데이트",
|
||||||
|
"actionListClients": "클라이언트 목록",
|
||||||
|
"actionGetClient": "클라이언트 가져오기",
|
||||||
|
"actionCreateSiteResource": "사이트 리소스 생성",
|
||||||
|
"actionDeleteSiteResource": "사이트 리소스 삭제",
|
||||||
|
"actionGetSiteResource": "사이트 리소스 가져오기",
|
||||||
|
"actionListSiteResources": "사이트 리소스 목록",
|
||||||
|
"actionUpdateSiteResource": "사이트 리소스 업데이트",
|
||||||
|
"actionListInvitations": "초대 목록",
|
||||||
"noneSelected": "선택된 항목 없음",
|
"noneSelected": "선택된 항목 없음",
|
||||||
"orgNotFound2": "조직이 없습니다.",
|
"orgNotFound2": "조직이 없습니다.",
|
||||||
"searchProgress": "검색...",
|
"searchProgress": "검색...",
|
||||||
@@ -1093,10 +1140,10 @@
|
|||||||
"sidebarAllUsers": "모든 사용자",
|
"sidebarAllUsers": "모든 사용자",
|
||||||
"sidebarIdentityProviders": "신원 공급자",
|
"sidebarIdentityProviders": "신원 공급자",
|
||||||
"sidebarLicense": "라이선스",
|
"sidebarLicense": "라이선스",
|
||||||
"sidebarClients": "Clients (Beta)",
|
"sidebarClients": "클라이언트 (Beta)",
|
||||||
"sidebarDomains": "도메인",
|
"sidebarDomains": "도메인",
|
||||||
"enableDockerSocket": "Docker 소켓 활성화",
|
"enableDockerSocket": "Docker 청사진 활성화",
|
||||||
"enableDockerSocketDescription": "컨테이너 정보를 채우기 위해 Docker 소켓 검색을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
|
"enableDockerSocketDescription": "블루프린트 레이블을 위한 Docker 소켓 레이블 수집을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.",
|
||||||
"enableDockerSocketLink": "자세히 알아보기",
|
"enableDockerSocketLink": "자세히 알아보기",
|
||||||
"viewDockerContainers": "도커 컨테이너 보기",
|
"viewDockerContainers": "도커 컨테이너 보기",
|
||||||
"containersIn": "{siteName}의 컨테이너",
|
"containersIn": "{siteName}의 컨테이너",
|
||||||
@@ -1161,7 +1208,7 @@
|
|||||||
"selectDomainTypeCnameName": "단일 도메인 (CNAME)",
|
"selectDomainTypeCnameName": "단일 도메인 (CNAME)",
|
||||||
"selectDomainTypeCnameDescription": "단일 하위 도메인 또는 특정 도메인 항목에 사용됩니다.",
|
"selectDomainTypeCnameDescription": "단일 하위 도메인 또는 특정 도메인 항목에 사용됩니다.",
|
||||||
"selectDomainTypeWildcardName": "와일드카드 도메인",
|
"selectDomainTypeWildcardName": "와일드카드 도메인",
|
||||||
"selectDomainTypeWildcardDescription": "This domain and its subdomains.",
|
"selectDomainTypeWildcardDescription": "이 도메인 및 그 하위 도메인.",
|
||||||
"domainDelegation": "단일 도메인",
|
"domainDelegation": "단일 도메인",
|
||||||
"selectType": "유형 선택",
|
"selectType": "유형 선택",
|
||||||
"actions": "작업",
|
"actions": "작업",
|
||||||
@@ -1195,22 +1242,64 @@
|
|||||||
"sidebarExpand": "확장하기",
|
"sidebarExpand": "확장하기",
|
||||||
"newtUpdateAvailable": "업데이트 가능",
|
"newtUpdateAvailable": "업데이트 가능",
|
||||||
"newtUpdateAvailableInfo": "뉴트의 새 버전이 출시되었습니다. 최상의 경험을 위해 최신 버전으로 업데이트하세요.",
|
"newtUpdateAvailableInfo": "뉴트의 새 버전이 출시되었습니다. 최상의 경험을 위해 최신 버전으로 업데이트하세요.",
|
||||||
"domainPickerEnterDomain": "Domain",
|
"domainPickerEnterDomain": "도메인",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com, 또는 그냥 myapp",
|
"domainPickerPlaceholder": "myapp.example.com",
|
||||||
"domainPickerDescription": "Enter the full domain of the resource to see available options.",
|
"domainPickerDescription": "리소스의 전체 도메인을 입력하여 사용 가능한 옵션을 확인하십시오.",
|
||||||
"domainPickerDescriptionSaas": "Enter a full domain, subdomain, or just a name to see available options",
|
"domainPickerDescriptionSaas": "전체 도메인, 서브도메인 또는 이름을 입력하여 사용 가능한 옵션을 확인하십시오.",
|
||||||
"domainPickerTabAll": "모두",
|
"domainPickerTabAll": "모두",
|
||||||
"domainPickerTabOrganization": "조직",
|
"domainPickerTabOrganization": "조직",
|
||||||
"domainPickerTabProvided": "제공 됨",
|
"domainPickerTabProvided": "제공 됨",
|
||||||
"domainPickerSortAsc": "A-Z",
|
"domainPickerSortAsc": "A-Z",
|
||||||
"domainPickerSortDesc": "Z-A",
|
"domainPickerSortDesc": "Z-A",
|
||||||
"domainPickerCheckingAvailability": "가용성을 확인 중...",
|
"domainPickerCheckingAvailability": "가용성을 확인 중...",
|
||||||
"domainPickerNoMatchingDomains": "No matching domains found. Try a different domain or check your organization's domain settings.",
|
"domainPickerNoMatchingDomains": "일치하는 도메인을 찾을 수 없습니다. 다른 도메인을 시도하거나 조직의 도메인 설정을 확인하십시오.",
|
||||||
"domainPickerOrganizationDomains": "조직 도메인",
|
"domainPickerOrganizationDomains": "조직 도메인",
|
||||||
"domainPickerProvidedDomains": "제공된 도메인",
|
"domainPickerProvidedDomains": "제공된 도메인",
|
||||||
"domainPickerSubdomain": "서브도메인: {subdomain}",
|
"domainPickerSubdomain": "서브도메인: {subdomain}",
|
||||||
"domainPickerNamespace": "이름 공간: {namespace}",
|
"domainPickerNamespace": "이름 공간: {namespace}",
|
||||||
"domainPickerShowMore": "더보기",
|
"domainPickerShowMore": "더보기",
|
||||||
|
"regionSelectorTitle": "지역 선택",
|
||||||
|
"regionSelectorInfo": "지역을 선택하면 위치에 따라 더 나은 성능이 제공됩니다. 서버와 같은 지역에 있을 필요는 없습니다.",
|
||||||
|
"regionSelectorPlaceholder": "지역 선택",
|
||||||
|
"regionSelectorComingSoon": "곧 출시 예정",
|
||||||
|
"billingLoadingSubscription": "구독 불러오는 중...",
|
||||||
|
"billingFreeTier": "무료 티어",
|
||||||
|
"billingWarningOverLimit": "경고: 하나 이상의 사용 한도를 초과했습니다. 구독을 수정하거나 사용량을 조정하기 전까지 사이트는 연결되지 않습니다.",
|
||||||
|
"billingUsageLimitsOverview": "사용 한도 개요",
|
||||||
|
"billingMonitorUsage": "설정된 한도에 대한 사용량을 모니터링합니다. 한도를 늘려야 하는 경우 support@fossorial.io로 연락하십시오.",
|
||||||
|
"billingDataUsage": "데이터 사용량",
|
||||||
|
"billingOnlineTime": "사이트 온라인 시간",
|
||||||
|
"billingUsers": "활성 사용자",
|
||||||
|
"billingDomains": "활성 도메인",
|
||||||
|
"billingRemoteExitNodes": "활성 자체 호스팅 노드",
|
||||||
|
"billingNoLimitConfigured": "구성된 한도가 없습니다.",
|
||||||
|
"billingEstimatedPeriod": "예상 청구 기간",
|
||||||
|
"billingIncludedUsage": "포함 사용량",
|
||||||
|
"billingIncludedUsageDescription": "현재 구독 계획에 포함된 사용량",
|
||||||
|
"billingFreeTierIncludedUsage": "무료 티어 사용 허용량",
|
||||||
|
"billingIncluded": "포함됨",
|
||||||
|
"billingEstimatedTotal": "예상 총액:",
|
||||||
|
"billingNotes": "노트",
|
||||||
|
"billingEstimateNote": "현재 사용량을 기반으로 한 추정치입니다.",
|
||||||
|
"billingActualChargesMayVary": "실제 청구 금액은 다를 수 있습니다.",
|
||||||
|
"billingBilledAtEnd": "청구 기간이 끝난 후 청구됩니다.",
|
||||||
|
"billingModifySubscription": "구독 수정",
|
||||||
|
"billingStartSubscription": "구독 시작",
|
||||||
|
"billingRecurringCharge": "반복 요금",
|
||||||
|
"billingManageSubscriptionSettings": "구독 설정 및 기본 설정을 관리합니다",
|
||||||
|
"billingNoActiveSubscription": "활성 구독이 없습니다. 사용 한도를 늘리려면 구독을 시작하십시오.",
|
||||||
|
"billingFailedToLoadSubscription": "구독을 불러오는 데 실패했습니다.",
|
||||||
|
"billingFailedToLoadUsage": "사용량을 불러오는 데 실패했습니다.",
|
||||||
|
"billingFailedToGetCheckoutUrl": "체크아웃 URL을 가져오는 데 실패했습니다.",
|
||||||
|
"billingPleaseTryAgainLater": "나중에 다시 시도하십시오.",
|
||||||
|
"billingCheckoutError": "체크아웃 오류",
|
||||||
|
"billingFailedToGetPortalUrl": "포털 URL을 가져오는 데 실패했습니다.",
|
||||||
|
"billingPortalError": "포털 오류",
|
||||||
|
"billingDataUsageInfo": "클라우드에 연결할 때 보안 터널을 통해 전송된 모든 데이터에 대해 비용이 청구됩니다. 여기에는 모든 사이트의 들어오고 나가는 트래픽이 포함됩니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용하는 경우 데이터는 요금이 청구되지 않습니다.",
|
||||||
|
"billingOnlineTimeInfo": "사이트가 클라우드에 연결된 시간에 따라 요금이 청구됩니다. 예를 들어, 44,640분은 사이트가 한 달 내내 24시간 작동하는 것과 같습니다. 사용량 한도에 도달하면 플랜을 업그레이드하거나 사용량을 줄일 때까지 사이트가 연결 해제됩니다. 노드를 사용할 때 시간은 요금이 청구되지 않습니다.",
|
||||||
|
"billingUsersInfo": "조직의 사용자마다 요금이 청구됩니다. 청구는 조직의 활성 사용자 계정 수에 따라 매일 계산됩니다.",
|
||||||
|
"billingDomainInfo": "조직의 도메인마다 요금이 청구됩니다. 청구는 조직의 활성 도메인 계정 수에 따라 매일 계산됩니다.",
|
||||||
|
"billingRemoteExitNodesInfo": "조직의 관리 노드마다 요금이 청구됩니다. 청구는 조직의 활성 관리 노드 수에 따라 매일 계산됩니다.",
|
||||||
"domainNotFound": "도메인을 찾을 수 없습니다",
|
"domainNotFound": "도메인을 찾을 수 없습니다",
|
||||||
"domainNotFoundDescription": "이 리소스는 도메인이 더 이상 시스템에 존재하지 않아 비활성화되었습니다. 이 리소스에 대한 새 도메인을 설정하세요.",
|
"domainNotFoundDescription": "이 리소스는 도메인이 더 이상 시스템에 존재하지 않아 비활성화되었습니다. 이 리소스에 대한 새 도메인을 설정하세요.",
|
||||||
"failed": "실패",
|
"failed": "실패",
|
||||||
@@ -1231,7 +1320,7 @@
|
|||||||
"securityKeyRemoveSuccess": "보안 키가 성공적으로 제거되었습니다",
|
"securityKeyRemoveSuccess": "보안 키가 성공적으로 제거되었습니다",
|
||||||
"securityKeyRemoveError": "보안 키 제거 실패",
|
"securityKeyRemoveError": "보안 키 제거 실패",
|
||||||
"securityKeyLoadError": "보안 키를 불러오는 데 실패했습니다",
|
"securityKeyLoadError": "보안 키를 불러오는 데 실패했습니다",
|
||||||
"securityKeyLogin": "Continue with security key",
|
"securityKeyLogin": "보안 키로 계속하기",
|
||||||
"securityKeyAuthError": "보안 키를 사용한 인증 실패",
|
"securityKeyAuthError": "보안 키를 사용한 인증 실패",
|
||||||
"securityKeyRecommendation": "항상 계정에 액세스할 수 있도록 다른 장치에 백업 보안 키를 등록하세요.",
|
"securityKeyRecommendation": "항상 계정에 액세스할 수 있도록 다른 장치에 백업 보안 키를 등록하세요.",
|
||||||
"registering": "등록 중...",
|
"registering": "등록 중...",
|
||||||
@@ -1265,7 +1354,7 @@
|
|||||||
"createDomainName": "이름:",
|
"createDomainName": "이름:",
|
||||||
"createDomainValue": "값:",
|
"createDomainValue": "값:",
|
||||||
"createDomainCnameRecords": "CNAME 레코드",
|
"createDomainCnameRecords": "CNAME 레코드",
|
||||||
"createDomainARecords": "A Records",
|
"createDomainARecords": "A 레코드",
|
||||||
"createDomainRecordNumber": "레코드 {number}",
|
"createDomainRecordNumber": "레코드 {number}",
|
||||||
"createDomainTxtRecords": "TXT 레코드",
|
"createDomainTxtRecords": "TXT 레코드",
|
||||||
"createDomainSaveTheseRecords": "이 레코드 저장",
|
"createDomainSaveTheseRecords": "이 레코드 저장",
|
||||||
@@ -1274,49 +1363,365 @@
|
|||||||
"createDomainDnsPropagationDescription": "DNS 변경 사항은 인터넷 전체에 전파되는 데 시간이 걸립니다. DNS 제공자와 TTL 설정에 따라 몇 분에서 48시간까지 걸릴 수 있습니다.",
|
"createDomainDnsPropagationDescription": "DNS 변경 사항은 인터넷 전체에 전파되는 데 시간이 걸립니다. DNS 제공자와 TTL 설정에 따라 몇 분에서 48시간까지 걸릴 수 있습니다.",
|
||||||
"resourcePortRequired": "HTTP 리소스가 아닌 경우 포트 번호가 필요합니다",
|
"resourcePortRequired": "HTTP 리소스가 아닌 경우 포트 번호가 필요합니다",
|
||||||
"resourcePortNotAllowed": "HTTP 리소스에 대해 포트 번호를 설정하지 마세요",
|
"resourcePortNotAllowed": "HTTP 리소스에 대해 포트 번호를 설정하지 마세요",
|
||||||
|
"billingPricingCalculatorLink": "가격 계산기",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "I agree to the",
|
"IAgreeToThe": "동의합니다",
|
||||||
"termsOfService": "terms of service",
|
"termsOfService": "서비스 약관",
|
||||||
"and": "and",
|
"and": "및",
|
||||||
"privacyPolicy": "privacy policy"
|
"privacyPolicy": "개인 정보 보호 정책"
|
||||||
},
|
},
|
||||||
"siteRequired": "Site is required.",
|
"siteRequired": "사이트가 필요합니다.",
|
||||||
"olmTunnel": "Olm Tunnel",
|
"olmTunnel": "Olm 터널",
|
||||||
"olmTunnelDescription": "Use Olm for client connectivity",
|
"olmTunnelDescription": "클라이언트 연결에 Olm 사용",
|
||||||
"errorCreatingClient": "Error creating client",
|
"errorCreatingClient": "클라이언트 생성 오류",
|
||||||
"clientDefaultsNotFound": "Client defaults not found",
|
"clientDefaultsNotFound": "클라이언트 기본값을 찾을 수 없습니다.",
|
||||||
"createClient": "Create Client",
|
"createClient": "클라이언트 생성",
|
||||||
"createClientDescription": "Create a new client for connecting to your sites",
|
"createClientDescription": "사이트에 연결하기 위한 새 클라이언트를 생성하십시오.",
|
||||||
"seeAllClients": "See All Clients",
|
"seeAllClients": "모든 클라이언트 보기",
|
||||||
"clientInformation": "Client Information",
|
"clientInformation": "클라이언트 정보",
|
||||||
"clientNamePlaceholder": "Client name",
|
"clientNamePlaceholder": "클라이언트 이름",
|
||||||
"address": "Address",
|
"address": "주소",
|
||||||
"subnetPlaceholder": "Subnet",
|
"subnetPlaceholder": "서브넷",
|
||||||
"addressDescription": "The address that this client will use for connectivity",
|
"addressDescription": "이 클라이언트가 연결에 사용할 주소",
|
||||||
"selectSites": "Select sites",
|
"selectSites": "사이트 선택",
|
||||||
"sitesDescription": "The client will have connectivity to the selected sites",
|
"sitesDescription": "클라이언트는 선택한 사이트에 연결됩니다.",
|
||||||
"clientInstallOlm": "Install Olm",
|
"clientInstallOlm": "Olm 설치",
|
||||||
"clientInstallOlmDescription": "Get Olm running on your system",
|
"clientInstallOlmDescription": "시스템에서 Olm을 실행하기",
|
||||||
"clientOlmCredentials": "Olm Credentials",
|
"clientOlmCredentials": "Olm 자격 증명",
|
||||||
"clientOlmCredentialsDescription": "This is how Olm will authenticate with the server",
|
"clientOlmCredentialsDescription": "Olm이 서버와 인증하는 방법입니다.",
|
||||||
"olmEndpoint": "Olm Endpoint",
|
"olmEndpoint": "Olm 엔드포인트",
|
||||||
"olmId": "Olm ID",
|
"olmId": "Olm ID",
|
||||||
"olmSecretKey": "Olm Secret Key",
|
"olmSecretKey": "Olm 비밀 키",
|
||||||
"clientCredentialsSave": "Save Your Credentials",
|
"clientCredentialsSave": "자격 증명 저장",
|
||||||
"clientCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.",
|
"clientCredentialsSaveDescription": "이것은 한 번만 볼 수 있습니다. 안전한 장소에 복사해 두세요.",
|
||||||
"generalSettingsDescription": "Configure the general settings for this client",
|
"generalSettingsDescription": "이 클라이언트에 대한 일반 설정을 구성하세요.",
|
||||||
"clientUpdated": "Client updated",
|
"clientUpdated": "클라이언트 업데이트됨",
|
||||||
"clientUpdatedDescription": "The client has been updated.",
|
"clientUpdatedDescription": "클라이언트가 업데이트되었습니다.",
|
||||||
"clientUpdateFailed": "Failed to update client",
|
"clientUpdateFailed": "클라이언트 업데이트 실패",
|
||||||
"clientUpdateError": "An error occurred while updating the client.",
|
"clientUpdateError": "클라이언트 업데이트 중 오류가 발생했습니다.",
|
||||||
"sitesFetchFailed": "Failed to fetch sites",
|
"sitesFetchFailed": "사이트 가져오기 실패",
|
||||||
"sitesFetchError": "An error occurred while fetching sites.",
|
"sitesFetchError": "사이트 가져오는 중 오류가 발생했습니다.",
|
||||||
"olmErrorFetchReleases": "An error occurred while fetching Olm releases.",
|
"olmErrorFetchReleases": "Olm 릴리즈 가져오는 중 오류가 발생했습니다.",
|
||||||
"olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.",
|
"olmErrorFetchLatest": "최신 Olm 릴리즈 가져오는 중 오류가 발생했습니다.",
|
||||||
"remoteSubnets": "Remote Subnets",
|
"remoteSubnets": "원격 서브넷",
|
||||||
"enterCidrRange": "Enter CIDR range",
|
"enterCidrRange": "CIDR 범위 입력",
|
||||||
"remoteSubnetsDescription": "Add CIDR ranges that can access this site remotely. Use format like 10.0.0.0/24 or 192.168.1.0/24.",
|
"remoteSubnetsDescription": "이 사이트에서 원격으로 액세스할 수 있는 CIDR 범위를 추가하세요. 10.0.0.0/24와 같은 형식을 사용하세요. 이는 VPN 클라이언트 연결에만 적용됩니다.",
|
||||||
"resourceEnableProxy": "Enable Public Proxy",
|
"resourceEnableProxy": "공개 프록시 사용",
|
||||||
"resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.",
|
"resourceEnableProxyDescription": "이 리소스에 대한 공개 프록시를 활성화하십시오. 이를 통해 네트워크 외부로부터 클라우드를 통해 열린 포트에서 리소스에 액세스할 수 있습니다. Traefik 구성이 필요합니다.",
|
||||||
"externalProxyEnabled": "External Proxy Enabled"
|
"externalProxyEnabled": "외부 프록시 활성화됨",
|
||||||
|
"addNewTarget": "새 대상 추가",
|
||||||
|
"targetsList": "대상 목록",
|
||||||
|
"targetErrorDuplicateTargetFound": "중복 대상 발견",
|
||||||
|
"healthCheckHealthy": "정상",
|
||||||
|
"healthCheckUnhealthy": "비정상",
|
||||||
|
"healthCheckUnknown": "알 수 없음",
|
||||||
|
"healthCheck": "상태 확인",
|
||||||
|
"configureHealthCheck": "상태 확인 설정",
|
||||||
|
"configureHealthCheckDescription": "{target}에 대한 상태 모니터링 설정",
|
||||||
|
"enableHealthChecks": "상태 확인 활성화",
|
||||||
|
"enableHealthChecksDescription": "이 대상을 모니터링하여 건강 상태를 확인하세요. 필요에 따라 대상과 다른 엔드포인트를 모니터링할 수 있습니다.",
|
||||||
|
"healthScheme": "방법",
|
||||||
|
"healthSelectScheme": "방법 선택",
|
||||||
|
"healthCheckPath": "경로",
|
||||||
|
"healthHostname": "IP / 호스트",
|
||||||
|
"healthPort": "포트",
|
||||||
|
"healthCheckPathDescription": "상태 확인을 위한 경로입니다.",
|
||||||
|
"healthyIntervalSeconds": "정상 간격",
|
||||||
|
"unhealthyIntervalSeconds": "비정상 간격",
|
||||||
|
"IntervalSeconds": "정상 간격",
|
||||||
|
"timeoutSeconds": "시간 초과",
|
||||||
|
"timeIsInSeconds": "시간은 초 단위입니다",
|
||||||
|
"retryAttempts": "재시도 횟수",
|
||||||
|
"expectedResponseCodes": "예상 응답 코드",
|
||||||
|
"expectedResponseCodesDescription": "정상 상태를 나타내는 HTTP 상태 코드입니다. 비워 두면 200-300이 정상으로 간주됩니다.",
|
||||||
|
"customHeaders": "사용자 정의 헤더",
|
||||||
|
"customHeadersDescription": "헤더는 새 줄로 구분됨: Header-Name: value",
|
||||||
|
"headersValidationError": "헤더는 형식이어야 합니다: 헤더명: 값.",
|
||||||
|
"saveHealthCheck": "상태 확인 저장",
|
||||||
|
"healthCheckSaved": "상태 확인이 저장되었습니다.",
|
||||||
|
"healthCheckSavedDescription": "상태 확인 구성이 성공적으로 저장되었습니다",
|
||||||
|
"healthCheckError": "상태 확인 오류",
|
||||||
|
"healthCheckErrorDescription": "상태 확인 구성을 저장하는 동안 오류가 발생했습니다",
|
||||||
|
"healthCheckPathRequired": "상태 확인 경로는 필수입니다.",
|
||||||
|
"healthCheckMethodRequired": "HTTP 방법은 필수입니다.",
|
||||||
|
"healthCheckIntervalMin": "확인 간격은 최소 5초여야 합니다.",
|
||||||
|
"healthCheckTimeoutMin": "시간 초과는 최소 1초여야 합니다.",
|
||||||
|
"healthCheckRetryMin": "재시도 횟수는 최소 1회여야 합니다.",
|
||||||
|
"httpMethod": "HTTP 메소드",
|
||||||
|
"selectHttpMethod": "HTTP 메소드 선택",
|
||||||
|
"domainPickerSubdomainLabel": "서브도메인",
|
||||||
|
"domainPickerBaseDomainLabel": "기본 도메인",
|
||||||
|
"domainPickerSearchDomains": "도메인 검색...",
|
||||||
|
"domainPickerNoDomainsFound": "찾을 수 없는 도메인이 없습니다",
|
||||||
|
"domainPickerLoadingDomains": "도메인 로딩 중...",
|
||||||
|
"domainPickerSelectBaseDomain": "기본 도메인 선택...",
|
||||||
|
"domainPickerNotAvailableForCname": "CNAME 도메인에는 사용할 수 없습니다",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "서브도메인을 입력하거나 기본 도메인을 사용하려면 공백으로 두십시오.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "사용 가능한 무료 도메인에서 검색 및 선택할 서브도메인 입력.",
|
||||||
|
"domainPickerFreeDomains": "무료 도메인",
|
||||||
|
"domainPickerSearchForAvailableDomains": "사용 가능한 도메인 검색",
|
||||||
|
"domainPickerNotWorkSelfHosted": "참고: 무료 제공 도메인은 현재 자체 호스팅 인스턴스에 사용할 수 없습니다.",
|
||||||
|
"resourceDomain": "도메인",
|
||||||
|
"resourceEditDomain": "도메인 수정",
|
||||||
|
"siteName": "사이트 이름",
|
||||||
|
"proxyPort": "포트",
|
||||||
|
"resourcesTableProxyResources": "프록시 리소스",
|
||||||
|
"resourcesTableClientResources": "클라이언트 리소스",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "프록시 리소스를 찾을 수 없습니다.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "내부 리소스를 찾을 수 없습니다.",
|
||||||
|
"resourcesTableDestination": "대상지",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "이 리소스는 다음과 함께 사용하기 위한 것입니다.",
|
||||||
|
"resourcesTableClients": "클라이언트",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "클라이언트와 연결되었을 때만 내부적으로 접근 가능합니다.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "클라이언트 리소스 수정",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "{resourceName}의 리소스 속성과 대상 구성을 업데이트하세요.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "리소스 속성",
|
||||||
|
"editInternalResourceDialogName": "이름",
|
||||||
|
"editInternalResourceDialogProtocol": "프로토콜",
|
||||||
|
"editInternalResourceDialogSitePort": "사이트 포트",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "대상 구성",
|
||||||
|
"editInternalResourceDialogCancel": "취소",
|
||||||
|
"editInternalResourceDialogSaveResource": "리소스 저장",
|
||||||
|
"editInternalResourceDialogSuccess": "성공",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "내부 리소스가 성공적으로 업데이트되었습니다",
|
||||||
|
"editInternalResourceDialogError": "오류",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "내부 리소스 업데이트 실패",
|
||||||
|
"editInternalResourceDialogNameRequired": "이름은 필수입니다.",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "이름은 255자 이하이어야 합니다.",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "프록시 포트는 최소 1이어야 합니다.",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "프록시 포트는 65536 미만이어야 합니다.",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "잘못된 IP 주소 형식",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "대상 포트는 최소 1이어야 합니다.",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "대상 포트는 65536 미만이어야 합니다.",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "사용 가능한 사이트가 없습니다.",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "내부 리소스를 생성하려면 서브넷이 구성된 최소 하나의 Newt 사이트가 필요합니다.",
|
||||||
|
"createInternalResourceDialogClose": "닫기",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "클라이언트 리소스 생성",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "선택한 사이트에 연결된 클라이언트에 접근할 새 리소스를 생성합니다.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "리소스 속성",
|
||||||
|
"createInternalResourceDialogName": "이름",
|
||||||
|
"createInternalResourceDialogSite": "사이트",
|
||||||
|
"createInternalResourceDialogSelectSite": "사이트 선택...",
|
||||||
|
"createInternalResourceDialogSearchSites": "사이트 검색...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "사이트를 찾을 수 없습니다.",
|
||||||
|
"createInternalResourceDialogProtocol": "프로토콜",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "사이트 포트",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "사이트에 연결되었을 때 리소스에 접근하기 위해 이 포트를 사용합니다.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "대상 설정",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "사이트 네트워크의 자원 IP 또는 호스트 네임 주소입니다.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "대상 IP에서 리소스에 접근할 수 있는 포트입니다.",
|
||||||
|
"createInternalResourceDialogCancel": "취소",
|
||||||
|
"createInternalResourceDialogCreateResource": "리소스 생성",
|
||||||
|
"createInternalResourceDialogSuccess": "성공",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "내부 리소스가 성공적으로 생성되었습니다.",
|
||||||
|
"createInternalResourceDialogError": "오류",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "내부 리소스 생성 실패",
|
||||||
|
"createInternalResourceDialogNameRequired": "이름은 필수입니다.",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "이름은 255자 이하이어야 합니다.",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "사이트를 선택하세요",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "프록시 포트는 최소 1이어야 합니다.",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "프록시 포트는 65536 미만이어야 합니다.",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "잘못된 IP 주소 형식",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "대상 포트는 최소 1이어야 합니다.",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "대상 포트는 65536 미만이어야 합니다.",
|
||||||
|
"siteConfiguration": "설정",
|
||||||
|
"siteAcceptClientConnections": "클라이언트 연결 허용",
|
||||||
|
"siteAcceptClientConnectionsDescription": "이 Newt 인스턴스를 게이트웨이로 사용하여 다른 장치가 연결될 수 있도록 허용합니다.",
|
||||||
|
"siteAddress": "사이트 주소",
|
||||||
|
"siteAddressDescription": "클라이언트가 연결하기 위한 호스트의 IP 주소를 지정합니다. 이는 클라이언트가 주소를 지정하기 위한 Pangolin 네트워크의 사이트 내부 주소입니다. 조직 서브넷 내에 있어야 합니다.",
|
||||||
|
"autoLoginExternalIdp": "외부 IDP로 자동 로그인",
|
||||||
|
"autoLoginExternalIdpDescription": "인증을 위해 외부 IDP로 사용자를 즉시 리디렉션합니다.",
|
||||||
|
"selectIdp": "IDP 선택",
|
||||||
|
"selectIdpPlaceholder": "IDP 선택...",
|
||||||
|
"selectIdpRequired": "자동 로그인이 활성화된 경우 IDP를 선택하십시오.",
|
||||||
|
"autoLoginTitle": "리디렉션 중",
|
||||||
|
"autoLoginDescription": "인증을 위해 외부 ID 공급자로 리디렉션 중입니다.",
|
||||||
|
"autoLoginProcessing": "인증 준비 중...",
|
||||||
|
"autoLoginRedirecting": "로그인으로 리디렉션 중...",
|
||||||
|
"autoLoginError": "자동 로그인 오류",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "ID 공급자로부터 리디렉션 URL을 받지 못했습니다.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "인증 URL 생성 실패.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "관리 자체 호스팅",
|
||||||
|
"remoteExitNodeDescription": "네트워크 연결성을 확장하기 위해 노드를 관리하세요",
|
||||||
|
"remoteExitNodes": "노드",
|
||||||
|
"searchRemoteExitNodes": "노드 검색...",
|
||||||
|
"remoteExitNodeAdd": "노드 추가",
|
||||||
|
"remoteExitNodeErrorDelete": "노드 삭제 오류",
|
||||||
|
"remoteExitNodeQuestionRemove": "조직에서 노드 {selectedNode}를 제거하시겠습니까?",
|
||||||
|
"remoteExitNodeMessageRemove": "한 번 제거되면 더 이상 노드에 접근할 수 없습니다.",
|
||||||
|
"remoteExitNodeMessageConfirm": "확인을 위해 아래에 노드 이름을 입력해 주세요.",
|
||||||
|
"remoteExitNodeConfirmDelete": "노드 삭제 확인",
|
||||||
|
"remoteExitNodeDelete": "노드 삭제",
|
||||||
|
"sidebarRemoteExitNodes": "노드",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "노드 생성",
|
||||||
|
"description": "네트워크 연결성을 확장하기 위해 새 노드를 생성하세요",
|
||||||
|
"viewAllButton": "모든 노드 보기",
|
||||||
|
"strategy": {
|
||||||
|
"title": "생성 전략",
|
||||||
|
"description": "노드를 직접 구성하거나 새 자격 증명을 생성하려면 이것을 선택하세요.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "노드 채택",
|
||||||
|
"description": "이미 노드의 자격 증명이 있는 경우 이것을 선택하세요."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "키 생성",
|
||||||
|
"description": "노드에 대한 새 키를 생성하려면 이것을 선택하세요"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "기존 노드 채택",
|
||||||
|
"description": "채택하려는 기존 노드의 자격 증명을 입력하세요",
|
||||||
|
"nodeIdLabel": "노드 ID",
|
||||||
|
"nodeIdDescription": "채택하려는 기존 노드의 ID",
|
||||||
|
"secretLabel": "비밀",
|
||||||
|
"secretDescription": "기존 노드의 비밀 키",
|
||||||
|
"submitButton": "노드 채택"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "생성된 자격 증명",
|
||||||
|
"description": "생성된 자격 증명을 사용하여 노드를 구성하세요",
|
||||||
|
"nodeIdTitle": "노드 ID",
|
||||||
|
"secretTitle": "비밀",
|
||||||
|
"saveCredentialsTitle": "구성에 자격 증명 추가",
|
||||||
|
"saveCredentialsDescription": "연결을 완료하려면 이러한 자격 증명을 자체 호스팅 Pangolin 노드 구성 파일에 추가하십시오.",
|
||||||
|
"submitButton": "노드 생성"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "기존 노드를 채택하려면 노드 ID와 비밀 키가 필요합니다"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "기본값 로드 실패",
|
||||||
|
"defaultsNotLoaded": "기본값 로드되지 않음",
|
||||||
|
"createFailed": "노드 생성 실패"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "노드가 성공적으로 생성되었습니다"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "노드 선택",
|
||||||
|
"remoteExitNodeSelectionDescription": "이 로컬 사이트에서 트래픽을 라우팅할 노드를 선택하세요",
|
||||||
|
"remoteExitNodeRequired": "로컬 사이트에 노드를 선택해야 합니다",
|
||||||
|
"noRemoteExitNodesAvailable": "사용 가능한 노드가 없습니다",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "이 조직에 사용 가능한 노드가 없습니다. 로컬 사이트를 사용하려면 먼저 노드를 생성하세요.",
|
||||||
|
"exitNode": "종단 노드",
|
||||||
|
"country": "국가",
|
||||||
|
"rulesMatchCountry": "현재 소스 IP를 기반으로 합니다",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "관리 자체 호스팅",
|
||||||
|
"description": "더 신뢰할 수 있고 낮은 유지보수의 자체 호스팅 팡골린 서버, 추가 기능 포함",
|
||||||
|
"introTitle": "관리 자체 호스팅 팡골린",
|
||||||
|
"introDescription": "는 자신의 데이터를 프라이빗하고 자체 호스팅을 유지하면서 더 간단하고 추가적인 신뢰성을 원하는 사람들을 위한 배포 옵션입니다.",
|
||||||
|
"introDetail": "이 옵션을 사용하면 여전히 자신의 팡골린 노드를 운영하고 - 터널, SSL 종료 및 트래픽 모두 서버에 유지됩니다. 차이점은 관리 및 모니터링이 클라우드 대시보드를 통해 처리되어 여러 혜택을 제공합니다.",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "더 간단한 운영",
|
||||||
|
"description": "자체 메일 서버를 운영하거나 복잡한 경고를 설정할 필요가 없습니다. 기본적으로 상태 점검 및 다운타임 경고를 받을 수 있습니다."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "자동 업데이트",
|
||||||
|
"description": "클라우드 대시보드는 빠르게 발전하므로 새로운 기능과 버그 수정 사항을 수동으로 새로운 컨테이너를 가져오지 않고도 받을 수 있습니다."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "유지보수 감소",
|
||||||
|
"description": "데이터베이스 마이그레이션, 백업 또는 추가 인프라를 관리할 필요가 없습니다. 저희가 클라우드에서 처리합니다."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "클라우드 장애 조치",
|
||||||
|
"description": "노드가 다운되면 터널이 클라우드의 프레즌스 포인트로 임시 전환되어 노드를 다시 온라인으로 가져올 때까지 유지됩니다."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "고가용성 (PoPs)",
|
||||||
|
"description": "계정에 여러 노드를 연결하여 이중성과 성능을 향상시킬 수 있습니다."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "향후 개선",
|
||||||
|
"description": "배포를 더욱 견고하게 만들기 위해 더 많은 분석, 경고, 및 관리 도구를 추가할 계획입니다."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "관리 자체 호스팅 옵션에 대해 더 알아보세요",
|
||||||
|
"documentation": "문서"
|
||||||
|
},
|
||||||
|
"convertButton": "이 노드를 관리 자체 호스팅으로 변환"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "국제 도메인 감지됨",
|
||||||
|
"willbestoredas": "다음으로 저장됩니다:",
|
||||||
|
"roleMappingDescription": "자동 프로비저닝이 활성화되면 사용자가 로그인할 때 역할이 할당되는 방법을 결정합니다.",
|
||||||
|
"selectRole": "역할 선택",
|
||||||
|
"roleMappingExpression": "표현식",
|
||||||
|
"selectRolePlaceholder": "역할 선택",
|
||||||
|
"selectRoleDescription": "이 신원 공급자로부터 모든 사용자에게 할당할 역할을 선택하십시오.",
|
||||||
|
"roleMappingExpressionDescription": "ID 토큰에서 역할 정보를 추출하기 위한 JMESPath 표현식을 입력하세요.",
|
||||||
|
"idpTenantIdRequired": "테넌트 ID가 필요합니다",
|
||||||
|
"invalidValue": "잘못된 값",
|
||||||
|
"idpTypeLabel": "신원 공급자 유형",
|
||||||
|
"roleMappingExpressionPlaceholder": "예: contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||||
|
"idpGoogleConfiguration": "Google 구성",
|
||||||
|
"idpGoogleConfigurationDescription": "Google OAuth2 자격 증명을 구성합니다.",
|
||||||
|
"idpGoogleClientIdDescription": "Google OAuth2 클라이언트 ID",
|
||||||
|
"idpGoogleClientSecretDescription": "Google OAuth2 클라이언트 비밀",
|
||||||
|
"idpAzureConfiguration": "Azure Entra ID 구성",
|
||||||
|
"idpAzureConfigurationDescription": "Azure Entra ID OAuth2 자격 증명을 구성합니다.",
|
||||||
|
"idpTenantId": "테넌트 ID",
|
||||||
|
"idpTenantIdPlaceholder": "your-tenant-id",
|
||||||
|
"idpAzureTenantIdDescription": "Azure 액티브 디렉터리 개요에서 찾은 Azure 테넌트 ID",
|
||||||
|
"idpAzureClientIdDescription": "Azure 앱 등록 클라이언트 ID",
|
||||||
|
"idpAzureClientSecretDescription": "Azure 앱 등록 클라이언트 비밀",
|
||||||
|
"idpGoogleTitle": "구글",
|
||||||
|
"idpGoogleAlt": "구글",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "애저",
|
||||||
|
"idpGoogleConfigurationTitle": "Google 구성",
|
||||||
|
"idpAzureConfigurationTitle": "Azure Entra ID 구성",
|
||||||
|
"idpTenantIdLabel": "테넌트 ID",
|
||||||
|
"idpAzureClientIdDescription2": "Azure 앱 등록 클라이언트 ID",
|
||||||
|
"idpAzureClientSecretDescription2": "Azure 앱 등록 클라이언트 비밀",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC 공급자",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC 공급자",
|
||||||
|
"subnet": "서브넷",
|
||||||
|
"subnetDescription": "이 조직의 네트워크 구성에 대한 서브넷입니다.",
|
||||||
|
"authPage": "인증 페이지",
|
||||||
|
"authPageDescription": "조직에 대한 인증 페이지를 구성합니다.",
|
||||||
|
"authPageDomain": "인증 페이지 도메인",
|
||||||
|
"noDomainSet": "도메인 설정 없음",
|
||||||
|
"changeDomain": "도메인 변경",
|
||||||
|
"selectDomain": "도메인 선택",
|
||||||
|
"restartCertificate": "인증서 재시작",
|
||||||
|
"editAuthPageDomain": "인증 페이지 도메인 편집",
|
||||||
|
"setAuthPageDomain": "인증 페이지 도메인 설정",
|
||||||
|
"failedToFetchCertificate": "인증서 가져오기 실패",
|
||||||
|
"failedToRestartCertificate": "인증서 재시작 실패",
|
||||||
|
"addDomainToEnableCustomAuthPages": "조직의 맞춤 인증 페이지를 활성화하려면 도메인을 추가하세요.",
|
||||||
|
"selectDomainForOrgAuthPage": "조직 인증 페이지에 대한 도메인을 선택하세요.",
|
||||||
|
"domainPickerProvidedDomain": "제공된 도메인",
|
||||||
|
"domainPickerFreeProvidedDomain": "무료 제공된 도메인",
|
||||||
|
"domainPickerVerified": "검증됨",
|
||||||
|
"domainPickerUnverified": "검증되지 않음",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "이 하위 도메인은 잘못된 문자 또는 구조를 포함하고 있습니다. 저장 시 자동으로 정리됩니다.",
|
||||||
|
"domainPickerError": "오류",
|
||||||
|
"domainPickerErrorLoadDomains": "조직 도메인 로드 실패",
|
||||||
|
"domainPickerErrorCheckAvailability": "도메인 가용성 확인 실패",
|
||||||
|
"domainPickerInvalidSubdomain": "잘못된 하위 도메인",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "입력 \"{sub}\"이(가) 유효하지 않으므로 제거되었습니다.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\"을(를) {domain}에 대해 유효하게 만들 수 없습니다.",
|
||||||
|
"domainPickerSubdomainSanitized": "하위 도메인 정리됨",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\"이(가) \"{sanitized}\"로 수정되었습니다",
|
||||||
|
"orgAuthSignInTitle": "조직에 로그인",
|
||||||
|
"orgAuthChooseIdpDescription": "계속하려면 신원 공급자를 선택하세요.",
|
||||||
|
"orgAuthNoIdpConfigured": "이 조직은 구성된 신원 공급자가 없습니다. 대신 Pangolin 아이덴티티로 로그인할 수 있습니다.",
|
||||||
|
"orgAuthSignInWithPangolin": "Pangolin으로 로그인",
|
||||||
|
"subscriptionRequiredToUse": "이 기능을 사용하려면 구독이 필요합니다.",
|
||||||
|
"idpDisabled": "신원 공급자가 비활성화되었습니다.",
|
||||||
|
"orgAuthPageDisabled": "조직 인증 페이지가 비활성화되었습니다.",
|
||||||
|
"domainRestartedDescription": "도메인 인증이 성공적으로 재시작되었습니다.",
|
||||||
|
"resourceAddEntrypointsEditFile": "파일 편집: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "파일 편집: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "이메일 인증이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.",
|
||||||
|
"twoFactorSetupRequired": "이중 인증 설정이 필요합니다. 이 단계를 완료하려면 {dashboardUrl}/auth/login 통해 다시 로그인하십시오. 그런 다음 여기로 돌아오세요.",
|
||||||
|
"authPageErrorUpdateMessage": "인증 페이지 설정을 업데이트하는 동안 오류가 발생했습니다",
|
||||||
|
"authPageUpdated": "인증 페이지가 성공적으로 업데이트되었습니다",
|
||||||
|
"healthCheckNotAvailable": "로컬",
|
||||||
|
"rewritePath": "경로 재작성",
|
||||||
|
"rewritePathDescription": "대상으로 전달하기 전에 경로를 선택적으로 재작성합니다."
|
||||||
}
|
}
|
||||||
|
|||||||
1727
messages/nb-NO.json
Normal file
1727
messages/nb-NO.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,14 @@
|
|||||||
"setupNewOrg": "Nieuwe organisatie",
|
"setupNewOrg": "Nieuwe organisatie",
|
||||||
"setupCreateOrg": "Nieuwe organisatie aanmaken",
|
"setupCreateOrg": "Nieuwe organisatie aanmaken",
|
||||||
"setupCreateResources": "Bronnen aanmaken",
|
"setupCreateResources": "Bronnen aanmaken",
|
||||||
"setupOrgName": "Naam organisatie",
|
"setupOrgName": "Naam van de organisatie",
|
||||||
"orgDisplayName": "Dit is de weergavenaam van uw organisatie.",
|
"orgDisplayName": "Dit is de weergavenaam van uw organisatie.",
|
||||||
"orgId": "Organisatie ID",
|
"orgId": "Organisatie ID",
|
||||||
"setupIdentifierMessage": "Dit is de unieke identificatie voor uw organisatie. Deze is gescheiden van de weergavenaam.",
|
"setupIdentifierMessage": "Dit is de unieke identificatie voor uw organisatie. Deze is gescheiden van de weergavenaam.",
|
||||||
"setupErrorIdentifier": "Organisatie-ID is al in gebruik. Kies een andere.",
|
"setupErrorIdentifier": "Organisatie-ID is al in gebruik. Kies een andere.",
|
||||||
"componentsErrorNoMemberCreate": "U bent momenteel geen lid van een organisatie. Maak een organisatie aan om aan de slag te gaan.",
|
"componentsErrorNoMemberCreate": "U bent momenteel geen lid van een organisatie. Maak een organisatie aan om aan de slag te gaan.",
|
||||||
"componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.",
|
"componentsErrorNoMember": "U bent momenteel geen lid van een organisatie.",
|
||||||
"welcome": "Welkom bij Pangolin",
|
"welcome": "Welkom bij Pangolin!",
|
||||||
"welcomeTo": "Welkom bij",
|
"welcomeTo": "Welkom bij",
|
||||||
"componentsCreateOrg": "Maak een Organisatie",
|
"componentsCreateOrg": "Maak een Organisatie",
|
||||||
"componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} one {één organisatie} other {# organisaties}}.",
|
"componentsMember": "Je bent lid van {count, plural, =0 {geen organisatie} one {één organisatie} other {# organisaties}}.",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"inviteErrorUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor deze gebruiker.",
|
"inviteErrorUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor deze gebruiker.",
|
||||||
"inviteLoginUser": "Controleer of je bent aangemeld als de juiste gebruiker.",
|
"inviteLoginUser": "Controleer of je bent aangemeld als de juiste gebruiker.",
|
||||||
"inviteErrorNoUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor een bestaande gebruiker.",
|
"inviteErrorNoUser": "Het spijt ons, maar de uitnodiging die u probeert te gebruiken is niet voor een bestaande gebruiker.",
|
||||||
"inviteCreateUser": "U moet eerst een account aanmaken",
|
"inviteCreateUser": "U moet eerst een account aanmaken.",
|
||||||
"goHome": "Ga naar huis",
|
"goHome": "Ga naar huis",
|
||||||
"inviteLogInOtherUser": "Log in als een andere gebruiker",
|
"inviteLogInOtherUser": "Log in als een andere gebruiker",
|
||||||
"createAnAccount": "Account aanmaken",
|
"createAnAccount": "Account aanmaken",
|
||||||
@@ -35,15 +35,15 @@
|
|||||||
"createAccount": "Account Aanmaken",
|
"createAccount": "Account Aanmaken",
|
||||||
"viewSettings": "Instellingen weergeven",
|
"viewSettings": "Instellingen weergeven",
|
||||||
"delete": "Verwijderen",
|
"delete": "Verwijderen",
|
||||||
"name": "naam",
|
"name": "Naam",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
"offline": "Offline",
|
"offline": "Offline",
|
||||||
"site": "Website",
|
"site": "Referentie",
|
||||||
"dataIn": "Gegevens in",
|
"dataIn": "Dataverbruik inkomend",
|
||||||
"dataOut": "Data Uit",
|
"dataOut": "Dataverbruik uitgaand",
|
||||||
"connectionType": "Type verbinding",
|
"connectionType": "Type verbinding",
|
||||||
"tunnelType": "Tunnel type",
|
"tunnelType": "Tunnel type",
|
||||||
"local": "lokaal",
|
"local": "Lokaal",
|
||||||
"edit": "Bewerken",
|
"edit": "Bewerken",
|
||||||
"siteConfirmDelete": "Verwijderen van site bevestigen",
|
"siteConfirmDelete": "Verwijderen van site bevestigen",
|
||||||
"siteDelete": "Site verwijderen",
|
"siteDelete": "Site verwijderen",
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"siteCreate": "Site maken",
|
"siteCreate": "Site maken",
|
||||||
"siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden",
|
"siteCreateDescription2": "Volg de onderstaande stappen om een nieuwe site aan te maken en te verbinden",
|
||||||
"siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen",
|
"siteCreateDescription": "Maak een nieuwe site aan om verbinding te maken met uw bronnen",
|
||||||
"close": "Afsluiten",
|
"close": "Sluiten",
|
||||||
"siteErrorCreate": "Fout bij maken site",
|
"siteErrorCreate": "Fout bij maken site",
|
||||||
"siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden",
|
"siteErrorCreateKeyPair": "Key pair of site standaard niet gevonden",
|
||||||
"siteErrorCreateDefaults": "Standaardinstellingen niet gevonden",
|
"siteErrorCreateDefaults": "Standaardinstellingen niet gevonden",
|
||||||
@@ -90,19 +90,21 @@
|
|||||||
"siteGeneralDescription": "Algemene instellingen voor deze site configureren",
|
"siteGeneralDescription": "Algemene instellingen voor deze site configureren",
|
||||||
"siteSettingDescription": "Configureer de instellingen op uw site",
|
"siteSettingDescription": "Configureer de instellingen op uw site",
|
||||||
"siteSetting": "{siteName} instellingen",
|
"siteSetting": "{siteName} instellingen",
|
||||||
"siteNewtTunnel": "Nieuwstunnel (Aanbevolen)",
|
"siteNewtTunnel": "Newttunnel (Aanbevolen)",
|
||||||
"siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.",
|
"siteNewtTunnelDescription": "Gemakkelijkste manier om een ingangspunt in uw netwerk te maken. Geen extra opzet.",
|
||||||
"siteWg": "Basis WireGuard",
|
"siteWg": "Basis WireGuard",
|
||||||
"siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.",
|
"siteWgDescription": "Gebruik een WireGuard client om een tunnel te bouwen. Handmatige NAT installatie vereist.",
|
||||||
|
"siteWgDescriptionSaas": "Gebruik elke WireGuard-client om een tunnel op te zetten. Handmatige NAT-instelling vereist. WERKT ALLEEN OP SELF HOSTED NODES",
|
||||||
"siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.",
|
"siteLocalDescription": "Alleen lokale bronnen. Geen tunneling.",
|
||||||
"siteSeeAll": "Alle werkruimtes bekijken",
|
"siteLocalDescriptionSaas": "Alleen lokale bronnen. Geen tunneling. WERKT ALLEEN OP SELF HOSTED NODES",
|
||||||
|
"siteSeeAll": "Alle sites bekijken",
|
||||||
"siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site",
|
"siteTunnelDescription": "Bepaal hoe u verbinding wilt maken met uw site",
|
||||||
"siteNewtCredentials": "Nieuwste aanmeldgegevens",
|
"siteNewtCredentials": "Nieuwste aanmeldgegevens",
|
||||||
"siteNewtCredentialsDescription": "Dit is hoe Newt zich zal verifiëren met de server",
|
"siteNewtCredentialsDescription": "Dit is hoe Newt zich zal verifiëren met de server",
|
||||||
"siteCredentialsSave": "Uw referenties opslaan",
|
"siteCredentialsSave": "Uw referenties opslaan",
|
||||||
"siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.",
|
"siteCredentialsSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.",
|
||||||
"siteInfo": "Site informatie",
|
"siteInfo": "Site informatie",
|
||||||
"status": "status",
|
"status": "Status",
|
||||||
"shareTitle": "Beheer deellinks",
|
"shareTitle": "Beheer deellinks",
|
||||||
"shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen",
|
"shareDescription": "Maak deelbare links aan om tijdelijke of permanente toegang tot uw bronnen te verlenen",
|
||||||
"shareSearch": "Zoek share links...",
|
"shareSearch": "Zoek share links...",
|
||||||
@@ -144,19 +146,19 @@
|
|||||||
"never": "Nooit",
|
"never": "Nooit",
|
||||||
"shareErrorSelectResource": "Selecteer een bron",
|
"shareErrorSelectResource": "Selecteer een bron",
|
||||||
"resourceTitle": "Bronnen beheren",
|
"resourceTitle": "Bronnen beheren",
|
||||||
"resourceDescription": "Veilige proxy's voor uw privé applicaties maken",
|
"resourceDescription": "Veilige proxy's voor uw privéapplicaties maken",
|
||||||
"resourcesSearch": "Zoek bronnen...",
|
"resourcesSearch": "Zoek bronnen...",
|
||||||
"resourceAdd": "Bron toevoegen",
|
"resourceAdd": "Bron toevoegen",
|
||||||
"resourceErrorDelte": "Fout bij verwijderen document",
|
"resourceErrorDelte": "Fout bij verwijderen document",
|
||||||
"authentication": "Authenticatie",
|
"authentication": "Authenticatie",
|
||||||
"protected": "Beschermd",
|
"protected": "Beschermd",
|
||||||
"notProtected": "Niet beschermd",
|
"notProtected": "Niet beveiligd",
|
||||||
"resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.",
|
"resourceMessageRemove": "Eenmaal verwijderd, zal het bestand niet langer toegankelijk zijn. Alle doelen die gekoppeld zijn aan het hulpbron, zullen ook verwijderd worden.",
|
||||||
"resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.",
|
"resourceMessageConfirm": "Om te bevestigen, typ de naam van de bron hieronder.",
|
||||||
"resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?",
|
"resourceQuestionRemove": "Weet u zeker dat u de resource {selectedResource} uit de organisatie wilt verwijderen?",
|
||||||
"resourceHTTP": "HTTPS bron",
|
"resourceHTTP": "HTTPS bron",
|
||||||
"resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.",
|
"resourceHTTPDescription": "Proxy verzoeken aan uw app via HTTPS via een subdomein of basisdomein.",
|
||||||
"resourceRaw": "Ruwe TCP/UDP bron",
|
"resourceRaw": "TCP/UDP bron",
|
||||||
"resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.",
|
"resourceRawDescription": "Proxy verzoeken naar je app via TCP/UDP met behulp van een poortnummer.",
|
||||||
"resourceCreate": "Bron maken",
|
"resourceCreate": "Bron maken",
|
||||||
"resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken",
|
"resourceCreateDescription": "Volg de onderstaande stappen om een nieuwe bron te maken",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Selecteer site",
|
"siteSelect": "Selecteer site",
|
||||||
"siteSearch": "Zoek site",
|
"siteSearch": "Zoek site",
|
||||||
"siteNotFound": "Geen site gevonden.",
|
"siteNotFound": "Geen site gevonden.",
|
||||||
"siteSelectionDescription": "Deze site zal connectiviteit met de bron geven.",
|
"selectCountry": "Selecteer land",
|
||||||
|
"searchCountries": "Zoek landen...",
|
||||||
|
"noCountryFound": "Geen land gevonden.",
|
||||||
|
"siteSelectionDescription": "Deze site zal connectiviteit met het doelwit bieden.",
|
||||||
"resourceType": "Type bron",
|
"resourceType": "Type bron",
|
||||||
"resourceTypeDescription": "Bepaal hoe u toegang wilt krijgen tot uw bron",
|
"resourceTypeDescription": "Bepaal hoe u toegang wilt krijgen tot uw bron",
|
||||||
"resourceHTTPSSettings": "HTTPS instellingen",
|
"resourceHTTPSSettings": "HTTPS instellingen",
|
||||||
@@ -181,7 +186,7 @@
|
|||||||
"protocolSelect": "Selecteer een protocol",
|
"protocolSelect": "Selecteer een protocol",
|
||||||
"resourcePortNumber": "Nummer van poort",
|
"resourcePortNumber": "Nummer van poort",
|
||||||
"resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.",
|
"resourcePortNumberDescription": "Het externe poortnummer naar proxyverzoeken.",
|
||||||
"cancel": "annuleren",
|
"cancel": "Annuleren",
|
||||||
"resourceConfig": "Configuratie tekstbouwstenen",
|
"resourceConfig": "Configuratie tekstbouwstenen",
|
||||||
"resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen",
|
"resourceConfigDescription": "Kopieer en plak deze configuratie-snippets om je TCP/UDP-bron in te stellen",
|
||||||
"resourceAddEntrypoints": "Traefik: Entrypoints toevoegen",
|
"resourceAddEntrypoints": "Traefik: Entrypoints toevoegen",
|
||||||
@@ -197,18 +202,20 @@
|
|||||||
"general": "Algemeen",
|
"general": "Algemeen",
|
||||||
"generalSettings": "Algemene instellingen",
|
"generalSettings": "Algemene instellingen",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
|
"internal": "Intern",
|
||||||
"rules": "Regels",
|
"rules": "Regels",
|
||||||
"resourceSettingDescription": "Configureer de instellingen op uw bron",
|
"resourceSettingDescription": "Configureer de instellingen op uw bron",
|
||||||
"resourceSetting": "{resourceName} instellingen",
|
"resourceSetting": "{resourceName} instellingen",
|
||||||
"alwaysAllow": "Altijd toestaan",
|
"alwaysAllow": "Altijd toestaan",
|
||||||
"alwaysDeny": "Altijd weigeren",
|
"alwaysDeny": "Altijd weigeren",
|
||||||
|
"passToAuth": "Passeren naar Auth",
|
||||||
"orgSettingsDescription": "Configureer de algemene instellingen van je organisatie",
|
"orgSettingsDescription": "Configureer de algemene instellingen van je organisatie",
|
||||||
"orgGeneralSettings": "Organisatie Instellingen",
|
"orgGeneralSettings": "Organisatie Instellingen",
|
||||||
"orgGeneralSettingsDescription": "Beheer de details en configuratie van uw organisatie",
|
"orgGeneralSettingsDescription": "Beheer de details en configuratie van uw organisatie",
|
||||||
"saveGeneralSettings": "Algemene instellingen opslaan",
|
"saveGeneralSettings": "Algemene instellingen opslaan",
|
||||||
"saveSettings": "Instellingen opslaan",
|
"saveSettings": "Instellingen opslaan",
|
||||||
"orgDangerZone": "Gevaarlijke zone",
|
"orgDangerZone": "Gevaarlijke zone",
|
||||||
"orgDangerZoneDescription": "Als u deze instantie verwijdert, is er geen weg terug. Wees het alstublieft zeker.",
|
"orgDangerZoneDescription": "Deze instantie verwijderen is onomkeerbaar. Bevestig alstublieft dat u wilt doorgaan.",
|
||||||
"orgDelete": "Verwijder organisatie",
|
"orgDelete": "Verwijder organisatie",
|
||||||
"orgDeleteConfirm": "Bevestig Verwijderen Organisatie",
|
"orgDeleteConfirm": "Bevestig Verwijderen Organisatie",
|
||||||
"orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.",
|
"orgMessageRemove": "Deze actie is onomkeerbaar en zal alle bijbehorende gegevens verwijderen.",
|
||||||
@@ -261,7 +268,7 @@
|
|||||||
"apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen",
|
"apiKeysGeneralSettingsDescription": "Bepaal wat deze API-sleutel kan doen",
|
||||||
"apiKeysList": "Uw API-sleutel",
|
"apiKeysList": "Uw API-sleutel",
|
||||||
"apiKeysSave": "Uw API-sleutel opslaan",
|
"apiKeysSave": "Uw API-sleutel opslaan",
|
||||||
"apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een beveiligde plek.",
|
"apiKeysSaveDescription": "Je kunt dit slechts één keer zien. Kopieer het naar een veilige plek.",
|
||||||
"apiKeysInfo": "Uw API-sleutel is:",
|
"apiKeysInfo": "Uw API-sleutel is:",
|
||||||
"apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd",
|
"apiKeysConfirmCopy": "Ik heb de API-sleutel gekopieerd",
|
||||||
"generate": "Genereren",
|
"generate": "Genereren",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Er is een fout opgetreden tijdens het toevoegen van de rol.",
|
"accessRoleErrorAddDescription": "Er is een fout opgetreden tijdens het toevoegen van de rol.",
|
||||||
"userSaved": "Gebruiker opgeslagen",
|
"userSaved": "Gebruiker opgeslagen",
|
||||||
"userSavedDescription": "De gebruiker is bijgewerkt.",
|
"userSavedDescription": "De gebruiker is bijgewerkt.",
|
||||||
|
"autoProvisioned": "Automatisch bevestigen",
|
||||||
|
"autoProvisionedDescription": "Toestaan dat deze gebruiker automatisch wordt beheerd door een identiteitsprovider",
|
||||||
"accessControlsDescription": "Beheer wat deze gebruiker toegang heeft tot en doet in de organisatie",
|
"accessControlsDescription": "Beheer wat deze gebruiker toegang heeft tot en doet in de organisatie",
|
||||||
"accessControlsSubmit": "Bewaar Toegangsbesturing",
|
"accessControlsSubmit": "Bewaar Toegangsbesturing",
|
||||||
"roles": "Rollen",
|
"roles": "Rollen",
|
||||||
@@ -490,12 +499,12 @@
|
|||||||
"targetTlsSniDescription": "De TLS servernaam om te gebruiken voor SNI. Laat leeg om de standaard te gebruiken.",
|
"targetTlsSniDescription": "De TLS servernaam om te gebruiken voor SNI. Laat leeg om de standaard te gebruiken.",
|
||||||
"targetTlsSubmit": "Instellingen opslaan",
|
"targetTlsSubmit": "Instellingen opslaan",
|
||||||
"targets": "Doelstellingen configuratie",
|
"targets": "Doelstellingen configuratie",
|
||||||
"targetsDescription": "Stel doelen in om verkeer naar uw diensten te leiden",
|
"targetsDescription": "Stel doelen in om verkeer naar uw backend-services te leiden",
|
||||||
"targetStickySessions": "Sticky sessies inschakelen",
|
"targetStickySessions": "Sticky sessies inschakelen",
|
||||||
"targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.",
|
"targetStickySessionsDescription": "Behoud verbindingen op hetzelfde backend doel voor hun hele sessie.",
|
||||||
"methodSelect": "Selecteer methode",
|
"methodSelect": "Selecteer methode",
|
||||||
"targetSubmit": "Doelwit toevoegen",
|
"targetSubmit": "Doelwit toevoegen",
|
||||||
"targetNoOne": "Geen doelwitten. Voeg een doel toe via het formulier.",
|
"targetNoOne": "Geen doel toegevoegd. Voeg deze toe via dit formulier.",
|
||||||
"targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.",
|
"targetNoOneDescription": "Het toevoegen van meer dan één doel hierboven zal de load balancering mogelijk maken.",
|
||||||
"targetsSubmit": "Doelstellingen opslaan",
|
"targetsSubmit": "Doelstellingen opslaan",
|
||||||
"proxyAdditional": "Extra Proxy-instellingen",
|
"proxyAdditional": "Extra Proxy-instellingen",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Ongeldig IP-adresformaat",
|
"ipAddressErrorInvalidFormat": "Ongeldig IP-adresformaat",
|
||||||
"ipAddressErrorInvalidOctet": "Ongeldige IP adres octet",
|
"ipAddressErrorInvalidOctet": "Ongeldige IP adres octet",
|
||||||
"path": "Pad",
|
"path": "Pad",
|
||||||
|
"matchPath": "Overeenkomend pad",
|
||||||
"ipAddressRange": "IP Bereik",
|
"ipAddressRange": "IP Bereik",
|
||||||
"rulesErrorFetch": "Regels ophalen mislukt",
|
"rulesErrorFetch": "Regels ophalen mislukt",
|
||||||
"rulesErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de regels",
|
"rulesErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de regels",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "acties",
|
"rulesActions": "acties",
|
||||||
"rulesActionAlwaysAllow": "Altijd toegestaan: Omzeil alle authenticatiemethoden",
|
"rulesActionAlwaysAllow": "Altijd toegestaan: Omzeil alle authenticatiemethoden",
|
||||||
"rulesActionAlwaysDeny": "Altijd weigeren: Blokkeer alle aanvragen, er kan geen verificatie worden geprobeerd",
|
"rulesActionAlwaysDeny": "Altijd weigeren: Blokkeer alle aanvragen, er kan geen verificatie worden geprobeerd",
|
||||||
|
"rulesActionPassToAuth": "Doorgeven aan Auth: Toestaan dat authenticatiemethoden worden geprobeerd",
|
||||||
"rulesMatchCriteria": "Overeenkomende criteria",
|
"rulesMatchCriteria": "Overeenkomende criteria",
|
||||||
"rulesMatchCriteriaIpAddress": "Overeenkomen met een specifiek IP-adres",
|
"rulesMatchCriteriaIpAddress": "Overeenkomen met een specifiek IP-adres",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Overeenkomen met een bereik van IP-adressen in de CIDR-notatie",
|
"rulesMatchCriteriaIpAddressRange": "Overeenkomen met een bereik van IP-adressen in de CIDR-notatie",
|
||||||
@@ -564,7 +575,7 @@
|
|||||||
"domainsErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen",
|
"domainsErrorFetchDescription": "Er is een fout opgetreden bij het ophalen van de domeinen",
|
||||||
"none": "geen",
|
"none": "geen",
|
||||||
"unknown": "onbekend",
|
"unknown": "onbekend",
|
||||||
"resources": "Hulpmiddelen",
|
"resources": "Bronnen",
|
||||||
"resourcesDescription": "Bronnen zijn proxies voor applicaties die op uw privénetwerk worden uitgevoerd. Maak een bron aan voor elke HTTP/HTTPS of onbewerkte TCP/UDP-service op uw privénetwerk. Elke bron moet verbonden zijn met een site om private, beveiligde verbinding mogelijk te maken via een versleutelde WireGuard tunnel.",
|
"resourcesDescription": "Bronnen zijn proxies voor applicaties die op uw privénetwerk worden uitgevoerd. Maak een bron aan voor elke HTTP/HTTPS of onbewerkte TCP/UDP-service op uw privénetwerk. Elke bron moet verbonden zijn met een site om private, beveiligde verbinding mogelijk te maken via een versleutelde WireGuard tunnel.",
|
||||||
"resourcesWireGuardConnect": "Beveiligde verbinding met WireGuard versleuteling",
|
"resourcesWireGuardConnect": "Beveiligde verbinding met WireGuard versleuteling",
|
||||||
"resourcesMultipleAuthenticationMethods": "Meerdere verificatiemethoden configureren",
|
"resourcesMultipleAuthenticationMethods": "Meerdere verificatiemethoden configureren",
|
||||||
@@ -590,7 +601,7 @@
|
|||||||
"newtId": "Newt-ID",
|
"newtId": "Newt-ID",
|
||||||
"newtSecretKey": "Nieuwe geheime sleutel",
|
"newtSecretKey": "Nieuwe geheime sleutel",
|
||||||
"architecture": "Architectuur",
|
"architecture": "Architectuur",
|
||||||
"sites": "Werkruimtes",
|
"sites": "Sites",
|
||||||
"siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.",
|
"siteWgAnyClients": "Gebruik een willekeurige WireGuard client om verbinding te maken. Je moet je interne bronnen aanspreken met behulp van de peer IP.",
|
||||||
"siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients",
|
"siteWgCompatibleAllClients": "Compatibel met alle WireGuard clients",
|
||||||
"siteWgManualConfigurationRequired": "Handmatige configuratie vereist",
|
"siteWgManualConfigurationRequired": "Handmatige configuratie vereist",
|
||||||
@@ -719,22 +730,22 @@
|
|||||||
"idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider {name} permanent wilt verwijderen?",
|
"idpQuestionRemove": "Weet u zeker dat u de identiteitsprovider {name} permanent wilt verwijderen?",
|
||||||
"idpMessageRemove": "Dit zal de identiteitsprovider en alle bijbehorende configuraties verwijderen. Gebruikers die via deze provider authenticeren, kunnen niet langer inloggen.",
|
"idpMessageRemove": "Dit zal de identiteitsprovider en alle bijbehorende configuraties verwijderen. Gebruikers die via deze provider authenticeren, kunnen niet langer inloggen.",
|
||||||
"idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.",
|
"idpMessageConfirm": "Om dit te bevestigen, typt u de naam van onderstaande identiteitsprovider.",
|
||||||
"idpConfirmDelete": "Bevestig verwijderen Identity Provider",
|
"idpConfirmDelete": "Bevestig verwijderen identiteit provider",
|
||||||
"idpDelete": "Identity Provider verwijderen",
|
"idpDelete": "Identiteit provider verwijderen",
|
||||||
"idp": "Identiteit aanbieders",
|
"idp": "Identiteitsproviders",
|
||||||
"idpSearch": "Identiteitsaanbieders zoeken...",
|
"idpSearch": "Identiteitsproviders zoeken...",
|
||||||
"idpAdd": "Identity Provider toevoegen",
|
"idpAdd": "Identiteit provider toevoegen",
|
||||||
"idpClientIdRequired": "Client-ID is vereist.",
|
"idpClientIdRequired": "Client ID is vereist.",
|
||||||
"idpClientSecretRequired": "Clientgeheim is vereist.",
|
"idpClientSecretRequired": "Client geheim is vereist.",
|
||||||
"idpErrorAuthUrlInvalid": "Authenticatie-URL moet een geldige URL zijn.",
|
"idpErrorAuthUrlInvalid": "Authenticatie URL moet een geldige URL zijn.",
|
||||||
"idpErrorTokenUrlInvalid": "Token-URL moet een geldige URL zijn.",
|
"idpErrorTokenUrlInvalid": "Token URL moet een geldige URL zijn.",
|
||||||
"idpPathRequired": "ID-pad is vereist.",
|
"idpPathRequired": "ID-pad is vereist.",
|
||||||
"idpScopeRequired": "Toepassingsgebieden zijn vereist.",
|
"idpScopeRequired": "Toepassingsgebieden zijn vereist.",
|
||||||
"idpOidcDescription": "Een OpenID Connect identity provider configureren",
|
"idpOidcDescription": "Een OpenID Connect identiteitsprovider configureren",
|
||||||
"idpCreatedDescription": "Identity provider succesvol aangemaakt",
|
"idpCreatedDescription": "Identiteitsprovider succesvol aangemaakt",
|
||||||
"idpCreate": "Identity Provider aanmaken",
|
"idpCreate": "Identiteitsprovider aanmaken",
|
||||||
"idpCreateDescription": "Een nieuwe identiteitsprovider voor gebruikersauthenticatie configureren",
|
"idpCreateDescription": "Een nieuwe identiteitsprovider voor authenticatie configureren",
|
||||||
"idpSeeAll": "Zie alle identiteitsaanbieders",
|
"idpSeeAll": "Zie alle Identiteitsproviders",
|
||||||
"idpSettingsDescription": "Configureer de basisinformatie voor uw identiteitsprovider",
|
"idpSettingsDescription": "Configureer de basisinformatie voor uw identiteitsprovider",
|
||||||
"idpDisplayName": "Een weergavenaam voor deze identiteitsprovider",
|
"idpDisplayName": "Een weergavenaam voor deze identiteitsprovider",
|
||||||
"idpAutoProvisionUsers": "Auto Provisie Gebruikers",
|
"idpAutoProvisionUsers": "Auto Provisie Gebruikers",
|
||||||
@@ -744,10 +755,10 @@
|
|||||||
"idpTypeDescription": "Selecteer het type identiteitsprovider dat u wilt configureren",
|
"idpTypeDescription": "Selecteer het type identiteitsprovider dat u wilt configureren",
|
||||||
"idpOidcConfigure": "OAuth2/OIDC configuratie",
|
"idpOidcConfigure": "OAuth2/OIDC configuratie",
|
||||||
"idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties",
|
"idpOidcConfigureDescription": "Configureer de eindpunten van de OAuth2/OIDC provider en referenties",
|
||||||
"idpClientId": "Klant ID",
|
"idpClientId": "Client ID",
|
||||||
"idpClientIdDescription": "De OAuth2-client-ID van uw identiteitsprovider",
|
"idpClientIdDescription": "De OAuth2 client ID van uw identiteitsprovider",
|
||||||
"idpClientSecret": "Clientgeheim",
|
"idpClientSecret": "Client Secret",
|
||||||
"idpClientSecretDescription": "Het OAuth2-clientgeheim van je identiteitsprovider",
|
"idpClientSecretDescription": "Het OAuth2 Client Secret van je identiteitsprovider",
|
||||||
"idpAuthUrl": "URL autorisatie",
|
"idpAuthUrl": "URL autorisatie",
|
||||||
"idpAuthUrlDescription": "De URL voor autorisatie OAuth2",
|
"idpAuthUrlDescription": "De URL voor autorisatie OAuth2",
|
||||||
"idpTokenUrl": "URL token",
|
"idpTokenUrl": "URL token",
|
||||||
@@ -793,7 +804,7 @@
|
|||||||
"defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.",
|
"defaultMappingsOrgDescription": "Deze expressie moet de org-ID teruggeven of waar om de gebruiker toegang te geven tot de organisatie.",
|
||||||
"defaultMappingsSubmit": "Standaard toewijzingen opslaan",
|
"defaultMappingsSubmit": "Standaard toewijzingen opslaan",
|
||||||
"orgPoliciesEdit": "Organisatie beleid bewerken",
|
"orgPoliciesEdit": "Organisatie beleid bewerken",
|
||||||
"org": "Rekening",
|
"org": "Organisatie",
|
||||||
"orgSelect": "Selecteer organisatie",
|
"orgSelect": "Selecteer organisatie",
|
||||||
"orgSearch": "Zoek in org",
|
"orgSearch": "Zoek in org",
|
||||||
"orgNotFound": "Geen org gevonden.",
|
"orgNotFound": "Geen org gevonden.",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "Pincode moet precies 6 cijfers zijn",
|
"pincodeRequirementsLength": "Pincode moet precies 6 cijfers zijn",
|
||||||
"pincodeRequirementsChars": "Pincode mag alleen cijfers bevatten",
|
"pincodeRequirementsChars": "Pincode mag alleen cijfers bevatten",
|
||||||
"passwordRequirementsLength": "Wachtwoord moet ten minste 1 teken lang zijn",
|
"passwordRequirementsLength": "Wachtwoord moet ten minste 1 teken lang zijn",
|
||||||
|
"passwordRequirementsTitle": "Wachtwoordvereisten:",
|
||||||
|
"passwordRequirementLength": "Minstens 8 tekens lang",
|
||||||
|
"passwordRequirementUppercase": "Minstens één hoofdletter",
|
||||||
|
"passwordRequirementLowercase": "Minstens één kleine letter",
|
||||||
|
"passwordRequirementNumber": "Minstens één cijfer",
|
||||||
|
"passwordRequirementSpecial": "Minstens één speciaal teken",
|
||||||
|
"passwordRequirementsMet": "✓ Wachtwoord voldoet aan alle vereisten",
|
||||||
|
"passwordStrength": "Wachtwoord sterkte",
|
||||||
|
"passwordStrengthWeak": "Zwak",
|
||||||
|
"passwordStrengthMedium": "Gemiddeld",
|
||||||
|
"passwordStrengthStrong": "Sterk",
|
||||||
|
"passwordRequirements": "Vereisten:",
|
||||||
|
"passwordRequirementLengthText": "8+ tekens",
|
||||||
|
"passwordRequirementUppercaseText": "Hoofdletter (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Kleine letter (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Cijfer (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Speciaal teken (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Wachtwoorden komen niet overeen",
|
||||||
"otpEmailRequirementsLength": "OTP moet minstens 1 teken lang zijn",
|
"otpEmailRequirementsLength": "OTP moet minstens 1 teken lang zijn",
|
||||||
"otpEmailSent": "OTP verzonden",
|
"otpEmailSent": "OTP verzonden",
|
||||||
"otpEmailSentDescription": "Een OTP is naar uw e-mail verzonden",
|
"otpEmailSentDescription": "Een OTP is naar uw e-mail verzonden",
|
||||||
@@ -896,7 +925,7 @@
|
|||||||
"inviteErrorExpired": "De uitnodiging is mogelijk verlopen",
|
"inviteErrorExpired": "De uitnodiging is mogelijk verlopen",
|
||||||
"inviteErrorRevoked": "De uitnodiging is mogelijk ingetrokken",
|
"inviteErrorRevoked": "De uitnodiging is mogelijk ingetrokken",
|
||||||
"inviteErrorTypo": "Er kan een typefout zijn in de uitnodigingslink",
|
"inviteErrorTypo": "Er kan een typefout zijn in de uitnodigingslink",
|
||||||
"pangolinSetup": "Setup - Pangolin",
|
"pangolinSetup": "Instellen - Pangolin",
|
||||||
"orgNameRequired": "Organisatienaam is vereist",
|
"orgNameRequired": "Organisatienaam is vereist",
|
||||||
"orgIdRequired": "Organisatie-ID is vereist",
|
"orgIdRequired": "Organisatie-ID is vereist",
|
||||||
"orgErrorCreate": "Fout opgetreden tijdens het aanmaken org",
|
"orgErrorCreate": "Fout opgetreden tijdens het aanmaken org",
|
||||||
@@ -948,25 +977,32 @@
|
|||||||
"supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!",
|
"supportKeyEnterDescription": "Ontmoet je eigen huisdier Pangolin!",
|
||||||
"githubUsername": "GitHub-gebruikersnaam",
|
"githubUsername": "GitHub-gebruikersnaam",
|
||||||
"supportKeyInput": "Supporter Sleutel",
|
"supportKeyInput": "Supporter Sleutel",
|
||||||
"supportKeyBuy": "Koop Supportersleutel",
|
"supportKeyBuy": "Koop supportersleutel",
|
||||||
"logoutError": "Fout bij uitloggen",
|
"logoutError": "Fout bij uitloggen",
|
||||||
"signingAs": "Ingelogd als",
|
"signingAs": "Ingelogd als",
|
||||||
"serverAdmin": "Server Beheerder",
|
"serverAdmin": "Server beheer",
|
||||||
|
"managedSelfhosted": "Beheerde Self-Hosted",
|
||||||
"otpEnable": "Twee-factor inschakelen",
|
"otpEnable": "Twee-factor inschakelen",
|
||||||
"otpDisable": "Tweestapsverificatie uitschakelen",
|
"otpDisable": "Tweestapsverificatie uitschakelen",
|
||||||
"logout": "Log uit",
|
"logout": "Log uit",
|
||||||
"licenseTierProfessionalRequired": "Professionele editie vereist",
|
"licenseTierProfessionalRequired": "Professionele editie vereist",
|
||||||
"licenseTierProfessionalRequiredDescription": "Deze functie is alleen beschikbaar in de Professional Edition.",
|
"licenseTierProfessionalRequiredDescription": "Deze functie is alleen beschikbaar in de Professional Edition.",
|
||||||
"actionGetOrg": "Krijg Organisatie",
|
"actionGetOrg": "Krijg Organisatie",
|
||||||
|
"updateOrgUser": "Org gebruiker bijwerken",
|
||||||
|
"createOrgUser": "Org gebruiker aanmaken",
|
||||||
"actionUpdateOrg": "Organisatie bijwerken",
|
"actionUpdateOrg": "Organisatie bijwerken",
|
||||||
"actionUpdateUser": "Gebruiker bijwerken",
|
"actionUpdateUser": "Gebruiker bijwerken",
|
||||||
"actionGetUser": "Gebruiker ophalen",
|
"actionGetUser": "Gebruiker ophalen",
|
||||||
"actionGetOrgUser": "Krijg organisatie-gebruiker",
|
"actionGetOrgUser": "Krijg organisatie-gebruiker",
|
||||||
"actionListOrgDomains": "Lijst organisatie domeinen",
|
"actionListOrgDomains": "Lijst organisatie domeinen",
|
||||||
"actionCreateSite": "Site maken",
|
"actionCreateSite": "Site aanmaken",
|
||||||
"actionDeleteSite": "Site verwijderen",
|
"actionDeleteSite": "Site verwijderen",
|
||||||
"actionGetSite": "Site ophalen",
|
"actionGetSite": "Site ophalen",
|
||||||
"actionListSites": "Sites weergeven",
|
"actionListSites": "Sites weergeven",
|
||||||
|
"actionApplyBlueprint": "Blauwdruk toepassen",
|
||||||
|
"setupToken": "Instel Token",
|
||||||
|
"setupTokenDescription": "Voer het setup-token in vanaf de serverconsole.",
|
||||||
|
"setupTokenRequired": "Setup-token is vereist",
|
||||||
"actionUpdateSite": "Site bijwerken",
|
"actionUpdateSite": "Site bijwerken",
|
||||||
"actionListSiteRoles": "Toon toegestane sitenollen",
|
"actionListSiteRoles": "Toon toegestane sitenollen",
|
||||||
"actionCreateResource": "Bron maken",
|
"actionCreateResource": "Bron maken",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Verwijder IDP Org Beleid",
|
"actionDeleteIdpOrg": "Verwijder IDP Org Beleid",
|
||||||
"actionListIdpOrgs": "Toon IDP Orgs",
|
"actionListIdpOrgs": "Toon IDP Orgs",
|
||||||
"actionUpdateIdpOrg": "IDP-org bijwerken",
|
"actionUpdateIdpOrg": "IDP-org bijwerken",
|
||||||
|
"actionCreateClient": "Client aanmaken",
|
||||||
|
"actionDeleteClient": "Verwijder klant",
|
||||||
|
"actionUpdateClient": "Klant bijwerken",
|
||||||
|
"actionListClients": "Lijst klanten",
|
||||||
|
"actionGetClient": "Client ophalen",
|
||||||
|
"actionCreateSiteResource": "Sitebron maken",
|
||||||
|
"actionDeleteSiteResource": "Document verwijderen van site",
|
||||||
|
"actionGetSiteResource": "Bron van site ophalen",
|
||||||
|
"actionListSiteResources": "Bronnen van site weergeven",
|
||||||
|
"actionUpdateSiteResource": "Document bijwerken van site",
|
||||||
|
"actionListInvitations": "Toon uitnodigingen",
|
||||||
"noneSelected": "Niet geselecteerd",
|
"noneSelected": "Niet geselecteerd",
|
||||||
"orgNotFound2": "Geen organisaties gevonden.",
|
"orgNotFound2": "Geen organisaties gevonden.",
|
||||||
"searchProgress": "Zoeken...",
|
"searchProgress": "Zoeken...",
|
||||||
@@ -1082,7 +1129,7 @@
|
|||||||
"sidebarOverview": "Overzicht.",
|
"sidebarOverview": "Overzicht.",
|
||||||
"sidebarHome": "Startpagina",
|
"sidebarHome": "Startpagina",
|
||||||
"sidebarSites": "Werkruimtes",
|
"sidebarSites": "Werkruimtes",
|
||||||
"sidebarResources": "Hulpmiddelen",
|
"sidebarResources": "Bronnen",
|
||||||
"sidebarAccessControl": "Toegangs controle",
|
"sidebarAccessControl": "Toegangs controle",
|
||||||
"sidebarUsers": "Gebruikers",
|
"sidebarUsers": "Gebruikers",
|
||||||
"sidebarInvitations": "Uitnodigingen",
|
"sidebarInvitations": "Uitnodigingen",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "Licentie",
|
"sidebarLicense": "Licentie",
|
||||||
"sidebarClients": "Clients (Bèta)",
|
"sidebarClients": "Clients (Bèta)",
|
||||||
"sidebarDomains": "Domeinen",
|
"sidebarDomains": "Domeinen",
|
||||||
"enableDockerSocket": "Docker Socket inschakelen",
|
"enableDockerSocket": "Schakel Docker Blauwdruk in",
|
||||||
"enableDockerSocketDescription": "Docker Socket-ontdekking inschakelen voor het invullen van containerinformatie. Socket-pad moet aan Newt worden verstrekt.",
|
"enableDockerSocketDescription": "Schakel Docker Socket label in voor blauwdruk labels. Pad naar Nieuw.",
|
||||||
"enableDockerSocketLink": "Meer informatie",
|
"enableDockerSocketLink": "Meer informatie",
|
||||||
"viewDockerContainers": "Bekijk Docker containers",
|
"viewDockerContainers": "Bekijk Docker containers",
|
||||||
"containersIn": "Containers in {siteName}",
|
"containersIn": "Containers in {siteName}",
|
||||||
@@ -1196,7 +1243,7 @@
|
|||||||
"newtUpdateAvailable": "Update beschikbaar",
|
"newtUpdateAvailable": "Update beschikbaar",
|
||||||
"newtUpdateAvailableInfo": "Er is een nieuwe versie van Newt beschikbaar. Update naar de nieuwste versie voor de beste ervaring.",
|
"newtUpdateAvailableInfo": "Er is een nieuwe versie van Newt beschikbaar. Update naar de nieuwste versie voor de beste ervaring.",
|
||||||
"domainPickerEnterDomain": "Domein",
|
"domainPickerEnterDomain": "Domein",
|
||||||
"domainPickerPlaceholder": "mijnapp.voorbeeld.com, api.v1.mijndomein.com, of gewoon mijnapp",
|
"domainPickerPlaceholder": "mijnapp.voorbeeld.nl",
|
||||||
"domainPickerDescription": "Voer de volledige domein van de bron in om beschikbare opties te zien.",
|
"domainPickerDescription": "Voer de volledige domein van de bron in om beschikbare opties te zien.",
|
||||||
"domainPickerDescriptionSaas": "Voer een volledig domein, subdomein of gewoon een naam in om beschikbare opties te zien",
|
"domainPickerDescriptionSaas": "Voer een volledig domein, subdomein of gewoon een naam in om beschikbare opties te zien",
|
||||||
"domainPickerTabAll": "Alles",
|
"domainPickerTabAll": "Alles",
|
||||||
@@ -1209,8 +1256,50 @@
|
|||||||
"domainPickerOrganizationDomains": "Organisatiedomeinen",
|
"domainPickerOrganizationDomains": "Organisatiedomeinen",
|
||||||
"domainPickerProvidedDomains": "Aangeboden domeinen",
|
"domainPickerProvidedDomains": "Aangeboden domeinen",
|
||||||
"domainPickerSubdomain": "Subdomein: {subdomain}",
|
"domainPickerSubdomain": "Subdomein: {subdomain}",
|
||||||
"domainPickerNamespace": "Namespace: {namespace}",
|
"domainPickerNamespace": "Naamruimte: {namespace}",
|
||||||
"domainPickerShowMore": "Meer weergeven",
|
"domainPickerShowMore": "Meer weergeven",
|
||||||
|
"regionSelectorTitle": "Selecteer Regio",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Abonnement laden...",
|
||||||
|
"billingFreeTier": "Gratis Niveau",
|
||||||
|
"billingWarningOverLimit": "Waarschuwing: U hebt een of meer gebruikslimieten overschreden. Uw sites maken geen verbinding totdat u uw abonnement aanpast of uw gebruik aanpast.",
|
||||||
|
"billingUsageLimitsOverview": "Overzicht gebruikslimieten",
|
||||||
|
"billingMonitorUsage": "Houd uw gebruik in de gaten ten opzichte van de ingestelde limieten. Als u verhoogde limieten nodig heeft, neem dan contact met ons op support@fossorial.io.",
|
||||||
|
"billingDataUsage": "Gegevensgebruik",
|
||||||
|
"billingOnlineTime": "Site Online Tijd",
|
||||||
|
"billingUsers": "Actieve Gebruikers",
|
||||||
|
"billingDomains": "Actieve Domeinen",
|
||||||
|
"billingRemoteExitNodes": "Actieve Zelfgehoste Nodes",
|
||||||
|
"billingNoLimitConfigured": "Geen limiet ingesteld",
|
||||||
|
"billingEstimatedPeriod": "Geschatte Facturatie Periode",
|
||||||
|
"billingIncludedUsage": "Opgenomen Gebruik",
|
||||||
|
"billingIncludedUsageDescription": "Gebruik inbegrepen in uw huidige abonnementsplan",
|
||||||
|
"billingFreeTierIncludedUsage": "Gratis niveau gebruikstoelagen",
|
||||||
|
"billingIncluded": "inbegrepen",
|
||||||
|
"billingEstimatedTotal": "Geschat Totaal:",
|
||||||
|
"billingNotes": "Notities",
|
||||||
|
"billingEstimateNote": "Dit is een schatting gebaseerd op uw huidige gebruik.",
|
||||||
|
"billingActualChargesMayVary": "Facturering kan variëren.",
|
||||||
|
"billingBilledAtEnd": "U wordt aan het einde van de factureringsperiode gefactureerd.",
|
||||||
|
"billingModifySubscription": "Abonnementsaanpassing",
|
||||||
|
"billingStartSubscription": "Abonnement Starten",
|
||||||
|
"billingRecurringCharge": "Terugkerende Kosten",
|
||||||
|
"billingManageSubscriptionSettings": "Beheer uw abonnementsinstellingen en voorkeuren",
|
||||||
|
"billingNoActiveSubscription": "U heeft geen actief abonnement. Start uw abonnement om gebruikslimieten te verhogen.",
|
||||||
|
"billingFailedToLoadSubscription": "Fout bij laden van abonnement",
|
||||||
|
"billingFailedToLoadUsage": "Niet gelukt om gebruik te laden",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Niet gelukt om checkout URL te krijgen",
|
||||||
|
"billingPleaseTryAgainLater": "Probeer het later opnieuw.",
|
||||||
|
"billingCheckoutError": "Checkout Fout",
|
||||||
|
"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 wordt gefactureerd voor elke gebruiker in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve gebruikersaccounts in uw organisatie.",
|
||||||
|
"billingDomainInfo": "U wordt gefactureerd voor elk domein in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve domeinaccounts in uw organisatie.",
|
||||||
|
"billingRemoteExitNodesInfo": "U wordt gefactureerd voor elke beheerde Node in uw organisatie. Facturering wordt dagelijks berekend op basis van het aantal actieve beheerde Nodes in uw organisatie.",
|
||||||
"domainNotFound": "Domein niet gevonden",
|
"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.",
|
"domainNotFoundDescription": "Deze bron is uitgeschakeld omdat het domein niet langer in ons systeem bestaat. Stel een nieuw domein in voor deze bron.",
|
||||||
"failed": "Mislukt",
|
"failed": "Mislukt",
|
||||||
@@ -1244,7 +1333,7 @@
|
|||||||
"twoFactorRequired": "Tweestapsverificatie is vereist om een beveiligingssleutel te registreren.",
|
"twoFactorRequired": "Tweestapsverificatie is vereist om een beveiligingssleutel te registreren.",
|
||||||
"twoFactor": "Tweestapsverificatie",
|
"twoFactor": "Tweestapsverificatie",
|
||||||
"adminEnabled2FaOnYourAccount": "Je beheerder heeft tweestapsverificatie voor {email} ingeschakeld. Voltooi het instellingsproces om verder te gaan.",
|
"adminEnabled2FaOnYourAccount": "Je beheerder heeft tweestapsverificatie voor {email} ingeschakeld. Voltooi het instellingsproces om verder te gaan.",
|
||||||
"continueToApplication": "Doorgaan naar de applicatie",
|
"continueToApplication": "Doorgaan naar applicatie",
|
||||||
"securityKeyAdd": "Beveiligingssleutel toevoegen",
|
"securityKeyAdd": "Beveiligingssleutel toevoegen",
|
||||||
"securityKeyRegisterTitle": "Nieuwe beveiligingssleutel registreren",
|
"securityKeyRegisterTitle": "Nieuwe beveiligingssleutel registreren",
|
||||||
"securityKeyRegisterDescription": "Verbind je beveiligingssleutel en voer een naam in om deze te identificeren",
|
"securityKeyRegisterDescription": "Verbind je beveiligingssleutel en voer een naam in om deze te identificeren",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "DNS-wijzigingen kunnen enige tijd duren om over het internet te worden verspreid. Dit kan enkele minuten tot 48 uur duren, afhankelijk van je DNS-provider en TTL-instellingen.",
|
"createDomainDnsPropagationDescription": "DNS-wijzigingen kunnen enige tijd duren om over het internet te worden verspreid. Dit kan enkele minuten tot 48 uur duren, afhankelijk van je DNS-provider en TTL-instellingen.",
|
||||||
"resourcePortRequired": "Poortnummer is vereist voor niet-HTTP-bronnen",
|
"resourcePortRequired": "Poortnummer is vereist voor niet-HTTP-bronnen",
|
||||||
"resourcePortNotAllowed": "Poortnummer mag niet worden ingesteld voor HTTP-bronnen",
|
"resourcePortNotAllowed": "Poortnummer mag niet worden ingesteld voor HTTP-bronnen",
|
||||||
|
"billingPricingCalculatorLink": "Prijs Calculator",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Ik ga akkoord met de",
|
"IAgreeToThe": "Ik ga akkoord met de",
|
||||||
"termsOfService": "servicevoorwaarden",
|
"termsOfService": "servicevoorwaarden",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "Er is een fout opgetreden bij het ophalen van de nieuwste Olm release.",
|
"olmErrorFetchLatest": "Er is een fout opgetreden bij het ophalen van de nieuwste Olm release.",
|
||||||
"remoteSubnets": "Externe Subnets",
|
"remoteSubnets": "Externe Subnets",
|
||||||
"enterCidrRange": "Voer CIDR-bereik in",
|
"enterCidrRange": "Voer CIDR-bereik in",
|
||||||
"remoteSubnetsDescription": "Voeg CIDR-bereiken toe die deze site op afstand kunnen openen. Gebruik een format zoals 10.0.0.0/24 of 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Voeg CIDR-bereiken toe die vanaf deze site op afstand toegankelijk zijn met behulp van clients. Gebruik een formaat zoals 10.0.0.0/24. Dit geldt ALLEEN voor VPN-clientconnectiviteit.",
|
||||||
"resourceEnableProxy": "Openbare proxy inschakelen",
|
"resourceEnableProxy": "Openbare proxy inschakelen",
|
||||||
"resourceEnableProxyDescription": "Schakel publieke proxy in voor deze resource. Dit maakt toegang tot de resource mogelijk vanuit het netwerk via de cloud met een open poort. Vereist Traefik-configuratie.",
|
"resourceEnableProxyDescription": "Schakel publieke proxy in voor deze resource. Dit maakt toegang tot de resource mogelijk vanuit het netwerk via de cloud met een open poort. Vereist Traefik-configuratie.",
|
||||||
"externalProxyEnabled": "Externe Proxy Ingeschakeld"
|
"externalProxyEnabled": "Externe Proxy Ingeschakeld",
|
||||||
|
"addNewTarget": "Voeg nieuw doelwit toe",
|
||||||
|
"targetsList": "Lijst met doelen",
|
||||||
|
"targetErrorDuplicateTargetFound": "Dubbel doelwit gevonden",
|
||||||
|
"healthCheckHealthy": "Gezond",
|
||||||
|
"healthCheckUnhealthy": "Ongezond",
|
||||||
|
"healthCheckUnknown": "Onbekend",
|
||||||
|
"healthCheck": "Gezondheidscontrole",
|
||||||
|
"configureHealthCheck": "Configureer Gezondheidscontrole",
|
||||||
|
"configureHealthCheckDescription": "Stel gezondheid monitor voor {target} in",
|
||||||
|
"enableHealthChecks": "Inschakelen Gezondheidscontroles",
|
||||||
|
"enableHealthChecksDescription": "Controleer de gezondheid van dit doel. U kunt een ander eindpunt monitoren dan het doel indien vereist.",
|
||||||
|
"healthScheme": "Methode",
|
||||||
|
"healthSelectScheme": "Selecteer methode",
|
||||||
|
"healthCheckPath": "Pad",
|
||||||
|
"healthHostname": "IP / Hostnaam",
|
||||||
|
"healthPort": "Poort",
|
||||||
|
"healthCheckPathDescription": "Het pad om de gezondheid status te controleren.",
|
||||||
|
"healthyIntervalSeconds": "Gezonde Interval",
|
||||||
|
"unhealthyIntervalSeconds": "Ongezonde Interval",
|
||||||
|
"IntervalSeconds": "Gezonde Interval",
|
||||||
|
"timeoutSeconds": "Timeout",
|
||||||
|
"timeIsInSeconds": "Tijd is in seconden",
|
||||||
|
"retryAttempts": "Herhaal Pogingen",
|
||||||
|
"expectedResponseCodes": "Verwachte Reactiecodes",
|
||||||
|
"expectedResponseCodesDescription": "HTTP-statuscode die gezonde status aangeeft. Indien leeg wordt 200-300 als gezond beschouwd.",
|
||||||
|
"customHeaders": "Aangepaste headers",
|
||||||
|
"customHeadersDescription": "Kopregeleinde: Header-Naam: waarde",
|
||||||
|
"headersValidationError": "Headers moeten in het formaat zijn: Header-Naam: waarde.",
|
||||||
|
"saveHealthCheck": "Opslaan Gezondheidscontrole",
|
||||||
|
"healthCheckSaved": "Gezondheidscontrole Opgeslagen",
|
||||||
|
"healthCheckSavedDescription": "Gezondheidscontrole configuratie succesvol opgeslagen",
|
||||||
|
"healthCheckError": "Gezondheidscontrole Fout",
|
||||||
|
"healthCheckErrorDescription": "Er is een fout opgetreden bij het opslaan van de configuratie van de gezondheidscontrole.",
|
||||||
|
"healthCheckPathRequired": "Gezondheidscontrole pad is vereist",
|
||||||
|
"healthCheckMethodRequired": "HTTP methode is vereist",
|
||||||
|
"healthCheckIntervalMin": "Controle interval moet minimaal 5 seconden zijn",
|
||||||
|
"healthCheckTimeoutMin": "Timeout moet minimaal 1 seconde zijn",
|
||||||
|
"healthCheckRetryMin": "Herhaal pogingen moet minimaal 1 zijn",
|
||||||
|
"httpMethod": "HTTP-methode",
|
||||||
|
"selectHttpMethod": "Selecteer HTTP-methode",
|
||||||
|
"domainPickerSubdomainLabel": "Subdomein",
|
||||||
|
"domainPickerBaseDomainLabel": "Basisdomein",
|
||||||
|
"domainPickerSearchDomains": "Zoek domeinen...",
|
||||||
|
"domainPickerNoDomainsFound": "Geen domeinen gevonden",
|
||||||
|
"domainPickerLoadingDomains": "Domeinen laden...",
|
||||||
|
"domainPickerSelectBaseDomain": "Selecteer basisdomein...",
|
||||||
|
"domainPickerNotAvailableForCname": "Niet beschikbaar voor CNAME-domeinen",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Voer een subdomein in of laat leeg om basisdomein te gebruiken.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Voer een subdomein in om te zoeken en te selecteren uit beschikbare gratis domeinen.",
|
||||||
|
"domainPickerFreeDomains": "Gratis Domeinen",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Zoek naar beschikbare domeinen",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Opmerking: Gratis aangeboden domeinen zijn momenteel niet beschikbaar voor zelf-gehoste instanties.",
|
||||||
|
"resourceDomain": "Domein",
|
||||||
|
"resourceEditDomain": "Domein bewerken",
|
||||||
|
"siteName": "Site Naam",
|
||||||
|
"proxyPort": "Poort",
|
||||||
|
"resourcesTableProxyResources": "Proxybronnen",
|
||||||
|
"resourcesTableClientResources": "Clientbronnen",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Geen proxybronnen gevonden.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Geen interne bronnen gevonden.",
|
||||||
|
"resourcesTableDestination": "Bestemming",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Deze bronnen zijn bedoeld voor gebruik met",
|
||||||
|
"resourcesTableClients": "Clienten",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "en zijn alleen intern toegankelijk wanneer verbonden met een client.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Bewerk clientbron",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Werk de eigenschapen van de bron en doelconfiguratie bij voor {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Bron eigenschappen",
|
||||||
|
"editInternalResourceDialogName": "Naam",
|
||||||
|
"editInternalResourceDialogProtocol": "Protocol",
|
||||||
|
"editInternalResourceDialogSitePort": "Site Poort",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Doelconfiguratie",
|
||||||
|
"editInternalResourceDialogCancel": "Annuleren",
|
||||||
|
"editInternalResourceDialogSaveResource": "Sla bron op",
|
||||||
|
"editInternalResourceDialogSuccess": "Succes",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Interne bron succesvol bijgewerkt",
|
||||||
|
"editInternalResourceDialogError": "Fout",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Het bijwerken van de interne bron is mislukt",
|
||||||
|
"editInternalResourceDialogNameRequired": "Naam is verplicht",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Naam mag niet langer zijn dan 255 tekens",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Proxy poort moet minstens 1 zijn",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Proxy poort moet minder dan 65536 zijn",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Ongeldig IP-adresformaat",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Bestemmingspoort moet minstens 1 zijn",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Bestemmingspoort moet minder dan 65536 zijn",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Geen sites beschikbaar",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "U moet ten minste één Newt-site hebben met een geconfigureerd subnet om interne bronnen aan te maken.",
|
||||||
|
"createInternalResourceDialogClose": "Sluiten",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Maak clientbron",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Maak een nieuwe bron die toegankelijk zal zijn voor clients die verbonden zijn met de geselecteerde site.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Bron-eigenschappen",
|
||||||
|
"createInternalResourceDialogName": "Naam",
|
||||||
|
"createInternalResourceDialogSite": "Site",
|
||||||
|
"createInternalResourceDialogSelectSite": "Selecteer site...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Zoek sites...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Geen sites gevonden.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protocol",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Site Poort",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Gebruik deze poort om toegang te krijgen tot de bron op de site wanneer verbonden met een client.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Doelconfiguratie",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "Het IP of hostnaam adres van de bron op het netwerk van de site.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "De poort op het bestemmings-IP waar de bron toegankelijk is.",
|
||||||
|
"createInternalResourceDialogCancel": "Annuleren",
|
||||||
|
"createInternalResourceDialogCreateResource": "Bron aanmaken",
|
||||||
|
"createInternalResourceDialogSuccess": "Succes",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Interne bron succesvol aangemaakt",
|
||||||
|
"createInternalResourceDialogError": "Fout",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Het aanmaken van de interne bron is mislukt",
|
||||||
|
"createInternalResourceDialogNameRequired": "Naam is verplicht",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Naam mag niet langer zijn dan 255 tekens",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Selecteer alstublieft een site",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Proxy poort moet minstens 1 zijn",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Proxy poort moet minder dan 65536 zijn",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Ongeldig IP-adresformaat",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Bestemmingspoort moet minstens 1 zijn",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Bestemmingspoort moet minder dan 65536 zijn",
|
||||||
|
"siteConfiguration": "Configuratie",
|
||||||
|
"siteAcceptClientConnections": "Accepteer clientverbindingen",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Sta toe dat andere apparaten verbinding maken via deze Newt-instantie als een gateway met behulp van clients.",
|
||||||
|
"siteAddress": "Siteadres",
|
||||||
|
"siteAddressDescription": "Specificeren het IP-adres van de host voor clients om verbinding mee te maken. Dit is het interne adres van de site in het Pangolin netwerk voor clients om te adresseren. Moet binnen het Organisatienetwerk vallen.",
|
||||||
|
"autoLoginExternalIdp": "Auto Login met Externe IDP",
|
||||||
|
"autoLoginExternalIdpDescription": "De gebruiker onmiddellijk doorsturen naar de externe IDP voor authenticatie.",
|
||||||
|
"selectIdp": "Selecteer IDP",
|
||||||
|
"selectIdpPlaceholder": "Kies een IDP...",
|
||||||
|
"selectIdpRequired": "Selecteer alstublieft een IDP wanneer automatisch inloggen is ingeschakeld.",
|
||||||
|
"autoLoginTitle": "Omleiden",
|
||||||
|
"autoLoginDescription": "Je wordt doorverwezen naar de externe identity provider voor authenticatie.",
|
||||||
|
"autoLoginProcessing": "Authenticatie voorbereiden...",
|
||||||
|
"autoLoginRedirecting": "Redirecting naar inloggen...",
|
||||||
|
"autoLoginError": "Auto Login Fout",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "Geen redirect URL ontvangen van de identity provider.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Genereren van authenticatie-URL mislukt.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Beheer Zelf-Gehoste",
|
||||||
|
"remoteExitNodeDescription": "Beheer knooppunten om uw netwerkverbinding uit te breiden",
|
||||||
|
"remoteExitNodes": "Nodes",
|
||||||
|
"searchRemoteExitNodes": "Knooppunten zoeken...",
|
||||||
|
"remoteExitNodeAdd": "Voeg node toe",
|
||||||
|
"remoteExitNodeErrorDelete": "Fout bij verwijderen node",
|
||||||
|
"remoteExitNodeQuestionRemove": "Weet u zeker dat u het node {selectedNode} uit de organisatie wilt verwijderen?",
|
||||||
|
"remoteExitNodeMessageRemove": "Eenmaal verwijderd, zal het knooppunt niet langer toegankelijk zijn.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Om te bevestigen, typ de naam van het knooppunt hieronder.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Bevestig verwijderen node",
|
||||||
|
"remoteExitNodeDelete": "Knoop verwijderen",
|
||||||
|
"sidebarRemoteExitNodes": "Nodes",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Maak node",
|
||||||
|
"description": "Maak een nieuwe node aan om uw netwerkverbinding uit te breiden",
|
||||||
|
"viewAllButton": "Alle nodes weergeven",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Creatie Strategie",
|
||||||
|
"description": "Kies dit om uw node handmatig te configureren of nieuwe referenties te genereren.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adopteer Node",
|
||||||
|
"description": "Kies dit als u al de referenties voor deze node heeft"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Genereer Sleutels",
|
||||||
|
"description": "Kies dit als u nieuwe sleutels voor het knooppunt wilt genereren"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Adopteer Bestaande Node",
|
||||||
|
"description": "Voer de referenties in van het bestaande knooppunt dat u wilt adopteren",
|
||||||
|
"nodeIdLabel": "Knooppunt ID",
|
||||||
|
"nodeIdDescription": "De ID van het knooppunt dat u wilt adopteren",
|
||||||
|
"secretLabel": "Geheim",
|
||||||
|
"secretDescription": "De geheime sleutel van de bestaande node",
|
||||||
|
"submitButton": "Knooppunt adopteren"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Gegeneerde Inloggegevens",
|
||||||
|
"description": "Gebruik deze gegenereerde inloggegevens om uw node te configureren",
|
||||||
|
"nodeIdTitle": "Knooppunt ID",
|
||||||
|
"secretTitle": "Geheim",
|
||||||
|
"saveCredentialsTitle": "Voeg Inloggegevens toe aan Config",
|
||||||
|
"saveCredentialsDescription": "Voeg deze inloggegevens toe aan uw zelf-gehoste Pangolin-node configuratiebestand om de verbinding te voltooien.",
|
||||||
|
"submitButton": "Maak node"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "Node ID en Secret zijn verplicht bij het overnemen van een bestaand knooppunt"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Niet gelukt om standaarden te laden",
|
||||||
|
"defaultsNotLoaded": "Standaarden niet geladen",
|
||||||
|
"createFailed": "Fout bij het maken van node"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Node succesvol aangemaakt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Knooppunt selectie",
|
||||||
|
"remoteExitNodeSelectionDescription": "Selecteer een node om het verkeer door te leiden voor deze lokale site",
|
||||||
|
"remoteExitNodeRequired": "Een node moet worden geselecteerd voor lokale sites",
|
||||||
|
"noRemoteExitNodesAvailable": "Geen knooppunten beschikbaar",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Er zijn geen knooppunten beschikbaar voor deze organisatie. Maak eerst een knooppunt aan om lokale sites te gebruiken.",
|
||||||
|
"exitNode": "Exit Node",
|
||||||
|
"country": "Land",
|
||||||
|
"rulesMatchCountry": "Momenteel gebaseerd op bron IP",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Beheerde Self-Hosted",
|
||||||
|
"description": "betrouwbaardere en slecht onderhouden Pangolin server met extra klokken en klokkenluiders",
|
||||||
|
"introTitle": "Beheerde zelfgehoste pangolin",
|
||||||
|
"introDescription": "is een implementatieoptie ontworpen voor mensen die eenvoud en extra betrouwbaarheid willen, terwijl hun gegevens privé en zelf georganiseerd blijven.",
|
||||||
|
"introDetail": "Met deze optie beheert u nog steeds uw eigen Pangolin node - uw tunnels, SSL-verbinding en verkeer alles op uw server. Het verschil is dat beheer en monitoring worden behandeld via onze cloud dashboard, wat een aantal voordelen oplevert:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Simpler operaties",
|
||||||
|
"description": "Je hoeft geen eigen mailserver te draaien of complexe waarschuwingen in te stellen. Je krijgt gezondheidscontroles en downtime meldingen uit de box."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Automatische updates",
|
||||||
|
"description": "Het cloud dashboard evolueert snel, zodat u nieuwe functies en bug fixes krijgt zonder elke keer handmatig nieuwe containers te moeten trekken."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Minder onderhoud",
|
||||||
|
"description": "Geen database migratie, back-ups of extra infrastructuur om te beheren. Dat behandelen we in de cloud."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Cloud fout",
|
||||||
|
"description": "Als uw node omlaag gaat, kunnen uw tunnels tijdelijk niet meer naar onze aanwezigheidspunten gaan totdat u hem weer online brengt."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Hoge beschikbaarheid (PoPs)",
|
||||||
|
"description": "U kunt ook meerdere nodes koppelen aan uw account voor ontslag en betere prestaties."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Toekomstige verbeteringen",
|
||||||
|
"description": "We zijn van plan om meer analytica, waarschuwing en beheerhulpmiddelen toe te voegen om uw implementatie nog steviger te maken."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Meer informatie over de optie voor zelf-verzorging in onze",
|
||||||
|
"documentation": "documentatie"
|
||||||
|
},
|
||||||
|
"convertButton": "Converteer deze node naar Beheerde Zelf-Hosted"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Internationaal Domein Gedetecteerd",
|
||||||
|
"willbestoredas": "Zal worden opgeslagen als:",
|
||||||
|
"roleMappingDescription": "Bepaal hoe rollen worden toegewezen aan gebruikers wanneer ze inloggen wanneer Auto Provision is ingeschakeld.",
|
||||||
|
"selectRole": "Selecteer een rol",
|
||||||
|
"roleMappingExpression": "Expressie",
|
||||||
|
"selectRolePlaceholder": "Kies een rol",
|
||||||
|
"selectRoleDescription": "Selecteer een rol om toe te wijzen aan alle gebruikers van deze identiteitsprovider",
|
||||||
|
"roleMappingExpressionDescription": "Voer een JMESPath expressie in om rolinformatie van de ID-token te extraheren",
|
||||||
|
"idpTenantIdRequired": "Tenant ID is vereist",
|
||||||
|
"invalidValue": "Ongeldige waarde",
|
||||||
|
"idpTypeLabel": "Identiteit provider type",
|
||||||
|
"roleMappingExpressionPlaceholder": "bijvoorbeeld bevat (groepen, 'admin') && 'Admin' ½ 'Member'",
|
||||||
|
"idpGoogleConfiguration": "Google Configuratie",
|
||||||
|
"idpGoogleConfigurationDescription": "Configureer uw Google OAuth2-referenties",
|
||||||
|
"idpGoogleClientIdDescription": "Uw Google OAuth2-client-ID",
|
||||||
|
"idpGoogleClientSecretDescription": "Uw Google OAuth2 Clientgeheim",
|
||||||
|
"idpAzureConfiguration": "Azure Entra ID configuratie",
|
||||||
|
"idpAzureConfigurationDescription": "Configureer uw Azure Entra ID OAuth2 referenties",
|
||||||
|
"idpTenantId": "Tenant-ID",
|
||||||
|
"idpTenantIdPlaceholder": "jouw-tenant-id",
|
||||||
|
"idpAzureTenantIdDescription": "Uw Azure tenant ID (gevonden in Azure Active Directory overzicht)",
|
||||||
|
"idpAzureClientIdDescription": "Uw Azure App registratie Client ID",
|
||||||
|
"idpAzureClientSecretDescription": "Uw Azure App registratie Client Secret",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Google Configuratie",
|
||||||
|
"idpAzureConfigurationTitle": "Azure Entra ID configuratie",
|
||||||
|
"idpTenantIdLabel": "Tenant-ID",
|
||||||
|
"idpAzureClientIdDescription2": "Uw Azure App registratie Client ID",
|
||||||
|
"idpAzureClientSecretDescription2": "Uw Azure App registratie Client Secret",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC provider",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Subnet",
|
||||||
|
"subnetDescription": "Het subnet van de netwerkconfiguratie van deze organisatie.",
|
||||||
|
"authPage": "Authenticatie pagina",
|
||||||
|
"authPageDescription": "De autorisatiepagina voor uw organisatie configureren",
|
||||||
|
"authPageDomain": "Authenticatie pagina domein",
|
||||||
|
"noDomainSet": "Geen domein ingesteld",
|
||||||
|
"changeDomain": "Domein wijzigen",
|
||||||
|
"selectDomain": "Domein selecteren",
|
||||||
|
"restartCertificate": "Certificaat opnieuw starten",
|
||||||
|
"editAuthPageDomain": "Authenticatiepagina domein bewerken",
|
||||||
|
"setAuthPageDomain": "Authenticatiepagina domein instellen",
|
||||||
|
"failedToFetchCertificate": "Certificaat ophalen mislukt",
|
||||||
|
"failedToRestartCertificate": "Kon certificaat niet opnieuw opstarten",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Voeg een domein toe om aangepaste authenticatiepagina's voor uw organisatie in te schakelen",
|
||||||
|
"selectDomainForOrgAuthPage": "Selecteer een domein voor de authenticatiepagina van de organisatie",
|
||||||
|
"domainPickerProvidedDomain": "Opgegeven domein",
|
||||||
|
"domainPickerFreeProvidedDomain": "Gratis verstrekt domein",
|
||||||
|
"domainPickerVerified": "Geverifieerd",
|
||||||
|
"domainPickerUnverified": "Ongeverifieerd",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Dit subdomein bevat ongeldige tekens of structuur. Het zal automatisch worden gesaneerd wanneer u opslaat.",
|
||||||
|
"domainPickerError": "Foutmelding",
|
||||||
|
"domainPickerErrorLoadDomains": "Fout bij het laden van organisatiedomeinen",
|
||||||
|
"domainPickerErrorCheckAvailability": "Kan domein beschikbaarheid niet controleren",
|
||||||
|
"domainPickerInvalidSubdomain": "Ongeldig subdomein",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "De invoer \"{sub}\" is verwijderd omdat het niet geldig is.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" kon niet geldig worden gemaakt voor {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Subdomein gesaniseerd",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" was gecorrigeerd op \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Meld je aan bij je organisatie",
|
||||||
|
"orgAuthChooseIdpDescription": "Kies uw identiteitsprovider om door te gaan",
|
||||||
|
"orgAuthNoIdpConfigured": "Deze organisatie heeft geen identiteitsproviders geconfigureerd. Je kunt in plaats daarvan inloggen met je Pangolin-identiteit.",
|
||||||
|
"orgAuthSignInWithPangolin": "Log in met Pangolin",
|
||||||
|
"subscriptionRequiredToUse": "Een abonnement is vereist om deze functie te gebruiken.",
|
||||||
|
"idpDisabled": "Identiteitsaanbieders zijn uitgeschakeld.",
|
||||||
|
"orgAuthPageDisabled": "Pagina voor organisatie-authenticatie is uitgeschakeld.",
|
||||||
|
"domainRestartedDescription": "Domeinverificatie met succes opnieuw gestart",
|
||||||
|
"resourceAddEntrypointsEditFile": "Bestand bewerken: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Bestand bewerken: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "E-mail verificatie is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.",
|
||||||
|
"twoFactorSetupRequired": "Tweestapsverificatie instellen is vereist. Log opnieuw in via {dashboardUrl}/auth/login voltooide deze stap. Kom daarna hier terug.",
|
||||||
|
"authPageErrorUpdateMessage": "Er is een fout opgetreden bij het bijwerken van de instellingen van de auth-pagina",
|
||||||
|
"authPageUpdated": "Auth-pagina succesvol bijgewerkt",
|
||||||
|
"healthCheckNotAvailable": "Lokaal",
|
||||||
|
"rewritePath": "Herschrijf Pad",
|
||||||
|
"rewritePathDescription": "Optioneel het pad herschrijven voordat je het naar het doel doorstuurt."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "Łatwiejszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.",
|
"siteNewtTunnelDescription": "Łatwiejszy sposób na stworzenie punktu wejścia w sieci. Nie ma dodatkowej konfiguracji.",
|
||||||
"siteWg": "Podstawowy WireGuard",
|
"siteWg": "Podstawowy WireGuard",
|
||||||
"siteWgDescription": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana jest ręczna konfiguracja NAT.",
|
"siteWgDescription": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana jest ręczna konfiguracja NAT.",
|
||||||
|
"siteWgDescriptionSaas": "Użyj dowolnego klienta WireGuard do utworzenia tunelu. Wymagana ręczna konfiguracja NAT. DZIAŁA TYLKO NA SAMODZIELNIE HOSTOWANYCH WĘZŁACH",
|
||||||
"siteLocalDescription": "Tylko lokalne zasoby. Brak tunelu.",
|
"siteLocalDescription": "Tylko lokalne zasoby. Brak tunelu.",
|
||||||
|
"siteLocalDescriptionSaas": "Tylko zasoby lokalne. Brak tunelowania. DZIAŁA TYLKO NA SAMODZIELNIE HOSTOWANYCH WĘZŁACH",
|
||||||
"siteSeeAll": "Zobacz wszystkie witryny",
|
"siteSeeAll": "Zobacz wszystkie witryny",
|
||||||
"siteTunnelDescription": "Określ jak chcesz połączyć się ze swoją stroną",
|
"siteTunnelDescription": "Określ jak chcesz połączyć się ze swoją stroną",
|
||||||
"siteNewtCredentials": "Aktualne dane logowania",
|
"siteNewtCredentials": "Aktualne dane logowania",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Wybierz witrynę",
|
"siteSelect": "Wybierz witrynę",
|
||||||
"siteSearch": "Szukaj witryny",
|
"siteSearch": "Szukaj witryny",
|
||||||
"siteNotFound": "Nie znaleziono witryny.",
|
"siteNotFound": "Nie znaleziono witryny.",
|
||||||
"siteSelectionDescription": "Ta strona zapewni połączenie z zasobem.",
|
"selectCountry": "Wybierz kraj",
|
||||||
|
"searchCountries": "Szukaj krajów...",
|
||||||
|
"noCountryFound": "Nie znaleziono kraju.",
|
||||||
|
"siteSelectionDescription": "Ta strona zapewni połączenie z celem.",
|
||||||
"resourceType": "Typ zasobu",
|
"resourceType": "Typ zasobu",
|
||||||
"resourceTypeDescription": "Określ jak chcesz uzyskać dostęp do swojego zasobu",
|
"resourceTypeDescription": "Określ jak chcesz uzyskać dostęp do swojego zasobu",
|
||||||
"resourceHTTPSSettings": "Ustawienia HTTPS",
|
"resourceHTTPSSettings": "Ustawienia HTTPS",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "Ogólny",
|
"general": "Ogólny",
|
||||||
"generalSettings": "Ustawienia ogólne",
|
"generalSettings": "Ustawienia ogólne",
|
||||||
"proxy": "Serwer pośredniczący",
|
"proxy": "Serwer pośredniczący",
|
||||||
|
"internal": "Wewętrzny",
|
||||||
"rules": "Regulamin",
|
"rules": "Regulamin",
|
||||||
"resourceSettingDescription": "Skonfiguruj ustawienia zasobu",
|
"resourceSettingDescription": "Skonfiguruj ustawienia zasobu",
|
||||||
"resourceSetting": "Ustawienia {resourceName}",
|
"resourceSetting": "Ustawienia {resourceName}",
|
||||||
"alwaysAllow": "Zawsze zezwalaj",
|
"alwaysAllow": "Zawsze zezwalaj",
|
||||||
"alwaysDeny": "Zawsze odmawiaj",
|
"alwaysDeny": "Zawsze odmawiaj",
|
||||||
|
"passToAuth": "Przekaż do Autoryzacji",
|
||||||
"orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji",
|
"orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji",
|
||||||
"orgGeneralSettings": "Ustawienia organizacji",
|
"orgGeneralSettings": "Ustawienia organizacji",
|
||||||
"orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją",
|
"orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Wystąpił błąd podczas dodawania użytkownika do roli.",
|
"accessRoleErrorAddDescription": "Wystąpił błąd podczas dodawania użytkownika do roli.",
|
||||||
"userSaved": "Użytkownik zapisany",
|
"userSaved": "Użytkownik zapisany",
|
||||||
"userSavedDescription": "Użytkownik został zaktualizowany.",
|
"userSavedDescription": "Użytkownik został zaktualizowany.",
|
||||||
|
"autoProvisioned": "Przesłane automatycznie",
|
||||||
|
"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",
|
"accessControlsDescription": "Zarządzaj tym, do czego użytkownik ma dostęp i co może robić w organizacji",
|
||||||
"accessControlsSubmit": "Zapisz kontrole dostępu",
|
"accessControlsSubmit": "Zapisz kontrole dostępu",
|
||||||
"roles": "Role",
|
"roles": "Role",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "Nazwa serwera TLS do użycia dla SNI. Pozostaw puste, aby użyć domyślnej.",
|
"targetTlsSniDescription": "Nazwa serwera TLS do użycia dla SNI. Pozostaw puste, aby użyć domyślnej.",
|
||||||
"targetTlsSubmit": "Zapisz ustawienia",
|
"targetTlsSubmit": "Zapisz ustawienia",
|
||||||
"targets": "Konfiguracja celów",
|
"targets": "Konfiguracja celów",
|
||||||
"targetsDescription": "Skonfiguruj cele do kierowania ruchu do swoich usług",
|
"targetsDescription": "Skonfiguruj cele do kierowania ruchu do usług zaplecza",
|
||||||
"targetStickySessions": "Włącz sesje trwałe",
|
"targetStickySessions": "Włącz sesje trwałe",
|
||||||
"targetStickySessionsDescription": "Utrzymuj połączenia na tym samym celu backendowym przez całą sesję.",
|
"targetStickySessionsDescription": "Utrzymuj połączenia na tym samym celu backendowym przez całą sesję.",
|
||||||
"methodSelect": "Wybierz metodę",
|
"methodSelect": "Wybierz metodę",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Nieprawidłowy format adresu IP",
|
"ipAddressErrorInvalidFormat": "Nieprawidłowy format adresu IP",
|
||||||
"ipAddressErrorInvalidOctet": "Nieprawidłowy oktet adresu IP",
|
"ipAddressErrorInvalidOctet": "Nieprawidłowy oktet adresu IP",
|
||||||
"path": "Ścieżka",
|
"path": "Ścieżka",
|
||||||
|
"matchPath": "Ścieżka dopasowania",
|
||||||
"ipAddressRange": "Zakres IP",
|
"ipAddressRange": "Zakres IP",
|
||||||
"rulesErrorFetch": "Nie udało się pobrać reguł",
|
"rulesErrorFetch": "Nie udało się pobrać reguł",
|
||||||
"rulesErrorFetchDescription": "Wystąpił błąd podczas pobierania reguł",
|
"rulesErrorFetchDescription": "Wystąpił błąd podczas pobierania reguł",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Akcje",
|
"rulesActions": "Akcje",
|
||||||
"rulesActionAlwaysAllow": "Zawsze zezwalaj: Pomiń wszystkie metody uwierzytelniania",
|
"rulesActionAlwaysAllow": "Zawsze zezwalaj: Pomiń wszystkie metody uwierzytelniania",
|
||||||
"rulesActionAlwaysDeny": "Zawsze odmawiaj: Blokuj wszystkie żądania; nie można próbować uwierzytelniania",
|
"rulesActionAlwaysDeny": "Zawsze odmawiaj: Blokuj wszystkie żądania; nie można próbować uwierzytelniania",
|
||||||
|
"rulesActionPassToAuth": "Przekaż do Autoryzacji: Zezwól na próby metod uwierzytelniania",
|
||||||
"rulesMatchCriteria": "Kryteria dopasowania",
|
"rulesMatchCriteria": "Kryteria dopasowania",
|
||||||
"rulesMatchCriteriaIpAddress": "Dopasuj konkretny adres IP",
|
"rulesMatchCriteriaIpAddress": "Dopasuj konkretny adres IP",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Dopasuj zakres adresów IP w notacji CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Dopasuj zakres adresów IP w notacji CIDR",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN musi składać się dokładnie z 6 cyfr",
|
"pincodeRequirementsLength": "PIN musi składać się dokładnie z 6 cyfr",
|
||||||
"pincodeRequirementsChars": "PIN może zawierać tylko cyfry",
|
"pincodeRequirementsChars": "PIN może zawierać tylko cyfry",
|
||||||
"passwordRequirementsLength": "Hasło musi mieć co najmniej 1 znak",
|
"passwordRequirementsLength": "Hasło musi mieć co najmniej 1 znak",
|
||||||
|
"passwordRequirementsTitle": "Wymagania dotyczące hasła:",
|
||||||
|
"passwordRequirementLength": "Przynajmniej 8 znaków długości",
|
||||||
|
"passwordRequirementUppercase": "Przynajmniej jedna wielka litera",
|
||||||
|
"passwordRequirementLowercase": "Przynajmniej jedna mała litera",
|
||||||
|
"passwordRequirementNumber": "Przynajmniej jedna cyfra",
|
||||||
|
"passwordRequirementSpecial": "Przynajmniej jeden znak specjalny",
|
||||||
|
"passwordRequirementsMet": "✓ Hasło spełnia wszystkie wymagania",
|
||||||
|
"passwordStrength": "Siła hasła",
|
||||||
|
"passwordStrengthWeak": "Słabe",
|
||||||
|
"passwordStrengthMedium": "Średnie",
|
||||||
|
"passwordStrengthStrong": "Silne",
|
||||||
|
"passwordRequirements": "Wymagania:",
|
||||||
|
"passwordRequirementLengthText": "8+ znaków",
|
||||||
|
"passwordRequirementUppercaseText": "Wielka litera (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Mała litera (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Cyfra (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Znak specjalny (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Hasła nie są zgodne",
|
||||||
"otpEmailRequirementsLength": "Kod jednorazowy musi mieć co najmniej 1 znak",
|
"otpEmailRequirementsLength": "Kod jednorazowy musi mieć co najmniej 1 znak",
|
||||||
"otpEmailSent": "Kod jednorazowy wysłany",
|
"otpEmailSent": "Kod jednorazowy wysłany",
|
||||||
"otpEmailSentDescription": "Kod jednorazowy został wysłany na Twój e-mail",
|
"otpEmailSentDescription": "Kod jednorazowy został wysłany na Twój e-mail",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "Błąd podczas wylogowywania",
|
"logoutError": "Błąd podczas wylogowywania",
|
||||||
"signingAs": "Zalogowany jako",
|
"signingAs": "Zalogowany jako",
|
||||||
"serverAdmin": "Administrator serwera",
|
"serverAdmin": "Administrator serwera",
|
||||||
|
"managedSelfhosted": "Zarządzane Samodzielnie-Hostingowane",
|
||||||
"otpEnable": "Włącz uwierzytelnianie dwuskładnikowe",
|
"otpEnable": "Włącz uwierzytelnianie dwuskładnikowe",
|
||||||
"otpDisable": "Wyłącz uwierzytelnianie dwuskładnikowe",
|
"otpDisable": "Wyłącz uwierzytelnianie dwuskładnikowe",
|
||||||
"logout": "Wyloguj się",
|
"logout": "Wyloguj się",
|
||||||
"licenseTierProfessionalRequired": "Wymagana edycja Professional",
|
"licenseTierProfessionalRequired": "Wymagana edycja Professional",
|
||||||
"licenseTierProfessionalRequiredDescription": "Ta funkcja jest dostępna tylko w edycji Professional.",
|
"licenseTierProfessionalRequiredDescription": "Ta funkcja jest dostępna tylko w edycji Professional.",
|
||||||
"actionGetOrg": "Pobierz organizację",
|
"actionGetOrg": "Pobierz organizację",
|
||||||
|
"updateOrgUser": "Aktualizuj użytkownika Org",
|
||||||
|
"createOrgUser": "Utwórz użytkownika Org",
|
||||||
"actionUpdateOrg": "Aktualizuj organizację",
|
"actionUpdateOrg": "Aktualizuj organizację",
|
||||||
"actionUpdateUser": "Zaktualizuj użytkownika",
|
"actionUpdateUser": "Zaktualizuj użytkownika",
|
||||||
"actionGetUser": "Pobierz użytkownika",
|
"actionGetUser": "Pobierz użytkownika",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "Usuń witrynę",
|
"actionDeleteSite": "Usuń witrynę",
|
||||||
"actionGetSite": "Pobierz witrynę",
|
"actionGetSite": "Pobierz witrynę",
|
||||||
"actionListSites": "Lista witryn",
|
"actionListSites": "Lista witryn",
|
||||||
|
"actionApplyBlueprint": "Zastosuj schemat",
|
||||||
|
"setupToken": "Skonfiguruj token",
|
||||||
|
"setupTokenDescription": "Wprowadź token konfiguracji z konsoli serwera.",
|
||||||
|
"setupTokenRequired": "Wymagany jest token konfiguracji",
|
||||||
"actionUpdateSite": "Aktualizuj witrynę",
|
"actionUpdateSite": "Aktualizuj witrynę",
|
||||||
"actionListSiteRoles": "Lista dozwolonych ról witryny",
|
"actionListSiteRoles": "Lista dozwolonych ról witryny",
|
||||||
"actionCreateResource": "Utwórz zasób",
|
"actionCreateResource": "Utwórz zasób",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Usuń politykę organizacji IDP",
|
"actionDeleteIdpOrg": "Usuń politykę organizacji IDP",
|
||||||
"actionListIdpOrgs": "Lista organizacji IDP",
|
"actionListIdpOrgs": "Lista organizacji IDP",
|
||||||
"actionUpdateIdpOrg": "Aktualizuj organizację IDP",
|
"actionUpdateIdpOrg": "Aktualizuj organizację IDP",
|
||||||
|
"actionCreateClient": "Utwórz klienta",
|
||||||
|
"actionDeleteClient": "Usuń klienta",
|
||||||
|
"actionUpdateClient": "Aktualizuj klienta",
|
||||||
|
"actionListClients": "Lista klientów",
|
||||||
|
"actionGetClient": "Pobierz klienta",
|
||||||
|
"actionCreateSiteResource": "Utwórz zasób witryny",
|
||||||
|
"actionDeleteSiteResource": "Usuń zasób strony",
|
||||||
|
"actionGetSiteResource": "Pobierz zasób strony",
|
||||||
|
"actionListSiteResources": "Lista zasobów strony",
|
||||||
|
"actionUpdateSiteResource": "Aktualizuj zasób strony",
|
||||||
|
"actionListInvitations": "Lista zaproszeń",
|
||||||
"noneSelected": "Nie wybrano",
|
"noneSelected": "Nie wybrano",
|
||||||
"orgNotFound2": "Nie znaleziono organizacji.",
|
"orgNotFound2": "Nie znaleziono organizacji.",
|
||||||
"searchProgress": "Szukaj...",
|
"searchProgress": "Szukaj...",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "Licencja",
|
"sidebarLicense": "Licencja",
|
||||||
"sidebarClients": "Klienci (Beta)",
|
"sidebarClients": "Klienci (Beta)",
|
||||||
"sidebarDomains": "Domeny",
|
"sidebarDomains": "Domeny",
|
||||||
"enableDockerSocket": "Włącz gniazdo dokera",
|
"enableDockerSocket": "Włącz schemat dokera",
|
||||||
"enableDockerSocketDescription": "Włącz wykrywanie Docker Socket w celu wypełnienia informacji o kontenerach. Ścieżka gniazda musi być dostarczona do Newt.",
|
"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",
|
"enableDockerSocketLink": "Dowiedz się więcej",
|
||||||
"viewDockerContainers": "Zobacz kontenery dokujące",
|
"viewDockerContainers": "Zobacz kontenery dokujące",
|
||||||
"containersIn": "Pojemniki w {siteName}",
|
"containersIn": "Pojemniki w {siteName}",
|
||||||
@@ -1109,7 +1156,7 @@
|
|||||||
"containerLabels": "Etykiety",
|
"containerLabels": "Etykiety",
|
||||||
"containerLabelsCount": "{count, plural, one {# etykieta} few {# etykiety} many {# etykiet} other {# etykiet}}",
|
"containerLabelsCount": "{count, plural, one {# etykieta} few {# etykiety} many {# etykiet} other {# etykiet}}",
|
||||||
"containerLabelsTitle": "Etykiety kontenera",
|
"containerLabelsTitle": "Etykiety kontenera",
|
||||||
"containerLabelEmpty": "<empty>",
|
"containerLabelEmpty": "<pusty>",
|
||||||
"containerPorts": "Porty",
|
"containerPorts": "Porty",
|
||||||
"containerPortsMore": "+{count} więcej",
|
"containerPortsMore": "+{count} więcej",
|
||||||
"containerActions": "Akcje",
|
"containerActions": "Akcje",
|
||||||
@@ -1196,7 +1243,7 @@
|
|||||||
"newtUpdateAvailable": "Dostępna aktualizacja",
|
"newtUpdateAvailable": "Dostępna aktualizacja",
|
||||||
"newtUpdateAvailableInfo": "Nowa wersja Newt jest dostępna. Prosimy o aktualizację do najnowszej wersji dla najlepszej pracy.",
|
"newtUpdateAvailableInfo": "Nowa wersja Newt jest dostępna. Prosimy o aktualizację do najnowszej wersji dla najlepszej pracy.",
|
||||||
"domainPickerEnterDomain": "Domena",
|
"domainPickerEnterDomain": "Domena",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com lub po prostu myapp",
|
"domainPickerPlaceholder": "mojapp.example.com",
|
||||||
"domainPickerDescription": "Wpisz pełną domenę zasobu, aby zobaczyć dostępne opcje.",
|
"domainPickerDescription": "Wpisz pełną domenę zasobu, aby zobaczyć dostępne opcje.",
|
||||||
"domainPickerDescriptionSaas": "Wprowadź pełną domenę, subdomenę lub po prostu nazwę, aby zobaczyć dostępne opcje",
|
"domainPickerDescriptionSaas": "Wprowadź pełną domenę, subdomenę lub po prostu nazwę, aby zobaczyć dostępne opcje",
|
||||||
"domainPickerTabAll": "Wszystko",
|
"domainPickerTabAll": "Wszystko",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "Subdomena: {subdomain}",
|
"domainPickerSubdomain": "Subdomena: {subdomain}",
|
||||||
"domainPickerNamespace": "Przestrzeń nazw: {namespace}",
|
"domainPickerNamespace": "Przestrzeń nazw: {namespace}",
|
||||||
"domainPickerShowMore": "Pokaż więcej",
|
"domainPickerShowMore": "Pokaż więcej",
|
||||||
|
"regionSelectorTitle": "Wybierz region",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Ładowanie subskrypcji...",
|
||||||
|
"billingFreeTier": "Darmowy pakiet",
|
||||||
|
"billingWarningOverLimit": "Ostrzeżenie: Przekroczyłeś jeden lub więcej limitów użytkowania. Twoje witryny nie połączą się, dopóki nie zmienisz subskrypcji lub nie dostosujesz użytkowania.",
|
||||||
|
"billingUsageLimitsOverview": "Przegląd Limitów Użytkowania",
|
||||||
|
"billingMonitorUsage": "Monitoruj swoje wykorzystanie w porównaniu do skonfigurowanych limitów. Jeśli potrzebujesz zwiększenia limitów, skontaktuj się z nami pod adresem support@fossorial.io.",
|
||||||
|
"billingDataUsage": "Użycie danych",
|
||||||
|
"billingOnlineTime": "Czas Online Strony",
|
||||||
|
"billingUsers": "Aktywni użytkownicy",
|
||||||
|
"billingDomains": "Aktywne domeny",
|
||||||
|
"billingRemoteExitNodes": "Aktywne samodzielnie-hostowane węzły",
|
||||||
|
"billingNoLimitConfigured": "Nie skonfigurowano limitu",
|
||||||
|
"billingEstimatedPeriod": "Szacowany Okres Rozliczeniowy",
|
||||||
|
"billingIncludedUsage": "Zawarte użycie",
|
||||||
|
"billingIncludedUsageDescription": "Użycie zawarte w obecnym planie subskrypcji",
|
||||||
|
"billingFreeTierIncludedUsage": "Limity użycia dla darmowego pakietu",
|
||||||
|
"billingIncluded": "zawarte",
|
||||||
|
"billingEstimatedTotal": "Szacowana Całkowita:",
|
||||||
|
"billingNotes": "Notatki",
|
||||||
|
"billingEstimateNote": "To jest szacunkowe, oparte na Twoim obecnym użyciu.",
|
||||||
|
"billingActualChargesMayVary": "Rzeczywiste opłaty mogą się różnić.",
|
||||||
|
"billingBilledAtEnd": "Zostaniesz obciążony na koniec okresu rozliczeniowego.",
|
||||||
|
"billingModifySubscription": "Modyfikuj Subskrypcję",
|
||||||
|
"billingStartSubscription": "Rozpocznij Subskrypcję",
|
||||||
|
"billingRecurringCharge": "Opłata Cyklowa",
|
||||||
|
"billingManageSubscriptionSettings": "Zarządzaj ustawieniami i preferencjami subskrypcji",
|
||||||
|
"billingNoActiveSubscription": "Nie masz aktywnej subskrypcji. Rozpocznij subskrypcję, aby zwiększyć limity użytkowania.",
|
||||||
|
"billingFailedToLoadSubscription": "Nie udało się załadować subskrypcji",
|
||||||
|
"billingFailedToLoadUsage": "Nie udało się załadować użycia",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Nie udało się uzyskać adresu URL zakupu",
|
||||||
|
"billingPleaseTryAgainLater": "Spróbuj ponownie później.",
|
||||||
|
"billingCheckoutError": "Błąd przy kasie",
|
||||||
|
"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": "Jesteś obciążany za każdego użytkownika w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont użytkowników w twojej organizacji.",
|
||||||
|
"billingDomainInfo": "Jesteś obciążany za każdą domenę w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych kont domen w twojej organizacji.",
|
||||||
|
"billingRemoteExitNodesInfo": "Jesteś obciążany za każdy zarządzany węzeł w twojej organizacji. Rozliczenia są obliczane codziennie na podstawie liczby aktywnych zarządzanych węzłów w twojej organizacji.",
|
||||||
"domainNotFound": "Nie znaleziono domeny",
|
"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.",
|
"domainNotFoundDescription": "Zasób jest wyłączony, ponieważ domena nie istnieje już w naszym systemie. Proszę ustawić nową domenę dla tego zasobu.",
|
||||||
"failed": "Niepowodzenie",
|
"failed": "Niepowodzenie",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "Zmiany DNS mogą zająć trochę czasu na rozpropagowanie się w Internecie. Może to potrwać od kilku minut do 48 godzin, w zależności od dostawcy DNS i ustawień TTL.",
|
"createDomainDnsPropagationDescription": "Zmiany DNS mogą zająć trochę czasu na rozpropagowanie się w Internecie. Może to potrwać od kilku minut do 48 godzin, w zależności od dostawcy DNS i ustawień TTL.",
|
||||||
"resourcePortRequired": "Numer portu jest wymagany dla zasobów non-HTTP",
|
"resourcePortRequired": "Numer portu jest wymagany dla zasobów non-HTTP",
|
||||||
"resourcePortNotAllowed": "Numer portu nie powinien być ustawiony dla zasobów HTTP",
|
"resourcePortNotAllowed": "Numer portu nie powinien być ustawiony dla zasobów HTTP",
|
||||||
|
"billingPricingCalculatorLink": "Kalkulator Cen",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Zgadzam się z",
|
"IAgreeToThe": "Zgadzam się z",
|
||||||
"termsOfService": "warunkami usługi",
|
"termsOfService": "warunkami usługi",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "Wystąpił błąd podczas pobierania najnowszego wydania Olm.",
|
"olmErrorFetchLatest": "Wystąpił błąd podczas pobierania najnowszego wydania Olm.",
|
||||||
"remoteSubnets": "Zdalne Podsieci",
|
"remoteSubnets": "Zdalne Podsieci",
|
||||||
"enterCidrRange": "Wprowadź zakres CIDR",
|
"enterCidrRange": "Wprowadź zakres CIDR",
|
||||||
"remoteSubnetsDescription": "Dodaj zakresy CIDR, które mogą uzyskać zdalny dostęp do tej witryny. Użyj formatu takiego jak 10.0.0.0/24 lub 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Dodaj zakresy CIDR, które można uzyskać zdalnie z tej strony za pomocą klientów. Użyj formatu jak 10.0.0.0/24. Dotyczy to WYŁĄCZNIE łączności klienta VPN.",
|
||||||
"resourceEnableProxy": "Włącz publiczny proxy",
|
"resourceEnableProxy": "Włącz publiczny proxy",
|
||||||
"resourceEnableProxyDescription": "Włącz publiczne proxy dla tego zasobu. To umożliwia dostęp do zasobu spoza sieci przez chmurę na otwartym porcie. Wymaga konfiguracji Traefik.",
|
"resourceEnableProxyDescription": "Włącz publiczne proxy dla tego zasobu. To umożliwia dostęp do zasobu spoza sieci przez chmurę na otwartym porcie. Wymaga konfiguracji Traefik.",
|
||||||
"externalProxyEnabled": "Zewnętrzny Proxy Włączony"
|
"externalProxyEnabled": "Zewnętrzny Proxy Włączony",
|
||||||
|
"addNewTarget": "Dodaj nowy cel",
|
||||||
|
"targetsList": "Lista celów",
|
||||||
|
"targetErrorDuplicateTargetFound": "Znaleziono duplikat celu",
|
||||||
|
"healthCheckHealthy": "Zdrowy",
|
||||||
|
"healthCheckUnhealthy": "Niezdrowy",
|
||||||
|
"healthCheckUnknown": "Nieznany",
|
||||||
|
"healthCheck": "Kontrola Zdrowia",
|
||||||
|
"configureHealthCheck": "Skonfiguruj Kontrolę Zdrowia",
|
||||||
|
"configureHealthCheckDescription": "Skonfiguruj monitorowanie zdrowia dla {target}",
|
||||||
|
"enableHealthChecks": "Włącz Kontrole Zdrowia",
|
||||||
|
"enableHealthChecksDescription": "Monitoruj zdrowie tego celu. Możesz monitorować inny punkt końcowy niż docelowy w razie potrzeby.",
|
||||||
|
"healthScheme": "Metoda",
|
||||||
|
"healthSelectScheme": "Wybierz metodę",
|
||||||
|
"healthCheckPath": "Ścieżka",
|
||||||
|
"healthHostname": "IP / Nazwa hosta",
|
||||||
|
"healthPort": "Port",
|
||||||
|
"healthCheckPathDescription": "Ścieżka do sprawdzania stanu zdrowia.",
|
||||||
|
"healthyIntervalSeconds": "Interwał Zdrowy",
|
||||||
|
"unhealthyIntervalSeconds": "Interwał Niezdrowy",
|
||||||
|
"IntervalSeconds": "Interwał Zdrowy",
|
||||||
|
"timeoutSeconds": "Limit Czasu",
|
||||||
|
"timeIsInSeconds": "Czas w sekundach",
|
||||||
|
"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.",
|
||||||
|
"customHeaders": "Niestandardowe nagłówki",
|
||||||
|
"customHeadersDescription": "Nagłówki oddzielone: Nazwa nagłówka: wartość",
|
||||||
|
"headersValidationError": "Nagłówki muszą być w formacie: Nazwa nagłówka: wartość.",
|
||||||
|
"saveHealthCheck": "Zapisz Kontrolę Zdrowia",
|
||||||
|
"healthCheckSaved": "Kontrola Zdrowia Zapisana",
|
||||||
|
"healthCheckSavedDescription": "Konfiguracja kontroli zdrowia została zapisana pomyślnie",
|
||||||
|
"healthCheckError": "Błąd Kontroli Zdrowia",
|
||||||
|
"healthCheckErrorDescription": "Wystąpił błąd podczas zapisywania konfiguracji kontroli zdrowia",
|
||||||
|
"healthCheckPathRequired": "Ścieżka kontroli zdrowia jest wymagana",
|
||||||
|
"healthCheckMethodRequired": "Metoda HTTP jest wymagana",
|
||||||
|
"healthCheckIntervalMin": "Interwał sprawdzania musi wynosić co najmniej 5 sekund",
|
||||||
|
"healthCheckTimeoutMin": "Limit czasu musi wynosić co najmniej 1 sekundę",
|
||||||
|
"healthCheckRetryMin": "Liczba prób ponowienia musi wynosić co najmniej 1",
|
||||||
|
"httpMethod": "Metoda HTTP",
|
||||||
|
"selectHttpMethod": "Wybierz metodę HTTP",
|
||||||
|
"domainPickerSubdomainLabel": "Poddomena",
|
||||||
|
"domainPickerBaseDomainLabel": "Domen bazowa",
|
||||||
|
"domainPickerSearchDomains": "Szukaj domen...",
|
||||||
|
"domainPickerNoDomainsFound": "Nie znaleziono domen",
|
||||||
|
"domainPickerLoadingDomains": "Ładowanie domen...",
|
||||||
|
"domainPickerSelectBaseDomain": "Wybierz domenę bazową...",
|
||||||
|
"domainPickerNotAvailableForCname": "Niedostępne dla domen CNAME",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Wprowadź poddomenę lub pozostaw puste, aby użyć domeny bazowej.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Wprowadź poddomenę, aby wyszukać i wybrać z dostępnych darmowych domen.",
|
||||||
|
"domainPickerFreeDomains": "Darmowe domeny",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Szukaj dostępnych domen",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Uwaga: Darmowe domeny nie są obecnie dostępne dla instancji samodzielnie-hostowanych.",
|
||||||
|
"resourceDomain": "Domena",
|
||||||
|
"resourceEditDomain": "Edytuj domenę",
|
||||||
|
"siteName": "Nazwa strony",
|
||||||
|
"proxyPort": "Port",
|
||||||
|
"resourcesTableProxyResources": "Zasoby proxy",
|
||||||
|
"resourcesTableClientResources": "Zasoby klienta",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Nie znaleziono zasobów proxy.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Nie znaleziono wewnętrznych zasobów.",
|
||||||
|
"resourcesTableDestination": "Miejsce docelowe",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Te zasoby są do użytku z",
|
||||||
|
"resourcesTableClients": "Klientami",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "i są dostępne tylko wewnętrznie po połączeniu z klientem.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Edytuj zasób klienta",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Zaktualizuj właściwości zasobu i konfigurację celu dla {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Właściwości zasobów",
|
||||||
|
"editInternalResourceDialogName": "Nazwa",
|
||||||
|
"editInternalResourceDialogProtocol": "Protokół",
|
||||||
|
"editInternalResourceDialogSitePort": "Port witryny",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Konfiguracja celu",
|
||||||
|
"editInternalResourceDialogCancel": "Anuluj",
|
||||||
|
"editInternalResourceDialogSaveResource": "Zapisz zasób",
|
||||||
|
"editInternalResourceDialogSuccess": "Sukces",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Wewnętrzny zasób zaktualizowany pomyślnie",
|
||||||
|
"editInternalResourceDialogError": "Błąd",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Nie udało się zaktualizować wewnętrznego zasobu",
|
||||||
|
"editInternalResourceDialogNameRequired": "Nazwa jest wymagana",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Nazwa nie może mieć więcej niż 255 znaków",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Port proxy musi wynosić przynajmniej 1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Port proxy nie może być większy niż 65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Nieprawidłowy format adresu IP",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Port docelowy musi wynosić przynajmniej 1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Port docelowy nie może być większy niż 65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Brak dostępnych stron",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Musisz mieć co najmniej jedną stronę Newt z skonfigurowanym podsiecią, aby tworzyć wewnętrzne zasoby.",
|
||||||
|
"createInternalResourceDialogClose": "Zamknij",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Utwórz zasób klienta",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Utwórz nowy zasób, który będzie dostępny dla klientów połączonych z wybraną stroną.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Właściwości zasobów",
|
||||||
|
"createInternalResourceDialogName": "Nazwa",
|
||||||
|
"createInternalResourceDialogSite": "Witryna",
|
||||||
|
"createInternalResourceDialogSelectSite": "Wybierz stronę...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Szukaj stron...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Nie znaleziono stron.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protokół",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Port witryny",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Użyj tego portu, aby uzyskać dostęp do zasobu na stronie, gdy połączony z klientem.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Konfiguracja celu",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "Adres IP lub nazwa hosta zasobu w sieci witryny.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "Port na docelowym IP, gdzie zasób jest dostępny.",
|
||||||
|
"createInternalResourceDialogCancel": "Anuluj",
|
||||||
|
"createInternalResourceDialogCreateResource": "Utwórz zasób",
|
||||||
|
"createInternalResourceDialogSuccess": "Sukces",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Wewnętrzny zasób utworzony pomyślnie",
|
||||||
|
"createInternalResourceDialogError": "Błąd",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Nie udało się utworzyć wewnętrznego zasobu",
|
||||||
|
"createInternalResourceDialogNameRequired": "Nazwa jest wymagana",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Nazwa nie może mieć więcej niż 255 znaków",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Proszę wybrać stronę",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Port proxy musi wynosić przynajmniej 1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Port proxy nie może być większy niż 65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Nieprawidłowy format adresu IP",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Port docelowy musi wynosić przynajmniej 1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Port docelowy nie może być większy niż 65536",
|
||||||
|
"siteConfiguration": "Konfiguracja",
|
||||||
|
"siteAcceptClientConnections": "Akceptuj połączenia klienta",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Pozwól innym urządzeniom połączyć się przez tę instancję Newt jako bramę za pomocą klientów.",
|
||||||
|
"siteAddress": "Adres strony",
|
||||||
|
"siteAddressDescription": "Podaj adres IP hosta, do którego klienci będą się łączyć. Jest to wewnętrzny adres strony w sieci Pangolin dla klientów do adresowania. Musi zawierać się w podsieci organizacji.",
|
||||||
|
"autoLoginExternalIdp": "Automatyczny login z zewnętrznym IDP",
|
||||||
|
"autoLoginExternalIdpDescription": "Natychmiastowe przekierowanie użytkownika do zewnętrznego IDP w celu uwierzytelnienia.",
|
||||||
|
"selectIdp": "Wybierz IDP",
|
||||||
|
"selectIdpPlaceholder": "Wybierz IDP...",
|
||||||
|
"selectIdpRequired": "Proszę wybrać IDP, gdy aktywne jest automatyczne logowanie.",
|
||||||
|
"autoLoginTitle": "Przekierowywanie",
|
||||||
|
"autoLoginDescription": "Przekierowanie do zewnętrznego dostawcy tożsamości w celu uwierzytelnienia.",
|
||||||
|
"autoLoginProcessing": "Przygotowywanie uwierzytelniania...",
|
||||||
|
"autoLoginRedirecting": "Przekierowanie do logowania...",
|
||||||
|
"autoLoginError": "Błąd automatycznego logowania",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "Nie otrzymano URL przekierowania od dostawcy tożsamości.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Nie udało się wygenerować URL uwierzytelniania.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Zarządzaj Samodzielnie-Hostingowane",
|
||||||
|
"remoteExitNodeDescription": "Zarządzaj węzłami w celu rozszerzenia połączenia z siecią",
|
||||||
|
"remoteExitNodes": "Węzły",
|
||||||
|
"searchRemoteExitNodes": "Szukaj węzłów...",
|
||||||
|
"remoteExitNodeAdd": "Dodaj węzeł",
|
||||||
|
"remoteExitNodeErrorDelete": "Błąd podczas usuwania węzła",
|
||||||
|
"remoteExitNodeQuestionRemove": "Czy na pewno chcesz usunąć węzeł {selectedNode} z organizacji?",
|
||||||
|
"remoteExitNodeMessageRemove": "Po usunięciu, węzeł nie będzie już dostępny.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Aby potwierdzić, wpisz nazwę węzła poniżej.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Potwierdź usunięcie węzła",
|
||||||
|
"remoteExitNodeDelete": "Usuń węzeł",
|
||||||
|
"sidebarRemoteExitNodes": "Węzły",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Utwórz węzeł",
|
||||||
|
"description": "Utwórz nowy węzeł, aby rozszerzyć połączenie z siecią",
|
||||||
|
"viewAllButton": "Zobacz wszystkie węzły",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Strategia Tworzenia",
|
||||||
|
"description": "Wybierz to, aby ręcznie skonfigurować węzeł lub wygenerować nowe poświadczenia.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Zaadoptuj Węzeł",
|
||||||
|
"description": "Wybierz to, jeśli masz już dane logowania dla węzła."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Generuj Klucze",
|
||||||
|
"description": "Wybierz to, jeśli chcesz wygenerować nowe klucze dla węzła"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Zaadoptuj Istniejący Węzeł",
|
||||||
|
"description": "Wprowadź dane logowania istniejącego węzła, który chcesz przyjąć",
|
||||||
|
"nodeIdLabel": "ID węzła",
|
||||||
|
"nodeIdDescription": "ID istniejącego węzła, który chcesz przyjąć",
|
||||||
|
"secretLabel": "Sekret",
|
||||||
|
"secretDescription": "Sekretny klucz istniejącego węzła",
|
||||||
|
"submitButton": "Przyjmij węzeł"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Wygenerowane Poświadczenia",
|
||||||
|
"description": "Użyj tych danych logowania, aby skonfigurować węzeł",
|
||||||
|
"nodeIdTitle": "ID węzła",
|
||||||
|
"secretTitle": "Sekret",
|
||||||
|
"saveCredentialsTitle": "Dodaj Poświadczenia do Konfiguracji",
|
||||||
|
"saveCredentialsDescription": "Dodaj te poświadczenia do pliku konfiguracyjnego swojego samodzielnie-hostowanego węzła Pangolin, aby zakończyć połączenie.",
|
||||||
|
"submitButton": "Utwórz węzeł"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "Identyfikator węzła i sekret są wymagane podczas przyjmowania istniejącego węzła"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Nie udało się załadować domyślnych ustawień",
|
||||||
|
"defaultsNotLoaded": "Domyślne ustawienia nie zostały załadowane",
|
||||||
|
"createFailed": "Nie udało się utworzyć węzła"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Węzeł utworzony pomyślnie"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Wybór węzła",
|
||||||
|
"remoteExitNodeSelectionDescription": "Wybierz węzeł do przekierowania ruchu dla tej lokalnej witryny",
|
||||||
|
"remoteExitNodeRequired": "Węzeł musi być wybrany dla lokalnych witryn",
|
||||||
|
"noRemoteExitNodesAvailable": "Brak dostępnych węzłów",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Węzły nie są dostępne dla tej organizacji. Utwórz węzeł, aby używać lokalnych witryn.",
|
||||||
|
"exitNode": "Węzeł Wyjściowy",
|
||||||
|
"country": "Kraj",
|
||||||
|
"rulesMatchCountry": "Obecnie bazuje na adresie IP źródła",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Zarządzane Samodzielnie-Hostingowane",
|
||||||
|
"description": "Większa niezawodność i niska konserwacja serwera Pangolin z dodatkowymi dzwonkami i sygnałami",
|
||||||
|
"introTitle": "Zarządzany samowystarczalny Pangolin",
|
||||||
|
"introDescription": "jest opcją wdrażania zaprojektowaną dla osób, które chcą prostoty i dodatkowej niezawodności, przy jednoczesnym utrzymaniu swoich danych prywatnych i samodzielnych.",
|
||||||
|
"introDetail": "Z tą opcją nadal obsługujesz swój własny węzeł Pangolin — tunele, zakończenie SSL i ruch na Twoim serwerze. Różnica polega na tym, że zarządzanie i monitorowanie odbywa się za pomocą naszej tablicy rozdzielczej, która odblokowuje szereg korzyści:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Uproszczone operacje",
|
||||||
|
"description": "Nie ma potrzeby uruchamiania własnego serwera pocztowego lub ustawiania skomplikowanych powiadomień. Będziesz mieć kontrolę zdrowia i powiadomienia o przestoju."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Automatyczne aktualizacje",
|
||||||
|
"description": "Panel chmury rozwija się szybko, więc otrzymujesz nowe funkcje i poprawki błędów bez konieczności ręcznego ciągnięcia nowych kontenerów za każdym razem."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Mniej konserwacji",
|
||||||
|
"description": "Brak migracji bazy danych, kopii zapasowych lub dodatkowej infrastruktury do zarządzania. Obsługujemy to w chmurze."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Przegrywanie w chmurze",
|
||||||
|
"description": "Jeśli Twój węzeł zostanie wyłączony, tunele mogą tymczasowo zawieść do naszych punktów w chmurze, dopóki nie przyniesiesz go z powrotem do trybu online."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Wysoka dostępność (PoPs)",
|
||||||
|
"description": "Możesz również dołączyć wiele węzłów do swojego konta w celu nadmiarowości i lepszej wydajności."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Przyszłe ulepszenia",
|
||||||
|
"description": "Planujemy dodać więcej narzędzi analitycznych, ostrzegawczych i zarządzania, aby zwiększyć odporność wdrożenia."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Dowiedz się więcej o opcji zarządzania samodzielnym hostingiem w naszym",
|
||||||
|
"documentation": "dokumentacja"
|
||||||
|
},
|
||||||
|
"convertButton": "Konwertuj ten węzeł do zarządzanego samodzielnie"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Wykryto międzynarodową domenę",
|
||||||
|
"willbestoredas": "Będą przechowywane jako:",
|
||||||
|
"roleMappingDescription": "Określ jak role są przypisywane do użytkowników podczas logowania się, gdy automatyczne świadczenie jest włączone.",
|
||||||
|
"selectRole": "Wybierz rolę",
|
||||||
|
"roleMappingExpression": "Wyrażenie",
|
||||||
|
"selectRolePlaceholder": "Wybierz rolę",
|
||||||
|
"selectRoleDescription": "Wybierz rolę do przypisania wszystkim użytkownikom od tego dostawcy tożsamości",
|
||||||
|
"roleMappingExpressionDescription": "Wprowadź wyrażenie JMESŚcieżki, aby wyodrębnić informacje o roli z tokenu ID",
|
||||||
|
"idpTenantIdRequired": "ID lokatora jest wymagane",
|
||||||
|
"invalidValue": "Nieprawidłowa wartość",
|
||||||
|
"idpTypeLabel": "Typ dostawcy tożsamości",
|
||||||
|
"roleMappingExpressionPlaceholder": "np. zawiera(grupy, 'admin') && 'Admin' || 'Członek'",
|
||||||
|
"idpGoogleConfiguration": "Konfiguracja Google",
|
||||||
|
"idpGoogleConfigurationDescription": "Skonfiguruj swoje poświadczenia Google OAuth2",
|
||||||
|
"idpGoogleClientIdDescription": "Twój identyfikator klienta Google OAuth2",
|
||||||
|
"idpGoogleClientSecretDescription": "Twój klucz klienta Google OAuth2",
|
||||||
|
"idpAzureConfiguration": "Konfiguracja Azure Entra ID",
|
||||||
|
"idpAzureConfigurationDescription": "Skonfiguruj swoje dane logowania OAuth2 Azure Entra",
|
||||||
|
"idpTenantId": "ID Najemcy",
|
||||||
|
"idpTenantIdPlaceholder": "twoj-lokator",
|
||||||
|
"idpAzureTenantIdDescription": "Twój identyfikator dzierżawcy Azure (znaleziony w Podglądzie Azure Active Directory",
|
||||||
|
"idpAzureClientIdDescription": "Twój identyfikator klienta rejestracji aplikacji Azure",
|
||||||
|
"idpAzureClientSecretDescription": "Klucz tajny Twojego klienta rejestracji aplikacji Azure",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Konfiguracja Google",
|
||||||
|
"idpAzureConfigurationTitle": "Konfiguracja Azure Entra ID",
|
||||||
|
"idpTenantIdLabel": "ID Najemcy",
|
||||||
|
"idpAzureClientIdDescription2": "Twój identyfikator klienta rejestracji aplikacji Azure",
|
||||||
|
"idpAzureClientSecretDescription2": "Klucz tajny Twojego klienta rejestracji aplikacji Azure",
|
||||||
|
"idpGoogleDescription": "Dostawca Google OAuth2/OIDC",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Podsieć",
|
||||||
|
"subnetDescription": "Podsieć dla konfiguracji sieci tej organizacji.",
|
||||||
|
"authPage": "Strona uwierzytelniania",
|
||||||
|
"authPageDescription": "Skonfiguruj stronę uwierzytelniania dla swojej organizacji",
|
||||||
|
"authPageDomain": "Domena strony uwierzytelniania",
|
||||||
|
"noDomainSet": "Nie ustawiono domeny",
|
||||||
|
"changeDomain": "Zmień domenę",
|
||||||
|
"selectDomain": "Wybierz domenę",
|
||||||
|
"restartCertificate": "Uruchom ponownie certyfikat",
|
||||||
|
"editAuthPageDomain": "Edytuj domenę strony uwierzytelniania",
|
||||||
|
"setAuthPageDomain": "Ustaw domenę strony uwierzytelniania",
|
||||||
|
"failedToFetchCertificate": "Nie udało się pobrać certyfikatu",
|
||||||
|
"failedToRestartCertificate": "Nie udało się ponownie uruchomić certyfikatu",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Dodaj domenę, aby włączyć niestandardowe strony uwierzytelniania dla Twojej organizacji",
|
||||||
|
"selectDomainForOrgAuthPage": "Wybierz domenę dla strony uwierzytelniania organizacji",
|
||||||
|
"domainPickerProvidedDomain": "Dostarczona domena",
|
||||||
|
"domainPickerFreeProvidedDomain": "Darmowa oferowana domena",
|
||||||
|
"domainPickerVerified": "Zweryfikowano",
|
||||||
|
"domainPickerUnverified": "Niezweryfikowane",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Ta subdomena zawiera nieprawidłowe znaki lub strukturę. Zostanie ona automatycznie oczyszczona po zapisaniu.",
|
||||||
|
"domainPickerError": "Błąd",
|
||||||
|
"domainPickerErrorLoadDomains": "Nie udało się załadować domen organizacji",
|
||||||
|
"domainPickerErrorCheckAvailability": "Nie udało się sprawdzić dostępności domeny",
|
||||||
|
"domainPickerInvalidSubdomain": "Nieprawidłowa subdomena",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "Wejście \"{sub}\" zostało usunięte, ponieważ jest nieprawidłowe.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" nie może być poprawne dla {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Poddomena oczyszczona",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" został skorygowany do \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Zaloguj się do swojej organizacji",
|
||||||
|
"orgAuthChooseIdpDescription": "Wybierz swojego dostawcę tożsamości, aby kontynuować",
|
||||||
|
"orgAuthNoIdpConfigured": "Ta organizacja nie ma skonfigurowanych żadnych dostawców tożsamości. Zamiast tego możesz zalogować się za pomocą swojej tożsamości Pangolin.",
|
||||||
|
"orgAuthSignInWithPangolin": "Zaloguj się używając Pangolin",
|
||||||
|
"subscriptionRequiredToUse": "Do korzystania z tej funkcji wymagana jest subskrypcja.",
|
||||||
|
"idpDisabled": "Dostawcy tożsamości są wyłączeni",
|
||||||
|
"orgAuthPageDisabled": "Strona autoryzacji organizacji jest wyłączona.",
|
||||||
|
"domainRestartedDescription": "Weryfikacja domeny zrestartowana pomyślnie",
|
||||||
|
"resourceAddEntrypointsEditFile": "Edytuj plik: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Edytuj plik: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "Weryfikacja adresu e-mail jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login zakończył ten krok. Następnie wróć tutaj.",
|
||||||
|
"twoFactorSetupRequired": "Konfiguracja uwierzytelniania dwuskładnikowego jest wymagana. Zaloguj się ponownie przez {dashboardUrl}/auth/login dokończ ten krok. Następnie wróć tutaj.",
|
||||||
|
"authPageErrorUpdateMessage": "Wystąpił błąd podczas aktualizacji ustawień strony uwierzytelniania",
|
||||||
|
"authPageUpdated": "Strona uwierzytelniania została pomyślnie zaktualizowana",
|
||||||
|
"healthCheckNotAvailable": "Lokalny",
|
||||||
|
"rewritePath": "Przepis Ścieżki",
|
||||||
|
"rewritePathDescription": "Opcjonalnie przepisz ścieżkę przed przesłaniem do celu."
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "Простейший способ создать точку входа в вашу сеть. Дополнительная настройка не требуется.",
|
"siteNewtTunnelDescription": "Простейший способ создать точку входа в вашу сеть. Дополнительная настройка не требуется.",
|
||||||
"siteWg": "Базовый WireGuard",
|
"siteWg": "Базовый WireGuard",
|
||||||
"siteWgDescription": "Используйте любой клиент WireGuard для открытия туннеля. Требуется ручная настройка NAT.",
|
"siteWgDescription": "Используйте любой клиент WireGuard для открытия туннеля. Требуется ручная настройка NAT.",
|
||||||
|
"siteWgDescriptionSaas": "Используйте любой клиент WireGuard для создания туннеля. Требуется ручная настройка NAT. РАБОТАЕТ ТОЛЬКО НА САМОСТОЯТЕЛЬНО РАЗМЕЩЕННЫХ УЗЛАХ",
|
||||||
"siteLocalDescription": "Только локальные ресурсы. Без туннелирования.",
|
"siteLocalDescription": "Только локальные ресурсы. Без туннелирования.",
|
||||||
|
"siteLocalDescriptionSaas": "Только локальные ресурсы. Без туннелирования. РАБОТАЕТ ТОЛЬКО НА САМОСТОЯТЕЛЬНО РАЗМЕЩЕННЫХ УЗЛАХ",
|
||||||
"siteSeeAll": "Просмотреть все сайты",
|
"siteSeeAll": "Просмотреть все сайты",
|
||||||
"siteTunnelDescription": "Выберите способ подключения к вашему сайту",
|
"siteTunnelDescription": "Выберите способ подключения к вашему сайту",
|
||||||
"siteNewtCredentials": "Учётные данные Newt",
|
"siteNewtCredentials": "Учётные данные Newt",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Выберите сайт",
|
"siteSelect": "Выберите сайт",
|
||||||
"siteSearch": "Поиск сайта",
|
"siteSearch": "Поиск сайта",
|
||||||
"siteNotFound": "Сайт не найден.",
|
"siteNotFound": "Сайт не найден.",
|
||||||
"siteSelectionDescription": "Этот сайт обеспечит подключение к ресурсу.",
|
"selectCountry": "Выберите страну",
|
||||||
|
"searchCountries": "Поиск стран...",
|
||||||
|
"noCountryFound": "Страна не найдена.",
|
||||||
|
"siteSelectionDescription": "Этот сайт предоставит подключение к цели.",
|
||||||
"resourceType": "Тип ресурса",
|
"resourceType": "Тип ресурса",
|
||||||
"resourceTypeDescription": "Определите, как вы хотите получать доступ к вашему ресурсу",
|
"resourceTypeDescription": "Определите, как вы хотите получать доступ к вашему ресурсу",
|
||||||
"resourceHTTPSSettings": "Настройки HTTPS",
|
"resourceHTTPSSettings": "Настройки HTTPS",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "Общие",
|
"general": "Общие",
|
||||||
"generalSettings": "Общие настройки",
|
"generalSettings": "Общие настройки",
|
||||||
"proxy": "Прокси",
|
"proxy": "Прокси",
|
||||||
|
"internal": "Внутренний",
|
||||||
"rules": "Правила",
|
"rules": "Правила",
|
||||||
"resourceSettingDescription": "Настройте параметры вашего ресурса",
|
"resourceSettingDescription": "Настройте параметры вашего ресурса",
|
||||||
"resourceSetting": "Настройки {resourceName}",
|
"resourceSetting": "Настройки {resourceName}",
|
||||||
"alwaysAllow": "Всегда разрешать",
|
"alwaysAllow": "Всегда разрешать",
|
||||||
"alwaysDeny": "Всегда запрещать",
|
"alwaysDeny": "Всегда запрещать",
|
||||||
|
"passToAuth": "Переход к аутентификации",
|
||||||
"orgSettingsDescription": "Настройте общие параметры вашей организации",
|
"orgSettingsDescription": "Настройте общие параметры вашей организации",
|
||||||
"orgGeneralSettings": "Настройки организации",
|
"orgGeneralSettings": "Настройки организации",
|
||||||
"orgGeneralSettingsDescription": "Управляйте данными и конфигурацией вашей организации",
|
"orgGeneralSettingsDescription": "Управляйте данными и конфигурацией вашей организации",
|
||||||
@@ -232,7 +239,7 @@
|
|||||||
"accessUserCreate": "Создать пользователя",
|
"accessUserCreate": "Создать пользователя",
|
||||||
"accessUserRemove": "Удалить пользователя",
|
"accessUserRemove": "Удалить пользователя",
|
||||||
"username": "Имя пользователя",
|
"username": "Имя пользователя",
|
||||||
"identityProvider": "Identity Provider",
|
"identityProvider": "Поставщик удостоверений",
|
||||||
"role": "Роль",
|
"role": "Роль",
|
||||||
"nameRequired": "Имя обязательно",
|
"nameRequired": "Имя обязательно",
|
||||||
"accessRolesManage": "Управление ролями",
|
"accessRolesManage": "Управление ролями",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Произошла ошибка при добавлении пользователя в роль.",
|
"accessRoleErrorAddDescription": "Произошла ошибка при добавлении пользователя в роль.",
|
||||||
"userSaved": "Пользователь сохранён",
|
"userSaved": "Пользователь сохранён",
|
||||||
"userSavedDescription": "Пользователь был обновлён.",
|
"userSavedDescription": "Пользователь был обновлён.",
|
||||||
|
"autoProvisioned": "Автоподбор",
|
||||||
|
"autoProvisionedDescription": "Разрешить автоматическое управление этим пользователем",
|
||||||
"accessControlsDescription": "Управляйте тем, к чему этот пользователь может получить доступ и что делать в организации",
|
"accessControlsDescription": "Управляйте тем, к чему этот пользователь может получить доступ и что делать в организации",
|
||||||
"accessControlsSubmit": "Сохранить контроль доступа",
|
"accessControlsSubmit": "Сохранить контроль доступа",
|
||||||
"roles": "Роли",
|
"roles": "Роли",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "Имя TLS сервера для использования в SNI. Оставьте пустым для использования по умолчанию.",
|
"targetTlsSniDescription": "Имя TLS сервера для использования в SNI. Оставьте пустым для использования по умолчанию.",
|
||||||
"targetTlsSubmit": "Сохранить настройки",
|
"targetTlsSubmit": "Сохранить настройки",
|
||||||
"targets": "Конфигурация целей",
|
"targets": "Конфигурация целей",
|
||||||
"targetsDescription": "Настройте цели для маршрутизации трафика к вашим сервисам",
|
"targetsDescription": "Настройте цели для маршрутизации трафика к вашим бэкэнд сервисам",
|
||||||
"targetStickySessions": "Включить фиксированные сессии",
|
"targetStickySessions": "Включить фиксированные сессии",
|
||||||
"targetStickySessionsDescription": "Сохранять соединения на одной и той же целевой точке в течение всей сессии.",
|
"targetStickySessionsDescription": "Сохранять соединения на одной и той же целевой точке в течение всей сессии.",
|
||||||
"methodSelect": "Выберите метод",
|
"methodSelect": "Выберите метод",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Неверный формат IP адреса",
|
"ipAddressErrorInvalidFormat": "Неверный формат IP адреса",
|
||||||
"ipAddressErrorInvalidOctet": "Неверный октет IP адреса",
|
"ipAddressErrorInvalidOctet": "Неверный октет IP адреса",
|
||||||
"path": "Путь",
|
"path": "Путь",
|
||||||
|
"matchPath": "Путь матча",
|
||||||
"ipAddressRange": "Диапазон IP",
|
"ipAddressRange": "Диапазон IP",
|
||||||
"rulesErrorFetch": "Не удалось получить правила",
|
"rulesErrorFetch": "Не удалось получить правила",
|
||||||
"rulesErrorFetchDescription": "Произошла ошибка при получении правил",
|
"rulesErrorFetchDescription": "Произошла ошибка при получении правил",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Действия",
|
"rulesActions": "Действия",
|
||||||
"rulesActionAlwaysAllow": "Всегда разрешать: Обойти все методы аутентификации",
|
"rulesActionAlwaysAllow": "Всегда разрешать: Обойти все методы аутентификации",
|
||||||
"rulesActionAlwaysDeny": "Всегда запрещать: Блокировать все запросы; аутентификация не может быть выполнена",
|
"rulesActionAlwaysDeny": "Всегда запрещать: Блокировать все запросы; аутентификация не может быть выполнена",
|
||||||
|
"rulesActionPassToAuth": "Переход к аутентификации: Разрешить попытки методов аутентификации",
|
||||||
"rulesMatchCriteria": "Критерии совпадения",
|
"rulesMatchCriteria": "Критерии совпадения",
|
||||||
"rulesMatchCriteriaIpAddress": "Совпадение с конкретным IP адресом",
|
"rulesMatchCriteriaIpAddress": "Совпадение с конкретным IP адресом",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Совпадение с диапазоном IP адресов в нотации CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Совпадение с диапазоном IP адресов в нотации CIDR",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN должен состоять ровно из 6 цифр",
|
"pincodeRequirementsLength": "PIN должен состоять ровно из 6 цифр",
|
||||||
"pincodeRequirementsChars": "PIN должен содержать только цифры",
|
"pincodeRequirementsChars": "PIN должен содержать только цифры",
|
||||||
"passwordRequirementsLength": "Пароль должен быть не менее 1 символа",
|
"passwordRequirementsLength": "Пароль должен быть не менее 1 символа",
|
||||||
|
"passwordRequirementsTitle": "Требования к паролю:",
|
||||||
|
"passwordRequirementLength": "Не менее 8 символов",
|
||||||
|
"passwordRequirementUppercase": "По крайней мере, одна заглавная буква",
|
||||||
|
"passwordRequirementLowercase": "По крайней мере, одна строчная буква",
|
||||||
|
"passwordRequirementNumber": "По крайней мере, одна цифра",
|
||||||
|
"passwordRequirementSpecial": "По крайней мере, один специальный символ",
|
||||||
|
"passwordRequirementsMet": "✓ Пароль соответствует всем требованиям",
|
||||||
|
"passwordStrength": "Сила пароля",
|
||||||
|
"passwordStrengthWeak": "Слабый",
|
||||||
|
"passwordStrengthMedium": "Средний",
|
||||||
|
"passwordStrengthStrong": "Сильный",
|
||||||
|
"passwordRequirements": "Требования:",
|
||||||
|
"passwordRequirementLengthText": "8+ символов",
|
||||||
|
"passwordRequirementUppercaseText": "Заглавная буква (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Строчная буква (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Цифра (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Специальный символ (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Пароли не совпадают",
|
||||||
"otpEmailRequirementsLength": "OTP должен быть не менее 1 символа",
|
"otpEmailRequirementsLength": "OTP должен быть не менее 1 символа",
|
||||||
"otpEmailSent": "OTP отправлен",
|
"otpEmailSent": "OTP отправлен",
|
||||||
"otpEmailSentDescription": "OTP был отправлен на ваш email",
|
"otpEmailSentDescription": "OTP был отправлен на ваш email",
|
||||||
@@ -925,74 +954,81 @@
|
|||||||
"supportKeyInvalid": "Недействительный ключ",
|
"supportKeyInvalid": "Недействительный ключ",
|
||||||
"supportKeyInvalidDescription": "Ваш ключ поддержки недействителен.",
|
"supportKeyInvalidDescription": "Ваш ключ поддержки недействителен.",
|
||||||
"supportKeyValid": "Действительный ключ",
|
"supportKeyValid": "Действительный ключ",
|
||||||
"supportKeyValidDescription": "Your supporter key has been validated. Thank you for your support!",
|
"supportKeyValidDescription": "Ваш ключ поддержки был проверен. Спасибо за поддержку!",
|
||||||
"supportKeyErrorValidationDescription": "Failed to validate supporter key.",
|
"supportKeyErrorValidationDescription": "Не удалось проверить ключ поддержки.",
|
||||||
"supportKey": "Support Development and Adopt a Pangolin!",
|
"supportKey": "Поддержите разработку и усыновите Панголина!",
|
||||||
"supportKeyDescription": "Приобретите ключ поддержки, чтобы помочь нам продолжать разработку Pangolin для сообщества. Ваш вклад позволяет нам уделять больше времени поддержке и добавлению новых функций в приложение для всех. Мы никогда не будем использовать это для платного доступа к функциям. Это отдельно от любой коммерческой версии.",
|
"supportKeyDescription": "Приобретите ключ поддержки, чтобы помочь нам продолжать разработку Pangolin для сообщества. Ваш вклад позволяет нам уделять больше времени поддержке и добавлению новых функций в приложение для всех. Мы никогда не будем использовать это для платного доступа к функциям. Это отдельно от любой коммерческой версии.",
|
||||||
"supportKeyPet": "You will also get to adopt and meet your very own pet Pangolin!",
|
"supportKeyPet": "Вы также сможете усыновить и встретить вашего собственного питомца Панголина!",
|
||||||
"supportKeyPurchase": "Payments are processed via GitHub. Afterward, you can retrieve your key on",
|
"supportKeyPurchase": "Платежи обрабатываются через GitHub. После этого вы сможете получить свой ключ на",
|
||||||
"supportKeyPurchaseLink": "our website",
|
"supportKeyPurchaseLink": "нашем сайте",
|
||||||
"supportKeyPurchase2": "and redeem it here.",
|
"supportKeyPurchase2": "и активировать его здесь.",
|
||||||
"supportKeyLearnMore": "Learn more.",
|
"supportKeyLearnMore": "Узнать больше.",
|
||||||
"supportKeyOptions": "Please select the option that best suits you.",
|
"supportKeyOptions": "Пожалуйста, выберите подходящий вам вариант.",
|
||||||
"supportKetOptionFull": "Full Supporter",
|
"supportKetOptionFull": "Полная поддержка",
|
||||||
"forWholeServer": "For the whole server",
|
"forWholeServer": "За весь сервер",
|
||||||
"lifetimePurchase": "Lifetime purchase",
|
"lifetimePurchase": "Пожизненная покупка",
|
||||||
"supporterStatus": "Supporter status",
|
"supporterStatus": "Статус поддержки",
|
||||||
"buy": "Buy",
|
"buy": "Купить",
|
||||||
"supportKeyOptionLimited": "Limited Supporter",
|
"supportKeyOptionLimited": "Лимитированная поддержка",
|
||||||
"forFiveUsers": "For 5 or less users",
|
"forFiveUsers": "За 5 или меньше пользователей",
|
||||||
"supportKeyRedeem": "Redeem Supporter Key",
|
"supportKeyRedeem": "Использовать ключ Поддержки",
|
||||||
"supportKeyHideSevenDays": "Hide for 7 days",
|
"supportKeyHideSevenDays": "Скрыть на 7 дней",
|
||||||
"supportKeyEnter": "Enter Supporter Key",
|
"supportKeyEnter": "Введите ключ поддержки",
|
||||||
"supportKeyEnterDescription": "Meet your very own pet Pangolin!",
|
"supportKeyEnterDescription": "Встречайте своего питомца Панголина!",
|
||||||
"githubUsername": "GitHub Username",
|
"githubUsername": "Имя пользователя Github",
|
||||||
"supportKeyInput": "Supporter Key",
|
"supportKeyInput": "Ключ поддержки",
|
||||||
"supportKeyBuy": "Buy Supporter Key",
|
"supportKeyBuy": "Ключ поддержки",
|
||||||
"logoutError": "Error logging out",
|
"logoutError": "Ошибка при выходе",
|
||||||
"signingAs": "Signed in as",
|
"signingAs": "Вы вошли как",
|
||||||
"serverAdmin": "Server Admin",
|
"serverAdmin": "Администратор сервера",
|
||||||
"otpEnable": "Enable Two-factor",
|
"managedSelfhosted": "Управляемый с самовывоза",
|
||||||
"otpDisable": "Disable Two-factor",
|
"otpEnable": "Включить Двухфакторную Аутентификацию",
|
||||||
"logout": "Log Out",
|
"otpDisable": "Отключить двухфакторную аутентификацию",
|
||||||
"licenseTierProfessionalRequired": "Professional Edition Required",
|
"logout": "Выйти",
|
||||||
|
"licenseTierProfessionalRequired": "Требуется профессиональная версия",
|
||||||
"licenseTierProfessionalRequiredDescription": "Эта функция доступна только в профессиональной версии.",
|
"licenseTierProfessionalRequiredDescription": "Эта функция доступна только в профессиональной версии.",
|
||||||
"actionGetOrg": "Get Organization",
|
"actionGetOrg": "Получить организацию",
|
||||||
"actionUpdateOrg": "Update Organization",
|
"updateOrgUser": "Обновить пользователя Org",
|
||||||
"actionUpdateUser": "Update User",
|
"createOrgUser": "Создать пользователя Org",
|
||||||
"actionGetUser": "Get User",
|
"actionUpdateOrg": "Обновить организацию",
|
||||||
"actionGetOrgUser": "Get Organization User",
|
"actionUpdateUser": "Обновить пользователя",
|
||||||
"actionListOrgDomains": "List Organization Domains",
|
"actionGetUser": "Получить пользователя",
|
||||||
"actionCreateSite": "Create Site",
|
"actionGetOrgUser": "Получить пользователя организации",
|
||||||
"actionDeleteSite": "Delete Site",
|
"actionListOrgDomains": "Список доменов организации",
|
||||||
"actionGetSite": "Get Site",
|
"actionCreateSite": "Создать сайт",
|
||||||
"actionListSites": "List Sites",
|
"actionDeleteSite": "Удалить сайт",
|
||||||
"actionUpdateSite": "Update Site",
|
"actionGetSite": "Получить сайт",
|
||||||
"actionListSiteRoles": "List Allowed Site Roles",
|
"actionListSites": "Список сайтов",
|
||||||
"actionCreateResource": "Create Resource",
|
"actionApplyBlueprint": "Применить чертёж",
|
||||||
"actionDeleteResource": "Delete Resource",
|
"setupToken": "Код настройки",
|
||||||
"actionGetResource": "Get Resource",
|
"setupTokenDescription": "Введите токен настройки из консоли сервера.",
|
||||||
"actionListResource": "List Resources",
|
"setupTokenRequired": "Токен настройки обязателен",
|
||||||
"actionUpdateResource": "Update Resource",
|
"actionUpdateSite": "Обновить сайт",
|
||||||
"actionListResourceUsers": "List Resource Users",
|
"actionListSiteRoles": "Список разрешенных ролей сайта",
|
||||||
"actionSetResourceUsers": "Set Resource Users",
|
"actionCreateResource": "Создать ресурс",
|
||||||
"actionSetAllowedResourceRoles": "Set Allowed Resource Roles",
|
"actionDeleteResource": "Удалить ресурс",
|
||||||
"actionListAllowedResourceRoles": "List Allowed Resource Roles",
|
"actionGetResource": "Получить ресурсы",
|
||||||
"actionSetResourcePassword": "Set Resource Password",
|
"actionListResource": "Список ресурсов",
|
||||||
"actionSetResourcePincode": "Set Resource Pincode",
|
"actionUpdateResource": "Обновить ресурс",
|
||||||
"actionSetResourceEmailWhitelist": "Set Resource Email Whitelist",
|
"actionListResourceUsers": "Список пользователей ресурсов",
|
||||||
"actionGetResourceEmailWhitelist": "Get Resource Email Whitelist",
|
"actionSetResourceUsers": "Список пользователей ресурсов",
|
||||||
"actionCreateTarget": "Create Target",
|
"actionSetAllowedResourceRoles": "Набор разрешенных ролей ресурсов",
|
||||||
"actionDeleteTarget": "Delete Target",
|
"actionListAllowedResourceRoles": "Список разрешенных ролей сайта",
|
||||||
"actionGetTarget": "Get Target",
|
"actionSetResourcePassword": "Задать пароль ресурса",
|
||||||
"actionListTargets": "List Targets",
|
"actionSetResourcePincode": "Установить ПИН-код ресурса",
|
||||||
"actionUpdateTarget": "Update Target",
|
"actionSetResourceEmailWhitelist": "Настроить белый список ресурсов email",
|
||||||
"actionCreateRole": "Create Role",
|
"actionGetResourceEmailWhitelist": "Получить белый список ресурсов email",
|
||||||
"actionDeleteRole": "Delete Role",
|
"actionCreateTarget": "Создать цель",
|
||||||
"actionGetRole": "Get Role",
|
"actionDeleteTarget": "Удалить цель",
|
||||||
"actionListRole": "List Roles",
|
"actionGetTarget": "Получить цель",
|
||||||
"actionUpdateRole": "Update Role",
|
"actionListTargets": "Список целей",
|
||||||
"actionListAllowedRoleResources": "List Allowed Role Resources",
|
"actionUpdateTarget": "Обновить цель",
|
||||||
|
"actionCreateRole": "Создать роль",
|
||||||
|
"actionDeleteRole": "Удалить роль",
|
||||||
|
"actionGetRole": "Получить Роль",
|
||||||
|
"actionListRole": "Список ролей",
|
||||||
|
"actionUpdateRole": "Обновить роль",
|
||||||
|
"actionListAllowedRoleResources": "Список разрешенных ролей сайта",
|
||||||
"actionInviteUser": "Пригласить пользователя",
|
"actionInviteUser": "Пригласить пользователя",
|
||||||
"actionRemoveUser": "Удалить пользователя",
|
"actionRemoveUser": "Удалить пользователя",
|
||||||
"actionListUsers": "Список пользователей",
|
"actionListUsers": "Список пользователей",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Удалить политику IDP организации",
|
"actionDeleteIdpOrg": "Удалить политику IDP организации",
|
||||||
"actionListIdpOrgs": "Список организаций IDP",
|
"actionListIdpOrgs": "Список организаций IDP",
|
||||||
"actionUpdateIdpOrg": "Обновить организацию IDP",
|
"actionUpdateIdpOrg": "Обновить организацию IDP",
|
||||||
|
"actionCreateClient": "Создать Клиента",
|
||||||
|
"actionDeleteClient": "Удалить Клиента",
|
||||||
|
"actionUpdateClient": "Обновить Клиента",
|
||||||
|
"actionListClients": "Список Клиентов",
|
||||||
|
"actionGetClient": "Получить Клиента",
|
||||||
|
"actionCreateSiteResource": "Создать ресурс сайта",
|
||||||
|
"actionDeleteSiteResource": "Удалить ресурс сайта ",
|
||||||
|
"actionGetSiteResource": "Получить ресурс сайта",
|
||||||
|
"actionListSiteResources": "Список ресурсов сайта",
|
||||||
|
"actionUpdateSiteResource": "Обновить ресурс сайта",
|
||||||
|
"actionListInvitations": "Список приглашений",
|
||||||
"noneSelected": "Ничего не выбрано",
|
"noneSelected": "Ничего не выбрано",
|
||||||
"orgNotFound2": "Организации не найдены.",
|
"orgNotFound2": "Организации не найдены.",
|
||||||
"searchProgress": "Поиск...",
|
"searchProgress": "Поиск...",
|
||||||
@@ -1093,10 +1140,10 @@
|
|||||||
"sidebarAllUsers": "Все пользователи",
|
"sidebarAllUsers": "Все пользователи",
|
||||||
"sidebarIdentityProviders": "Поставщики удостоверений",
|
"sidebarIdentityProviders": "Поставщики удостоверений",
|
||||||
"sidebarLicense": "Лицензия",
|
"sidebarLicense": "Лицензия",
|
||||||
"sidebarClients": "Clients (Beta)",
|
"sidebarClients": "Клиенты (бета)",
|
||||||
"sidebarDomains": "Domains",
|
"sidebarDomains": "Домены",
|
||||||
"enableDockerSocket": "Включить Docker Socket",
|
"enableDockerSocket": "Включить чертёж Docker",
|
||||||
"enableDockerSocketDescription": "Включить обнаружение Docker Socket для заполнения информации о контейнерах. Путь к сокету должен быть предоставлен Newt.",
|
"enableDockerSocketDescription": "Включить scraping ярлыка Docker Socket для ярлыков чертежей. Путь к сокету должен быть предоставлен в Newt.",
|
||||||
"enableDockerSocketLink": "Узнать больше",
|
"enableDockerSocketLink": "Узнать больше",
|
||||||
"viewDockerContainers": "Просмотр контейнеров Docker",
|
"viewDockerContainers": "Просмотр контейнеров Docker",
|
||||||
"containersIn": "Контейнеры в {siteName}",
|
"containersIn": "Контейнеры в {siteName}",
|
||||||
@@ -1134,189 +1181,547 @@
|
|||||||
"dark": "тёмная",
|
"dark": "тёмная",
|
||||||
"system": "системная",
|
"system": "системная",
|
||||||
"theme": "Тема",
|
"theme": "Тема",
|
||||||
"subnetRequired": "Subnet is required",
|
"subnetRequired": "Требуется подсеть",
|
||||||
"initialSetupTitle": "Начальная настройка сервера",
|
"initialSetupTitle": "Начальная настройка сервера",
|
||||||
"initialSetupDescription": "Создайте первоначальную учётную запись администратора сервера. Может существовать только один администратор сервера. Вы всегда можете изменить эти учётные данные позже.",
|
"initialSetupDescription": "Создайте первоначальную учётную запись администратора сервера. Может существовать только один администратор сервера. Вы всегда можете изменить эти учётные данные позже.",
|
||||||
"createAdminAccount": "Создать учётную запись администратора",
|
"createAdminAccount": "Создать учётную запись администратора",
|
||||||
"setupErrorCreateAdmin": "Произошла ошибка при создании учётной записи администратора сервера.",
|
"setupErrorCreateAdmin": "Произошла ошибка при создании учётной записи администратора сервера.",
|
||||||
"certificateStatus": "Certificate Status",
|
"certificateStatus": "Статус сертификата",
|
||||||
"loading": "Loading",
|
"loading": "Загрузка",
|
||||||
"restart": "Restart",
|
"restart": "Перезагрузка",
|
||||||
"domains": "Domains",
|
"domains": "Домены",
|
||||||
"domainsDescription": "Manage domains for your organization",
|
"domainsDescription": "Управление доменами для вашей организации",
|
||||||
"domainsSearch": "Search domains...",
|
"domainsSearch": "Поиск доменов...",
|
||||||
"domainAdd": "Add Domain",
|
"domainAdd": "Добавить Домен",
|
||||||
"domainAddDescription": "Register a new domain with your organization",
|
"domainAddDescription": "Зарегистрировать новый домен в вашей организации",
|
||||||
"domainCreate": "Create Domain",
|
"domainCreate": "Создать Домен",
|
||||||
"domainCreatedDescription": "Domain created successfully",
|
"domainCreatedDescription": "Домен успешно создан",
|
||||||
"domainDeletedDescription": "Domain deleted successfully",
|
"domainDeletedDescription": "Домен успешно удален",
|
||||||
"domainQuestionRemove": "Are you sure you want to remove the domain {domain} from your account?",
|
"domainQuestionRemove": "Вы уверены, что хотите удалить домен {domain} из вашего аккаунта?",
|
||||||
"domainMessageRemove": "Once removed, the domain will no longer be associated with your account.",
|
"domainMessageRemove": "После удаления домен больше не будет связан с вашей учетной записью.",
|
||||||
"domainMessageConfirm": "To confirm, please type the domain name below.",
|
"domainMessageConfirm": "Для подтверждения введите ниже имя домена.",
|
||||||
"domainConfirmDelete": "Confirm Delete Domain",
|
"domainConfirmDelete": "Подтвердить удаление домена",
|
||||||
"domainDelete": "Delete Domain",
|
"domainDelete": "Удалить Домен",
|
||||||
"domain": "Domain",
|
"domain": "Домен",
|
||||||
"selectDomainTypeNsName": "Domain Delegation (NS)",
|
"selectDomainTypeNsName": "Делегация домена (NS)",
|
||||||
"selectDomainTypeNsDescription": "This domain and all its subdomains. Use this when you want to control an entire domain zone.",
|
"selectDomainTypeNsDescription": "Этот домен и все его субдомены. Используйте это, когда вы хотите управлять всей доменной зоной.",
|
||||||
"selectDomainTypeCnameName": "Single Domain (CNAME)",
|
"selectDomainTypeCnameName": "Одиночный домен (CNAME)",
|
||||||
"selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.",
|
"selectDomainTypeCnameDescription": "Только этот конкретный домен. Используйте это для отдельных субдоменов или отдельных записей домена.",
|
||||||
"selectDomainTypeWildcardName": "Wildcard Domain",
|
"selectDomainTypeWildcardName": "Подставной домен",
|
||||||
"selectDomainTypeWildcardDescription": "This domain and its subdomains.",
|
"selectDomainTypeWildcardDescription": "Этот домен и его субдомены.",
|
||||||
"domainDelegation": "Single Domain",
|
"domainDelegation": "Единый домен",
|
||||||
"selectType": "Select a type",
|
"selectType": "Выберите тип",
|
||||||
"actions": "Actions",
|
"actions": "Действия",
|
||||||
"refresh": "Refresh",
|
"refresh": "Обновить",
|
||||||
"refreshError": "Failed to refresh data",
|
"refreshError": "Не удалось обновить данные",
|
||||||
"verified": "Verified",
|
"verified": "Подтверждено",
|
||||||
"pending": "Pending",
|
"pending": "В ожидании",
|
||||||
"sidebarBilling": "Billing",
|
"sidebarBilling": "Выставление счетов",
|
||||||
"billing": "Billing",
|
"billing": "Выставление счетов",
|
||||||
"orgBillingDescription": "Manage your billing information and subscriptions",
|
"orgBillingDescription": "Управляйте информацией о выставлении счетов и подписками",
|
||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
"pangolinHosted": "Pangolin Hosted",
|
"pangolinHosted": "Pangolin Hosted",
|
||||||
"fossorial": "Fossorial",
|
"fossorial": "Fossorial",
|
||||||
"completeAccountSetup": "Complete Account Setup",
|
"completeAccountSetup": "Завершите настройку аккаунта",
|
||||||
"completeAccountSetupDescription": "Set your password to get started",
|
"completeAccountSetupDescription": "Установите ваш пароль, чтобы начать",
|
||||||
"accountSetupSent": "We'll send an account setup code to this email address.",
|
"accountSetupSent": "Мы отправим код для настройки аккаунта на этот email адрес.",
|
||||||
"accountSetupCode": "Setup Code",
|
"accountSetupCode": "Код настройки",
|
||||||
"accountSetupCodeDescription": "Check your email for the setup code.",
|
"accountSetupCodeDescription": "Проверьте вашу почту для получения кода настройки.",
|
||||||
"passwordCreate": "Create Password",
|
"passwordCreate": "Создать пароль",
|
||||||
"passwordCreateConfirm": "Confirm Password",
|
"passwordCreateConfirm": "Подтвердите пароль",
|
||||||
"accountSetupSubmit": "Send Setup Code",
|
"accountSetupSubmit": "Отправить код настройки",
|
||||||
"completeSetup": "Complete Setup",
|
"completeSetup": "Завершить настройку",
|
||||||
"accountSetupSuccess": "Account setup completed! Welcome to Pangolin!",
|
"accountSetupSuccess": "Настройка аккаунта завершена! Добро пожаловать в Pangolin!",
|
||||||
"documentation": "Documentation",
|
"documentation": "Документация",
|
||||||
"saveAllSettings": "Save All Settings",
|
"saveAllSettings": "Сохранить все настройки",
|
||||||
"settingsUpdated": "Settings updated",
|
"settingsUpdated": "Настройки обновлены",
|
||||||
"settingsUpdatedDescription": "All settings have been updated successfully",
|
"settingsUpdatedDescription": "Все настройки успешно обновлены",
|
||||||
"settingsErrorUpdate": "Failed to update settings",
|
"settingsErrorUpdate": "Не удалось обновить настройки",
|
||||||
"settingsErrorUpdateDescription": "An error occurred while updating settings",
|
"settingsErrorUpdateDescription": "Произошла ошибка при обновлении настроек",
|
||||||
"sidebarCollapse": "Collapse",
|
"sidebarCollapse": "Свернуть",
|
||||||
"sidebarExpand": "Expand",
|
"sidebarExpand": "Развернуть",
|
||||||
"newtUpdateAvailable": "Update Available",
|
"newtUpdateAvailable": "Доступно обновление",
|
||||||
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.",
|
"newtUpdateAvailableInfo": "Доступна новая версия Newt. Пожалуйста, обновитесь до последней версии для лучшего опыта.",
|
||||||
"domainPickerEnterDomain": "Domain",
|
"domainPickerEnterDomain": "Домен",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com, or just myapp",
|
"domainPickerPlaceholder": "myapp.example.com",
|
||||||
"domainPickerDescription": "Enter the full domain of the resource to see available options.",
|
"domainPickerDescription": "Введите полный домен ресурса, чтобы увидеть доступные опции.",
|
||||||
"domainPickerDescriptionSaas": "Enter a full domain, subdomain, or just a name to see available options",
|
"domainPickerDescriptionSaas": "Введите полный домен, поддомен или просто имя, чтобы увидеть доступные опции",
|
||||||
"domainPickerTabAll": "All",
|
"domainPickerTabAll": "Все",
|
||||||
"domainPickerTabOrganization": "Organization",
|
"domainPickerTabOrganization": "Организация",
|
||||||
"domainPickerTabProvided": "Provided",
|
"domainPickerTabProvided": "Предоставлено",
|
||||||
"domainPickerSortAsc": "A-Z",
|
"domainPickerSortAsc": "А-Я",
|
||||||
"domainPickerSortDesc": "Z-A",
|
"domainPickerSortDesc": "Я-А",
|
||||||
"domainPickerCheckingAvailability": "Checking availability...",
|
"domainPickerCheckingAvailability": "Проверка доступности...",
|
||||||
"domainPickerNoMatchingDomains": "No matching domains found. Try a different domain or check your organization's domain settings.",
|
"domainPickerNoMatchingDomains": "Не найдены сопоставимые домены. Попробуйте другой домен или проверьте настройки доменов вашей организации.",
|
||||||
"domainPickerOrganizationDomains": "Organization Domains",
|
"domainPickerOrganizationDomains": "Домены организации",
|
||||||
"domainPickerProvidedDomains": "Provided Domains",
|
"domainPickerProvidedDomains": "Предоставленные домены",
|
||||||
"domainPickerSubdomain": "Subdomain: {subdomain}",
|
"domainPickerSubdomain": "Поддомен: {subdomain}",
|
||||||
"domainPickerNamespace": "Namespace: {namespace}",
|
"domainPickerNamespace": "Пространство имен: {namespace}",
|
||||||
"domainPickerShowMore": "Show More",
|
"domainPickerShowMore": "Показать еще",
|
||||||
"domainNotFound": "Domain Not Found",
|
"regionSelectorTitle": "Выберите регион",
|
||||||
"domainNotFoundDescription": "This resource is disabled because the domain no longer exists our system. Please set a new domain for this resource.",
|
"regionSelectorInfo": "Выбор региона помогает нам обеспечить лучшее качество обслуживания для вашего расположения. Вам необязательно находиться в том же регионе, что и ваш сервер.",
|
||||||
"failed": "Failed",
|
"regionSelectorPlaceholder": "Выбор региона",
|
||||||
"createNewOrgDescription": "Create a new organization",
|
"regionSelectorComingSoon": "Скоро будет",
|
||||||
"organization": "Organization",
|
"billingLoadingSubscription": "Загрузка подписки...",
|
||||||
"port": "Port",
|
"billingFreeTier": "Бесплатный уровень",
|
||||||
"securityKeyManage": "Manage Security Keys",
|
"billingWarningOverLimit": "Предупреждение: Вы превысили одну или несколько границ использования. Ваши сайты не подключатся, пока вы не измените подписку или не скорректируете использование.",
|
||||||
"securityKeyDescription": "Add or remove security keys for passwordless authentication",
|
"billingUsageLimitsOverview": "Обзор лимитов использования",
|
||||||
"securityKeyRegister": "Register New Security Key",
|
"billingMonitorUsage": "Контролируйте использование в соответствии с установленными лимитами. Если вам требуется увеличение лимитов, пожалуйста, свяжитесь с нами support@fossorial.io.",
|
||||||
"securityKeyList": "Your Security Keys",
|
"billingDataUsage": "Использование данных",
|
||||||
"securityKeyNone": "No security keys registered yet",
|
"billingOnlineTime": "Время работы сайта",
|
||||||
"securityKeyNameRequired": "Name is required",
|
"billingUsers": "Активные пользователи",
|
||||||
"securityKeyRemove": "Remove",
|
"billingDomains": "Активные домены",
|
||||||
"securityKeyLastUsed": "Last used: {date}",
|
"billingRemoteExitNodes": "Активные самоуправляемые узлы",
|
||||||
"securityKeyNameLabel": "Security Key Name",
|
"billingNoLimitConfigured": "Лимит не установлен",
|
||||||
"securityKeyRegisterSuccess": "Security key registered successfully",
|
"billingEstimatedPeriod": "Предполагаемый период выставления счетов",
|
||||||
"securityKeyRegisterError": "Failed to register security key",
|
"billingIncludedUsage": "Включенное использование",
|
||||||
"securityKeyRemoveSuccess": "Security key removed successfully",
|
"billingIncludedUsageDescription": "Использование, включенное в ваш текущий план подписки",
|
||||||
"securityKeyRemoveError": "Failed to remove security key",
|
"billingFreeTierIncludedUsage": "Бесплатное использование ограничений",
|
||||||
"securityKeyLoadError": "Failed to load security keys",
|
"billingIncluded": "включено",
|
||||||
"securityKeyLogin": "Continue with security key",
|
"billingEstimatedTotal": "Предполагаемая сумма:",
|
||||||
"securityKeyAuthError": "Failed to authenticate with security key",
|
"billingNotes": "Заметки",
|
||||||
"securityKeyRecommendation": "Register a backup security key on another device to ensure you always have access to your account.",
|
"billingEstimateNote": "Это приблизительная оценка на основании вашего текущего использования.",
|
||||||
"registering": "Registering...",
|
"billingActualChargesMayVary": "Фактические начисления могут отличаться.",
|
||||||
"securityKeyPrompt": "Please verify your identity using your security key. Make sure your security key is connected and ready.",
|
"billingBilledAtEnd": "С вас будет выставлен счет в конце периода выставления счетов.",
|
||||||
"securityKeyBrowserNotSupported": "Your browser doesn't support security keys. Please use a modern browser like Chrome, Firefox, or Safari.",
|
"billingModifySubscription": "Изменить подписку",
|
||||||
"securityKeyPermissionDenied": "Please allow access to your security key to continue signing in.",
|
"billingStartSubscription": "Начать подписку",
|
||||||
"securityKeyRemovedTooQuickly": "Please keep your security key connected until the sign-in process completes.",
|
"billingRecurringCharge": "Периодический взнос",
|
||||||
"securityKeyNotSupported": "Your security key may not be compatible. Please try a different security key.",
|
"billingManageSubscriptionSettings": "Управляйте настройками и предпочтениями вашей подписки",
|
||||||
"securityKeyUnknownError": "There was a problem using your security key. Please try again.",
|
"billingNoActiveSubscription": "У вас нет активной подписки. Начните подписку, чтобы увеличить лимиты использования.",
|
||||||
"twoFactorRequired": "Two-factor authentication is required to register a security key.",
|
"billingFailedToLoadSubscription": "Не удалось загрузить подписку",
|
||||||
"twoFactor": "Two-Factor Authentication",
|
"billingFailedToLoadUsage": "Не удалось загрузить использование",
|
||||||
"adminEnabled2FaOnYourAccount": "Your administrator has enabled two-factor authentication for {email}. Please complete the setup process to continue.",
|
"billingFailedToGetCheckoutUrl": "Не удалось получить URL-адрес для оплаты",
|
||||||
"continueToApplication": "Continue to Application",
|
"billingPleaseTryAgainLater": "Пожалуйста, повторите попытку позже.",
|
||||||
"securityKeyAdd": "Add Security Key",
|
"billingCheckoutError": "Ошибка при оформлении заказа",
|
||||||
"securityKeyRegisterTitle": "Register New Security Key",
|
"billingFailedToGetPortalUrl": "Не удалось получить URL-адрес портала",
|
||||||
"securityKeyRegisterDescription": "Connect your security key and enter a name to identify it",
|
"billingPortalError": "Ошибка портала",
|
||||||
"securityKeyTwoFactorRequired": "Two-Factor Authentication Required",
|
"billingDataUsageInfo": "Вы несете ответственность за все данные, переданные через безопасные туннели при подключении к облаку. Это включает как входящий, так и исходящий трафик на всех ваших сайтах. При достижении лимита ваши сайты будут отключаться до тех пор, пока вы не обновите план или не уменьшите его использование. При использовании узлов не взимается плата.",
|
||||||
"securityKeyTwoFactorDescription": "Please enter your two-factor authentication code to register the security key",
|
"billingOnlineTimeInfo": "Вы тарифицируете на то, как долго ваши сайты будут подключены к облаку. Например, 44 640 минут равны одному сайту, работающему круглосуточно за весь месяц. Когда вы достигните лимита, ваши сайты будут отключаться до тех пор, пока вы не обновите тарифный план или не сократите нагрузку. При использовании узлов не тарифицируется.",
|
||||||
"securityKeyTwoFactorRemoveDescription": "Please enter your two-factor authentication code to remove the security key",
|
"billingUsersInfo": "С вас взимается плата за каждого пользователя в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей пользователей в вашей организации.",
|
||||||
"securityKeyTwoFactorCode": "Two-Factor Code",
|
"billingDomainInfo": "С вас взимается плата за каждый домен в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных учетных записей доменов в вашей организации.",
|
||||||
"securityKeyRemoveTitle": "Remove Security Key",
|
"billingRemoteExitNodesInfo": "С вас взимается плата за каждый управляемый узел в вашей организации. Оплата рассчитывается ежедневно исходя из количества активных управляемых узлов в вашей организации.",
|
||||||
"securityKeyRemoveDescription": "Enter your password to remove the security key \"{name}\"",
|
"domainNotFound": "Домен не найден",
|
||||||
"securityKeyNoKeysRegistered": "No security keys registered",
|
"domainNotFoundDescription": "Этот ресурс отключен, так как домен больше не существует в нашей системе. Пожалуйста, установите новый домен для этого ресурса.",
|
||||||
"securityKeyNoKeysDescription": "Add a security key to enhance your account security",
|
"failed": "Ошибка",
|
||||||
"createDomainRequired": "Domain is required",
|
"createNewOrgDescription": "Создать новую организацию",
|
||||||
"createDomainAddDnsRecords": "Add DNS Records",
|
"organization": "Организация",
|
||||||
"createDomainAddDnsRecordsDescription": "Add the following DNS records to your domain provider to complete the setup.",
|
"port": "Порт",
|
||||||
"createDomainNsRecords": "NS Records",
|
"securityKeyManage": "Управление ключами безопасности",
|
||||||
"createDomainRecord": "Record",
|
"securityKeyDescription": "Добавить или удалить ключи безопасности для аутентификации без пароля",
|
||||||
"createDomainType": "Type:",
|
"securityKeyRegister": "Зарегистрировать новый ключ безопасности",
|
||||||
"createDomainName": "Name:",
|
"securityKeyList": "Ваши ключи безопасности",
|
||||||
"createDomainValue": "Value:",
|
"securityKeyNone": "Ключи безопасности еще не зарегистрированы",
|
||||||
"createDomainCnameRecords": "CNAME Records",
|
"securityKeyNameRequired": "Имя обязательно",
|
||||||
"createDomainARecords": "A Records",
|
"securityKeyRemove": "Удалить",
|
||||||
"createDomainRecordNumber": "Record {number}",
|
"securityKeyLastUsed": "Последнее использование: {date}",
|
||||||
"createDomainTxtRecords": "TXT Records",
|
"securityKeyNameLabel": "Имя ключа безопасности",
|
||||||
"createDomainSaveTheseRecords": "Save These Records",
|
"securityKeyRegisterSuccess": "Ключ безопасности успешно зарегистрирован",
|
||||||
"createDomainSaveTheseRecordsDescription": "Make sure to save these DNS records as you will not see them again.",
|
"securityKeyRegisterError": "Не удалось зарегистрировать ключ безопасности",
|
||||||
"createDomainDnsPropagation": "DNS Propagation",
|
"securityKeyRemoveSuccess": "Ключ безопасности успешно удален",
|
||||||
"createDomainDnsPropagationDescription": "DNS changes may take some time to propagate across the internet. This can take anywhere from a few minutes to 48 hours, depending on your DNS provider and TTL settings.",
|
"securityKeyRemoveError": "Не удалось удалить ключ безопасности",
|
||||||
"resourcePortRequired": "Port number is required for non-HTTP resources",
|
"securityKeyLoadError": "Не удалось загрузить ключи безопасности",
|
||||||
"resourcePortNotAllowed": "Port number should not be set for HTTP resources",
|
"securityKeyLogin": "Продолжить с ключом безопасности",
|
||||||
|
"securityKeyAuthError": "Не удалось аутентифицироваться с ключом безопасности",
|
||||||
|
"securityKeyRecommendation": "Зарегистрируйте резервный ключ безопасности на другом устройстве, чтобы всегда иметь доступ к вашему аккаунту.",
|
||||||
|
"registering": "Регистрация...",
|
||||||
|
"securityKeyPrompt": "Пожалуйста, подтвердите свою личность с использованием вашего ключа безопасности. Убедитесь, что ваш ключ безопасности подключен и готов.",
|
||||||
|
"securityKeyBrowserNotSupported": "Ваш браузер не поддерживает ключи безопасности. Пожалуйста, используйте современный браузер, такой как Chrome, Firefox или Safari.",
|
||||||
|
"securityKeyPermissionDenied": "Пожалуйста, разрешите доступ к вашему ключу безопасности, чтобы продолжить вход.",
|
||||||
|
"securityKeyRemovedTooQuickly": "Пожалуйста, держите ваш ключ безопасности подключенным, пока процесс входа не завершится.",
|
||||||
|
"securityKeyNotSupported": "Ваш ключ безопасности может быть несовместим. Попробуйте другой ключ безопасности.",
|
||||||
|
"securityKeyUnknownError": "Произошла проблема при использовании вашего ключа безопасности. Пожалуйста, попробуйте еще раз.",
|
||||||
|
"twoFactorRequired": "Для регистрации ключа безопасности требуется двухфакторная аутентификация.",
|
||||||
|
"twoFactor": "Двухфакторная аутентификация",
|
||||||
|
"adminEnabled2FaOnYourAccount": "Ваш администратор включил двухфакторную аутентификацию для {email}. Пожалуйста, завершите процесс настройки, чтобы продолжить.",
|
||||||
|
"continueToApplication": "Перейти к приложению",
|
||||||
|
"securityKeyAdd": "Добавить ключ безопасности",
|
||||||
|
"securityKeyRegisterTitle": "Регистрация нового ключа безопасности",
|
||||||
|
"securityKeyRegisterDescription": "Подключите свой ключ безопасности и введите имя для его идентификации",
|
||||||
|
"securityKeyTwoFactorRequired": "Требуется двухфакторная аутентификация",
|
||||||
|
"securityKeyTwoFactorDescription": "Пожалуйста, введите ваш код двухфакторной аутентификации для регистрации ключа безопасности",
|
||||||
|
"securityKeyTwoFactorRemoveDescription": "Пожалуйста, введите ваш код двухфакторной аутентификации для удаления ключа безопасности",
|
||||||
|
"securityKeyTwoFactorCode": "Код двухфакторной аутентификации",
|
||||||
|
"securityKeyRemoveTitle": "Удалить ключ безопасности",
|
||||||
|
"securityKeyRemoveDescription": "Введите ваш пароль для удаления ключа безопасности \"{name}\"",
|
||||||
|
"securityKeyNoKeysRegistered": "Ключи безопасности не зарегистрированы",
|
||||||
|
"securityKeyNoKeysDescription": "Добавьте ключ безопасности, чтобы повысить безопасность вашего аккаунта",
|
||||||
|
"createDomainRequired": "Домен обязателен",
|
||||||
|
"createDomainAddDnsRecords": "Добавить DNS записи",
|
||||||
|
"createDomainAddDnsRecordsDescription": "Добавьте следующие DNS записи у вашего провайдера доменных имен для завершения настройки.",
|
||||||
|
"createDomainNsRecords": "NS Записи",
|
||||||
|
"createDomainRecord": "Запись",
|
||||||
|
"createDomainType": "Тип:",
|
||||||
|
"createDomainName": "Имя:",
|
||||||
|
"createDomainValue": "Значение:",
|
||||||
|
"createDomainCnameRecords": "CNAME Записи",
|
||||||
|
"createDomainARecords": "A Записи",
|
||||||
|
"createDomainRecordNumber": "Запись {number}",
|
||||||
|
"createDomainTxtRecords": "TXT Записи",
|
||||||
|
"createDomainSaveTheseRecords": "Сохранить эти записи",
|
||||||
|
"createDomainSaveTheseRecordsDescription": "Обязательно сохраните эти DNS записи, так как вы их больше не увидите.",
|
||||||
|
"createDomainDnsPropagation": "Распространение DNS",
|
||||||
|
"createDomainDnsPropagationDescription": "Изменения DNS могут занять некоторое время для распространения через интернет. Это может занять от нескольких минут до 48 часов в зависимости от вашего DNS провайдера и настроек TTL.",
|
||||||
|
"resourcePortRequired": "Номер порта необходим для не-HTTP ресурсов",
|
||||||
|
"resourcePortNotAllowed": "Номер порта не должен быть установлен для HTTP ресурсов",
|
||||||
|
"billingPricingCalculatorLink": "Калькулятор расценок",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "I agree to the",
|
"IAgreeToThe": "Я согласен с",
|
||||||
"termsOfService": "terms of service",
|
"termsOfService": "условия использования",
|
||||||
"and": "and",
|
"and": "и",
|
||||||
"privacyPolicy": "privacy policy"
|
"privacyPolicy": "политика конфиденциальности"
|
||||||
},
|
},
|
||||||
"siteRequired": "Site is required.",
|
"siteRequired": "Необходимо указать сайт.",
|
||||||
"olmTunnel": "Olm Tunnel",
|
"olmTunnel": "Olm Туннель",
|
||||||
"olmTunnelDescription": "Use Olm for client connectivity",
|
"olmTunnelDescription": "Используйте Olm для подключений клиентов",
|
||||||
"errorCreatingClient": "Error creating client",
|
"errorCreatingClient": "Ошибка при создании клиента",
|
||||||
"clientDefaultsNotFound": "Client defaults not found",
|
"clientDefaultsNotFound": "Настройки клиента по умолчанию не найдены",
|
||||||
"createClient": "Create Client",
|
"createClient": "Создать клиента",
|
||||||
"createClientDescription": "Create a new client for connecting to your sites",
|
"createClientDescription": "Создайте нового клиента для подключения к вашим сайтам",
|
||||||
"seeAllClients": "See All Clients",
|
"seeAllClients": "Просмотреть всех клиентов",
|
||||||
"clientInformation": "Client Information",
|
"clientInformation": "Информация о клиенте",
|
||||||
"clientNamePlaceholder": "Client name",
|
"clientNamePlaceholder": "Имя клиента",
|
||||||
"address": "Address",
|
"address": "Адрес",
|
||||||
"subnetPlaceholder": "Subnet",
|
"subnetPlaceholder": "Подсеть",
|
||||||
"addressDescription": "The address that this client will use for connectivity",
|
"addressDescription": "Адрес, который этот клиент будет использовать для подключения",
|
||||||
"selectSites": "Select sites",
|
"selectSites": "Выберите сайты",
|
||||||
"sitesDescription": "The client will have connectivity to the selected sites",
|
"sitesDescription": "Клиент будет иметь подключение к выбранным сайтам",
|
||||||
"clientInstallOlm": "Install Olm",
|
"clientInstallOlm": "Установить Olm",
|
||||||
"clientInstallOlmDescription": "Get Olm running on your system",
|
"clientInstallOlmDescription": "Запустите Olm на вашей системе",
|
||||||
"clientOlmCredentials": "Olm Credentials",
|
"clientOlmCredentials": "Учётные данные Olm",
|
||||||
"clientOlmCredentialsDescription": "This is how Olm will authenticate with the server",
|
"clientOlmCredentialsDescription": "Так Olm будет аутентифицироваться через сервер",
|
||||||
"olmEndpoint": "Olm Endpoint",
|
"olmEndpoint": "Конечная точка Olm",
|
||||||
"olmId": "Olm ID",
|
"olmId": "Olm ID",
|
||||||
"olmSecretKey": "Olm Secret Key",
|
"olmSecretKey": "Секретный ключ Olm",
|
||||||
"clientCredentialsSave": "Save Your Credentials",
|
"clientCredentialsSave": "Сохраните ваши учётные данные",
|
||||||
"clientCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.",
|
"clientCredentialsSaveDescription": "Вы сможете увидеть их только один раз. Обязательно скопируйте в безопасное место.",
|
||||||
"generalSettingsDescription": "Configure the general settings for this client",
|
"generalSettingsDescription": "Настройте общие параметры для этого клиента",
|
||||||
"clientUpdated": "Client updated",
|
"clientUpdated": "Клиент обновлен",
|
||||||
"clientUpdatedDescription": "The client has been updated.",
|
"clientUpdatedDescription": "Клиент был обновлён.",
|
||||||
"clientUpdateFailed": "Failed to update client",
|
"clientUpdateFailed": "Не удалось обновить клиента",
|
||||||
"clientUpdateError": "An error occurred while updating the client.",
|
"clientUpdateError": "Произошла ошибка при обновлении клиента.",
|
||||||
"sitesFetchFailed": "Failed to fetch sites",
|
"sitesFetchFailed": "Не удалось получить сайты",
|
||||||
"sitesFetchError": "An error occurred while fetching sites.",
|
"sitesFetchError": "Произошла ошибка при получении сайтов.",
|
||||||
"olmErrorFetchReleases": "An error occurred while fetching Olm releases.",
|
"olmErrorFetchReleases": "Произошла ошибка при получении релизов Olm.",
|
||||||
"olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.",
|
"olmErrorFetchLatest": "Произошла ошибка при получении последнего релиза Olm.",
|
||||||
"remoteSubnets": "Remote Subnets",
|
"remoteSubnets": "Удалённые подсети",
|
||||||
"enterCidrRange": "Enter CIDR range",
|
"enterCidrRange": "Введите диапазон CIDR",
|
||||||
"remoteSubnetsDescription": "Add CIDR ranges that can access this site remotely. Use format like 10.0.0.0/24 or 192.168.1.0/24.",
|
"remoteSubnetsDescription": "Добавьте диапазоны адресов CIDR, которые можно получить из этого сайта удаленно, используя клиентов. Используйте формат 10.0.0.0/24. Это относится ТОЛЬКО к подключению через VPN клиентов.",
|
||||||
"resourceEnableProxy": "Enable Public Proxy",
|
"resourceEnableProxy": "Включить публичный прокси",
|
||||||
"resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.",
|
"resourceEnableProxyDescription": "Включите публичное проксирование для этого ресурса. Это позволяет получить доступ к ресурсу извне сети через облако через открытый порт. Требуется конфигурация Traefik.",
|
||||||
"externalProxyEnabled": "External Proxy Enabled"
|
"externalProxyEnabled": "Внешний прокси включен",
|
||||||
|
"addNewTarget": "Добавить новую цель",
|
||||||
|
"targetsList": "Список целей",
|
||||||
|
"targetErrorDuplicateTargetFound": "Обнаружена дублирующаяся цель",
|
||||||
|
"healthCheckHealthy": "Здоровый",
|
||||||
|
"healthCheckUnhealthy": "Нездоровый",
|
||||||
|
"healthCheckUnknown": "Неизвестно",
|
||||||
|
"healthCheck": "Проверка здоровья",
|
||||||
|
"configureHealthCheck": "Настроить проверку здоровья",
|
||||||
|
"configureHealthCheckDescription": "Настройте мониторинг состояния для {target}",
|
||||||
|
"enableHealthChecks": "Включить проверки здоровья",
|
||||||
|
"enableHealthChecksDescription": "Мониторинг здоровья этой цели. При необходимости можно контролировать другую конечную точку.",
|
||||||
|
"healthScheme": "Метод",
|
||||||
|
"healthSelectScheme": "Выберите метод",
|
||||||
|
"healthCheckPath": "Путь",
|
||||||
|
"healthHostname": "IP / хост",
|
||||||
|
"healthPort": "Порт",
|
||||||
|
"healthCheckPathDescription": "Путь к проверке состояния здоровья.",
|
||||||
|
"healthyIntervalSeconds": "Интервал здоровых состояний",
|
||||||
|
"unhealthyIntervalSeconds": "Интервал нездоровых состояний",
|
||||||
|
"IntervalSeconds": "Интервал здоровых состояний",
|
||||||
|
"timeoutSeconds": "Тайм-аут",
|
||||||
|
"timeIsInSeconds": "Время указано в секундах",
|
||||||
|
"retryAttempts": "Количество попыток повторного запроса",
|
||||||
|
"expectedResponseCodes": "Ожидаемые коды ответов",
|
||||||
|
"expectedResponseCodesDescription": "HTTP-код состояния, указывающий на здоровое состояние. Если оставить пустым, 200-300 считается здоровым.",
|
||||||
|
"customHeaders": "Пользовательские заголовки",
|
||||||
|
"customHeadersDescription": "Заголовки новой строки, разделённые: название заголовка: значение",
|
||||||
|
"headersValidationError": "Заголовки должны быть в формате: Название заголовка: значение.",
|
||||||
|
"saveHealthCheck": "Сохранить проверку здоровья",
|
||||||
|
"healthCheckSaved": "Проверка здоровья сохранена",
|
||||||
|
"healthCheckSavedDescription": "Конфигурация проверки состояния успешно сохранена",
|
||||||
|
"healthCheckError": "Ошибка проверки состояния",
|
||||||
|
"healthCheckErrorDescription": "Произошла ошибка при сохранении конфигурации проверки состояния",
|
||||||
|
"healthCheckPathRequired": "Требуется путь проверки состояния",
|
||||||
|
"healthCheckMethodRequired": "Требуется метод HTTP",
|
||||||
|
"healthCheckIntervalMin": "Интервал проверки должен составлять не менее 5 секунд",
|
||||||
|
"healthCheckTimeoutMin": "Тайм-аут должен составлять не менее 1 секунды",
|
||||||
|
"healthCheckRetryMin": "Количество попыток должно быть не менее 1",
|
||||||
|
"httpMethod": "HTTP метод",
|
||||||
|
"selectHttpMethod": "Выберите HTTP метод",
|
||||||
|
"domainPickerSubdomainLabel": "Поддомен",
|
||||||
|
"domainPickerBaseDomainLabel": "Основной домен",
|
||||||
|
"domainPickerSearchDomains": "Поиск доменов...",
|
||||||
|
"domainPickerNoDomainsFound": "Доменов не найдено",
|
||||||
|
"domainPickerLoadingDomains": "Загрузка доменов...",
|
||||||
|
"domainPickerSelectBaseDomain": "Выбор основного домена...",
|
||||||
|
"domainPickerNotAvailableForCname": "Не доступно для CNAME доменов",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Введите поддомен или оставьте пустым для использования основного домена.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Введите поддомен для поиска и выбора из доступных свободных доменов.",
|
||||||
|
"domainPickerFreeDomains": "Свободные домены",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Поиск доступных доменов",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Примечание: бесплатные предоставляемые домены в данный момент недоступны для самоуправляемых экземпляров.",
|
||||||
|
"resourceDomain": "Домен",
|
||||||
|
"resourceEditDomain": "Редактировать домен",
|
||||||
|
"siteName": "Имя сайта",
|
||||||
|
"proxyPort": "Порт",
|
||||||
|
"resourcesTableProxyResources": "Проксированные ресурсы",
|
||||||
|
"resourcesTableClientResources": "Клиентские ресурсы",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Проксированных ресурсов не найдено.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Внутренних ресурсов не найдено.",
|
||||||
|
"resourcesTableDestination": "Пункт назначения",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Эти ресурсы предназначены для использования с",
|
||||||
|
"resourcesTableClients": "Клиенты",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "и доступны только внутренне при подключении с клиентом.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "Редактировать ресурс клиента",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "Обновите свойства ресурса и настройку цели для {resourceName}.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Свойства ресурса",
|
||||||
|
"editInternalResourceDialogName": "Имя",
|
||||||
|
"editInternalResourceDialogProtocol": "Протокол",
|
||||||
|
"editInternalResourceDialogSitePort": "Порт сайта",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Настройка цели",
|
||||||
|
"editInternalResourceDialogCancel": "Отмена",
|
||||||
|
"editInternalResourceDialogSaveResource": "Сохранить ресурс",
|
||||||
|
"editInternalResourceDialogSuccess": "Успешно",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Внутренний ресурс успешно обновлен",
|
||||||
|
"editInternalResourceDialogError": "Ошибка",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Не удалось обновить внутренний ресурс",
|
||||||
|
"editInternalResourceDialogNameRequired": "Имя обязательно",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Имя не должно быть длиннее 255 символов",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Порт прокси должен быть не менее 1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Порт прокси должен быть меньше 65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Неверный формат IP адреса",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Целевой порт должен быть не менее 1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Целевой порт должен быть меньше 65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Нет доступных сайтов",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Вам необходимо иметь хотя бы один сайт Newt с настроенной подсетью для создания внутреннего ресурса.",
|
||||||
|
"createInternalResourceDialogClose": "Закрыть",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "Создать ресурс клиента",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Создайте новый ресурс, который будет доступен клиентам, подключенным к выбранному сайту.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Свойства ресурса",
|
||||||
|
"createInternalResourceDialogName": "Имя",
|
||||||
|
"createInternalResourceDialogSite": "Сайт",
|
||||||
|
"createInternalResourceDialogSelectSite": "Выберите сайт...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Поиск сайтов...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Сайты не найдены.",
|
||||||
|
"createInternalResourceDialogProtocol": "Протокол",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Порт сайта",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "Используйте этот порт для доступа к ресурсу на сайте при подключении с клиентом.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Настройка цели",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "IP или адрес хоста ресурса в сети сайта.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "Порт на IP-адресе назначения, где доступен ресурс.",
|
||||||
|
"createInternalResourceDialogCancel": "Отмена",
|
||||||
|
"createInternalResourceDialogCreateResource": "Создать ресурс",
|
||||||
|
"createInternalResourceDialogSuccess": "Успешно",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Внутренний ресурс успешно создан",
|
||||||
|
"createInternalResourceDialogError": "Ошибка",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Не удалось создать внутренний ресурс",
|
||||||
|
"createInternalResourceDialogNameRequired": "Имя обязательно",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Имя должно содержать менее 255 символов",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Пожалуйста, выберите сайт",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Прокси-порт должен быть не менее 1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Прокси-порт должен быть меньше 65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Неверный формат IP-адреса",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Целевой порт должен быть не менее 1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Целевой порт должен быть меньше 65536",
|
||||||
|
"siteConfiguration": "Конфигурация",
|
||||||
|
"siteAcceptClientConnections": "Принимать подключения клиентов",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Разрешите другим устройствам подключаться через этот экземпляр Newt в качестве шлюза с использованием клиентов.",
|
||||||
|
"siteAddress": "Адрес сайта",
|
||||||
|
"siteAddressDescription": "Укажите IP-адрес хоста для подключения клиентов. Это внутренний адрес сайта в сети Pangolin для адресации клиентов. Должен находиться в пределах подсети организационного уровня.",
|
||||||
|
"autoLoginExternalIdp": "Автоматический вход с внешним провайдером",
|
||||||
|
"autoLoginExternalIdpDescription": "Немедленно перенаправьте пользователя к внешнему провайдеру для аутентификации.",
|
||||||
|
"selectIdp": "Выберите провайдера",
|
||||||
|
"selectIdpPlaceholder": "Выберите провайдера...",
|
||||||
|
"selectIdpRequired": "Пожалуйста, выберите провайдера, когда автоматический вход включен.",
|
||||||
|
"autoLoginTitle": "Перенаправление",
|
||||||
|
"autoLoginDescription": "Перенаправление вас к внешнему провайдеру для аутентификации.",
|
||||||
|
"autoLoginProcessing": "Подготовка аутентификации...",
|
||||||
|
"autoLoginRedirecting": "Перенаправление к входу...",
|
||||||
|
"autoLoginError": "Ошибка автоматического входа",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "URL-адрес перенаправления не получен от провайдера удостоверения.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Не удалось сгенерировать URL-адрес аутентификации.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Управление самоуправляемым",
|
||||||
|
"remoteExitNodeDescription": "Управляйте узлами для расширения сетевого подключения",
|
||||||
|
"remoteExitNodes": "Узлы",
|
||||||
|
"searchRemoteExitNodes": "Поиск узлов...",
|
||||||
|
"remoteExitNodeAdd": "Добавить узел",
|
||||||
|
"remoteExitNodeErrorDelete": "Ошибка удаления узла",
|
||||||
|
"remoteExitNodeQuestionRemove": "Вы уверены, что хотите удалить узел {selectedNode} из организации?",
|
||||||
|
"remoteExitNodeMessageRemove": "После удаления узел больше не будет доступен.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Для подтверждения введите имя узла ниже.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Подтвердите удаление узла",
|
||||||
|
"remoteExitNodeDelete": "Удалить узел",
|
||||||
|
"sidebarRemoteExitNodes": "Узлы",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Создать узел",
|
||||||
|
"description": "Создайте новый узел, чтобы расширить сетевое подключение",
|
||||||
|
"viewAllButton": "Все узлы",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Стратегия создания",
|
||||||
|
"description": "Выберите эту опцию для настройки вашего узла или создания новых учетных данных.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Принять узел",
|
||||||
|
"description": "Выберите это, если у вас уже есть учетные данные для узла."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Сгенерировать ключи",
|
||||||
|
"description": "Выберите это, если вы хотите создать новые ключи для узла"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Принять существующий узел",
|
||||||
|
"description": "Введите учетные данные существующего узла, который вы хотите принять",
|
||||||
|
"nodeIdLabel": "ID узла",
|
||||||
|
"nodeIdDescription": "ID существующего узла, который вы хотите принять",
|
||||||
|
"secretLabel": "Секретный ключ",
|
||||||
|
"secretDescription": "Секретный ключ существующего узла",
|
||||||
|
"submitButton": "Принять узел"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Сгенерированные учетные данные",
|
||||||
|
"description": "Используйте эти учётные данные для настройки вашего узла",
|
||||||
|
"nodeIdTitle": "ID узла",
|
||||||
|
"secretTitle": "Секретный ключ",
|
||||||
|
"saveCredentialsTitle": "Добавить учетные данные в конфигурацию",
|
||||||
|
"saveCredentialsDescription": "Добавьте эти учетные данные в файл конфигурации вашего самоуправляемого узла Pangolin, чтобы завершить подключение.",
|
||||||
|
"submitButton": "Создать узел"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "ID узла и секрет требуются при установке существующего узла"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Не удалось загрузить параметры по умолчанию",
|
||||||
|
"defaultsNotLoaded": "Параметры по умолчанию не загружены",
|
||||||
|
"createFailed": "Не удалось создать узел"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Узел успешно создан"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Выбор узла",
|
||||||
|
"remoteExitNodeSelectionDescription": "Выберите узел для маршрутизации трафика для этого локального сайта",
|
||||||
|
"remoteExitNodeRequired": "Узел должен быть выбран для локальных сайтов",
|
||||||
|
"noRemoteExitNodesAvailable": "Нет доступных узлов",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Для этой организации узлы не доступны. Сначала создайте узел, чтобы использовать локальные сайты.",
|
||||||
|
"exitNode": "Узел выхода",
|
||||||
|
"country": "Страна",
|
||||||
|
"rulesMatchCountry": "В настоящее время основано на исходном IP",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "Управляемый с самовывоза",
|
||||||
|
"description": "Более надежный и низко обслуживаемый сервер Pangolin с дополнительными колокольнями и свистками",
|
||||||
|
"introTitle": "Управляемый Само-Хост Панголина",
|
||||||
|
"introDescription": "- это вариант развертывания, предназначенный для людей, которые хотят простоты и надёжности, сохраняя при этом свои данные конфиденциальными и самостоятельными.",
|
||||||
|
"introDetail": "С помощью этой опции вы по-прежнему используете узел Pangolin — туннели, SSL, и весь остающийся на вашем сервере. Разница заключается в том, что управление и мониторинг осуществляются через нашу панель инструментов из облака, которая открывает ряд преимуществ:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Более простые операции",
|
||||||
|
"description": "Не нужно запускать свой собственный почтовый сервер или настроить комплексное оповещение. Вы будете получать проверки состояния здоровья и оповещения о неисправностях из коробки."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Автоматическое обновление",
|
||||||
|
"description": "Панель управления в облаке развивается быстро, так что вы получаете новые функции и исправления ошибок, без необходимости каждый раз получать новые контейнеры."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Меньше обслуживания",
|
||||||
|
"description": "Нет миграции баз данных, резервных копий или дополнительной инфраструктуры для управления. Мы обрабатываем это в облаке."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Облачное срабатывание",
|
||||||
|
"description": "Если ваш узел исчезнет, ваши туннели могут временно прерваться до наших облачных точек присутствия, пока вы не вернете его в сети."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Высокая доступность (PoP)",
|
||||||
|
"description": "Вы также можете прикрепить несколько узлов к вашему аккаунту для избыточности и лучшей производительности."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Будущие улучшения",
|
||||||
|
"description": "Мы планируем добавить дополнительные инструменты аналитики, оповещения и управления, чтобы сделать установку еще более надежной."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Узнайте больше о опции Managed Self-Hosted в нашей",
|
||||||
|
"documentation": "документация"
|
||||||
|
},
|
||||||
|
"convertButton": "Конвертировать этот узел в управляемый себе-хост"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Обнаружен международный домен",
|
||||||
|
"willbestoredas": "Будет храниться как:",
|
||||||
|
"roleMappingDescription": "Определите, как роли, назначаемые пользователям, когда они войдут в систему автоматического профиля.",
|
||||||
|
"selectRole": "Выберите роль",
|
||||||
|
"roleMappingExpression": "Выражение",
|
||||||
|
"selectRolePlaceholder": "Выберите роль",
|
||||||
|
"selectRoleDescription": "Выберите роль, чтобы назначить всем пользователям этого поставщика идентификации",
|
||||||
|
"roleMappingExpressionDescription": "Введите выражение JMESPath, чтобы извлечь информацию о роли из ID токена",
|
||||||
|
"idpTenantIdRequired": "Требуется ID владельца",
|
||||||
|
"invalidValue": "Неверное значение",
|
||||||
|
"idpTypeLabel": "Тип поставщика удостоверений",
|
||||||
|
"roleMappingExpressionPlaceholder": "например, contains(groups, 'admin') && 'Admin' || 'Member'",
|
||||||
|
"idpGoogleConfiguration": "Конфигурация Google",
|
||||||
|
"idpGoogleConfigurationDescription": "Настройка учетных данных Google OAuth2",
|
||||||
|
"idpGoogleClientIdDescription": "Ваш Google OAuth2 ID клиента",
|
||||||
|
"idpGoogleClientSecretDescription": "Ваш Google OAuth2 Секрет",
|
||||||
|
"idpAzureConfiguration": "Конфигурация Azure Entra ID",
|
||||||
|
"idpAzureConfigurationDescription": "Настройте учетные данные Azure Entra ID OAuth2",
|
||||||
|
"idpTenantId": "Идентификатор арендатора",
|
||||||
|
"idpTenantIdPlaceholder": "ваш тенант-id",
|
||||||
|
"idpAzureTenantIdDescription": "Идентификатор арендатора Azure (найден в обзоре Active Directory Azure)",
|
||||||
|
"idpAzureClientIdDescription": "Ваш идентификатор клиента Azure App",
|
||||||
|
"idpAzureClientSecretDescription": "Секрет регистрации клиента Azure App",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Конфигурация Google",
|
||||||
|
"idpAzureConfigurationTitle": "Конфигурация Azure Entra ID",
|
||||||
|
"idpTenantIdLabel": "Идентификатор арендатора",
|
||||||
|
"idpAzureClientIdDescription2": "Ваш идентификатор клиента Azure App",
|
||||||
|
"idpAzureClientSecretDescription2": "Секрет регистрации клиента Azure App",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC провайдер",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "Подсеть",
|
||||||
|
"subnetDescription": "Подсеть для конфигурации сети этой организации.",
|
||||||
|
"authPage": "Страница авторизации",
|
||||||
|
"authPageDescription": "Настройка страницы авторизации для вашей организации",
|
||||||
|
"authPageDomain": "Домен страницы авторизации",
|
||||||
|
"noDomainSet": "Домен не установлен",
|
||||||
|
"changeDomain": "Изменить домен",
|
||||||
|
"selectDomain": "Выберите домен",
|
||||||
|
"restartCertificate": "Перезапустить сертификат",
|
||||||
|
"editAuthPageDomain": "Редактировать домен страницы авторизации",
|
||||||
|
"setAuthPageDomain": "Установить домен страницы авторизации",
|
||||||
|
"failedToFetchCertificate": "Не удалось получить сертификат",
|
||||||
|
"failedToRestartCertificate": "Не удалось перезапустить сертификат",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Добавьте домен для включения пользовательских страниц аутентификации для вашей организации",
|
||||||
|
"selectDomainForOrgAuthPage": "Выберите домен для страницы аутентификации организации",
|
||||||
|
"domainPickerProvidedDomain": "Домен предоставлен",
|
||||||
|
"domainPickerFreeProvidedDomain": "Бесплатный домен",
|
||||||
|
"domainPickerVerified": "Подтверждено",
|
||||||
|
"domainPickerUnverified": "Не подтверждено",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Этот поддомен содержит недопустимые символы или структуру. Он будет очищен автоматически при сохранении.",
|
||||||
|
"domainPickerError": "Ошибка",
|
||||||
|
"domainPickerErrorLoadDomains": "Не удалось загрузить домены организации",
|
||||||
|
"domainPickerErrorCheckAvailability": "Не удалось проверить доступность домена",
|
||||||
|
"domainPickerInvalidSubdomain": "Неверный поддомен",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "Ввод \"{sub}\" был удален, потому что он недействителен.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" не может быть действительным для {domain}.",
|
||||||
|
"domainPickerSubdomainSanitized": "Субдомен очищен",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" был исправлен на \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "Войдите в свою организацию",
|
||||||
|
"orgAuthChooseIdpDescription": "Выберите своего поставщика удостоверений личности для продолжения",
|
||||||
|
"orgAuthNoIdpConfigured": "Эта организация не имеет настроенных поставщиков идентификационных данных. Вместо этого вы можете войти в свой Pangolin.",
|
||||||
|
"orgAuthSignInWithPangolin": "Войти через Pangolin",
|
||||||
|
"subscriptionRequiredToUse": "Для использования этой функции требуется подписка.",
|
||||||
|
"idpDisabled": "Провайдеры идентификации отключены.",
|
||||||
|
"orgAuthPageDisabled": "Страница авторизации организации отключена.",
|
||||||
|
"domainRestartedDescription": "Проверка домена успешно перезапущена",
|
||||||
|
"resourceAddEntrypointsEditFile": "Редактировать файл: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Редактировать файл: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "Требуется подтверждение адреса электронной почты. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.",
|
||||||
|
"twoFactorSetupRequired": "Требуется настройка двухфакторной аутентификации. Пожалуйста, войдите снова через {dashboardUrl}/auth/login завершить этот шаг. Затем вернитесь сюда.",
|
||||||
|
"authPageErrorUpdateMessage": "Произошла ошибка при обновлении настроек страницы авторизации",
|
||||||
|
"authPageUpdated": "Страница авторизации успешно обновлена",
|
||||||
|
"healthCheckNotAvailable": "Локальный",
|
||||||
|
"rewritePath": "Переписать путь",
|
||||||
|
"rewritePathDescription": "При необходимости, измените путь перед пересылкой к целевому адресу."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "Ağınıza giriş noktası oluşturmanın en kolay yolu. Ekstra kurulum gerekmez.",
|
"siteNewtTunnelDescription": "Ağınıza giriş noktası oluşturmanın en kolay yolu. Ekstra kurulum gerekmez.",
|
||||||
"siteWg": "Temel WireGuard",
|
"siteWg": "Temel WireGuard",
|
||||||
"siteWgDescription": "Bir tünel oluşturmak için herhangi bir WireGuard istemcisi kullanın. Manuel NAT kurulumu gereklidir.",
|
"siteWgDescription": "Bir tünel oluşturmak için herhangi bir WireGuard istemcisi kullanın. Manuel NAT kurulumu gereklidir.",
|
||||||
|
"siteWgDescriptionSaas": "Bir tünel oluşturmak için herhangi bir WireGuard istemcisi kullanın. Manuel NAT kurulumu gereklidir. YALNIZCA SELF HOSTED DÜĞÜMLERDE ÇALIŞIR",
|
||||||
"siteLocalDescription": "Yalnızca yerel kaynaklar. Tünelleme yok.",
|
"siteLocalDescription": "Yalnızca yerel kaynaklar. Tünelleme yok.",
|
||||||
|
"siteLocalDescriptionSaas": "Yalnızca yerel kaynaklar. Tünel yok. YALNIZCA SELF HOSTED DÜĞÜMLERDE ÇALIŞIR",
|
||||||
"siteSeeAll": "Tüm Siteleri Gör",
|
"siteSeeAll": "Tüm Siteleri Gör",
|
||||||
"siteTunnelDescription": "Sitenize nasıl bağlanmak istediğinizi belirleyin",
|
"siteTunnelDescription": "Sitenize nasıl bağlanmak istediğinizi belirleyin",
|
||||||
"siteNewtCredentials": "Newt Kimlik Bilgileri",
|
"siteNewtCredentials": "Newt Kimlik Bilgileri",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "Site seç",
|
"siteSelect": "Site seç",
|
||||||
"siteSearch": "Site ara",
|
"siteSearch": "Site ara",
|
||||||
"siteNotFound": "Herhangi bir site bulunamadı.",
|
"siteNotFound": "Herhangi bir site bulunamadı.",
|
||||||
"siteSelectionDescription": "Bu site, kaynağa bağlanabilirliği sağlayacaktır.",
|
"selectCountry": "Ülke Seç",
|
||||||
|
"searchCountries": "Ülkeleri ara...",
|
||||||
|
"noCountryFound": "Ülke bulunamadı.",
|
||||||
|
"siteSelectionDescription": "Bu site hedefe bağlantı sağlayacaktır.",
|
||||||
"resourceType": "Kaynak Türü",
|
"resourceType": "Kaynak Türü",
|
||||||
"resourceTypeDescription": "Kaynağınıza nasıl erişmek istediğinizi belirleyin",
|
"resourceTypeDescription": "Kaynağınıza nasıl erişmek istediğinizi belirleyin",
|
||||||
"resourceHTTPSSettings": "HTTPS Ayarları",
|
"resourceHTTPSSettings": "HTTPS Ayarları",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "Genel",
|
"general": "Genel",
|
||||||
"generalSettings": "Genel Ayarlar",
|
"generalSettings": "Genel Ayarlar",
|
||||||
"proxy": "Vekil Sunucu",
|
"proxy": "Vekil Sunucu",
|
||||||
|
"internal": "Dahili",
|
||||||
"rules": "Kurallar",
|
"rules": "Kurallar",
|
||||||
"resourceSettingDescription": "Kaynağınızdaki ayarları yapılandırın",
|
"resourceSettingDescription": "Kaynağınızdaki ayarları yapılandırın",
|
||||||
"resourceSetting": "{resourceName} Ayarları",
|
"resourceSetting": "{resourceName} Ayarları",
|
||||||
"alwaysAllow": "Her Zaman İzin Ver",
|
"alwaysAllow": "Her Zaman İzin Ver",
|
||||||
"alwaysDeny": "Her Zaman Reddet",
|
"alwaysDeny": "Her Zaman Reddet",
|
||||||
|
"passToAuth": "Kimlik Doğrulamasına Geç",
|
||||||
"orgSettingsDescription": "Organizasyonunuzun genel ayarlarını yapılandırın",
|
"orgSettingsDescription": "Organizasyonunuzun genel ayarlarını yapılandırın",
|
||||||
"orgGeneralSettings": "Organizasyon Ayarları",
|
"orgGeneralSettings": "Organizasyon Ayarları",
|
||||||
"orgGeneralSettingsDescription": "Organizasyon detaylarınızı ve yapılandırmanızı yönetin",
|
"orgGeneralSettingsDescription": "Organizasyon detaylarınızı ve yapılandırmanızı yönetin",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "Kullanıcı role eklenirken bir hata oluştu.",
|
"accessRoleErrorAddDescription": "Kullanıcı role eklenirken bir hata oluştu.",
|
||||||
"userSaved": "Kullanıcı kaydedildi",
|
"userSaved": "Kullanıcı kaydedildi",
|
||||||
"userSavedDescription": "Kullanıcı güncellenmiştir.",
|
"userSavedDescription": "Kullanıcı güncellenmiştir.",
|
||||||
|
"autoProvisioned": "Otomatik Sağlandı",
|
||||||
|
"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",
|
"accessControlsDescription": "Bu kullanıcının organizasyonda neleri erişebileceğini ve yapabileceğini yönetin",
|
||||||
"accessControlsSubmit": "Erişim Kontrollerini Kaydet",
|
"accessControlsSubmit": "Erişim Kontrollerini Kaydet",
|
||||||
"roles": "Roller",
|
"roles": "Roller",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "SNI için kullanılacak TLS Sunucu Adı'",
|
"targetTlsSniDescription": "SNI için kullanılacak TLS Sunucu Adı'",
|
||||||
"targetTlsSubmit": "Ayarları Kaydet",
|
"targetTlsSubmit": "Ayarları Kaydet",
|
||||||
"targets": "Hedefler Konfigürasyonu",
|
"targets": "Hedefler Konfigürasyonu",
|
||||||
"targetsDescription": "Trafiği hizmetlerinize yönlendirmek için hedefleri ayarlayın",
|
"targetsDescription": "Trafiği arka uç hizmetlerinize yönlendirmek için hedefleri ayarlayın",
|
||||||
"targetStickySessions": "Yapışkan Oturumları Etkinleştir",
|
"targetStickySessions": "Yapışkan Oturumları Etkinleştir",
|
||||||
"targetStickySessionsDescription": "Bağlantıları oturum süresince aynı arka uç hedef üzerinde tutun.",
|
"targetStickySessionsDescription": "Bağlantıları oturum süresince aynı arka uç hedef üzerinde tutun.",
|
||||||
"methodSelect": "Yöntemi Seç",
|
"methodSelect": "Yöntemi Seç",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "Geçersiz IP adresi formatı",
|
"ipAddressErrorInvalidFormat": "Geçersiz IP adresi formatı",
|
||||||
"ipAddressErrorInvalidOctet": "Geçersiz IP adresi okteti",
|
"ipAddressErrorInvalidOctet": "Geçersiz IP adresi okteti",
|
||||||
"path": "Yol",
|
"path": "Yol",
|
||||||
|
"matchPath": "Yol Eşleştir",
|
||||||
"ipAddressRange": "IP Aralığı",
|
"ipAddressRange": "IP Aralığı",
|
||||||
"rulesErrorFetch": "Kurallar alınamadı",
|
"rulesErrorFetch": "Kurallar alınamadı",
|
||||||
"rulesErrorFetchDescription": "Kurallar alınırken bir hata oluştu",
|
"rulesErrorFetchDescription": "Kurallar alınırken bir hata oluştu",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "Aksiyonlar",
|
"rulesActions": "Aksiyonlar",
|
||||||
"rulesActionAlwaysAllow": "Her Zaman İzin Ver: Tüm kimlik doğrulama yöntemlerini atlayın",
|
"rulesActionAlwaysAllow": "Her Zaman İzin Ver: Tüm kimlik doğrulama yöntemlerini atlayın",
|
||||||
"rulesActionAlwaysDeny": "Her Zaman Reddedin: Tüm istekleri engelleyin; kimlik doğrulaması yapılamaz",
|
"rulesActionAlwaysDeny": "Her Zaman Reddedin: Tüm istekleri engelleyin; kimlik doğrulaması yapılamaz",
|
||||||
|
"rulesActionPassToAuth": "Kimlik Doğrulamasına Geç: Kimlik doğrulama yöntemlerinin denenmesine izin ver",
|
||||||
"rulesMatchCriteria": "Eşleşme Kriterleri",
|
"rulesMatchCriteria": "Eşleşme Kriterleri",
|
||||||
"rulesMatchCriteriaIpAddress": "Belirli bir IP adresi ile eşleşme",
|
"rulesMatchCriteriaIpAddress": "Belirli bir IP adresi ile eşleşme",
|
||||||
"rulesMatchCriteriaIpAddressRange": "CIDR gösteriminde bir IP adresi aralığı ile eşleşme",
|
"rulesMatchCriteriaIpAddressRange": "CIDR gösteriminde bir IP adresi aralığı ile eşleşme",
|
||||||
@@ -806,7 +817,7 @@
|
|||||||
"redirectUrl": "Yönlendirme URL'si",
|
"redirectUrl": "Yönlendirme URL'si",
|
||||||
"redirectUrlAbout": "Yönlendirme URL'si Hakkında",
|
"redirectUrlAbout": "Yönlendirme URL'si Hakkında",
|
||||||
"redirectUrlAboutDescription": "Bu, kimlik doğrulamasından sonra kullanıcıların yönlendirileceği URL'dir. Bu URL'yi kimlik sağlayıcınızın ayarlarında yapılandırmanız gerekir.",
|
"redirectUrlAboutDescription": "Bu, kimlik doğrulamasından sonra kullanıcıların yönlendirileceği URL'dir. Bu URL'yi kimlik sağlayıcınızın ayarlarında yapılandırmanız gerekir.",
|
||||||
"pangolinAuth": "Auth - Pangolin",
|
"pangolinAuth": "Yetkilendirme - Pangolin",
|
||||||
"verificationCodeLengthRequirements": "Doğrulama kodunuz 8 karakter olmalıdır.",
|
"verificationCodeLengthRequirements": "Doğrulama kodunuz 8 karakter olmalıdır.",
|
||||||
"errorOccurred": "Bir hata oluştu",
|
"errorOccurred": "Bir hata oluştu",
|
||||||
"emailErrorVerify": "E-posta doğrulanamadı: ",
|
"emailErrorVerify": "E-posta doğrulanamadı: ",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN kesinlikle 6 haneli olmalıdır",
|
"pincodeRequirementsLength": "PIN kesinlikle 6 haneli olmalıdır",
|
||||||
"pincodeRequirementsChars": "PIN sadece numaralardan oluşmalıdır",
|
"pincodeRequirementsChars": "PIN sadece numaralardan oluşmalıdır",
|
||||||
"passwordRequirementsLength": "Şifre en az 1 karakter uzunluğunda olmalıdır",
|
"passwordRequirementsLength": "Şifre en az 1 karakter uzunluğunda olmalıdır",
|
||||||
|
"passwordRequirementsTitle": "Şifre gereksinimleri:",
|
||||||
|
"passwordRequirementLength": "En az 8 karakter uzunluğunda",
|
||||||
|
"passwordRequirementUppercase": "En az bir büyük harf",
|
||||||
|
"passwordRequirementLowercase": "En az bir küçük harf",
|
||||||
|
"passwordRequirementNumber": "En az bir sayı",
|
||||||
|
"passwordRequirementSpecial": "En az bir özel karakter",
|
||||||
|
"passwordRequirementsMet": "✓ Şifre tüm gereksinimleri karşılıyor",
|
||||||
|
"passwordStrength": "Şifre gücü",
|
||||||
|
"passwordStrengthWeak": "Zayıf",
|
||||||
|
"passwordStrengthMedium": "Orta",
|
||||||
|
"passwordStrengthStrong": "Güçlü",
|
||||||
|
"passwordRequirements": "Gereksinimler:",
|
||||||
|
"passwordRequirementLengthText": "8+ karakter",
|
||||||
|
"passwordRequirementUppercaseText": "Büyük harf (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "Küçük harf (a-z)",
|
||||||
|
"passwordRequirementNumberText": "Sayı (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "Özel karakter (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "Parolalar eşleşmiyor",
|
||||||
"otpEmailRequirementsLength": "OTP en az 1 karakter uzunluğunda olmalıdır",
|
"otpEmailRequirementsLength": "OTP en az 1 karakter uzunluğunda olmalıdır",
|
||||||
"otpEmailSent": "OTP Gönderildi",
|
"otpEmailSent": "OTP Gönderildi",
|
||||||
"otpEmailSentDescription": "E-posta adresinize bir OTP gönderildi",
|
"otpEmailSentDescription": "E-posta adresinize bir OTP gönderildi",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "Çıkış yaparken hata",
|
"logoutError": "Çıkış yaparken hata",
|
||||||
"signingAs": "Olarak giriş yapıldı",
|
"signingAs": "Olarak giriş yapıldı",
|
||||||
"serverAdmin": "Sunucu Yöneticisi",
|
"serverAdmin": "Sunucu Yöneticisi",
|
||||||
|
"managedSelfhosted": "Yönetilen Self-Hosted",
|
||||||
"otpEnable": "İki faktörlü özelliğini etkinleştir",
|
"otpEnable": "İki faktörlü özelliğini etkinleştir",
|
||||||
"otpDisable": "İki faktörlü özelliğini devre dışı bırak",
|
"otpDisable": "İki faktörlü özelliğini devre dışı bırak",
|
||||||
"logout": "Çıkış Yap",
|
"logout": "Çıkış Yap",
|
||||||
"licenseTierProfessionalRequired": "Profesyonel Sürüme Gereklidir",
|
"licenseTierProfessionalRequired": "Profesyonel Sürüme Gereklidir",
|
||||||
"licenseTierProfessionalRequiredDescription": "Bu özellik yalnızca Professional Edition'da kullanılabilir.",
|
"licenseTierProfessionalRequiredDescription": "Bu özellik yalnızca Professional Edition'da kullanılabilir.",
|
||||||
"actionGetOrg": "Kuruluşu Al",
|
"actionGetOrg": "Kuruluşu Al",
|
||||||
|
"updateOrgUser": "Organizasyon Kullanıcısını Güncelle",
|
||||||
|
"createOrgUser": "Organizasyon Kullanıcısı Oluştur",
|
||||||
"actionUpdateOrg": "Kuruluşu Güncelle",
|
"actionUpdateOrg": "Kuruluşu Güncelle",
|
||||||
"actionUpdateUser": "Kullanıcıyı Güncelle",
|
"actionUpdateUser": "Kullanıcıyı Güncelle",
|
||||||
"actionGetUser": "Kullanıcıyı Getir",
|
"actionGetUser": "Kullanıcıyı Getir",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "Siteyi Sil",
|
"actionDeleteSite": "Siteyi Sil",
|
||||||
"actionGetSite": "Siteyi Al",
|
"actionGetSite": "Siteyi Al",
|
||||||
"actionListSites": "Siteleri Listele",
|
"actionListSites": "Siteleri Listele",
|
||||||
|
"actionApplyBlueprint": "Planı Uygula",
|
||||||
|
"setupToken": "Kurulum Simgesi",
|
||||||
|
"setupTokenDescription": "Sunucu konsolundan kurulum simgesini girin.",
|
||||||
|
"setupTokenRequired": "Kurulum simgesi gerekli",
|
||||||
"actionUpdateSite": "Siteyi Güncelle",
|
"actionUpdateSite": "Siteyi Güncelle",
|
||||||
"actionListSiteRoles": "İzin Verilen Site Rolleri Listele",
|
"actionListSiteRoles": "İzin Verilen Site Rolleri Listele",
|
||||||
"actionCreateResource": "Kaynak Oluştur",
|
"actionCreateResource": "Kaynak Oluştur",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "Kimlik Sağlayıcı Organizasyon Politikasını Sil",
|
"actionDeleteIdpOrg": "Kimlik Sağlayıcı Organizasyon Politikasını Sil",
|
||||||
"actionListIdpOrgs": "Kimlik Sağlayıcı Organizasyonları Listele",
|
"actionListIdpOrgs": "Kimlik Sağlayıcı Organizasyonları Listele",
|
||||||
"actionUpdateIdpOrg": "Kimlik Sağlayıcı Organizasyonu Güncelle",
|
"actionUpdateIdpOrg": "Kimlik Sağlayıcı Organizasyonu Güncelle",
|
||||||
|
"actionCreateClient": "Müşteri Oluştur",
|
||||||
|
"actionDeleteClient": "Müşteri Sil",
|
||||||
|
"actionUpdateClient": "Müşteri Güncelle",
|
||||||
|
"actionListClients": "Müşterileri Listele",
|
||||||
|
"actionGetClient": "Müşteriyi Al",
|
||||||
|
"actionCreateSiteResource": "Site Kaynağı Oluştur",
|
||||||
|
"actionDeleteSiteResource": "Site Kaynağını Sil",
|
||||||
|
"actionGetSiteResource": "Site Kaynağını Al",
|
||||||
|
"actionListSiteResources": "Site Kaynaklarını Listele",
|
||||||
|
"actionUpdateSiteResource": "Site Kaynağını Güncelle",
|
||||||
|
"actionListInvitations": "Davetiyeleri Listele",
|
||||||
"noneSelected": "Hiçbiri seçili değil",
|
"noneSelected": "Hiçbiri seçili değil",
|
||||||
"orgNotFound2": "Hiçbir organizasyon bulunamadı.",
|
"orgNotFound2": "Hiçbir organizasyon bulunamadı.",
|
||||||
"searchProgress": "Ara...",
|
"searchProgress": "Ara...",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "Lisans",
|
"sidebarLicense": "Lisans",
|
||||||
"sidebarClients": "Müşteriler (Beta)",
|
"sidebarClients": "Müşteriler (Beta)",
|
||||||
"sidebarDomains": "Alan Adları",
|
"sidebarDomains": "Alan Adları",
|
||||||
"enableDockerSocket": "Docker Soketi Etkinleştir",
|
"enableDockerSocket": "Docker Soketini Etkinleştir",
|
||||||
"enableDockerSocketDescription": "Konteyner bilgilerini doldurmak için Docker Socket keşfini etkinleştirin. Socket yolu Newt'e sağlanmalıdır.",
|
"enableDockerSocketDescription": "Plan etiketleri için Docker Socket etiket toplamasını etkinleştirin. Newt'e soket yolu sağlanmalıdır.",
|
||||||
"enableDockerSocketLink": "Daha fazla bilgi",
|
"enableDockerSocketLink": "Daha fazla bilgi",
|
||||||
"viewDockerContainers": "Docker Konteynerlerini Görüntüle",
|
"viewDockerContainers": "Docker Konteynerlerini Görüntüle",
|
||||||
"containersIn": "{siteName} içindeki konteynerler",
|
"containersIn": "{siteName} içindeki konteynerler",
|
||||||
@@ -1195,8 +1242,8 @@
|
|||||||
"sidebarExpand": "Genişlet",
|
"sidebarExpand": "Genişlet",
|
||||||
"newtUpdateAvailable": "Güncelleme Mevcut",
|
"newtUpdateAvailable": "Güncelleme Mevcut",
|
||||||
"newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.",
|
"newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.",
|
||||||
"domainPickerEnterDomain": "Domain",
|
"domainPickerEnterDomain": "Alan Adı",
|
||||||
"domainPickerPlaceholder": "myapp.example.com, api.v1.mydomain.com veya sadece myapp",
|
"domainPickerPlaceholder": "myapp.example.com",
|
||||||
"domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.",
|
"domainPickerDescription": "Mevcut seçenekleri görmek için kaynağın tam etki alanını girin.",
|
||||||
"domainPickerDescriptionSaas": "Mevcut seçenekleri görmek için tam etki alanı, alt etki alanı veya sadece bir isim girin",
|
"domainPickerDescriptionSaas": "Mevcut seçenekleri görmek için tam etki alanı, alt etki alanı veya sadece bir isim girin",
|
||||||
"domainPickerTabAll": "Tümü",
|
"domainPickerTabAll": "Tümü",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "Alt Alan: {subdomain}",
|
"domainPickerSubdomain": "Alt Alan: {subdomain}",
|
||||||
"domainPickerNamespace": "Ad Alanı: {namespace}",
|
"domainPickerNamespace": "Ad Alanı: {namespace}",
|
||||||
"domainPickerShowMore": "Daha Fazla Göster",
|
"domainPickerShowMore": "Daha Fazla Göster",
|
||||||
|
"regionSelectorTitle": "Bölge Seç",
|
||||||
|
"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",
|
||||||
|
"billingLoadingSubscription": "Abonelik yükleniyor...",
|
||||||
|
"billingFreeTier": "Ücretsiz Dilim",
|
||||||
|
"billingWarningOverLimit": "Uyarı: Bir veya daha fazla kullanım limitini aştınız. Aboneliğinizi değiştirmediğiniz veya kullanımı ayarlamadığınız sürece siteleriniz bağlanmayacaktır.",
|
||||||
|
"billingUsageLimitsOverview": "Kullanım Limitleri Genel Görünümü",
|
||||||
|
"billingMonitorUsage": "Kullanımınızı yapılandırılmış limitlerle karşılaştırın. Limitlerin artırılmasına ihtiyacınız varsa, lütfen support@fossorial.io adresinden bizimle iletişime geçin.",
|
||||||
|
"billingDataUsage": "Veri Kullanımı",
|
||||||
|
"billingOnlineTime": "Site Çevrimiçi Süresi",
|
||||||
|
"billingUsers": "Aktif Kullanıcılar",
|
||||||
|
"billingDomains": "Aktif Alanlar",
|
||||||
|
"billingRemoteExitNodes": "Aktif Öz-Host Düğümleri",
|
||||||
|
"billingNoLimitConfigured": "Hiçbir limit yapılandırılmadı",
|
||||||
|
"billingEstimatedPeriod": "Tahmini Fatura Dönemi",
|
||||||
|
"billingIncludedUsage": "Dahil Kullanım",
|
||||||
|
"billingIncludedUsageDescription": "Mevcut abonelik planınıza bağlı kullanım",
|
||||||
|
"billingFreeTierIncludedUsage": "Ücretsiz dilim kullanım hakları",
|
||||||
|
"billingIncluded": "dahil",
|
||||||
|
"billingEstimatedTotal": "Tahmini Toplam:",
|
||||||
|
"billingNotes": "Notlar",
|
||||||
|
"billingEstimateNote": "Bu, mevcut kullanımınıza dayalı bir tahmindir.",
|
||||||
|
"billingActualChargesMayVary": "Asıl ücretler farklılık gösterebilir.",
|
||||||
|
"billingBilledAtEnd": "Fatura döneminin sonunda fatura düzenlenecektir.",
|
||||||
|
"billingModifySubscription": "Aboneliği Düzenle",
|
||||||
|
"billingStartSubscription": "Aboneliği Başlat",
|
||||||
|
"billingRecurringCharge": "Yinelenen Ücret",
|
||||||
|
"billingManageSubscriptionSettings": "Abonelik ayarlarınızı ve tercihlerinizi yönetin",
|
||||||
|
"billingNoActiveSubscription": "Aktif bir aboneliğiniz yok. Kullanım limitlerini artırmak için aboneliğinizi başlatın.",
|
||||||
|
"billingFailedToLoadSubscription": "Abonelik yüklenemedi",
|
||||||
|
"billingFailedToLoadUsage": "Kullanım yüklenemedi",
|
||||||
|
"billingFailedToGetCheckoutUrl": "Ödeme URL'si alınamadı",
|
||||||
|
"billingPleaseTryAgainLater": "Lütfen daha sonra tekrar deneyin.",
|
||||||
|
"billingCheckoutError": "Ödeme Hatası",
|
||||||
|
"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, hesabınızdaki 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, hesabınızdaki 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, hesabınızdaki aktif yönetilen Düğümler sayısına göre günlük olarak hesaplanır.",
|
||||||
"domainNotFound": "Alan Adı Bulunamadı",
|
"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.",
|
"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",
|
"failed": "Başarısız",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "DNS değişikliklerinin internet genelinde yayılması zaman alabilir. DNS sağlayıcınız ve TTL ayarlarına bağlı olarak bu birkaç dakika ile 48 saat arasında değişebilir.",
|
"createDomainDnsPropagationDescription": "DNS değişikliklerinin internet genelinde yayılması zaman alabilir. DNS sağlayıcınız ve TTL ayarlarına bağlı olarak bu birkaç dakika ile 48 saat arasında değişebilir.",
|
||||||
"resourcePortRequired": "HTTP dışı kaynaklar için bağlantı noktası numarası gereklidir",
|
"resourcePortRequired": "HTTP dışı kaynaklar için bağlantı noktası numarası gereklidir",
|
||||||
"resourcePortNotAllowed": "HTTP kaynakları için bağlantı noktası numarası ayarlanmamalı",
|
"resourcePortNotAllowed": "HTTP kaynakları için bağlantı noktası numarası ayarlanmamalı",
|
||||||
|
"billingPricingCalculatorLink": "Fiyat Hesaplayıcı",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "Kabul ediyorum",
|
"IAgreeToThe": "Kabul ediyorum",
|
||||||
"termsOfService": "hizmet şartları",
|
"termsOfService": "hizmet şartları",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "En son Olm yayını alınırken bir hata oluştu.",
|
"olmErrorFetchLatest": "En son Olm yayını alınırken bir hata oluştu.",
|
||||||
"remoteSubnets": "Uzak Alt Ağlar",
|
"remoteSubnets": "Uzak Alt Ağlar",
|
||||||
"enterCidrRange": "CIDR aralığını girin",
|
"enterCidrRange": "CIDR aralığını girin",
|
||||||
"remoteSubnetsDescription": "Bu siteye uzaktan erişebilecek CIDR aralıklarını ekleyin. 10.0.0.0/24 veya 192.168.1.0/24 gibi formatlar kullanın.",
|
"remoteSubnetsDescription": "Bu siteye uzaktan erişilebilen CIDR aralıklarını ekleyin. 10.0.0.0/24 formatını kullanın. Bu YALNIZCA VPN istemci bağlantıları için geçerlidir.",
|
||||||
"resourceEnableProxy": "Genel Proxy'i Etkinleştir",
|
"resourceEnableProxy": "Genel Proxy'i Etkinleştir",
|
||||||
"resourceEnableProxyDescription": "Bu kaynağa genel proxy erişimini etkinleştirin. Bu sayede ağ dışından açık bir port üzerinden kaynağa bulut aracılığıyla erişim sağlanır. Traefik yapılandırması gereklidir.",
|
"resourceEnableProxyDescription": "Bu kaynağa genel proxy erişimini etkinleştirin. Bu sayede ağ dışından açık bir port üzerinden kaynağa bulut aracılığıyla erişim sağlanır. Traefik yapılandırması gereklidir.",
|
||||||
"externalProxyEnabled": "Dış Proxy Etkinleştirildi"
|
"externalProxyEnabled": "Dış Proxy Etkinleştirildi",
|
||||||
|
"addNewTarget": "Yeni Hedef Ekle",
|
||||||
|
"targetsList": "Hedefler Listesi",
|
||||||
|
"targetErrorDuplicateTargetFound": "Yinelenen hedef bulundu",
|
||||||
|
"healthCheckHealthy": "Sağlıklı",
|
||||||
|
"healthCheckUnhealthy": "Sağlıksız",
|
||||||
|
"healthCheckUnknown": "Bilinmiyor",
|
||||||
|
"healthCheck": "Sağlık Kontrolü",
|
||||||
|
"configureHealthCheck": "Sağlık Kontrolünü Yapılandır",
|
||||||
|
"configureHealthCheckDescription": "{hedef} için sağlık izleme kurun",
|
||||||
|
"enableHealthChecks": "Sağlık Kontrollerini Etkinleştir",
|
||||||
|
"enableHealthChecksDescription": "Bu hedefin sağlığını izleyin. Gerekirse hedef dışındaki bir son noktayı izleyebilirsiniz.",
|
||||||
|
"healthScheme": "Yöntem",
|
||||||
|
"healthSelectScheme": "Yöntem Seç",
|
||||||
|
"healthCheckPath": "Yol",
|
||||||
|
"healthHostname": "IP / Hostname",
|
||||||
|
"healthPort": "Bağlantı Noktası",
|
||||||
|
"healthCheckPathDescription": "Sağlık durumunu kontrol etmek için yol.",
|
||||||
|
"healthyIntervalSeconds": "Sağlıklı Aralık",
|
||||||
|
"unhealthyIntervalSeconds": "Sağlıksız Aralık",
|
||||||
|
"IntervalSeconds": "Sağlıklı Aralık",
|
||||||
|
"timeoutSeconds": "Zaman Aşımı",
|
||||||
|
"timeIsInSeconds": "Zaman saniye cinsindendir",
|
||||||
|
"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.",
|
||||||
|
"customHeaders": "Özel Başlıklar",
|
||||||
|
"customHeadersDescription": "Başlıklar yeni satırla ayrılmış: Başlık-Adı: değer",
|
||||||
|
"headersValidationError": "Başlıklar şu formatta olmalıdır: Başlık-Adı: değer.",
|
||||||
|
"saveHealthCheck": "Sağlık Kontrolünü Kaydet",
|
||||||
|
"healthCheckSaved": "Sağlık Kontrolü Kaydedildi",
|
||||||
|
"healthCheckSavedDescription": "Sağlık kontrol yapılandırması başarıyla kaydedildi",
|
||||||
|
"healthCheckError": "Sağlık Kontrol Hatası",
|
||||||
|
"healthCheckErrorDescription": "Sağlık kontrol yapılandırması kaydedilirken bir hata oluştu",
|
||||||
|
"healthCheckPathRequired": "Sağlık kontrol yolu gereklidir",
|
||||||
|
"healthCheckMethodRequired": "HTTP yöntemi gereklidir",
|
||||||
|
"healthCheckIntervalMin": "Kontrol aralığı en az 5 saniye olmalıdır",
|
||||||
|
"healthCheckTimeoutMin": "Zaman aşımı en az 1 saniye olmalıdır",
|
||||||
|
"healthCheckRetryMin": "Tekrar deneme girişimleri en az 1 olmalıdır",
|
||||||
|
"httpMethod": "HTTP Yöntemi",
|
||||||
|
"selectHttpMethod": "HTTP yöntemini seçin",
|
||||||
|
"domainPickerSubdomainLabel": "Alt Alan Adı",
|
||||||
|
"domainPickerBaseDomainLabel": "Temel Alan Adı",
|
||||||
|
"domainPickerSearchDomains": "Alan adlarını ara...",
|
||||||
|
"domainPickerNoDomainsFound": "Hiçbir alan adı bulunamadı",
|
||||||
|
"domainPickerLoadingDomains": "Alan adları yükleniyor...",
|
||||||
|
"domainPickerSelectBaseDomain": "Temel alan adını seçin...",
|
||||||
|
"domainPickerNotAvailableForCname": "CNAME alan adları için kullanılabilir değil",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "Alt alan adını girin veya temel alan adını kullanmak için boş bırakın.",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "Mevcut ücretsiz alan adları arasından aramak ve seçmek için bir alt alan adı girin.",
|
||||||
|
"domainPickerFreeDomains": "Ücretsiz Alan Adları",
|
||||||
|
"domainPickerSearchForAvailableDomains": "Mevcut alan adlarını ara",
|
||||||
|
"domainPickerNotWorkSelfHosted": "Not: Ücretsiz sağlanan alan adları şu anda öz-host edilmiş örnekler için kullanılabilir değildir.",
|
||||||
|
"resourceDomain": "Alan Adı",
|
||||||
|
"resourceEditDomain": "Alan Adını Düzenle",
|
||||||
|
"siteName": "Site Adı",
|
||||||
|
"proxyPort": "Bağlantı Noktası",
|
||||||
|
"resourcesTableProxyResources": "Proxy Kaynaklar",
|
||||||
|
"resourcesTableClientResources": "İstemci Kaynaklar",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "Hiçbir proxy kaynağı bulunamadı.",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "Hiçbir dahili kaynak bulunamadı.",
|
||||||
|
"resourcesTableDestination": "Hedef",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "Bu kaynaklar ile kullanılmak için",
|
||||||
|
"resourcesTableClients": "İstemciler",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "veyalnızca bir istemci ile bağlandığında dahili olarak erişilebilir.",
|
||||||
|
"editInternalResourceDialogEditClientResource": "İstemci Kaynağı Düzenleyin",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "{resourceName} için kaynak özelliklerini ve hedef yapılandırmasını güncelleyin.",
|
||||||
|
"editInternalResourceDialogResourceProperties": "Kaynak Özellikleri",
|
||||||
|
"editInternalResourceDialogName": "Ad",
|
||||||
|
"editInternalResourceDialogProtocol": "Protokol",
|
||||||
|
"editInternalResourceDialogSitePort": "Site Bağlantı Noktası",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "Hedef Yapılandırma",
|
||||||
|
"editInternalResourceDialogCancel": "İptal",
|
||||||
|
"editInternalResourceDialogSaveResource": "Kaynağı Kaydet",
|
||||||
|
"editInternalResourceDialogSuccess": "Başarı",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "Dahili kaynak başarıyla güncellendi",
|
||||||
|
"editInternalResourceDialogError": "Hata",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "Dahili kaynak güncellenemedi",
|
||||||
|
"editInternalResourceDialogNameRequired": "Ad gerekli",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "Ad 255 karakterden kısa olmalıdır",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "Proxy bağlantı noktası en az 1 olmalıdır",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "Proxy bağlantı noktası 65536'dan küçük olmalıdır",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "Geçersiz IP adresi formatı",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "Hedef bağlantı noktası en az 1 olmalıdır",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "Hedef bağlantı noktası 65536'dan küçük olmalıdır",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "Site Bulunamadı",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "Dahili kaynak oluşturmak için en az bir Newt sitesine ve alt ağa sahip olmalısınız.",
|
||||||
|
"createInternalResourceDialogClose": "Kapat",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "İstemci Kaynağı Oluştur",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "Seçilen siteye bağlı istemciler için erişilebilir olacak yeni bir kaynak oluşturun.",
|
||||||
|
"createInternalResourceDialogResourceProperties": "Kaynak Özellikleri",
|
||||||
|
"createInternalResourceDialogName": "Ad",
|
||||||
|
"createInternalResourceDialogSite": "Site",
|
||||||
|
"createInternalResourceDialogSelectSite": "Site seç...",
|
||||||
|
"createInternalResourceDialogSearchSites": "Siteleri ara...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "Site bulunamadı.",
|
||||||
|
"createInternalResourceDialogProtocol": "Protokol",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "Site Bağlantı Noktası",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "İstemci ile bağlanıldığında site üzerindeki kaynağa erişmek için bu bağlantı noktasını kullanın.",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "Hedef Yapılandırma",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "Kaynağın site ağındaki IP veya ana bilgisayar adresi.",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "Kaynağa erişilebilecek hedef IP üzerindeki bağlantı noktası.",
|
||||||
|
"createInternalResourceDialogCancel": "İptal",
|
||||||
|
"createInternalResourceDialogCreateResource": "Kaynak Oluştur",
|
||||||
|
"createInternalResourceDialogSuccess": "Başarı",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "Dahili kaynak başarıyla oluşturuldu",
|
||||||
|
"createInternalResourceDialogError": "Hata",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "Dahili kaynak oluşturulamadı",
|
||||||
|
"createInternalResourceDialogNameRequired": "Ad gerekli",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "Ad 255 karakterden kısa olmalıdır",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "Lütfen bir site seçin",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "Proxy bağlantı noktası en az 1 olmalıdır",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "Proxy bağlantı noktası 65536'dan küçük olmalıdır",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "Geçersiz IP adresi formatı",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "Hedef bağlantı noktası en az 1 olmalıdır",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "Hedef bağlantı noktası 65536'dan küçük olmalıdır",
|
||||||
|
"siteConfiguration": "Yapılandırma",
|
||||||
|
"siteAcceptClientConnections": "İstemci Bağlantılarını Kabul Et",
|
||||||
|
"siteAcceptClientConnectionsDescription": "Bu Newt örneğini bir geçit olarak kullanarak diğer cihazların bağlanmasına izin verin.",
|
||||||
|
"siteAddress": "Site Adresi",
|
||||||
|
"siteAddressDescription": "İstemcilerin bağlanması için hostun IP adresini belirtin. Bu, Pangolin ağındaki sitenin iç adresidir ve istemciler için atlas olmalıdır. Org alt ağına düşmelidir.",
|
||||||
|
"autoLoginExternalIdp": "Harici IDP ile Otomatik Giriş",
|
||||||
|
"autoLoginExternalIdpDescription": "Kullanıcıyı kimlik doğrulama için otomatik olarak harici IDP'ye yönlendirin.",
|
||||||
|
"selectIdp": "IDP Seç",
|
||||||
|
"selectIdpPlaceholder": "IDP seçin...",
|
||||||
|
"selectIdpRequired": "Otomatik giriş etkinleştirildiğinde lütfen bir IDP seçin.",
|
||||||
|
"autoLoginTitle": "Yönlendiriliyor",
|
||||||
|
"autoLoginDescription": "Kimlik doğrulama için harici kimlik sağlayıcıya yönlendiriliyorsunuz.",
|
||||||
|
"autoLoginProcessing": "Kimlik doğrulama hazırlanıyor...",
|
||||||
|
"autoLoginRedirecting": "Girişe yönlendiriliyorsunuz...",
|
||||||
|
"autoLoginError": "Otomatik Giriş Hatası",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "Kimlik sağlayıcıdan yönlendirme URL'si alınamadı.",
|
||||||
|
"autoLoginErrorGeneratingUrl": "Kimlik doğrulama URL'si oluşturulamadı.",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "Öz-Host Yönetim",
|
||||||
|
"remoteExitNodeDescription": "Ağ bağlantınızı genişletmek için düğümleri yönetin",
|
||||||
|
"remoteExitNodes": "Düğümler",
|
||||||
|
"searchRemoteExitNodes": "Düğüm ara...",
|
||||||
|
"remoteExitNodeAdd": "Düğüm Ekle",
|
||||||
|
"remoteExitNodeErrorDelete": "Düğüm silinirken hata oluştu",
|
||||||
|
"remoteExitNodeQuestionRemove": "{selectedNode} düğümünü organizasyondan kaldırmak istediğinizden emin misiniz?",
|
||||||
|
"remoteExitNodeMessageRemove": "Kaldırıldığında, düğüm artık erişilebilir olmayacaktır.",
|
||||||
|
"remoteExitNodeMessageConfirm": "Onaylamak için lütfen aşağıya düğümün adını yazın.",
|
||||||
|
"remoteExitNodeConfirmDelete": "Düğüm Silmeyi Onayla",
|
||||||
|
"remoteExitNodeDelete": "Düğümü Sil",
|
||||||
|
"sidebarRemoteExitNodes": "Düğümler",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "Düğüm Oluştur",
|
||||||
|
"description": "Ağ bağlantınızı genişletmek için yeni bir düğüm oluşturun",
|
||||||
|
"viewAllButton": "Tüm Düğümleri Gör",
|
||||||
|
"strategy": {
|
||||||
|
"title": "Oluşturma Stratejisi",
|
||||||
|
"description": "Düğümünüzü manuel olarak yapılandırmak veya yeni kimlik bilgileri oluşturmak için bunu seçin.",
|
||||||
|
"adopt": {
|
||||||
|
"title": "Düğüm Benimse",
|
||||||
|
"description": "Zaten düğüm için kimlik bilgilerine sahipseniz bunu seçin."
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Anahtarları Oluştur",
|
||||||
|
"description": "Düğüm için yeni anahtarlar oluşturmak istiyorsanız bunu seçin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "Mevcut Düğümü Benimse",
|
||||||
|
"description": "Adayacağınız mevcut düğümün kimlik bilgilerini girin",
|
||||||
|
"nodeIdLabel": "Düğüm ID",
|
||||||
|
"nodeIdDescription": "Adayacağınız mevcut düğümün ID'si",
|
||||||
|
"secretLabel": "Gizli",
|
||||||
|
"secretDescription": "Mevcut düğümün gizli anahtarı",
|
||||||
|
"submitButton": "Düğümü Benimse"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "Oluşturulan Kimlik Bilgileri",
|
||||||
|
"description": "Düğümünüzü yapılandırmak için oluşturulan bu kimlik bilgilerini kullanın",
|
||||||
|
"nodeIdTitle": "Düğüm ID",
|
||||||
|
"secretTitle": "Gizli",
|
||||||
|
"saveCredentialsTitle": "Kimlik Bilgilerini Yapılandırmaya Ekle",
|
||||||
|
"saveCredentialsDescription": "Bağlantıyı tamamlamak için bu kimlik bilgilerini öz-host Pangolin düğüm yapılandırma dosyanıza ekleyin.",
|
||||||
|
"submitButton": "Düğüm Oluştur"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "Mevcut bir düğümü benimserken Düğüm ID ve Gizli anahtar gereklidir"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "Varsayılanlar yüklenemedi",
|
||||||
|
"defaultsNotLoaded": "Varsayılanlar yüklenmedi",
|
||||||
|
"createFailed": "Düğüm oluşturulamadı"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "Düğüm başarıyla oluşturuldu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "Düğüm Seçimi",
|
||||||
|
"remoteExitNodeSelectionDescription": "Yerel site için trafiği yönlendirecek düğümü seçin",
|
||||||
|
"remoteExitNodeRequired": "Yerel siteler için bir düğüm seçilmelidir",
|
||||||
|
"noRemoteExitNodesAvailable": "Düğüm Bulunamadı",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "Bu organizasyon için düğüm mevcut değil. Yerel siteleri kullanmak için önce bir düğüm oluşturun.",
|
||||||
|
"exitNode": "Çıkış Düğümü",
|
||||||
|
"country": "Ülke",
|
||||||
|
"rulesMatchCountry": "Şu anda kaynak IP'ye dayanarak",
|
||||||
|
"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",
|
||||||
|
"introTitle": "Yönetilen Kendi Kendine Barındırılan Pangolin",
|
||||||
|
"introDescription": "Bu, basitlik ve ekstra güvenilirlik arayan, ancak verilerini gizli tutmak ve kendi sunucularında barındırmak isteyen kişiler için tasarlanmış bir dağıtım seçeneğidir.",
|
||||||
|
"introDetail": "Bu seçenekle, kendi Pangolin düğümünüzü çalıştırmaya devam edersiniz — tünelleriniz, SSL bitişiniz ve trafiğiniz tamamen sunucunuzda kalır. Fark, yönetim ve izlemeyi bulut panomuz üzerinden gerçekleştiririz, bu da bir dizi avantaj sağlar:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "Daha basit işlemler",
|
||||||
|
"description": "Kendi e-posta sunucunuzu çalıştırmanıza veya karmaşık uyarılar kurmanıza gerek yok. Sağlık kontrolleri ve kesinti uyarılarını kutudan çıktığı gibi alırsınız."
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "Otomatik güncellemeler",
|
||||||
|
"description": "Bulut panosu hızla gelişir, böylece her seferinde yeni konteynerler manuel olarak çekmeden yeni özellikler ve hata düzeltmeleri alırsınız."
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "Daha az bakım",
|
||||||
|
"description": "Veritabanı geçişleri, yedeklemeler veya ekstra altyapı yönetimi yok. Biz bunu bulutta hallederiz."
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "Bulut yedekleme",
|
||||||
|
"description": "Düğümünüz kapandığında, tünelleriniz geçici olarak bulut bağlantı noktalarımıza geçebilir, böylece tekrar çevrimiçi hale getirene kadar tünelleriniz kesintiye uğramaz."
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "Yüksek kullanılabilirlik (Bağlantı Noktaları)",
|
||||||
|
"description": "Yedeklilik ve daha iyi performans için hesabınıza birden fazla düğüm bağlayabilirsiniz."
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "Gelecek iyileştirmeler",
|
||||||
|
"description": "Dağıtımınızı daha sağlam hale getirmek amacıyla daha fazla analiz, uyarı ve yönetim aracı eklemeyi planlıyoruz."
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "Yönetilen Kendi Kendine Barındırılan seçeneği hakkında daha fazla bilgi edinin",
|
||||||
|
"documentation": "dokümantasyon"
|
||||||
|
},
|
||||||
|
"convertButton": "Bu Düğümü Yönetilen Kendi Kendine Barındırma Dönüştürün"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "Uluslararası Alan Adı Tespit Edildi",
|
||||||
|
"willbestoredas": "Şu şekilde depolanacak:",
|
||||||
|
"roleMappingDescription": "Otomatik Sağlama etkinleştirildiğinde kullanıcıların oturum açarken rollerin nasıl atandığını belirleyin.",
|
||||||
|
"selectRole": "Bir Rol Seçin",
|
||||||
|
"roleMappingExpression": "İfade",
|
||||||
|
"selectRolePlaceholder": "Bir rol seçin",
|
||||||
|
"selectRoleDescription": "Bu kimlik sağlayıcısından tüm kullanıcılara atanacak bir rol seçin",
|
||||||
|
"roleMappingExpressionDescription": "Rol bilgilerini ID tokeninden çıkarmak için bir JMESPath ifadesi girin",
|
||||||
|
"idpTenantIdRequired": "Kiracı Kimliği gereklidir",
|
||||||
|
"invalidValue": "Geçersiz değer",
|
||||||
|
"idpTypeLabel": "Kimlik Sağlayıcı Türü",
|
||||||
|
"roleMappingExpressionPlaceholder": "örn., contains(gruplar, 'yönetici') && 'Yönetici' || 'Üye'",
|
||||||
|
"idpGoogleConfiguration": "Google Yapılandırması",
|
||||||
|
"idpGoogleConfigurationDescription": "Google OAuth2 kimlik bilgilerinizi yapılandırın",
|
||||||
|
"idpGoogleClientIdDescription": "Google OAuth2 İstemci Kimliğiniz",
|
||||||
|
"idpGoogleClientSecretDescription": "Google OAuth2 İstemci Sırrınız",
|
||||||
|
"idpAzureConfiguration": "Azure Entra ID Yapılandırması",
|
||||||
|
"idpAzureConfigurationDescription": "Azure Entra ID OAuth2 kimlik bilgilerinizi yapılandırın",
|
||||||
|
"idpTenantId": "Kiracı Kimliği",
|
||||||
|
"idpTenantIdPlaceholder": "kiraci-kimliginiz",
|
||||||
|
"idpAzureTenantIdDescription": "Azure kiracı kimliğiniz (Azure Active Directory genel bakışında bulunur)",
|
||||||
|
"idpAzureClientIdDescription": "Azure Uygulama Kaydı İstemci Kimliğiniz",
|
||||||
|
"idpAzureClientSecretDescription": "Azure Uygulama Kaydı İstemci Sırrınız",
|
||||||
|
"idpGoogleTitle": "Google",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Google Yapılandırması",
|
||||||
|
"idpAzureConfigurationTitle": "Azure Entra ID Yapılandırması",
|
||||||
|
"idpTenantIdLabel": "Kiracı Kimliği",
|
||||||
|
"idpAzureClientIdDescription2": "Azure Uygulama Kaydı İstemci Kimliğiniz",
|
||||||
|
"idpAzureClientSecretDescription2": "Azure Uygulama Kaydı İstemci Sırrınız",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC sağlayıcısı",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC sağlayıcısı",
|
||||||
|
"subnet": "Alt ağ",
|
||||||
|
"subnetDescription": "Bu organizasyonun ağ yapılandırması için alt ağ.",
|
||||||
|
"authPage": "Yetkilendirme Sayfası",
|
||||||
|
"authPageDescription": "Kuruluşunuz için yetkilendirme sayfasını yapılandırın",
|
||||||
|
"authPageDomain": "Yetkilendirme Sayfası Alanı",
|
||||||
|
"noDomainSet": "Alan belirlenmedi",
|
||||||
|
"changeDomain": "Alanı Değiştir",
|
||||||
|
"selectDomain": "Alan Seçin",
|
||||||
|
"restartCertificate": "Sertifikayı Yenile",
|
||||||
|
"editAuthPageDomain": "Yetkilendirme Sayfası Alanını Düzenle",
|
||||||
|
"setAuthPageDomain": "Yetkilendirme Sayfası Alanını Ayarla",
|
||||||
|
"failedToFetchCertificate": "Sertifika getirilemedi",
|
||||||
|
"failedToRestartCertificate": "Sertifika yeniden başlatılamadı",
|
||||||
|
"addDomainToEnableCustomAuthPages": "Kuruluşunuz için özel kimlik doğrulama sayfalarını etkinleştirmek için bir alan ekleyin",
|
||||||
|
"selectDomainForOrgAuthPage": "Kuruluşun kimlik doğrulama sayfası için bir alan seçin",
|
||||||
|
"domainPickerProvidedDomain": "Sağlanan Alan Adı",
|
||||||
|
"domainPickerFreeProvidedDomain": "Ücretsiz Sağlanan Alan Adı",
|
||||||
|
"domainPickerVerified": "Doğrulandı",
|
||||||
|
"domainPickerUnverified": "Doğrulanmadı",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "Bu alt alan adı geçersiz karakterler veya yapı içeriyor. Kaydettiğinizde otomatik olarak temizlenecektir.",
|
||||||
|
"domainPickerError": "Hata",
|
||||||
|
"domainPickerErrorLoadDomains": "Organizasyon alan adları yüklenemedi",
|
||||||
|
"domainPickerErrorCheckAvailability": "Alan adı kullanılabilirliği kontrol edilemedi",
|
||||||
|
"domainPickerInvalidSubdomain": "Geçersiz alt alan adı",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "Girdi \"{sub}\" geçersiz olduğu için kaldırıldı.",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" {domain} için geçerli yapılamadı.",
|
||||||
|
"domainPickerSubdomainSanitized": "Alt alan adı temizlendi",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" \"{sanitized}\" olarak düzeltildi",
|
||||||
|
"orgAuthSignInTitle": "Kuruluşunuza giriş yapın",
|
||||||
|
"orgAuthChooseIdpDescription": "Devam etmek için kimlik sağlayıcınızı seçin",
|
||||||
|
"orgAuthNoIdpConfigured": "Bu kuruluşta yapılandırılmış kimlik sağlayıcı yok. Bunun yerine Pangolin kimliğinizle giriş yapabilirsiniz.",
|
||||||
|
"orgAuthSignInWithPangolin": "Pangolin ile Giriş Yap",
|
||||||
|
"subscriptionRequiredToUse": "Bu özelliği kullanmak için abonelik gerekmektedir.",
|
||||||
|
"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ı",
|
||||||
|
"resourceAddEntrypointsEditFile": "Dosyayı düzenle: config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "Dosyayı düzenle: docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "E-posta doğrulaması gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.",
|
||||||
|
"twoFactorSetupRequired": "İki faktörlü kimlik doğrulama ayarı gereklidir. Bu adımı tamamlamak için lütfen tekrar {dashboardUrl}/auth/login üzerinden oturum açın. Sonra buraya geri dönün.",
|
||||||
|
"authPageErrorUpdateMessage": "Kimlik doğrulama sayfası ayarları güncellenirken bir hata oluştu.",
|
||||||
|
"authPageUpdated": "Kimlik doğrulama sayfası başarıyla güncellendi",
|
||||||
|
"healthCheckNotAvailable": "Yerel",
|
||||||
|
"rewritePath": "Yolu Yeniden Yaz",
|
||||||
|
"rewritePathDescription": "Seçenek olarak hedefe iletmeden önce yolu yeniden yazın."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,9 @@
|
|||||||
"siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。",
|
"siteNewtTunnelDescription": "最简单的方式来连接到您的网络。不需要任何额外设置。",
|
||||||
"siteWg": "基本 WireGuard",
|
"siteWg": "基本 WireGuard",
|
||||||
"siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。",
|
"siteWgDescription": "使用任何 WireGuard 客户端来建立隧道。需要手动配置 NAT。",
|
||||||
|
"siteWgDescriptionSaas": "使用任何WireGuard客户端建立隧道。需要手动配置NAT。仅适用于自托管节点。",
|
||||||
"siteLocalDescription": "仅限本地资源。不需要隧道。",
|
"siteLocalDescription": "仅限本地资源。不需要隧道。",
|
||||||
|
"siteLocalDescriptionSaas": "仅本地资源。无需隧道。仅适用于自托管节点。",
|
||||||
"siteSeeAll": "查看所有站点",
|
"siteSeeAll": "查看所有站点",
|
||||||
"siteTunnelDescription": "确定如何连接到您的网站",
|
"siteTunnelDescription": "确定如何连接到您的网站",
|
||||||
"siteNewtCredentials": "Newt 凭据",
|
"siteNewtCredentials": "Newt 凭据",
|
||||||
@@ -166,7 +168,10 @@
|
|||||||
"siteSelect": "选择站点",
|
"siteSelect": "选择站点",
|
||||||
"siteSearch": "搜索站点",
|
"siteSearch": "搜索站点",
|
||||||
"siteNotFound": "未找到站点。",
|
"siteNotFound": "未找到站点。",
|
||||||
"siteSelectionDescription": "此站点将为资源提供连接。",
|
"selectCountry": "选择国家",
|
||||||
|
"searchCountries": "搜索国家...",
|
||||||
|
"noCountryFound": "找不到国家。",
|
||||||
|
"siteSelectionDescription": "此站点将为目标提供连接。",
|
||||||
"resourceType": "资源类型",
|
"resourceType": "资源类型",
|
||||||
"resourceTypeDescription": "确定如何访问您的资源",
|
"resourceTypeDescription": "确定如何访问您的资源",
|
||||||
"resourceHTTPSSettings": "HTTPS 设置",
|
"resourceHTTPSSettings": "HTTPS 设置",
|
||||||
@@ -197,11 +202,13 @@
|
|||||||
"general": "概览",
|
"general": "概览",
|
||||||
"generalSettings": "常规设置",
|
"generalSettings": "常规设置",
|
||||||
"proxy": "代理服务器",
|
"proxy": "代理服务器",
|
||||||
|
"internal": "内部设置",
|
||||||
"rules": "规则",
|
"rules": "规则",
|
||||||
"resourceSettingDescription": "配置您资源上的设置",
|
"resourceSettingDescription": "配置您资源上的设置",
|
||||||
"resourceSetting": "{resourceName} 设置",
|
"resourceSetting": "{resourceName} 设置",
|
||||||
"alwaysAllow": "一律允许",
|
"alwaysAllow": "一律允许",
|
||||||
"alwaysDeny": "一律拒绝",
|
"alwaysDeny": "一律拒绝",
|
||||||
|
"passToAuth": "传递至认证",
|
||||||
"orgSettingsDescription": "配置您组织的一般设置",
|
"orgSettingsDescription": "配置您组织的一般设置",
|
||||||
"orgGeneralSettings": "组织设置",
|
"orgGeneralSettings": "组织设置",
|
||||||
"orgGeneralSettingsDescription": "管理您的机构详细信息和配置",
|
"orgGeneralSettingsDescription": "管理您的机构详细信息和配置",
|
||||||
@@ -450,6 +457,8 @@
|
|||||||
"accessRoleErrorAddDescription": "添加用户到角色时出错。",
|
"accessRoleErrorAddDescription": "添加用户到角色时出错。",
|
||||||
"userSaved": "用户已保存",
|
"userSaved": "用户已保存",
|
||||||
"userSavedDescription": "用户已更新。",
|
"userSavedDescription": "用户已更新。",
|
||||||
|
"autoProvisioned": "自动设置",
|
||||||
|
"autoProvisionedDescription": "允许此用户由身份提供商自动管理",
|
||||||
"accessControlsDescription": "管理此用户在组织中可以访问和做什么",
|
"accessControlsDescription": "管理此用户在组织中可以访问和做什么",
|
||||||
"accessControlsSubmit": "保存访问控制",
|
"accessControlsSubmit": "保存访问控制",
|
||||||
"roles": "角色",
|
"roles": "角色",
|
||||||
@@ -490,7 +499,7 @@
|
|||||||
"targetTlsSniDescription": "SNI使用的 TLS 服务器名称。留空使用默认值。",
|
"targetTlsSniDescription": "SNI使用的 TLS 服务器名称。留空使用默认值。",
|
||||||
"targetTlsSubmit": "保存设置",
|
"targetTlsSubmit": "保存设置",
|
||||||
"targets": "目标配置",
|
"targets": "目标配置",
|
||||||
"targetsDescription": "设置目标来路由流量到您的服务",
|
"targetsDescription": "设置目标来路由流量到您的后端服务",
|
||||||
"targetStickySessions": "启用置顶会话",
|
"targetStickySessions": "启用置顶会话",
|
||||||
"targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。",
|
"targetStickySessionsDescription": "将连接保持在同一个后端目标的整个会话中。",
|
||||||
"methodSelect": "选择方法",
|
"methodSelect": "选择方法",
|
||||||
@@ -507,6 +516,7 @@
|
|||||||
"ipAddressErrorInvalidFormat": "无效的 IP 地址格式",
|
"ipAddressErrorInvalidFormat": "无效的 IP 地址格式",
|
||||||
"ipAddressErrorInvalidOctet": "无效的 IP 地址",
|
"ipAddressErrorInvalidOctet": "无效的 IP 地址",
|
||||||
"path": "路径",
|
"path": "路径",
|
||||||
|
"matchPath": "匹配路径",
|
||||||
"ipAddressRange": "IP 范围",
|
"ipAddressRange": "IP 范围",
|
||||||
"rulesErrorFetch": "获取规则失败",
|
"rulesErrorFetch": "获取规则失败",
|
||||||
"rulesErrorFetchDescription": "获取规则时出错",
|
"rulesErrorFetchDescription": "获取规则时出错",
|
||||||
@@ -542,6 +552,7 @@
|
|||||||
"rulesActions": "行动",
|
"rulesActions": "行动",
|
||||||
"rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法",
|
"rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法",
|
||||||
"rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证",
|
"rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证",
|
||||||
|
"rulesActionPassToAuth": "传递至认证:允许尝试身份验证方法",
|
||||||
"rulesMatchCriteria": "匹配条件",
|
"rulesMatchCriteria": "匹配条件",
|
||||||
"rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址",
|
"rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址",
|
||||||
"rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址",
|
"rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址",
|
||||||
@@ -587,7 +598,7 @@
|
|||||||
"newtErrorFetchReleases": "无法获取版本信息: {err}",
|
"newtErrorFetchReleases": "无法获取版本信息: {err}",
|
||||||
"newtErrorFetchLatest": "无法获取最新版信息: {err}",
|
"newtErrorFetchLatest": "无法获取最新版信息: {err}",
|
||||||
"newtEndpoint": "Newt 端点",
|
"newtEndpoint": "Newt 端点",
|
||||||
"newtId": "Newt ID",
|
"newtId": "Newt ID",
|
||||||
"newtSecretKey": "Newt 私钥",
|
"newtSecretKey": "Newt 私钥",
|
||||||
"architecture": "架构",
|
"architecture": "架构",
|
||||||
"sites": "站点",
|
"sites": "站点",
|
||||||
@@ -833,6 +844,24 @@
|
|||||||
"pincodeRequirementsLength": "PIN码必须是6位数字",
|
"pincodeRequirementsLength": "PIN码必须是6位数字",
|
||||||
"pincodeRequirementsChars": "PIN 必须只包含数字",
|
"pincodeRequirementsChars": "PIN 必须只包含数字",
|
||||||
"passwordRequirementsLength": "密码必须至少 1 个字符长",
|
"passwordRequirementsLength": "密码必须至少 1 个字符长",
|
||||||
|
"passwordRequirementsTitle": "密码要求:",
|
||||||
|
"passwordRequirementLength": "至少8个字符长",
|
||||||
|
"passwordRequirementUppercase": "至少一个大写字母",
|
||||||
|
"passwordRequirementLowercase": "至少一个小写字母",
|
||||||
|
"passwordRequirementNumber": "至少一个数字",
|
||||||
|
"passwordRequirementSpecial": "至少一个特殊字符",
|
||||||
|
"passwordRequirementsMet": "✓ 密码满足所有要求",
|
||||||
|
"passwordStrength": "密码强度",
|
||||||
|
"passwordStrengthWeak": "弱",
|
||||||
|
"passwordStrengthMedium": "中",
|
||||||
|
"passwordStrengthStrong": "强",
|
||||||
|
"passwordRequirements": "要求:",
|
||||||
|
"passwordRequirementLengthText": "8+ 个字符",
|
||||||
|
"passwordRequirementUppercaseText": "大写字母 (A-Z)",
|
||||||
|
"passwordRequirementLowercaseText": "小写字母 (a-z)",
|
||||||
|
"passwordRequirementNumberText": "数字 (0-9)",
|
||||||
|
"passwordRequirementSpecialText": "特殊字符 (!@#$%...)",
|
||||||
|
"passwordsDoNotMatch": "密码不匹配",
|
||||||
"otpEmailRequirementsLength": "OTP 必须至少 1 个字符长",
|
"otpEmailRequirementsLength": "OTP 必须至少 1 个字符长",
|
||||||
"otpEmailSent": "OTP 已发送",
|
"otpEmailSent": "OTP 已发送",
|
||||||
"otpEmailSentDescription": "OTP 已经发送到您的电子邮件",
|
"otpEmailSentDescription": "OTP 已经发送到您的电子邮件",
|
||||||
@@ -952,12 +981,15 @@
|
|||||||
"logoutError": "注销错误",
|
"logoutError": "注销错误",
|
||||||
"signingAs": "登录为",
|
"signingAs": "登录为",
|
||||||
"serverAdmin": "服务器管理员",
|
"serverAdmin": "服务器管理员",
|
||||||
|
"managedSelfhosted": "托管自托管",
|
||||||
"otpEnable": "启用双因子认证",
|
"otpEnable": "启用双因子认证",
|
||||||
"otpDisable": "禁用双因子认证",
|
"otpDisable": "禁用双因子认证",
|
||||||
"logout": "登出",
|
"logout": "登出",
|
||||||
"licenseTierProfessionalRequired": "需要专业版",
|
"licenseTierProfessionalRequired": "需要专业版",
|
||||||
"licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。",
|
"licenseTierProfessionalRequiredDescription": "此功能仅在专业版可用。",
|
||||||
"actionGetOrg": "获取组织",
|
"actionGetOrg": "获取组织",
|
||||||
|
"updateOrgUser": "更新组织用户",
|
||||||
|
"createOrgUser": "创建组织用户",
|
||||||
"actionUpdateOrg": "更新组织",
|
"actionUpdateOrg": "更新组织",
|
||||||
"actionUpdateUser": "更新用户",
|
"actionUpdateUser": "更新用户",
|
||||||
"actionGetUser": "获取用户",
|
"actionGetUser": "获取用户",
|
||||||
@@ -967,6 +999,10 @@
|
|||||||
"actionDeleteSite": "删除站点",
|
"actionDeleteSite": "删除站点",
|
||||||
"actionGetSite": "获取站点",
|
"actionGetSite": "获取站点",
|
||||||
"actionListSites": "站点列表",
|
"actionListSites": "站点列表",
|
||||||
|
"actionApplyBlueprint": "应用蓝图",
|
||||||
|
"setupToken": "设置令牌",
|
||||||
|
"setupTokenDescription": "从服务器控制台输入设置令牌。",
|
||||||
|
"setupTokenRequired": "需要设置令牌",
|
||||||
"actionUpdateSite": "更新站点",
|
"actionUpdateSite": "更新站点",
|
||||||
"actionListSiteRoles": "允许站点角色列表",
|
"actionListSiteRoles": "允许站点角色列表",
|
||||||
"actionCreateResource": "创建资源",
|
"actionCreateResource": "创建资源",
|
||||||
@@ -1022,6 +1058,17 @@
|
|||||||
"actionDeleteIdpOrg": "删除 IDP组织策略",
|
"actionDeleteIdpOrg": "删除 IDP组织策略",
|
||||||
"actionListIdpOrgs": "列出 IDP组织",
|
"actionListIdpOrgs": "列出 IDP组织",
|
||||||
"actionUpdateIdpOrg": "更新 IDP组织",
|
"actionUpdateIdpOrg": "更新 IDP组织",
|
||||||
|
"actionCreateClient": "创建客户端",
|
||||||
|
"actionDeleteClient": "删除客户端",
|
||||||
|
"actionUpdateClient": "更新客户端",
|
||||||
|
"actionListClients": "列出客户端",
|
||||||
|
"actionGetClient": "获取客户端",
|
||||||
|
"actionCreateSiteResource": "创建站点资源",
|
||||||
|
"actionDeleteSiteResource": "删除站点资源",
|
||||||
|
"actionGetSiteResource": "获取站点资源",
|
||||||
|
"actionListSiteResources": "列出站点资源",
|
||||||
|
"actionUpdateSiteResource": "更新站点资源",
|
||||||
|
"actionListInvitations": "邀请列表",
|
||||||
"noneSelected": "未选择",
|
"noneSelected": "未选择",
|
||||||
"orgNotFound2": "未找到组织。",
|
"orgNotFound2": "未找到组织。",
|
||||||
"searchProgress": "搜索中...",
|
"searchProgress": "搜索中...",
|
||||||
@@ -1095,8 +1142,8 @@
|
|||||||
"sidebarLicense": "证书",
|
"sidebarLicense": "证书",
|
||||||
"sidebarClients": "客户端(测试版)",
|
"sidebarClients": "客户端(测试版)",
|
||||||
"sidebarDomains": "域",
|
"sidebarDomains": "域",
|
||||||
"enableDockerSocket": "启用停靠套接字",
|
"enableDockerSocket": "启用 Docker 蓝图",
|
||||||
"enableDockerSocketDescription": "启用 Docker Socket 发现以填充容器信息。必须向 Newt 提供 Socket 路径。",
|
"enableDockerSocketDescription": "启用 Docker Socket 标签擦除蓝图标签。套接字路径必须提供给新的。",
|
||||||
"enableDockerSocketLink": "了解更多",
|
"enableDockerSocketLink": "了解更多",
|
||||||
"viewDockerContainers": "查看停靠容器",
|
"viewDockerContainers": "查看停靠容器",
|
||||||
"containersIn": "{siteName} 中的容器",
|
"containersIn": "{siteName} 中的容器",
|
||||||
@@ -1109,7 +1156,7 @@
|
|||||||
"containerLabels": "标签",
|
"containerLabels": "标签",
|
||||||
"containerLabelsCount": "{count, plural, other {# 标签}}",
|
"containerLabelsCount": "{count, plural, other {# 标签}}",
|
||||||
"containerLabelsTitle": "容器标签",
|
"containerLabelsTitle": "容器标签",
|
||||||
"containerLabelEmpty": "<empty>",
|
"containerLabelEmpty": "<为空>",
|
||||||
"containerPorts": "端口",
|
"containerPorts": "端口",
|
||||||
"containerPortsMore": "+{count} 更多",
|
"containerPortsMore": "+{count} 更多",
|
||||||
"containerActions": "行动",
|
"containerActions": "行动",
|
||||||
@@ -1196,7 +1243,7 @@
|
|||||||
"newtUpdateAvailable": "更新可用",
|
"newtUpdateAvailable": "更新可用",
|
||||||
"newtUpdateAvailableInfo": "新版本的 Newt 已可用。请更新到最新版本以获得最佳体验。",
|
"newtUpdateAvailableInfo": "新版本的 Newt 已可用。请更新到最新版本以获得最佳体验。",
|
||||||
"domainPickerEnterDomain": "域名",
|
"domainPickerEnterDomain": "域名",
|
||||||
"domainPickerPlaceholder": "myapp.example.com、api.v1.mydomain.com 或仅 myapp",
|
"domainPickerPlaceholder": "example.com",
|
||||||
"domainPickerDescription": "输入资源的完整域名以查看可用选项。",
|
"domainPickerDescription": "输入资源的完整域名以查看可用选项。",
|
||||||
"domainPickerDescriptionSaas": "输入完整域名、子域或名称以查看可用选项。",
|
"domainPickerDescriptionSaas": "输入完整域名、子域或名称以查看可用选项。",
|
||||||
"domainPickerTabAll": "所有",
|
"domainPickerTabAll": "所有",
|
||||||
@@ -1211,6 +1258,48 @@
|
|||||||
"domainPickerSubdomain": "子域:{subdomain}",
|
"domainPickerSubdomain": "子域:{subdomain}",
|
||||||
"domainPickerNamespace": "命名空间:{namespace}",
|
"domainPickerNamespace": "命名空间:{namespace}",
|
||||||
"domainPickerShowMore": "显示更多",
|
"domainPickerShowMore": "显示更多",
|
||||||
|
"regionSelectorTitle": "选择区域",
|
||||||
|
"regionSelectorInfo": "选择区域以帮助提升您所在地的性能。您不必与服务器在相同的区域。",
|
||||||
|
"regionSelectorPlaceholder": "选择一个区域",
|
||||||
|
"regionSelectorComingSoon": "即将推出",
|
||||||
|
"billingLoadingSubscription": "正在加载订阅...",
|
||||||
|
"billingFreeTier": "免费层",
|
||||||
|
"billingWarningOverLimit": "警告:您已超出一个或多个使用限制。在您修改订阅或调整使用情况之前,您的站点将无法连接。",
|
||||||
|
"billingUsageLimitsOverview": "使用限制概览",
|
||||||
|
"billingMonitorUsage": "监控您的使用情况以对比已配置的限制。如需提高限制请联系我们 support@fossorial.io。",
|
||||||
|
"billingDataUsage": "数据使用情况",
|
||||||
|
"billingOnlineTime": "站点在线时间",
|
||||||
|
"billingUsers": "活跃用户",
|
||||||
|
"billingDomains": "活跃域",
|
||||||
|
"billingRemoteExitNodes": "活跃自托管节点",
|
||||||
|
"billingNoLimitConfigured": "未配置限制",
|
||||||
|
"billingEstimatedPeriod": "估计结算周期",
|
||||||
|
"billingIncludedUsage": "包含的使用量",
|
||||||
|
"billingIncludedUsageDescription": "您当前订阅计划中包含的使用量",
|
||||||
|
"billingFreeTierIncludedUsage": "免费层使用额度",
|
||||||
|
"billingIncluded": "包含",
|
||||||
|
"billingEstimatedTotal": "预计总额:",
|
||||||
|
"billingNotes": "备注",
|
||||||
|
"billingEstimateNote": "这是根据您当前使用情况的估算。",
|
||||||
|
"billingActualChargesMayVary": "实际费用可能会有变化。",
|
||||||
|
"billingBilledAtEnd": "您将在结算周期结束时被计费。",
|
||||||
|
"billingModifySubscription": "修改订阅",
|
||||||
|
"billingStartSubscription": "开始订阅",
|
||||||
|
"billingRecurringCharge": "周期性收费",
|
||||||
|
"billingManageSubscriptionSettings": "管理您的订阅设置和偏好",
|
||||||
|
"billingNoActiveSubscription": "您没有活跃的订阅。开始订阅以增加使用限制。",
|
||||||
|
"billingFailedToLoadSubscription": "无法加载订阅",
|
||||||
|
"billingFailedToLoadUsage": "无法加载使用情况",
|
||||||
|
"billingFailedToGetCheckoutUrl": "无法获取结账网址",
|
||||||
|
"billingPleaseTryAgainLater": "请稍后再试。",
|
||||||
|
"billingCheckoutError": "结账错误",
|
||||||
|
"billingFailedToGetPortalUrl": "无法获取门户网址",
|
||||||
|
"billingPortalError": "门户错误",
|
||||||
|
"billingDataUsageInfo": "当连接到云端时,您将为通过安全隧道传输的所有数据收取费用。 这包括您所有站点的进出流量。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取数据。",
|
||||||
|
"billingOnlineTimeInfo": "您要根据您的网站连接到云端的时间长短收取费用。 例如,44,640分钟等于一个24/7全月运行的网站。 当您达到上限时,您的站点将断开连接,直到您升级计划或减少使用。使用节点时不收取费用。",
|
||||||
|
"billingUsersInfo": "根据您组织中的活跃用户数量收费。按日计算账单。",
|
||||||
|
"billingDomainInfo": "根据组织中活跃域的数量收费。按日计算账单。",
|
||||||
|
"billingRemoteExitNodesInfo": "根据您组织中已管理节点的数量收费。按日计算账单。",
|
||||||
"domainNotFound": "域未找到",
|
"domainNotFound": "域未找到",
|
||||||
"domainNotFoundDescription": "此资源已禁用,因为该域不再在我们的系统中存在。请为此资源设置一个新域。",
|
"domainNotFoundDescription": "此资源已禁用,因为该域不再在我们的系统中存在。请为此资源设置一个新域。",
|
||||||
"failed": "失败",
|
"failed": "失败",
|
||||||
@@ -1244,7 +1333,7 @@
|
|||||||
"twoFactorRequired": "注册安全密钥需要两步验证。",
|
"twoFactorRequired": "注册安全密钥需要两步验证。",
|
||||||
"twoFactor": "两步验证",
|
"twoFactor": "两步验证",
|
||||||
"adminEnabled2FaOnYourAccount": "管理员已为{email}启用两步验证。请完成设置以继续。",
|
"adminEnabled2FaOnYourAccount": "管理员已为{email}启用两步验证。请完成设置以继续。",
|
||||||
"continueToApplication": "继续到应用程序",
|
"continueToApplication": "继续应用",
|
||||||
"securityKeyAdd": "添加安全密钥",
|
"securityKeyAdd": "添加安全密钥",
|
||||||
"securityKeyRegisterTitle": "注册新安全密钥",
|
"securityKeyRegisterTitle": "注册新安全密钥",
|
||||||
"securityKeyRegisterDescription": "连接您的安全密钥并输入名称以便识别",
|
"securityKeyRegisterDescription": "连接您的安全密钥并输入名称以便识别",
|
||||||
@@ -1274,6 +1363,7 @@
|
|||||||
"createDomainDnsPropagationDescription": "DNS 更改可能需要一些时间才能在互联网上传播。这可能需要从几分钟到 48 小时,具体取决于您的 DNS 提供商和 TTL 设置。",
|
"createDomainDnsPropagationDescription": "DNS 更改可能需要一些时间才能在互联网上传播。这可能需要从几分钟到 48 小时,具体取决于您的 DNS 提供商和 TTL 设置。",
|
||||||
"resourcePortRequired": "非 HTTP 资源必须输入端口号",
|
"resourcePortRequired": "非 HTTP 资源必须输入端口号",
|
||||||
"resourcePortNotAllowed": "HTTP 资源不应设置端口号",
|
"resourcePortNotAllowed": "HTTP 资源不应设置端口号",
|
||||||
|
"billingPricingCalculatorLink": "价格计算器",
|
||||||
"signUpTerms": {
|
"signUpTerms": {
|
||||||
"IAgreeToThe": "我同意",
|
"IAgreeToThe": "我同意",
|
||||||
"termsOfService": "服务条款",
|
"termsOfService": "服务条款",
|
||||||
@@ -1300,7 +1390,7 @@
|
|||||||
"clientOlmCredentials": "Olm 凭据",
|
"clientOlmCredentials": "Olm 凭据",
|
||||||
"clientOlmCredentialsDescription": "这是 Olm 服务器的身份验证方式",
|
"clientOlmCredentialsDescription": "这是 Olm 服务器的身份验证方式",
|
||||||
"olmEndpoint": "Olm 端点",
|
"olmEndpoint": "Olm 端点",
|
||||||
"olmId": "Olm ID",
|
"olmId": "Olm ID",
|
||||||
"olmSecretKey": "Olm 私钥",
|
"olmSecretKey": "Olm 私钥",
|
||||||
"clientCredentialsSave": "保存您的凭据",
|
"clientCredentialsSave": "保存您的凭据",
|
||||||
"clientCredentialsSaveDescription": "该信息仅会显示一次,请确保将其复制到安全位置。",
|
"clientCredentialsSaveDescription": "该信息仅会显示一次,请确保将其复制到安全位置。",
|
||||||
@@ -1315,8 +1405,323 @@
|
|||||||
"olmErrorFetchLatest": "获取最新 Olm 发布版本时出错。",
|
"olmErrorFetchLatest": "获取最新 Olm 发布版本时出错。",
|
||||||
"remoteSubnets": "远程子网",
|
"remoteSubnets": "远程子网",
|
||||||
"enterCidrRange": "输入 CIDR 范围",
|
"enterCidrRange": "输入 CIDR 范围",
|
||||||
"remoteSubnetsDescription": "添加能远程访问此站点的 CIDR 范围。使用格式如 10.0.0.0/24 或 192.168.1.0/24。",
|
"remoteSubnetsDescription": "添加可以通过客户端远程访问该站点的CIDR范围。使用类似10.0.0.0/24的格式。这仅适用于VPN客户端连接。",
|
||||||
"resourceEnableProxy": "启用公共代理",
|
"resourceEnableProxy": "启用公共代理",
|
||||||
"resourceEnableProxyDescription": "启用到此资源的公共代理。这允许外部网络通过开放端口访问资源。需要 Traefik 配置。",
|
"resourceEnableProxyDescription": "启用到此资源的公共代理。这允许外部网络通过开放端口访问资源。需要 Traefik 配置。",
|
||||||
"externalProxyEnabled": "外部代理已启用"
|
"externalProxyEnabled": "外部代理已启用",
|
||||||
|
"addNewTarget": "添加新目标",
|
||||||
|
"targetsList": "目标列表",
|
||||||
|
"targetErrorDuplicateTargetFound": "找到重复的目标",
|
||||||
|
"healthCheckHealthy": "正常",
|
||||||
|
"healthCheckUnhealthy": "不正常",
|
||||||
|
"healthCheckUnknown": "未知",
|
||||||
|
"healthCheck": "健康检查",
|
||||||
|
"configureHealthCheck": "配置健康检查",
|
||||||
|
"configureHealthCheckDescription": "为 {target} 设置健康监控",
|
||||||
|
"enableHealthChecks": "启用健康检查",
|
||||||
|
"enableHealthChecksDescription": "监视此目标的健康状况。如果需要,您可以监视一个不同的终点。",
|
||||||
|
"healthScheme": "方法",
|
||||||
|
"healthSelectScheme": "选择方法",
|
||||||
|
"healthCheckPath": "路径",
|
||||||
|
"healthHostname": "IP / 主机",
|
||||||
|
"healthPort": "端口",
|
||||||
|
"healthCheckPathDescription": "用于检查健康状态的路径。",
|
||||||
|
"healthyIntervalSeconds": "正常间隔",
|
||||||
|
"unhealthyIntervalSeconds": "不正常间隔",
|
||||||
|
"IntervalSeconds": "正常间隔",
|
||||||
|
"timeoutSeconds": "超时",
|
||||||
|
"timeIsInSeconds": "时间以秒为单位",
|
||||||
|
"retryAttempts": "重试次数",
|
||||||
|
"expectedResponseCodes": "期望响应代码",
|
||||||
|
"expectedResponseCodesDescription": "HTTP 状态码表示健康状态。如留空,200-300 被视为健康。",
|
||||||
|
"customHeaders": "自定义标题",
|
||||||
|
"customHeadersDescription": "头部新行分隔:头部名称:值",
|
||||||
|
"headersValidationError": "头部必须是格式:头部名称:值。",
|
||||||
|
"saveHealthCheck": "保存健康检查",
|
||||||
|
"healthCheckSaved": "健康检查已保存",
|
||||||
|
"healthCheckSavedDescription": "健康检查配置已成功保存。",
|
||||||
|
"healthCheckError": "健康检查错误",
|
||||||
|
"healthCheckErrorDescription": "保存健康检查配置时出错",
|
||||||
|
"healthCheckPathRequired": "健康检查路径为必填项",
|
||||||
|
"healthCheckMethodRequired": "HTTP 方法为必填项",
|
||||||
|
"healthCheckIntervalMin": "检查间隔必须至少为 5 秒",
|
||||||
|
"healthCheckTimeoutMin": "超时必须至少为 1 秒",
|
||||||
|
"healthCheckRetryMin": "重试次数必须至少为 1 次",
|
||||||
|
"httpMethod": "HTTP 方法",
|
||||||
|
"selectHttpMethod": "选择 HTTP 方法",
|
||||||
|
"domainPickerSubdomainLabel": "子域名",
|
||||||
|
"domainPickerBaseDomainLabel": "根域名",
|
||||||
|
"domainPickerSearchDomains": "搜索域名...",
|
||||||
|
"domainPickerNoDomainsFound": "未找到域名",
|
||||||
|
"domainPickerLoadingDomains": "加载域名...",
|
||||||
|
"domainPickerSelectBaseDomain": "选择根域名...",
|
||||||
|
"domainPickerNotAvailableForCname": "不适用于CNAME域",
|
||||||
|
"domainPickerEnterSubdomainOrLeaveBlank": "输入子域名或留空以使用根域名。",
|
||||||
|
"domainPickerEnterSubdomainToSearch": "输入一个子域名以搜索并从可用免费域名中选择。",
|
||||||
|
"domainPickerFreeDomains": "免费域名",
|
||||||
|
"domainPickerSearchForAvailableDomains": "搜索可用域名",
|
||||||
|
"domainPickerNotWorkSelfHosted": "注意:自托管实例当前不提供免费的域名。",
|
||||||
|
"resourceDomain": "域名",
|
||||||
|
"resourceEditDomain": "编辑域名",
|
||||||
|
"siteName": "站点名称",
|
||||||
|
"proxyPort": "端口",
|
||||||
|
"resourcesTableProxyResources": "代理资源",
|
||||||
|
"resourcesTableClientResources": "客户端资源",
|
||||||
|
"resourcesTableNoProxyResourcesFound": "未找到代理资源。",
|
||||||
|
"resourcesTableNoInternalResourcesFound": "未找到内部资源。",
|
||||||
|
"resourcesTableDestination": "目标",
|
||||||
|
"resourcesTableTheseResourcesForUseWith": "这些资源供...使用",
|
||||||
|
"resourcesTableClients": "客户端",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "且仅在与客户端连接时可内部访问。",
|
||||||
|
"editInternalResourceDialogEditClientResource": "编辑客户端资源",
|
||||||
|
"editInternalResourceDialogUpdateResourceProperties": "更新{resourceName}的资源属性和目标配置。",
|
||||||
|
"editInternalResourceDialogResourceProperties": "资源属性",
|
||||||
|
"editInternalResourceDialogName": "名称",
|
||||||
|
"editInternalResourceDialogProtocol": "协议",
|
||||||
|
"editInternalResourceDialogSitePort": "站点端口",
|
||||||
|
"editInternalResourceDialogTargetConfiguration": "目标配置",
|
||||||
|
"editInternalResourceDialogCancel": "取消",
|
||||||
|
"editInternalResourceDialogSaveResource": "保存资源",
|
||||||
|
"editInternalResourceDialogSuccess": "成功",
|
||||||
|
"editInternalResourceDialogInternalResourceUpdatedSuccessfully": "内部资源更新成功",
|
||||||
|
"editInternalResourceDialogError": "错误",
|
||||||
|
"editInternalResourceDialogFailedToUpdateInternalResource": "更新内部资源失败",
|
||||||
|
"editInternalResourceDialogNameRequired": "名称为必填项",
|
||||||
|
"editInternalResourceDialogNameMaxLength": "名称长度必须小于255个字符",
|
||||||
|
"editInternalResourceDialogProxyPortMin": "代理端口必须至少为1",
|
||||||
|
"editInternalResourceDialogProxyPortMax": "代理端口必须小于65536",
|
||||||
|
"editInternalResourceDialogInvalidIPAddressFormat": "无效的IP地址格式",
|
||||||
|
"editInternalResourceDialogDestinationPortMin": "目标端口必须至少为1",
|
||||||
|
"editInternalResourceDialogDestinationPortMax": "目标端口必须小于65536",
|
||||||
|
"createInternalResourceDialogNoSitesAvailable": "暂无可用站点",
|
||||||
|
"createInternalResourceDialogNoSitesAvailableDescription": "您需要至少配置一个子网的Newt站点来创建内部资源。",
|
||||||
|
"createInternalResourceDialogClose": "关闭",
|
||||||
|
"createInternalResourceDialogCreateClientResource": "创建客户端资源",
|
||||||
|
"createInternalResourceDialogCreateClientResourceDescription": "创建一个新资源,该资源将可供连接到所选站点的客户端访问。",
|
||||||
|
"createInternalResourceDialogResourceProperties": "资源属性",
|
||||||
|
"createInternalResourceDialogName": "名称",
|
||||||
|
"createInternalResourceDialogSite": "站点",
|
||||||
|
"createInternalResourceDialogSelectSite": "选择站点...",
|
||||||
|
"createInternalResourceDialogSearchSites": "搜索站点...",
|
||||||
|
"createInternalResourceDialogNoSitesFound": "未找到站点。",
|
||||||
|
"createInternalResourceDialogProtocol": "协议",
|
||||||
|
"createInternalResourceDialogTcp": "TCP",
|
||||||
|
"createInternalResourceDialogUdp": "UDP",
|
||||||
|
"createInternalResourceDialogSitePort": "站点端口",
|
||||||
|
"createInternalResourceDialogSitePortDescription": "使用此端口在连接到客户端时访问站点上的资源。",
|
||||||
|
"createInternalResourceDialogTargetConfiguration": "目标配置",
|
||||||
|
"createInternalResourceDialogDestinationIPDescription": "站点网络上资源的IP或主机名地址。",
|
||||||
|
"createInternalResourceDialogDestinationPortDescription": "资源在目标IP上可访问的端口。",
|
||||||
|
"createInternalResourceDialogCancel": "取消",
|
||||||
|
"createInternalResourceDialogCreateResource": "创建资源",
|
||||||
|
"createInternalResourceDialogSuccess": "成功",
|
||||||
|
"createInternalResourceDialogInternalResourceCreatedSuccessfully": "内部资源创建成功",
|
||||||
|
"createInternalResourceDialogError": "错误",
|
||||||
|
"createInternalResourceDialogFailedToCreateInternalResource": "创建内部资源失败",
|
||||||
|
"createInternalResourceDialogNameRequired": "名称为必填项",
|
||||||
|
"createInternalResourceDialogNameMaxLength": "名称长度必须小于255个字符",
|
||||||
|
"createInternalResourceDialogPleaseSelectSite": "请选择一个站点",
|
||||||
|
"createInternalResourceDialogProxyPortMin": "代理端口必须至少为1",
|
||||||
|
"createInternalResourceDialogProxyPortMax": "代理端口必须小于65536",
|
||||||
|
"createInternalResourceDialogInvalidIPAddressFormat": "无效的IP地址格式",
|
||||||
|
"createInternalResourceDialogDestinationPortMin": "目标端口必须至少为1",
|
||||||
|
"createInternalResourceDialogDestinationPortMax": "目标端口必须小于65536",
|
||||||
|
"siteConfiguration": "配置",
|
||||||
|
"siteAcceptClientConnections": "接受客户端连接",
|
||||||
|
"siteAcceptClientConnectionsDescription": "允许其他设备通过此Newt实例使用客户端作为网关连接。",
|
||||||
|
"siteAddress": "站点地址",
|
||||||
|
"siteAddressDescription": "指定主机的IP地址以供客户端连接。这是Pangolin网络中站点的内部地址,供客户端访问。必须在Org子网内。",
|
||||||
|
"autoLoginExternalIdp": "自动使用外部IDP登录",
|
||||||
|
"autoLoginExternalIdpDescription": "立即将用户重定向到外部IDP进行身份验证。",
|
||||||
|
"selectIdp": "选择IDP",
|
||||||
|
"selectIdpPlaceholder": "选择一个IDP...",
|
||||||
|
"selectIdpRequired": "在启用自动登录时,请选择一个IDP。",
|
||||||
|
"autoLoginTitle": "重定向中",
|
||||||
|
"autoLoginDescription": "正在将您重定向到外部身份提供商进行身份验证。",
|
||||||
|
"autoLoginProcessing": "准备身份验证...",
|
||||||
|
"autoLoginRedirecting": "重定向到登录...",
|
||||||
|
"autoLoginError": "自动登录错误",
|
||||||
|
"autoLoginErrorNoRedirectUrl": "未从身份提供商收到重定向URL。",
|
||||||
|
"autoLoginErrorGeneratingUrl": "生成身份验证URL失败。",
|
||||||
|
"remoteExitNodeManageRemoteExitNodes": "管理自托管",
|
||||||
|
"remoteExitNodeDescription": "管理节点以扩展您的网络连接",
|
||||||
|
"remoteExitNodes": "节点",
|
||||||
|
"searchRemoteExitNodes": "搜索节点...",
|
||||||
|
"remoteExitNodeAdd": "添加节点",
|
||||||
|
"remoteExitNodeErrorDelete": "删除节点时出错",
|
||||||
|
"remoteExitNodeQuestionRemove": "您确定要从组织中删除 {selectedNode} 节点吗?",
|
||||||
|
"remoteExitNodeMessageRemove": "一旦删除,该节点将不再能够访问。",
|
||||||
|
"remoteExitNodeMessageConfirm": "要确认,请输入以下节点的名称。",
|
||||||
|
"remoteExitNodeConfirmDelete": "确认删除节点",
|
||||||
|
"remoteExitNodeDelete": "删除节点",
|
||||||
|
"sidebarRemoteExitNodes": "节点",
|
||||||
|
"remoteExitNodeCreate": {
|
||||||
|
"title": "创建节点",
|
||||||
|
"description": "创建一个新节点来扩展您的网络连接",
|
||||||
|
"viewAllButton": "查看所有节点",
|
||||||
|
"strategy": {
|
||||||
|
"title": "创建策略",
|
||||||
|
"description": "选择此选项以手动配置您的节点或生成新凭据。",
|
||||||
|
"adopt": {
|
||||||
|
"title": "采纳节点",
|
||||||
|
"description": "如果您已经拥有该节点的凭据,请选择此项。"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "生成密钥",
|
||||||
|
"description": "如果您想为节点生成新密钥,请选择此选项"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adopt": {
|
||||||
|
"title": "采纳现有节点",
|
||||||
|
"description": "输入您想要采用的现有节点的凭据",
|
||||||
|
"nodeIdLabel": "节点 ID",
|
||||||
|
"nodeIdDescription": "您想要采用的现有节点的 ID",
|
||||||
|
"secretLabel": "密钥",
|
||||||
|
"secretDescription": "现有节点的秘密密钥",
|
||||||
|
"submitButton": "采用节点"
|
||||||
|
},
|
||||||
|
"generate": {
|
||||||
|
"title": "生成的凭据",
|
||||||
|
"description": "使用这些生成的凭据来配置您的节点",
|
||||||
|
"nodeIdTitle": "节点 ID",
|
||||||
|
"secretTitle": "密钥",
|
||||||
|
"saveCredentialsTitle": "将凭据添加到配置中",
|
||||||
|
"saveCredentialsDescription": "将这些凭据添加到您的自托管 Pangolin 节点配置文件中以完成连接。",
|
||||||
|
"submitButton": "创建节点"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"adoptRequired": "在通过现有节点时需要节点ID和密钥"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"loadDefaultsFailed": "无法加载默认值",
|
||||||
|
"defaultsNotLoaded": "默认值未加载",
|
||||||
|
"createFailed": "创建节点失败"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"created": "节点创建成功"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remoteExitNodeSelection": "节点选择",
|
||||||
|
"remoteExitNodeSelectionDescription": "为此本地站点选择要路由流量的节点",
|
||||||
|
"remoteExitNodeRequired": "必须为本地站点选择节点",
|
||||||
|
"noRemoteExitNodesAvailable": "无可用节点",
|
||||||
|
"noRemoteExitNodesAvailableDescription": "此组织没有可用的节点。首先创建一个节点来使用本地站点。",
|
||||||
|
"exitNode": "出口节点",
|
||||||
|
"country": "国家",
|
||||||
|
"rulesMatchCountry": "当前基于源 IP",
|
||||||
|
"managedSelfHosted": {
|
||||||
|
"title": "托管自托管",
|
||||||
|
"description": "更可靠和低维护自我托管的 Pangolin 服务器,带有额外的铃声和告密器",
|
||||||
|
"introTitle": "托管自托管的潘戈林公司",
|
||||||
|
"introDescription": "这是一种部署选择,为那些希望简洁和额外可靠的人设计,同时仍然保持他们的数据的私密性和自我托管性。",
|
||||||
|
"introDetail": "通过此选项,您仍然运行您自己的 Pangolin 节点 — — 您的隧道、SSL 终止,并且流量在您的服务器上保持所有状态。 不同之处在于,管理和监测是通过我们的云层仪表板进行的,该仪表板开启了一些好处:",
|
||||||
|
"benefitSimplerOperations": {
|
||||||
|
"title": "简单的操作",
|
||||||
|
"description": "无需运行您自己的邮件服务器或设置复杂的警报。您将从方框中获得健康检查和下限提醒。"
|
||||||
|
},
|
||||||
|
"benefitAutomaticUpdates": {
|
||||||
|
"title": "自动更新",
|
||||||
|
"description": "云仪表盘快速演化,所以您可以获得新的功能和错误修复,而不必每次手动拉取新的容器。"
|
||||||
|
},
|
||||||
|
"benefitLessMaintenance": {
|
||||||
|
"title": "减少维护时间",
|
||||||
|
"description": "没有要管理的数据库迁移、备份或额外的基础设施。我们在云端处理这个问题。"
|
||||||
|
},
|
||||||
|
"benefitCloudFailover": {
|
||||||
|
"title": "云失败",
|
||||||
|
"description": "如果您的节点被关闭,您的隧道可能暂时无法连接到我们的云端,直到您将其重新连接上线。"
|
||||||
|
},
|
||||||
|
"benefitHighAvailability": {
|
||||||
|
"title": "高可用率(PoPs)",
|
||||||
|
"description": "您还可以将多个节点添加到您的帐户中以获取冗余和更好的性能。"
|
||||||
|
},
|
||||||
|
"benefitFutureEnhancements": {
|
||||||
|
"title": "将来的改进",
|
||||||
|
"description": "我们正在计划添加更多的分析、警报和管理工具,使你的部署更加有力。"
|
||||||
|
},
|
||||||
|
"docsAlert": {
|
||||||
|
"text": "在我们中更多地了解管理下的自托管选项",
|
||||||
|
"documentation": "文档"
|
||||||
|
},
|
||||||
|
"convertButton": "将此节点转换为管理自托管的"
|
||||||
|
},
|
||||||
|
"internationaldomaindetected": "检测到国际域",
|
||||||
|
"willbestoredas": "储存为:",
|
||||||
|
"roleMappingDescription": "确定当用户启用自动配送时如何分配他们的角色。",
|
||||||
|
"selectRole": "选择角色",
|
||||||
|
"roleMappingExpression": "表达式",
|
||||||
|
"selectRolePlaceholder": "选择角色",
|
||||||
|
"selectRoleDescription": "选择一个角色,从此身份提供商分配给所有用户",
|
||||||
|
"roleMappingExpressionDescription": "输入一个 JMESPath 表达式来从 ID 令牌提取角色信息",
|
||||||
|
"idpTenantIdRequired": "租户ID是必需的",
|
||||||
|
"invalidValue": "无效的值",
|
||||||
|
"idpTypeLabel": "身份提供者类型",
|
||||||
|
"roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'",
|
||||||
|
"idpGoogleConfiguration": "Google 配置",
|
||||||
|
"idpGoogleConfigurationDescription": "配置您的 Google OAuth2 凭据",
|
||||||
|
"idpGoogleClientIdDescription": "您的 Google OAuth2 客户端 ID",
|
||||||
|
"idpGoogleClientSecretDescription": "您的 Google OAuth2 客户端密钥",
|
||||||
|
"idpAzureConfiguration": "Azure Entra ID 配置",
|
||||||
|
"idpAzureConfigurationDescription": "配置您的 Azure Entra ID OAuth2 凭据",
|
||||||
|
"idpTenantId": "租户 ID",
|
||||||
|
"idpTenantIdPlaceholder": "您的租户ID",
|
||||||
|
"idpAzureTenantIdDescription": "您的 Azure 租户ID (在 Azure Active Directory 概览中发现)",
|
||||||
|
"idpAzureClientIdDescription": "您的 Azure 应用程序注册客户端 ID",
|
||||||
|
"idpAzureClientSecretDescription": "您的 Azure 应用程序注册客户端密钥",
|
||||||
|
"idpGoogleTitle": "谷歌",
|
||||||
|
"idpGoogleAlt": "Google",
|
||||||
|
"idpAzureTitle": "Azure Entra ID",
|
||||||
|
"idpAzureAlt": "Azure",
|
||||||
|
"idpGoogleConfigurationTitle": "Google 配置",
|
||||||
|
"idpAzureConfigurationTitle": "Azure Entra ID 配置",
|
||||||
|
"idpTenantIdLabel": "租户 ID",
|
||||||
|
"idpAzureClientIdDescription2": "您的 Azure 应用程序注册客户端 ID",
|
||||||
|
"idpAzureClientSecretDescription2": "您的 Azure 应用程序注册客户端密钥",
|
||||||
|
"idpGoogleDescription": "Google OAuth2/OIDC 提供商",
|
||||||
|
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||||
|
"subnet": "子网",
|
||||||
|
"subnetDescription": "此组织网络配置的子网。",
|
||||||
|
"authPage": "认证页面",
|
||||||
|
"authPageDescription": "配置您的组织认证页面",
|
||||||
|
"authPageDomain": "认证页面域",
|
||||||
|
"noDomainSet": "没有域设置",
|
||||||
|
"changeDomain": "更改域",
|
||||||
|
"selectDomain": "选择域",
|
||||||
|
"restartCertificate": "重新启动证书",
|
||||||
|
"editAuthPageDomain": "编辑认证页面域",
|
||||||
|
"setAuthPageDomain": "设置认证页面域",
|
||||||
|
"failedToFetchCertificate": "获取证书失败",
|
||||||
|
"failedToRestartCertificate": "重新启动证书失败",
|
||||||
|
"addDomainToEnableCustomAuthPages": "为您的组织添加域名以启用自定义认证页面",
|
||||||
|
"selectDomainForOrgAuthPage": "选择组织认证页面的域",
|
||||||
|
"domainPickerProvidedDomain": "提供的域",
|
||||||
|
"domainPickerFreeProvidedDomain": "免费提供的域",
|
||||||
|
"domainPickerVerified": "已验证",
|
||||||
|
"domainPickerUnverified": "未验证",
|
||||||
|
"domainPickerInvalidSubdomainStructure": "此子域包含无效的字符或结构。当您保存时,它将被自动清除。",
|
||||||
|
"domainPickerError": "错误",
|
||||||
|
"domainPickerErrorLoadDomains": "加载组织域名失败",
|
||||||
|
"domainPickerErrorCheckAvailability": "检查域可用性失败",
|
||||||
|
"domainPickerInvalidSubdomain": "无效的子域",
|
||||||
|
"domainPickerInvalidSubdomainRemoved": "输入 \"{sub}\" 已被移除,因为其无效。",
|
||||||
|
"domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" 无法为 {domain} 变为有效。",
|
||||||
|
"domainPickerSubdomainSanitized": "子域已净化",
|
||||||
|
"domainPickerSubdomainCorrected": "\"{sub}\" 已被更正为 \"{sanitized}\"",
|
||||||
|
"orgAuthSignInTitle": "登录到您的组织",
|
||||||
|
"orgAuthChooseIdpDescription": "选择您的身份提供商以继续",
|
||||||
|
"orgAuthNoIdpConfigured": "此机构没有配置任何身份提供者。您可以使用您的 Pangolin 身份登录。",
|
||||||
|
"orgAuthSignInWithPangolin": "使用 Pangolin 登录",
|
||||||
|
"subscriptionRequiredToUse": "需要订阅才能使用此功能。",
|
||||||
|
"idpDisabled": "身份提供者已禁用。",
|
||||||
|
"orgAuthPageDisabled": "组织认证页面已禁用。",
|
||||||
|
"domainRestartedDescription": "域验证重新启动成功",
|
||||||
|
"resourceAddEntrypointsEditFile": "编辑文件:config/traefik/traefik_config.yml",
|
||||||
|
"resourceExposePortsEditFile": "编辑文件:docker-compose.yml",
|
||||||
|
"emailVerificationRequired": "需要电子邮件验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。",
|
||||||
|
"twoFactorSetupRequired": "需要设置双因素身份验证。 请通过 {dashboardUrl}/auth/login 再次登录以完成此步骤。 然后,回到这里。",
|
||||||
|
"authPageErrorUpdateMessage": "更新身份验证页面设置时出错",
|
||||||
|
"authPageUpdated": "身份验证页面更新成功",
|
||||||
|
"healthCheckNotAvailable": "本地的",
|
||||||
|
"rewritePath": "重写路径",
|
||||||
|
"rewritePathDescription": "在转发到目标之前,可以选择重写路径。"
|
||||||
}
|
}
|
||||||
|
|||||||
10868
package-lock.json
generated
10868
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
121
package.json
121
package.json
@@ -19,46 +19,52 @@
|
|||||||
"db:sqlite:studio": "drizzle-kit studio --config=./drizzle.sqlite.config.ts",
|
"db:sqlite:studio": "drizzle-kit studio --config=./drizzle.sqlite.config.ts",
|
||||||
"db:pg:studio": "drizzle-kit studio --config=./drizzle.pg.config.ts",
|
"db:pg:studio": "drizzle-kit studio --config=./drizzle.pg.config.ts",
|
||||||
"db:clear-migrations": "rm -rf server/migrations",
|
"db:clear-migrations": "rm -rf server/migrations",
|
||||||
|
"set:oss": "echo 'export const build = \"oss\" as any;' > server/build.ts",
|
||||||
|
"set:saas": "echo 'export const build = \"saas\" as any;' > server/build.ts",
|
||||||
|
"set:enterprise": "echo 'export const build = \"enterprise\" as any;' > server/build.ts",
|
||||||
|
"set:sqlite": "echo 'export * from \"./sqlite\";' > server/db/index.ts",
|
||||||
|
"set:pg": "echo 'export * from \"./pg\";' > server/db/index.ts",
|
||||||
"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: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",
|
"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",
|
||||||
"start:sqlite": "DB_TYPE=sqlite NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
|
"start": "ENVIRONMENT=prod node dist/migrations.mjs && ENVIRONMENT=prod NODE_ENV=development node --enable-source-maps dist/server.mjs",
|
||||||
"start:pg": "DB_TYPE=pg NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
|
|
||||||
"email": "email dev --dir server/emails/templates --port 3005",
|
"email": "email dev --dir server/emails/templates --port 3005",
|
||||||
"build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs"
|
"build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs",
|
||||||
|
"db:sqlite:seed-exit-node": "sqlite3 config/db/db.sqlite \"INSERT INTO exitNodes (exitNodeId, name, address, endpoint, publicKey, listenPort, reachableAt, maxConnections, online, lastPing, type, region) VALUES (null, 'test', '10.0.0.1/24', 'localhost', 'MJ44MpnWGxMZURgxW/fWXDFsejhabnEFYDo60LQwK3A=', 1234, 'http://localhost:3003', 123, 1, null, 'gerbil', null);\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
||||||
"@hookform/resolvers": "3.9.1",
|
"@aws-sdk/client-s3": "3.837.0",
|
||||||
|
"@hookform/resolvers": "5.2.2",
|
||||||
"@node-rs/argon2": "^2.0.2",
|
"@node-rs/argon2": "^2.0.2",
|
||||||
"@oslojs/crypto": "1.0.1",
|
"@oslojs/crypto": "1.0.1",
|
||||||
"@oslojs/encoding": "1.1.0",
|
"@oslojs/encoding": "1.1.0",
|
||||||
"@radix-ui/react-avatar": "1.1.10",
|
"@radix-ui/react-avatar": "1.1.10",
|
||||||
"@radix-ui/react-checkbox": "1.3.2",
|
"@radix-ui/react-checkbox": "1.3.3",
|
||||||
"@radix-ui/react-collapsible": "1.1.11",
|
"@radix-ui/react-collapsible": "1.1.12",
|
||||||
"@radix-ui/react-dialog": "1.1.14",
|
"@radix-ui/react-dialog": "1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "2.1.15",
|
"@radix-ui/react-dropdown-menu": "2.1.16",
|
||||||
"@radix-ui/react-icons": "1.3.2",
|
"@radix-ui/react-icons": "1.3.2",
|
||||||
"@radix-ui/react-label": "2.1.7",
|
"@radix-ui/react-label": "2.1.7",
|
||||||
"@radix-ui/react-popover": "1.1.14",
|
"@radix-ui/react-popover": "1.1.15",
|
||||||
"@radix-ui/react-progress": "^1.1.7",
|
"@radix-ui/react-progress": "^1.1.7",
|
||||||
"@radix-ui/react-radio-group": "1.3.7",
|
"@radix-ui/react-radio-group": "1.3.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
"@radix-ui/react-select": "2.2.5",
|
"@radix-ui/react-select": "2.2.6",
|
||||||
"@radix-ui/react-separator": "1.1.7",
|
"@radix-ui/react-separator": "1.1.7",
|
||||||
"@radix-ui/react-slot": "1.2.3",
|
"@radix-ui/react-slot": "1.2.3",
|
||||||
"@radix-ui/react-switch": "1.2.5",
|
"@radix-ui/react-switch": "1.2.6",
|
||||||
"@radix-ui/react-tabs": "1.1.12",
|
"@radix-ui/react-tabs": "1.1.13",
|
||||||
"@radix-ui/react-toast": "1.2.14",
|
"@radix-ui/react-toast": "1.2.15",
|
||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@react-email/components": "0.3.1",
|
"@react-email/components": "0.5.5",
|
||||||
"@react-email/render": "^1.1.2",
|
"@react-email/render": "^1.2.0",
|
||||||
"@simplewebauthn/browser": "^13.1.0",
|
"@react-email/tailwind": "1.2.2",
|
||||||
"@simplewebauthn/server": "^9.0.3",
|
"@simplewebauthn/browser": "^13.2.0",
|
||||||
"@react-email/tailwind": "1.2.1",
|
"@simplewebauthn/server": "^13.2.1",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@tanstack/react-table": "8.21.3",
|
"@tanstack/react-table": "8.21.3",
|
||||||
"arctic": "^3.7.0",
|
"arctic": "^3.7.0",
|
||||||
"axios": "1.10.0",
|
"axios": "^1.12.2",
|
||||||
"better-sqlite3": "11.7.0",
|
"better-sqlite3": "11.7.0",
|
||||||
"canvas-confetti": "1.9.3",
|
"canvas-confetti": "1.9.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -69,82 +75,89 @@
|
|||||||
"cookies": "^0.9.1",
|
"cookies": "^0.9.1",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"drizzle-orm": "0.44.2",
|
"drizzle-orm": "0.44.6",
|
||||||
"eslint": "9.31.0",
|
"eslint": "9.35.0",
|
||||||
"eslint-config-next": "15.3.5",
|
"eslint-config-next": "15.5.4",
|
||||||
"express": "4.21.2",
|
"express": "5.1.0",
|
||||||
"express-rate-limit": "7.5.1",
|
"express-rate-limit": "8.1.0",
|
||||||
"glob": "11.0.3",
|
"glob": "11.0.3",
|
||||||
"helmet": "8.1.0",
|
"helmet": "8.1.0",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"input-otp": "1.4.2",
|
"input-otp": "1.4.2",
|
||||||
|
"ioredis": "5.6.1",
|
||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lucide-react": "0.525.0",
|
"lucide-react": "^0.544.0",
|
||||||
|
"maxmind": "5.0.0",
|
||||||
"moment": "2.30.1",
|
"moment": "2.30.1",
|
||||||
"next": "15.3.5",
|
"next": "15.5.4",
|
||||||
"next-intl": "^4.3.4",
|
"next-intl": "^4.3.9",
|
||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"node-cache": "5.1.2",
|
"node-cache": "5.1.2",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "7.0.5",
|
"nodemailer": "7.0.6",
|
||||||
"npm": "^11.4.2",
|
"npm": "^11.6.1",
|
||||||
"oslo": "1.2.1",
|
"oslo": "1.2.1",
|
||||||
"pg": "^8.16.2",
|
"pg": "^8.16.2",
|
||||||
|
"posthog-node": "^5.8.4",
|
||||||
"qrcode.react": "4.2.0",
|
"qrcode.react": "4.2.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.1",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.1",
|
||||||
"react-easy-sort": "^1.6.0",
|
"react-easy-sort": "^1.7.0",
|
||||||
"react-hook-form": "7.60.0",
|
"react-hook-form": "7.62.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"rebuild": "0.1.2",
|
"rebuild": "0.1.2",
|
||||||
|
"reodotdev": "^1.0.0",
|
||||||
|
"resend": "^6.1.1",
|
||||||
"semver": "^7.7.2",
|
"semver": "^7.7.2",
|
||||||
|
"stripe": "18.2.1",
|
||||||
"swagger-ui-express": "^5.0.1",
|
"swagger-ui-express": "^5.0.1",
|
||||||
"tailwind-merge": "3.3.1",
|
"tailwind-merge": "3.3.1",
|
||||||
"tw-animate-css": "^1.3.5",
|
"tw-animate-css": "^1.3.8",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^13.0.0",
|
||||||
"vaul": "1.1.2",
|
"vaul": "1.1.2",
|
||||||
"winston": "3.17.0",
|
"winston": "3.17.0",
|
||||||
"winston-daily-rotate-file": "5.0.0",
|
"winston-daily-rotate-file": "5.0.0",
|
||||||
"ws": "8.18.3",
|
"ws": "8.18.3",
|
||||||
|
"yargs": "18.0.0",
|
||||||
"zod": "3.25.76",
|
"zod": "3.25.76",
|
||||||
"zod-validation-error": "3.5.2",
|
"zod-validation-error": "3.5.2"
|
||||||
"yargs": "18.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dotenvx/dotenvx": "1.47.6",
|
"@dotenvx/dotenvx": "1.51.0",
|
||||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||||
"@tailwindcss/postcss": "^4.1.10",
|
"@react-email/preview-server": "4.2.12",
|
||||||
|
"@tailwindcss/postcss": "^4.1.14",
|
||||||
"@types/better-sqlite3": "7.6.12",
|
"@types/better-sqlite3": "7.6.12",
|
||||||
"@types/cookie-parser": "1.4.9",
|
"@types/cookie-parser": "1.4.9",
|
||||||
"@types/cors": "2.8.19",
|
"@types/cors": "2.8.19",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/express": "5.0.0",
|
"@types/express": "5.0.3",
|
||||||
"@types/express-session": "^1.18.2",
|
"@types/express-session": "^1.18.2",
|
||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
"@types/js-yaml": "4.0.9",
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^24",
|
"@types/node": "24.6.2",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "7.0.2",
|
||||||
"@types/pg": "8.15.4",
|
"@types/pg": "8.15.5",
|
||||||
"@types/react": "19.1.8",
|
"@types/react": "19.1.16",
|
||||||
"@types/react-dom": "19.1.6",
|
"@types/react-dom": "19.1.9",
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.7.1",
|
||||||
"@types/swagger-ui-express": "^4.1.8",
|
"@types/swagger-ui-express": "^4.1.8",
|
||||||
"@types/ws": "8.18.1",
|
"@types/ws": "8.18.1",
|
||||||
"@types/yargs": "17.0.33",
|
"@types/yargs": "17.0.33",
|
||||||
"drizzle-kit": "0.31.4",
|
"drizzle-kit": "0.31.5",
|
||||||
"esbuild": "0.25.6",
|
"esbuild": "0.25.10",
|
||||||
"esbuild-node-externals": "1.18.0",
|
"esbuild-node-externals": "1.18.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"react-email": "4.1.0",
|
"react-email": "4.2.12",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.4",
|
||||||
"tsc-alias": "1.8.16",
|
"tsc-alias": "1.8.16",
|
||||||
"tsx": "4.20.3",
|
"tsx": "4.20.6",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"typescript-eslint": "^8.36.0"
|
"typescript-eslint": "^8.45.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"emblor": {
|
"emblor": {
|
||||||
|
|||||||
BIN
public/idp/azure.png
Normal file
BIN
public/idp/azure.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
BIN
public/idp/google.png
Normal file
BIN
public/idp/google.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -7,16 +7,21 @@ import {
|
|||||||
errorHandlerMiddleware,
|
errorHandlerMiddleware,
|
||||||
notFoundMiddleware
|
notFoundMiddleware
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
|
import { corsWithLoginPageSupport } from "@server/middlewares/private/corsWithLoginPage";
|
||||||
import { authenticated, unauthenticated } from "@server/routers/external";
|
import { authenticated, unauthenticated } from "@server/routers/external";
|
||||||
import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws";
|
import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws";
|
||||||
import { logIncomingMiddleware } from "./middlewares/logIncoming";
|
import { logIncomingMiddleware } from "./middlewares/logIncoming";
|
||||||
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
|
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
|
||||||
import helmet from "helmet";
|
import helmet from "helmet";
|
||||||
import rateLimit from "express-rate-limit";
|
import { stripeWebhookHandler } from "@server/routers/private/billing/webhooks";
|
||||||
|
import { build } from "./build";
|
||||||
|
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "./types/HttpCode";
|
import HttpCode from "./types/HttpCode";
|
||||||
import requestTimeoutMiddleware from "./middlewares/requestTimeout";
|
import requestTimeoutMiddleware from "./middlewares/requestTimeout";
|
||||||
import { createStore } from "./lib/rateLimitStore";
|
import { createStore } from "@server/lib/private/rateLimitStore";
|
||||||
|
import hybridRouter from "@server/routers/private/hybrid";
|
||||||
|
import { stripDuplicateSesions } from "./middlewares/stripDuplicateSessions";
|
||||||
|
|
||||||
const dev = config.isDev;
|
const dev = config.isDev;
|
||||||
const externalPort = config.getRawConfig().server.external_port;
|
const externalPort = config.getRawConfig().server.external_port;
|
||||||
@@ -30,32 +35,46 @@ export function createApiServer() {
|
|||||||
apiServer.set("trust proxy", trustProxy);
|
apiServer.set("trust proxy", trustProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (build == "saas") {
|
||||||
|
apiServer.post(
|
||||||
|
`${prefix}/billing/webhooks`,
|
||||||
|
express.raw({ type: "application/json" }),
|
||||||
|
stripeWebhookHandler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const corsConfig = config.getRawConfig().server.cors;
|
const corsConfig = config.getRawConfig().server.cors;
|
||||||
|
|
||||||
const options = {
|
if (build == "oss") {
|
||||||
...(corsConfig?.origins
|
const options = {
|
||||||
? { origin: corsConfig.origins }
|
...(corsConfig?.origins
|
||||||
: {
|
? { origin: corsConfig.origins }
|
||||||
origin: (origin: any, callback: any) => {
|
: {
|
||||||
callback(null, true);
|
origin: (origin: any, callback: any) => {
|
||||||
}
|
callback(null, true);
|
||||||
}),
|
}
|
||||||
...(corsConfig?.methods && { methods: corsConfig.methods }),
|
}),
|
||||||
...(corsConfig?.allowed_headers && {
|
...(corsConfig?.methods && { methods: corsConfig.methods }),
|
||||||
allowedHeaders: corsConfig.allowed_headers
|
...(corsConfig?.allowed_headers && {
|
||||||
}),
|
allowedHeaders: corsConfig.allowed_headers
|
||||||
credentials: !(corsConfig?.credentials === false)
|
}),
|
||||||
};
|
credentials: !(corsConfig?.credentials === false)
|
||||||
|
};
|
||||||
|
|
||||||
logger.debug("Using CORS options", options);
|
logger.debug("Using CORS options", options);
|
||||||
|
|
||||||
apiServer.use(cors(options));
|
apiServer.use(cors(options));
|
||||||
|
} else {
|
||||||
|
// Use the custom CORS middleware with loginPage support
|
||||||
|
apiServer.use(corsWithLoginPageSupport(corsConfig));
|
||||||
|
}
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
apiServer.use(helmet());
|
apiServer.use(helmet());
|
||||||
apiServer.use(csrfProtectionMiddleware);
|
apiServer.use(csrfProtectionMiddleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apiServer.use(stripDuplicateSesions);
|
||||||
apiServer.use(cookieParser());
|
apiServer.use(cookieParser());
|
||||||
apiServer.use(express.json());
|
apiServer.use(express.json());
|
||||||
|
|
||||||
@@ -70,7 +89,8 @@ export function createApiServer() {
|
|||||||
60 *
|
60 *
|
||||||
1000,
|
1000,
|
||||||
max: config.getRawConfig().rate_limits.global.max_requests,
|
max: config.getRawConfig().rate_limits.global.max_requests,
|
||||||
keyGenerator: (req) => `apiServerGlobal:${req.ip}:${req.path}`,
|
keyGenerator: (req) =>
|
||||||
|
`apiServerGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.global.max_requests} requests every ${config.getRawConfig().rate_limits.global.window_minutes} minute(s).`;
|
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.global.max_requests} requests every ${config.getRawConfig().rate_limits.global.window_minutes} minute(s).`;
|
||||||
return next(
|
return next(
|
||||||
@@ -85,6 +105,9 @@ export function createApiServer() {
|
|||||||
// API routes
|
// API routes
|
||||||
apiServer.use(logIncomingMiddleware);
|
apiServer.use(logIncomingMiddleware);
|
||||||
apiServer.use(prefix, unauthenticated);
|
apiServer.use(prefix, unauthenticated);
|
||||||
|
if (build !== "oss") {
|
||||||
|
apiServer.use(`${prefix}/hybrid`, hybridRouter);
|
||||||
|
}
|
||||||
apiServer.use(prefix, authenticated);
|
apiServer.use(prefix, authenticated);
|
||||||
|
|
||||||
// WebSocket routes
|
// WebSocket routes
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { userActions, roleActions, userOrgs } from "@server/db";
|
|||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import { sendUsageNotification } from "@server/routers/org";
|
||||||
|
|
||||||
export enum ActionsEnum {
|
export enum ActionsEnum {
|
||||||
createOrgUser = "createOrgUser",
|
createOrgUser = "createOrgUser",
|
||||||
@@ -69,6 +70,11 @@ export enum ActionsEnum {
|
|||||||
deleteResourceRule = "deleteResourceRule",
|
deleteResourceRule = "deleteResourceRule",
|
||||||
listResourceRules = "listResourceRules",
|
listResourceRules = "listResourceRules",
|
||||||
updateResourceRule = "updateResourceRule",
|
updateResourceRule = "updateResourceRule",
|
||||||
|
createSiteResource = "createSiteResource",
|
||||||
|
deleteSiteResource = "deleteSiteResource",
|
||||||
|
getSiteResource = "getSiteResource",
|
||||||
|
listSiteResources = "listSiteResources",
|
||||||
|
updateSiteResource = "updateSiteResource",
|
||||||
createClient = "createClient",
|
createClient = "createClient",
|
||||||
deleteClient = "deleteClient",
|
deleteClient = "deleteClient",
|
||||||
updateClient = "updateClient",
|
updateClient = "updateClient",
|
||||||
@@ -93,9 +99,24 @@ export enum ActionsEnum {
|
|||||||
listApiKeyActions = "listApiKeyActions",
|
listApiKeyActions = "listApiKeyActions",
|
||||||
listApiKeys = "listApiKeys",
|
listApiKeys = "listApiKeys",
|
||||||
getApiKey = "getApiKey",
|
getApiKey = "getApiKey",
|
||||||
|
getCertificate = "getCertificate",
|
||||||
|
restartCertificate = "restartCertificate",
|
||||||
|
billing = "billing",
|
||||||
createOrgDomain = "createOrgDomain",
|
createOrgDomain = "createOrgDomain",
|
||||||
deleteOrgDomain = "deleteOrgDomain",
|
deleteOrgDomain = "deleteOrgDomain",
|
||||||
restartOrgDomain = "restartOrgDomain"
|
restartOrgDomain = "restartOrgDomain",
|
||||||
|
sendUsageNotification = "sendUsageNotification",
|
||||||
|
createRemoteExitNode = "createRemoteExitNode",
|
||||||
|
updateRemoteExitNode = "updateRemoteExitNode",
|
||||||
|
getRemoteExitNode = "getRemoteExitNode",
|
||||||
|
listRemoteExitNode = "listRemoteExitNode",
|
||||||
|
deleteRemoteExitNode = "deleteRemoteExitNode",
|
||||||
|
updateOrgUser = "updateOrgUser",
|
||||||
|
createLoginPage = "createLoginPage",
|
||||||
|
updateLoginPage = "updateLoginPage",
|
||||||
|
getLoginPage = "getLoginPage",
|
||||||
|
deleteLoginPage = "deleteLoginPage",
|
||||||
|
applyBlueprint = "applyBlueprint"
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkUserActionPermission(
|
export async function checkUserActionPermission(
|
||||||
|
|||||||
@@ -3,13 +3,7 @@ import {
|
|||||||
encodeHexLowerCase
|
encodeHexLowerCase
|
||||||
} from "@oslojs/encoding";
|
} from "@oslojs/encoding";
|
||||||
import { sha256 } from "@oslojs/crypto/sha2";
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
import {
|
import { resourceSessions, Session, sessions, User, users } from "@server/db";
|
||||||
resourceSessions,
|
|
||||||
Session,
|
|
||||||
sessions,
|
|
||||||
User,
|
|
||||||
users
|
|
||||||
} from "@server/db";
|
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { eq, inArray } from "drizzle-orm";
|
import { eq, inArray } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
@@ -24,8 +18,9 @@ export const SESSION_COOKIE_EXPIRES =
|
|||||||
60 *
|
60 *
|
||||||
60 *
|
60 *
|
||||||
config.getRawConfig().server.dashboard_session_length_hours;
|
config.getRawConfig().server.dashboard_session_length_hours;
|
||||||
export const COOKIE_DOMAIN =
|
export const COOKIE_DOMAIN = config.getRawConfig().app.dashboard_url
|
||||||
"." + new URL(config.getRawConfig().app.dashboard_url).hostname;
|
? new URL(config.getRawConfig().app.dashboard_url!).hostname
|
||||||
|
: undefined;
|
||||||
|
|
||||||
export function generateSessionToken(): string {
|
export function generateSessionToken(): string {
|
||||||
const bytes = new Uint8Array(20);
|
const bytes = new Uint8Array(20);
|
||||||
@@ -98,8 +93,8 @@ export async function invalidateSession(sessionId: string): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
await trx
|
await trx
|
||||||
.delete(resourceSessions)
|
.delete(resourceSessions)
|
||||||
.where(eq(resourceSessions.userSessionId, sessionId));
|
.where(eq(resourceSessions.userSessionId, sessionId));
|
||||||
await trx.delete(sessions).where(eq(sessions.sessionId, sessionId));
|
await trx.delete(sessions).where(eq(sessions.sessionId, sessionId));
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -111,9 +106,9 @@ export async function invalidateAllSessions(userId: string): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
const userSessions = await trx
|
const userSessions = await trx
|
||||||
.select()
|
.select()
|
||||||
.from(sessions)
|
.from(sessions)
|
||||||
.where(eq(sessions.userId, userId));
|
.where(eq(sessions.userId, userId));
|
||||||
await trx.delete(resourceSessions).where(
|
await trx.delete(resourceSessions).where(
|
||||||
inArray(
|
inArray(
|
||||||
resourceSessions.userSessionId,
|
resourceSessions.userSessionId,
|
||||||
|
|||||||
85
server/auth/sessions/privateRemoteExitNode.ts
Normal file
85
server/auth/sessions/privateRemoteExitNode.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
encodeHexLowerCase,
|
||||||
|
} from "@oslojs/encoding";
|
||||||
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
|
import { RemoteExitNode, remoteExitNodes, remoteExitNodeSessions, RemoteExitNodeSession } from "@server/db";
|
||||||
|
import { db } from "@server/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export const EXPIRES = 1000 * 60 * 60 * 24 * 30;
|
||||||
|
|
||||||
|
export async function createRemoteExitNodeSession(
|
||||||
|
token: string,
|
||||||
|
remoteExitNodeId: string,
|
||||||
|
): Promise<RemoteExitNodeSession> {
|
||||||
|
const sessionId = encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(token)),
|
||||||
|
);
|
||||||
|
const session: RemoteExitNodeSession = {
|
||||||
|
sessionId: sessionId,
|
||||||
|
remoteExitNodeId,
|
||||||
|
expiresAt: new Date(Date.now() + EXPIRES).getTime(),
|
||||||
|
};
|
||||||
|
await db.insert(remoteExitNodeSessions).values(session);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateRemoteExitNodeSessionToken(
|
||||||
|
token: string,
|
||||||
|
): Promise<SessionValidationResult> {
|
||||||
|
const sessionId = encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(token)),
|
||||||
|
);
|
||||||
|
const result = await db
|
||||||
|
.select({ remoteExitNode: remoteExitNodes, session: remoteExitNodeSessions })
|
||||||
|
.from(remoteExitNodeSessions)
|
||||||
|
.innerJoin(remoteExitNodes, eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodes.remoteExitNodeId))
|
||||||
|
.where(eq(remoteExitNodeSessions.sessionId, sessionId));
|
||||||
|
if (result.length < 1) {
|
||||||
|
return { session: null, remoteExitNode: null };
|
||||||
|
}
|
||||||
|
const { remoteExitNode, session } = result[0];
|
||||||
|
if (Date.now() >= session.expiresAt) {
|
||||||
|
await db
|
||||||
|
.delete(remoteExitNodeSessions)
|
||||||
|
.where(eq(remoteExitNodeSessions.sessionId, session.sessionId));
|
||||||
|
return { session: null, remoteExitNode: null };
|
||||||
|
}
|
||||||
|
if (Date.now() >= session.expiresAt - (EXPIRES / 2)) {
|
||||||
|
session.expiresAt = new Date(
|
||||||
|
Date.now() + EXPIRES,
|
||||||
|
).getTime();
|
||||||
|
await db
|
||||||
|
.update(remoteExitNodeSessions)
|
||||||
|
.set({
|
||||||
|
expiresAt: session.expiresAt,
|
||||||
|
})
|
||||||
|
.where(eq(remoteExitNodeSessions.sessionId, session.sessionId));
|
||||||
|
}
|
||||||
|
return { session, remoteExitNode };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function invalidateRemoteExitNodeSession(sessionId: string): Promise<void> {
|
||||||
|
await db.delete(remoteExitNodeSessions).where(eq(remoteExitNodeSessions.sessionId, sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function invalidateAllRemoteExitNodeSessions(remoteExitNodeId: string): Promise<void> {
|
||||||
|
await db.delete(remoteExitNodeSessions).where(eq(remoteExitNodeSessions.remoteExitNodeId, remoteExitNodeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SessionValidationResult =
|
||||||
|
| { session: RemoteExitNodeSession; remoteExitNode: RemoteExitNode }
|
||||||
|
| { session: null; remoteExitNode: null };
|
||||||
@@ -4,6 +4,9 @@ import { resourceSessions, ResourceSession } from "@server/db";
|
|||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
import axios from "axios";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { tokenManager } from "@server/lib/tokenManager";
|
||||||
|
|
||||||
export const SESSION_COOKIE_NAME =
|
export const SESSION_COOKIE_NAME =
|
||||||
config.getRawConfig().server.session_cookie_name;
|
config.getRawConfig().server.session_cookie_name;
|
||||||
@@ -62,6 +65,29 @@ export async function validateResourceSessionToken(
|
|||||||
token: string,
|
token: string,
|
||||||
resourceId: number
|
resourceId: number
|
||||||
): Promise<ResourceSessionValidationResult> {
|
): Promise<ResourceSessionValidationResult> {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/session/validate`, {
|
||||||
|
token: token
|
||||||
|
}, await tokenManager.getAuthHeader());
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error validating resource session token in hybrid mode:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error validating resource session token in hybrid mode:", error);
|
||||||
|
}
|
||||||
|
return { resourceSession: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const sessionId = encodeHexLowerCase(
|
const sessionId = encodeHexLowerCase(
|
||||||
sha256(new TextEncoder().encode(token))
|
sha256(new TextEncoder().encode(token))
|
||||||
);
|
);
|
||||||
@@ -173,14 +199,14 @@ export function serializeResourceSessionCookie(
|
|||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (!isHttp) {
|
if (!isHttp) {
|
||||||
if (expiresAt === undefined) {
|
if (expiresAt === undefined) {
|
||||||
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Secure; Domain=${domain}`;
|
||||||
}
|
}
|
||||||
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${domain}`;
|
||||||
} else {
|
} else {
|
||||||
if (expiresAt === undefined) {
|
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}`;
|
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${domain}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,9 +216,9 @@ export function createBlankResourceSessionTokenCookie(
|
|||||||
isHttp: boolean = false
|
isHttp: boolean = false
|
||||||
): string {
|
): string {
|
||||||
if (!isHttp) {
|
if (!isHttp) {
|
||||||
return `${cookieName}_s=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure; Domain=${domain}`;
|
||||||
} else {
|
} else {
|
||||||
return `${cookieName}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Domain=${"." + domain}`;
|
return `${cookieName}=; HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Domain=${domain}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export const build = "oss" as any;
|
|
||||||
1014
server/db/countries.ts
Normal file
1014
server/db/countries.ts
Normal file
File diff suppressed because it is too large
Load Diff
13
server/db/maxmind.ts
Normal file
13
server/db/maxmind.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import maxmind, { CountryResponse, Reader } from "maxmind";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
|
let maxmindLookup: Reader<CountryResponse> | null;
|
||||||
|
if (config.getRawConfig().server.maxmind_db_path) {
|
||||||
|
maxmindLookup = await maxmind.open<CountryResponse>(
|
||||||
|
config.getRawConfig().server.maxmind_db_path!
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
maxmindLookup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { maxmindLookup };
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { db } from "@server/db";
|
import { db, resources, siteResources } from "@server/db";
|
||||||
import { exitNodes, sites } from "@server/db";
|
import { exitNodes, sites } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { __DIRNAME } from "@server/lib/consts";
|
import { __DIRNAME } from "@server/lib/consts";
|
||||||
@@ -34,6 +34,44 @@ export async function getUniqueSiteName(orgId: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUniqueResourceName(orgId: string): Promise<string> {
|
||||||
|
let loops = 0;
|
||||||
|
while (true) {
|
||||||
|
if (loops > 100) {
|
||||||
|
throw new Error("Could not generate a unique name");
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = generateName();
|
||||||
|
const count = await db
|
||||||
|
.select({ niceId: resources.niceId, orgId: resources.orgId })
|
||||||
|
.from(resources)
|
||||||
|
.where(and(eq(resources.niceId, name), eq(resources.orgId, orgId)));
|
||||||
|
if (count.length === 0) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
loops++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUniqueSiteResourceName(orgId: string): Promise<string> {
|
||||||
|
let loops = 0;
|
||||||
|
while (true) {
|
||||||
|
if (loops > 100) {
|
||||||
|
throw new Error("Could not generate a unique name");
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = generateName();
|
||||||
|
const count = await db
|
||||||
|
.select({ niceId: siteResources.niceId, orgId: siteResources.orgId })
|
||||||
|
.from(siteResources)
|
||||||
|
.where(and(eq(siteResources.niceId, name), eq(siteResources.orgId, orgId)));
|
||||||
|
if (count.length === 0) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
loops++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getUniqueExitNodeEndpointName(): Promise<string> {
|
export async function getUniqueExitNodeEndpointName(): Promise<string> {
|
||||||
let loops = 0;
|
let loops = 0;
|
||||||
const count = await db
|
const count = await db
|
||||||
|
|||||||
@@ -7,9 +7,22 @@ function createDb() {
|
|||||||
const config = readConfigFile();
|
const config = readConfigFile();
|
||||||
|
|
||||||
if (!config.postgres) {
|
if (!config.postgres) {
|
||||||
throw new Error(
|
// check the environment variables for postgres config
|
||||||
"Postgres configuration is missing in the configuration file."
|
if (process.env.POSTGRES_CONNECTION_STRING) {
|
||||||
);
|
config.postgres = {
|
||||||
|
connection_string: process.env.POSTGRES_CONNECTION_STRING
|
||||||
|
};
|
||||||
|
if (process.env.POSTGRES_REPLICA_CONNECTION_STRINGS) {
|
||||||
|
const replicas = process.env.POSTGRES_REPLICA_CONNECTION_STRINGS.split(",").map((conn) => ({
|
||||||
|
connection_string: conn.trim()
|
||||||
|
}));
|
||||||
|
config.postgres.replicas = replicas;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"Postgres configuration is missing in the configuration file."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectionString = config.postgres?.connection_string;
|
const connectionString = config.postgres?.connection_string;
|
||||||
@@ -26,7 +39,7 @@ function createDb() {
|
|||||||
connectionString,
|
connectionString,
|
||||||
max: 20,
|
max: 20,
|
||||||
idleTimeoutMillis: 30000,
|
idleTimeoutMillis: 30000,
|
||||||
connectionTimeoutMillis: 2000,
|
connectionTimeoutMillis: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const replicas = [];
|
const replicas = [];
|
||||||
@@ -39,7 +52,7 @@ function createDb() {
|
|||||||
connectionString: conn.connection_string,
|
connectionString: conn.connection_string,
|
||||||
max: 10,
|
max: 10,
|
||||||
idleTimeoutMillis: 30000,
|
idleTimeoutMillis: 30000,
|
||||||
connectionTimeoutMillis: 2000,
|
connectionTimeoutMillis: 5000,
|
||||||
});
|
});
|
||||||
replicas.push(DrizzlePostgres(replicaPool));
|
replicas.push(DrizzlePostgres(replicaPool));
|
||||||
}
|
}
|
||||||
@@ -50,3 +63,4 @@ function createDb() {
|
|||||||
|
|
||||||
export const db = createDb();
|
export const db = createDb();
|
||||||
export default db;
|
export default db;
|
||||||
|
export type Transaction = Parameters<Parameters<typeof db["transaction"]>[0]>[0];
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./driver";
|
export * from "./driver";
|
||||||
export * from "./schema";
|
export * from "./schema";
|
||||||
|
export * from "./privateSchema";
|
||||||
|
|||||||
245
server/db/pg/privateSchema.ts
Normal file
245
server/db/pg/privateSchema.ts
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
pgTable,
|
||||||
|
serial,
|
||||||
|
varchar,
|
||||||
|
boolean,
|
||||||
|
integer,
|
||||||
|
bigint,
|
||||||
|
real,
|
||||||
|
text
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { InferSelectModel } from "drizzle-orm";
|
||||||
|
import { domains, orgs, targets, users, exitNodes, sessions } from "./schema";
|
||||||
|
|
||||||
|
export const certificates = pgTable("certificates", {
|
||||||
|
certId: serial("certId").primaryKey(),
|
||||||
|
domain: varchar("domain", { length: 255 }).notNull().unique(),
|
||||||
|
domainId: varchar("domainId").references(() => domains.domainId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
}),
|
||||||
|
wildcard: boolean("wildcard").default(false),
|
||||||
|
status: varchar("status", { length: 50 }).notNull().default("pending"), // pending, requested, valid, expired, failed
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }),
|
||||||
|
lastRenewalAttempt: bigint("lastRenewalAttempt", { mode: "number" }),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||||
|
updatedAt: bigint("updatedAt", { mode: "number" }).notNull(),
|
||||||
|
orderId: varchar("orderId", { length: 500 }),
|
||||||
|
errorMessage: text("errorMessage"),
|
||||||
|
renewalCount: integer("renewalCount").default(0),
|
||||||
|
certFile: text("certFile"),
|
||||||
|
keyFile: text("keyFile")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dnsChallenge = pgTable("dnsChallenges", {
|
||||||
|
dnsChallengeId: serial("dnsChallengeId").primaryKey(),
|
||||||
|
domain: varchar("domain", { length: 255 }).notNull(),
|
||||||
|
token: varchar("token", { length: 255 }).notNull(),
|
||||||
|
keyAuthorization: varchar("keyAuthorization", { length: 1000 }).notNull(),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
|
||||||
|
completed: boolean("completed").default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const account = pgTable("account", {
|
||||||
|
accountId: serial("accountId").primaryKey(),
|
||||||
|
userId: varchar("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.userId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const customers = pgTable("customers", {
|
||||||
|
customerId: varchar("customerId", { length: 255 }).primaryKey().notNull(),
|
||||||
|
orgId: varchar("orgId", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
// accountId: integer("accountId")
|
||||||
|
// .references(() => account.accountId, { onDelete: "cascade" }), // Optional, if using accounts
|
||||||
|
email: varchar("email", { length: 255 }),
|
||||||
|
name: varchar("name", { length: 255 }),
|
||||||
|
phone: varchar("phone", { length: 50 }),
|
||||||
|
address: text("address"),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||||
|
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const subscriptions = pgTable("subscriptions", {
|
||||||
|
subscriptionId: varchar("subscriptionId", { length: 255 })
|
||||||
|
.primaryKey()
|
||||||
|
.notNull(),
|
||||||
|
customerId: varchar("customerId", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => customers.customerId, { onDelete: "cascade" }),
|
||||||
|
status: varchar("status", { length: 50 }).notNull().default("active"), // active, past_due, canceled, unpaid
|
||||||
|
canceledAt: bigint("canceledAt", { mode: "number" }),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||||
|
updatedAt: bigint("updatedAt", { mode: "number" }),
|
||||||
|
billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const subscriptionItems = pgTable("subscriptionItems", {
|
||||||
|
subscriptionItemId: serial("subscriptionItemId").primaryKey(),
|
||||||
|
subscriptionId: varchar("subscriptionId", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => subscriptions.subscriptionId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
}),
|
||||||
|
planId: varchar("planId", { length: 255 }).notNull(),
|
||||||
|
priceId: varchar("priceId", { length: 255 }),
|
||||||
|
meterId: varchar("meterId", { length: 255 }),
|
||||||
|
unitAmount: real("unitAmount"),
|
||||||
|
tiers: text("tiers"),
|
||||||
|
interval: varchar("interval", { length: 50 }),
|
||||||
|
currentPeriodStart: bigint("currentPeriodStart", { mode: "number" }),
|
||||||
|
currentPeriodEnd: bigint("currentPeriodEnd", { mode: "number" }),
|
||||||
|
name: varchar("name", { length: 255 })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const accountDomains = pgTable("accountDomains", {
|
||||||
|
accountId: integer("accountId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => account.accountId, { onDelete: "cascade" }),
|
||||||
|
domainId: varchar("domainId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => domains.domainId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const usage = pgTable("usage", {
|
||||||
|
usageId: varchar("usageId", { length: 255 }).primaryKey(),
|
||||||
|
featureId: varchar("featureId", { length: 255 }).notNull(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
|
.notNull(),
|
||||||
|
meterId: varchar("meterId", { length: 255 }),
|
||||||
|
instantaneousValue: real("instantaneousValue"),
|
||||||
|
latestValue: real("latestValue").notNull(),
|
||||||
|
previousValue: real("previousValue"),
|
||||||
|
updatedAt: bigint("updatedAt", { mode: "number" }).notNull(),
|
||||||
|
rolledOverAt: bigint("rolledOverAt", { mode: "number" }),
|
||||||
|
nextRolloverAt: bigint("nextRolloverAt", { mode: "number" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const limits = pgTable("limits", {
|
||||||
|
limitId: varchar("limitId", { length: 255 }).primaryKey(),
|
||||||
|
featureId: varchar("featureId", { length: 255 }).notNull(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.references(() => orgs.orgId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
|
value: real("value"),
|
||||||
|
description: text("description")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const usageNotifications = pgTable("usageNotifications", {
|
||||||
|
notificationId: serial("notificationId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
featureId: varchar("featureId", { length: 255 }).notNull(),
|
||||||
|
limitId: varchar("limitId", { length: 255 }).notNull(),
|
||||||
|
notificationType: varchar("notificationType", { length: 50 }).notNull(),
|
||||||
|
sentAt: bigint("sentAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const domainNamespaces = pgTable("domainNamespaces", {
|
||||||
|
domainNamespaceId: varchar("domainNamespaceId", {
|
||||||
|
length: 255
|
||||||
|
}).primaryKey(),
|
||||||
|
domainId: varchar("domainId")
|
||||||
|
.references(() => domains.domainId, {
|
||||||
|
onDelete: "set null"
|
||||||
|
})
|
||||||
|
.notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const exitNodeOrgs = pgTable("exitNodeOrgs", {
|
||||||
|
exitNodeId: integer("exitNodeId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => exitNodes.exitNodeId, { onDelete: "cascade" }),
|
||||||
|
orgId: text("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const remoteExitNodes = pgTable("remoteExitNode", {
|
||||||
|
remoteExitNodeId: varchar("id").primaryKey(),
|
||||||
|
secretHash: varchar("secretHash").notNull(),
|
||||||
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
|
version: varchar("version"),
|
||||||
|
exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const remoteExitNodeSessions = pgTable("remoteExitNodeSession", {
|
||||||
|
sessionId: varchar("id").primaryKey(),
|
||||||
|
remoteExitNodeId: varchar("remoteExitNodeId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => remoteExitNodes.remoteExitNodeId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
}),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const loginPage = pgTable("loginPage", {
|
||||||
|
loginPageId: serial("loginPageId").primaryKey(),
|
||||||
|
subdomain: varchar("subdomain"),
|
||||||
|
fullDomain: varchar("fullDomain"),
|
||||||
|
exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, {
|
||||||
|
onDelete: "set null"
|
||||||
|
}),
|
||||||
|
domainId: varchar("domainId").references(() => domains.domainId, {
|
||||||
|
onDelete: "set null"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const loginPageOrg = pgTable("loginPageOrg", {
|
||||||
|
loginPageId: integer("loginPageId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => loginPage.loginPageId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sessionTransferToken = pgTable("sessionTransferToken", {
|
||||||
|
token: varchar("token").primaryKey(),
|
||||||
|
sessionId: varchar("sessionId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => sessions.sessionId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
}),
|
||||||
|
encryptedSession: text("encryptedSession").notNull(),
|
||||||
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Limit = InferSelectModel<typeof limits>;
|
||||||
|
export type Account = InferSelectModel<typeof account>;
|
||||||
|
export type Certificate = InferSelectModel<typeof certificates>;
|
||||||
|
export type DnsChallenge = InferSelectModel<typeof dnsChallenge>;
|
||||||
|
export type Customer = InferSelectModel<typeof customers>;
|
||||||
|
export type Subscription = InferSelectModel<typeof subscriptions>;
|
||||||
|
export type SubscriptionItem = InferSelectModel<typeof subscriptionItems>;
|
||||||
|
export type Usage = InferSelectModel<typeof usage>;
|
||||||
|
export type UsageLimit = InferSelectModel<typeof limits>;
|
||||||
|
export type AccountDomain = InferSelectModel<typeof accountDomains>;
|
||||||
|
export type UsageNotification = InferSelectModel<typeof usageNotifications>;
|
||||||
|
export type RemoteExitNode = InferSelectModel<typeof remoteExitNodes>;
|
||||||
|
export type RemoteExitNodeSession = InferSelectModel<
|
||||||
|
typeof remoteExitNodeSessions
|
||||||
|
>;
|
||||||
|
export type ExitNodeOrg = InferSelectModel<typeof exitNodeOrgs>;
|
||||||
|
export type LoginPage = InferSelectModel<typeof loginPage>;
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
text
|
text
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { InferSelectModel } from "drizzle-orm";
|
import { InferSelectModel } from "drizzle-orm";
|
||||||
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
export const domains = pgTable("domains", {
|
export const domains = pgTable("domains", {
|
||||||
domainId: varchar("domainId").primaryKey(),
|
domainId: varchar("domainId").primaryKey(),
|
||||||
@@ -23,7 +24,9 @@ export const domains = pgTable("domains", {
|
|||||||
export const orgs = pgTable("orgs", {
|
export const orgs = pgTable("orgs", {
|
||||||
orgId: varchar("orgId").primaryKey(),
|
orgId: varchar("orgId").primaryKey(),
|
||||||
name: varchar("name").notNull(),
|
name: varchar("name").notNull(),
|
||||||
subnet: varchar("subnet")
|
subnet: varchar("subnet"),
|
||||||
|
createdAt: text("createdAt"),
|
||||||
|
settings: text("settings") // JSON blob of org-specific settings
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgDomains = pgTable("orgDomains", {
|
export const orgDomains = pgTable("orgDomains", {
|
||||||
@@ -65,16 +68,16 @@ export const sites = pgTable("sites", {
|
|||||||
|
|
||||||
export const resources = pgTable("resources", {
|
export const resources = pgTable("resources", {
|
||||||
resourceId: serial("resourceId").primaryKey(),
|
resourceId: serial("resourceId").primaryKey(),
|
||||||
siteId: integer("siteId")
|
resourceGuid: varchar("resourceGuid", { length: 36 })
|
||||||
.references(() => sites.siteId, {
|
.unique()
|
||||||
onDelete: "cascade"
|
.notNull()
|
||||||
})
|
.$defaultFn(() => randomUUID()),
|
||||||
.notNull(),
|
|
||||||
orgId: varchar("orgId")
|
orgId: varchar("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
})
|
})
|
||||||
.notNull(),
|
.notNull(),
|
||||||
|
niceId: text("niceId").notNull(),
|
||||||
name: varchar("name").notNull(),
|
name: varchar("name").notNull(),
|
||||||
subdomain: varchar("subdomain"),
|
subdomain: varchar("subdomain"),
|
||||||
fullDomain: varchar("fullDomain"),
|
fullDomain: varchar("fullDomain"),
|
||||||
@@ -96,6 +99,10 @@ export const resources = pgTable("resources", {
|
|||||||
tlsServerName: varchar("tlsServerName"),
|
tlsServerName: varchar("tlsServerName"),
|
||||||
setHostHeader: varchar("setHostHeader"),
|
setHostHeader: varchar("setHostHeader"),
|
||||||
enableProxy: boolean("enableProxy").default(true),
|
enableProxy: boolean("enableProxy").default(true),
|
||||||
|
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
}),
|
||||||
|
headers: text("headers") // comma-separated list of headers to add to the request
|
||||||
});
|
});
|
||||||
|
|
||||||
export const targets = pgTable("targets", {
|
export const targets = pgTable("targets", {
|
||||||
@@ -105,11 +112,42 @@ export const targets = pgTable("targets", {
|
|||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
})
|
})
|
||||||
.notNull(),
|
.notNull(),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.references(() => sites.siteId, {
|
||||||
|
onDelete: "cascade"
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
ip: varchar("ip").notNull(),
|
ip: varchar("ip").notNull(),
|
||||||
method: varchar("method"),
|
method: varchar("method"),
|
||||||
port: integer("port").notNull(),
|
port: integer("port").notNull(),
|
||||||
internalPort: integer("internalPort"),
|
internalPort: integer("internalPort"),
|
||||||
enabled: boolean("enabled").notNull().default(true)
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
|
path: text("path"),
|
||||||
|
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
||||||
|
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
||||||
|
rewritePathType: text("rewritePathType"), // exact, prefix, regex, stripPrefix
|
||||||
|
priority: integer("priority").notNull().default(100)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const targetHealthCheck = pgTable("targetHealthCheck", {
|
||||||
|
targetHealthCheckId: serial("targetHealthCheckId").primaryKey(),
|
||||||
|
targetId: integer("targetId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => targets.targetId, { onDelete: "cascade" }),
|
||||||
|
hcEnabled: boolean("hcEnabled").notNull().default(false),
|
||||||
|
hcPath: varchar("hcPath"),
|
||||||
|
hcScheme: varchar("hcScheme"),
|
||||||
|
hcMode: varchar("hcMode").default("http"),
|
||||||
|
hcHostname: varchar("hcHostname"),
|
||||||
|
hcPort: integer("hcPort"),
|
||||||
|
hcInterval: integer("hcInterval").default(30), // in seconds
|
||||||
|
hcUnhealthyInterval: integer("hcUnhealthyInterval").default(30), // in seconds
|
||||||
|
hcTimeout: integer("hcTimeout").default(5), // in seconds
|
||||||
|
hcHeaders: varchar("hcHeaders"),
|
||||||
|
hcFollowRedirects: boolean("hcFollowRedirects").default(true),
|
||||||
|
hcMethod: varchar("hcMethod").default("GET"),
|
||||||
|
hcStatus: integer("hcStatus"), // http code
|
||||||
|
hcHealth: text("hcHealth").default("unknown") // "unknown", "healthy", "unhealthy"
|
||||||
});
|
});
|
||||||
|
|
||||||
export const exitNodes = pgTable("exitNodes", {
|
export const exitNodes = pgTable("exitNodes", {
|
||||||
@@ -120,7 +158,29 @@ export const exitNodes = pgTable("exitNodes", {
|
|||||||
publicKey: varchar("publicKey").notNull(),
|
publicKey: varchar("publicKey").notNull(),
|
||||||
listenPort: integer("listenPort").notNull(),
|
listenPort: integer("listenPort").notNull(),
|
||||||
reachableAt: varchar("reachableAt"),
|
reachableAt: varchar("reachableAt"),
|
||||||
maxConnections: integer("maxConnections")
|
maxConnections: integer("maxConnections"),
|
||||||
|
online: boolean("online").notNull().default(false),
|
||||||
|
lastPing: integer("lastPing"),
|
||||||
|
type: text("type").default("gerbil"), // gerbil, remoteExitNode
|
||||||
|
region: varchar("region")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const siteResources = pgTable("siteResources", {
|
||||||
|
// this is for the clients
|
||||||
|
siteResourceId: serial("siteResourceId").primaryKey(),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
niceId: varchar("niceId").notNull(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
protocol: varchar("protocol").notNull(),
|
||||||
|
proxyPort: integer("proxyPort").notNull(),
|
||||||
|
destinationPort: integer("destinationPort").notNull(),
|
||||||
|
destinationIp: varchar("destinationIp").notNull(),
|
||||||
|
enabled: boolean("enabled").notNull().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const users = pgTable("user", {
|
export const users = pgTable("user", {
|
||||||
@@ -189,7 +249,8 @@ export const userOrgs = pgTable("userOrgs", {
|
|||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId),
|
.references(() => roles.roleId),
|
||||||
isOwner: boolean("isOwner").notNull().default(false)
|
isOwner: boolean("isOwner").notNull().default(false),
|
||||||
|
autoProvisioned: boolean("autoProvisioned").default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const emailVerificationCodes = pgTable("emailVerificationCodes", {
|
export const emailVerificationCodes = pgTable("emailVerificationCodes", {
|
||||||
@@ -405,13 +466,49 @@ export const resourceRules = pgTable("resourceRules", {
|
|||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
templateRuleId: integer("templateRuleId")
|
||||||
|
.references(() => templateRules.ruleId, { onDelete: "cascade" }),
|
||||||
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
|
priority: integer("priority").notNull(),
|
||||||
|
action: varchar("action").notNull(), // ACCEPT, DROP, PASS
|
||||||
|
match: varchar("match").notNull(), // CIDR, PATH, IP
|
||||||
|
value: varchar("value").notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rule templates (reusable rule sets)
|
||||||
|
export const ruleTemplates = pgTable("ruleTemplates", {
|
||||||
|
templateId: varchar("templateId").primaryKey(),
|
||||||
|
orgId: varchar("orgId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
description: varchar("description"),
|
||||||
|
createdAt: bigint("createdAt", { mode: "number" }).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rules within templates
|
||||||
|
export const templateRules = pgTable("templateRules", {
|
||||||
|
ruleId: serial("ruleId").primaryKey(),
|
||||||
|
templateId: varchar("templateId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => ruleTemplates.templateId, { onDelete: "cascade" }),
|
||||||
enabled: boolean("enabled").notNull().default(true),
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
priority: integer("priority").notNull(),
|
priority: integer("priority").notNull(),
|
||||||
action: varchar("action").notNull(), // ACCEPT, DROP
|
action: varchar("action").notNull(), // ACCEPT, DROP
|
||||||
match: varchar("match").notNull(), // CIDR, PATH, IP
|
match: varchar("match").notNull(), // CIDR, IP, PATH
|
||||||
value: varchar("value").notNull()
|
value: varchar("value").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Template assignments to resources
|
||||||
|
export const resourceTemplates = pgTable("resourceTemplates", {
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
templateId: varchar("templateId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => ruleTemplates.templateId, { onDelete: "cascade" })
|
||||||
|
});
|
||||||
|
|
||||||
export const supporterKey = pgTable("supporterKey", {
|
export const supporterKey = pgTable("supporterKey", {
|
||||||
keyId: serial("keyId").primaryKey(),
|
keyId: serial("keyId").primaryKey(),
|
||||||
key: varchar("key").notNull(),
|
key: varchar("key").notNull(),
|
||||||
@@ -435,6 +532,7 @@ export const idpOidcConfig = pgTable("idpOidcConfig", {
|
|||||||
idpId: integer("idpId")
|
idpId: integer("idpId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => idp.idpId, { onDelete: "cascade" }),
|
.references(() => idp.idpId, { onDelete: "cascade" }),
|
||||||
|
variant: varchar("variant").notNull().default("oidc"),
|
||||||
clientId: varchar("clientId").notNull(),
|
clientId: varchar("clientId").notNull(),
|
||||||
clientSecret: varchar("clientSecret").notNull(),
|
clientSecret: varchar("clientSecret").notNull(),
|
||||||
authUrl: varchar("authUrl").notNull(),
|
authUrl: varchar("authUrl").notNull(),
|
||||||
@@ -512,10 +610,10 @@ export const clients = pgTable("clients", {
|
|||||||
megabytesIn: real("bytesIn"),
|
megabytesIn: real("bytesIn"),
|
||||||
megabytesOut: real("bytesOut"),
|
megabytesOut: real("bytesOut"),
|
||||||
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
|
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
|
||||||
lastPing: varchar("lastPing"),
|
lastPing: integer("lastPing"),
|
||||||
type: varchar("type").notNull(), // "olm"
|
type: varchar("type").notNull(), // "olm"
|
||||||
online: boolean("online").notNull().default(false),
|
online: boolean("online").notNull().default(false),
|
||||||
endpoint: varchar("endpoint"),
|
// endpoint: varchar("endpoint"),
|
||||||
lastHolePunch: integer("lastHolePunch"),
|
lastHolePunch: integer("lastHolePunch"),
|
||||||
maxConnections: integer("maxConnections")
|
maxConnections: integer("maxConnections")
|
||||||
});
|
});
|
||||||
@@ -527,13 +625,15 @@ export const clientSites = pgTable("clientSites", {
|
|||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" }),
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
isRelayed: boolean("isRelayed").notNull().default(false)
|
isRelayed: boolean("isRelayed").notNull().default(false),
|
||||||
|
endpoint: varchar("endpoint")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const olms = pgTable("olms", {
|
export const olms = pgTable("olms", {
|
||||||
olmId: varchar("id").primaryKey(),
|
olmId: varchar("id").primaryKey(),
|
||||||
secretHash: varchar("secretHash").notNull(),
|
secretHash: varchar("secretHash").notNull(),
|
||||||
dateCreated: varchar("dateCreated").notNull(),
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
|
version: text("version"),
|
||||||
clientId: integer("clientId").references(() => clients.clientId, {
|
clientId: integer("clientId").references(() => clients.clientId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
})
|
})
|
||||||
@@ -591,6 +691,14 @@ export const webauthnChallenge = pgTable("webauthnChallenge", {
|
|||||||
expiresAt: bigint("expiresAt", { mode: "number" }).notNull() // Unix timestamp
|
expiresAt: bigint("expiresAt", { mode: "number" }).notNull() // Unix timestamp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setupTokens = pgTable("setupTokens", {
|
||||||
|
tokenId: varchar("tokenId").primaryKey(),
|
||||||
|
token: varchar("token").notNull(),
|
||||||
|
used: boolean("used").notNull().default(false),
|
||||||
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
|
dateUsed: varchar("dateUsed")
|
||||||
|
});
|
||||||
|
|
||||||
export type Org = InferSelectModel<typeof orgs>;
|
export type Org = InferSelectModel<typeof orgs>;
|
||||||
export type User = InferSelectModel<typeof users>;
|
export type User = InferSelectModel<typeof users>;
|
||||||
export type Site = InferSelectModel<typeof sites>;
|
export type Site = InferSelectModel<typeof sites>;
|
||||||
@@ -636,3 +744,10 @@ export type OlmSession = InferSelectModel<typeof olmSessions>;
|
|||||||
export type UserClient = InferSelectModel<typeof userClients>;
|
export type UserClient = InferSelectModel<typeof userClients>;
|
||||||
export type RoleClient = InferSelectModel<typeof roleClients>;
|
export type RoleClient = InferSelectModel<typeof roleClients>;
|
||||||
export type OrgDomains = InferSelectModel<typeof orgDomains>;
|
export type OrgDomains = InferSelectModel<typeof orgDomains>;
|
||||||
|
export type SiteResource = InferSelectModel<typeof siteResources>;
|
||||||
|
export type SetupToken = InferSelectModel<typeof setupTokens>;
|
||||||
|
export type HostMeta = InferSelectModel<typeof hostMeta>;
|
||||||
|
export type TargetHealthCheck = InferSelectModel<typeof targetHealthCheck>;
|
||||||
|
export type RuleTemplate = InferSelectModel<typeof ruleTemplates>;
|
||||||
|
export type TemplateRule = InferSelectModel<typeof templateRules>;
|
||||||
|
export type ResourceTemplate = InferSelectModel<typeof resourceTemplates>;
|
||||||
|
|||||||
202
server/db/private/rateLimit.test.ts
Normal file
202
server/db/private/rateLimit.test.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Simple test file for the rate limit service with Redis
|
||||||
|
// Run with: npx ts-node rateLimitService.test.ts
|
||||||
|
|
||||||
|
import { RateLimitService } from './rateLimit';
|
||||||
|
|
||||||
|
function generateClientId() {
|
||||||
|
return 'client-' + Math.random().toString(36).substring(2, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTests() {
|
||||||
|
console.log('Starting Rate Limit Service Tests...\n');
|
||||||
|
|
||||||
|
const rateLimitService = new RateLimitService();
|
||||||
|
let testsPassed = 0;
|
||||||
|
let testsTotal = 0;
|
||||||
|
|
||||||
|
// Helper function to run a test
|
||||||
|
async function test(name: string, testFn: () => Promise<void>) {
|
||||||
|
testsTotal++;
|
||||||
|
try {
|
||||||
|
await testFn();
|
||||||
|
console.log(`✅ ${name}`);
|
||||||
|
testsPassed++;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`❌ ${name}: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function for assertions
|
||||||
|
function assert(condition: boolean, message: string) {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Basic rate limiting
|
||||||
|
await test('Should allow requests under the limit', async () => {
|
||||||
|
const clientId = generateClientId();
|
||||||
|
const maxRequests = 5;
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRequests - 1; i++) {
|
||||||
|
const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
assert(!result.isLimited, `Request ${i + 1} should be allowed`);
|
||||||
|
assert(result.totalHits === i + 1, `Expected ${i + 1} hits, got ${result.totalHits}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 2: Rate limit blocking
|
||||||
|
await test('Should block requests over the limit', async () => {
|
||||||
|
const clientId = generateClientId();
|
||||||
|
const maxRequests = 30;
|
||||||
|
|
||||||
|
// Use up all allowed requests
|
||||||
|
for (let i = 0; i < maxRequests - 1; i++) {
|
||||||
|
const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
assert(!result.isLimited, `Request ${i + 1} should be allowed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next request should be blocked
|
||||||
|
const blockedResult = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
assert(blockedResult.isLimited, 'Request should be blocked');
|
||||||
|
assert(blockedResult.reason === 'global', 'Should be blocked for global reason');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 3: Message type limits
|
||||||
|
await test('Should handle message type limits', async () => {
|
||||||
|
const clientId = generateClientId();
|
||||||
|
const globalMax = 10;
|
||||||
|
const messageTypeMax = 2;
|
||||||
|
|
||||||
|
// Send messages of type 'ping' up to the limit
|
||||||
|
for (let i = 0; i < messageTypeMax - 1; i++) {
|
||||||
|
const result = await rateLimitService.checkRateLimit(
|
||||||
|
clientId,
|
||||||
|
'ping',
|
||||||
|
globalMax,
|
||||||
|
messageTypeMax
|
||||||
|
);
|
||||||
|
assert(!result.isLimited, `Ping message ${i + 1} should be allowed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next 'ping' should be blocked
|
||||||
|
const blockedResult = await rateLimitService.checkRateLimit(
|
||||||
|
clientId,
|
||||||
|
'ping',
|
||||||
|
globalMax,
|
||||||
|
messageTypeMax
|
||||||
|
);
|
||||||
|
assert(blockedResult.isLimited, 'Ping message should be blocked');
|
||||||
|
assert(blockedResult.reason === 'message_type:ping', 'Should be blocked for message type');
|
||||||
|
|
||||||
|
// Other message types should still work
|
||||||
|
const otherResult = await rateLimitService.checkRateLimit(
|
||||||
|
clientId,
|
||||||
|
'pong',
|
||||||
|
globalMax,
|
||||||
|
messageTypeMax
|
||||||
|
);
|
||||||
|
assert(!otherResult.isLimited, 'Pong message should be allowed');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 4: Reset functionality
|
||||||
|
await test('Should reset client correctly', async () => {
|
||||||
|
const clientId = generateClientId();
|
||||||
|
const maxRequests = 3;
|
||||||
|
|
||||||
|
// Use up some requests
|
||||||
|
await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
await rateLimitService.checkRateLimit(clientId, 'test', maxRequests);
|
||||||
|
|
||||||
|
// Reset the client
|
||||||
|
await rateLimitService.resetKey(clientId);
|
||||||
|
|
||||||
|
// Should be able to make fresh requests
|
||||||
|
const result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
assert(!result.isLimited, 'Request after reset should be allowed');
|
||||||
|
assert(result.totalHits === 1, 'Should have 1 hit after reset');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 5: Different clients are independent
|
||||||
|
await test('Should handle different clients independently', async () => {
|
||||||
|
const client1 = generateClientId();
|
||||||
|
const client2 = generateClientId();
|
||||||
|
const maxRequests = 2;
|
||||||
|
|
||||||
|
// Client 1 uses up their limit
|
||||||
|
await rateLimitService.checkRateLimit(client1, undefined, maxRequests);
|
||||||
|
await rateLimitService.checkRateLimit(client1, undefined, maxRequests);
|
||||||
|
const client1Blocked = await rateLimitService.checkRateLimit(client1, undefined, maxRequests);
|
||||||
|
assert(client1Blocked.isLimited, 'Client 1 should be blocked');
|
||||||
|
|
||||||
|
// Client 2 should still be able to make requests
|
||||||
|
const client2Result = await rateLimitService.checkRateLimit(client2, undefined, maxRequests);
|
||||||
|
assert(!client2Result.isLimited, 'Client 2 should not be blocked');
|
||||||
|
assert(client2Result.totalHits === 1, 'Client 2 should have 1 hit');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 6: Decrement functionality
|
||||||
|
await test('Should decrement correctly', async () => {
|
||||||
|
const clientId = generateClientId();
|
||||||
|
const maxRequests = 5;
|
||||||
|
|
||||||
|
// Make some requests
|
||||||
|
await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
let result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
assert(result.totalHits === 3, 'Should have 3 hits before decrement');
|
||||||
|
|
||||||
|
// Decrement
|
||||||
|
await rateLimitService.decrementRateLimit(clientId);
|
||||||
|
|
||||||
|
// Next request should reflect the decrement
|
||||||
|
result = await rateLimitService.checkRateLimit(clientId, undefined, maxRequests);
|
||||||
|
assert(result.totalHits === 3, 'Should have 3 hits after decrement + increment');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait a moment for any pending Redis operations
|
||||||
|
console.log('\nWaiting for Redis sync...');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Force sync to test Redis integration
|
||||||
|
await test('Should sync to Redis', async () => {
|
||||||
|
await rateLimitService.forceSyncAllPendingData();
|
||||||
|
// If this doesn't throw, Redis sync is working
|
||||||
|
assert(true, 'Redis sync completed');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await rateLimitService.cleanup();
|
||||||
|
|
||||||
|
// Results
|
||||||
|
console.log(`\n--- Test Results ---`);
|
||||||
|
console.log(`✅ Passed: ${testsPassed}/${testsTotal}`);
|
||||||
|
console.log(`❌ Failed: ${testsTotal - testsPassed}/${testsTotal}`);
|
||||||
|
|
||||||
|
if (testsPassed === testsTotal) {
|
||||||
|
console.log('\n🎉 All tests passed!');
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
console.log('\n💥 Some tests failed!');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the tests
|
||||||
|
runTests().catch(error => {
|
||||||
|
console.error('Test runner error:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
458
server/db/private/rateLimit.ts
Normal file
458
server/db/private/rateLimit.ts
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import redisManager from "@server/db/private/redis";
|
||||||
|
import { build } from "@server/build";
|
||||||
|
|
||||||
|
// Rate limiting configuration
|
||||||
|
export const RATE_LIMIT_WINDOW = 60; // 1 minute in seconds
|
||||||
|
export const RATE_LIMIT_MAX_REQUESTS = 100;
|
||||||
|
export const RATE_LIMIT_PER_MESSAGE_TYPE = 20; // Per message type limit within the window
|
||||||
|
|
||||||
|
// Configuration for batched Redis sync
|
||||||
|
export const REDIS_SYNC_THRESHOLD = 15; // Sync to Redis every N messages
|
||||||
|
export const REDIS_SYNC_FORCE_INTERVAL = 30000; // Force sync every 30 seconds as backup
|
||||||
|
|
||||||
|
interface RateLimitTracker {
|
||||||
|
count: number;
|
||||||
|
windowStart: number;
|
||||||
|
pendingCount: number;
|
||||||
|
lastSyncedCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RateLimitResult {
|
||||||
|
isLimited: boolean;
|
||||||
|
reason?: string;
|
||||||
|
totalHits?: number;
|
||||||
|
resetTime?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RateLimitService {
|
||||||
|
private localRateLimitTracker: Map<string, RateLimitTracker> = new Map();
|
||||||
|
private localMessageTypeRateLimitTracker: Map<string, RateLimitTracker> = new Map();
|
||||||
|
private cleanupInterval: NodeJS.Timeout | null = null;
|
||||||
|
private forceSyncInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (build == "oss") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start cleanup and sync intervals
|
||||||
|
this.cleanupInterval = setInterval(() => {
|
||||||
|
this.cleanupLocalRateLimit().catch((error) => {
|
||||||
|
logger.error("Error during rate limit cleanup:", error);
|
||||||
|
});
|
||||||
|
}, 60000); // Run cleanup every minute
|
||||||
|
|
||||||
|
this.forceSyncInterval = setInterval(() => {
|
||||||
|
this.forceSyncAllPendingData().catch((error) => {
|
||||||
|
logger.error("Error during force sync:", error);
|
||||||
|
});
|
||||||
|
}, REDIS_SYNC_FORCE_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redis keys
|
||||||
|
private getRateLimitKey(clientId: string): string {
|
||||||
|
return `ratelimit:${clientId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMessageTypeRateLimitKey(clientId: string, messageType: string): string {
|
||||||
|
return `ratelimit:${clientId}:${messageType}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to sync local rate limit data to Redis
|
||||||
|
private async syncRateLimitToRedis(
|
||||||
|
clientId: string,
|
||||||
|
tracker: RateLimitTracker
|
||||||
|
): Promise<void> {
|
||||||
|
if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const globalKey = this.getRateLimitKey(clientId);
|
||||||
|
|
||||||
|
// Get current value and add pending count
|
||||||
|
const currentValue = await redisManager.hget(
|
||||||
|
globalKey,
|
||||||
|
currentTime.toString()
|
||||||
|
);
|
||||||
|
const newValue = (
|
||||||
|
parseInt(currentValue || "0") + tracker.pendingCount
|
||||||
|
).toString();
|
||||||
|
await redisManager.hset(globalKey, currentTime.toString(), newValue);
|
||||||
|
|
||||||
|
// Set TTL using the client directly
|
||||||
|
if (redisManager.getClient()) {
|
||||||
|
await redisManager
|
||||||
|
.getClient()
|
||||||
|
.expire(globalKey, RATE_LIMIT_WINDOW + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tracking
|
||||||
|
tracker.lastSyncedCount = tracker.count;
|
||||||
|
tracker.pendingCount = 0;
|
||||||
|
|
||||||
|
logger.debug(`Synced global rate limit to Redis for client ${clientId}`);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to sync global rate limit to Redis:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async syncMessageTypeRateLimitToRedis(
|
||||||
|
clientId: string,
|
||||||
|
messageType: string,
|
||||||
|
tracker: RateLimitTracker
|
||||||
|
): Promise<void> {
|
||||||
|
if (!redisManager.isRedisEnabled() || tracker.pendingCount === 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const messageTypeKey = this.getMessageTypeRateLimitKey(clientId, messageType);
|
||||||
|
|
||||||
|
// Get current value and add pending count
|
||||||
|
const currentValue = await redisManager.hget(
|
||||||
|
messageTypeKey,
|
||||||
|
currentTime.toString()
|
||||||
|
);
|
||||||
|
const newValue = (
|
||||||
|
parseInt(currentValue || "0") + tracker.pendingCount
|
||||||
|
).toString();
|
||||||
|
await redisManager.hset(
|
||||||
|
messageTypeKey,
|
||||||
|
currentTime.toString(),
|
||||||
|
newValue
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set TTL using the client directly
|
||||||
|
if (redisManager.getClient()) {
|
||||||
|
await redisManager
|
||||||
|
.getClient()
|
||||||
|
.expire(messageTypeKey, RATE_LIMIT_WINDOW + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tracking
|
||||||
|
tracker.lastSyncedCount = tracker.count;
|
||||||
|
tracker.pendingCount = 0;
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`Synced message type rate limit to Redis for client ${clientId}, type ${messageType}`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to sync message type rate limit to Redis:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize local tracker from Redis data
|
||||||
|
private async initializeLocalTracker(clientId: string): Promise<RateLimitTracker> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const windowStart = currentTime - RATE_LIMIT_WINDOW;
|
||||||
|
|
||||||
|
if (!redisManager.isRedisEnabled()) {
|
||||||
|
return {
|
||||||
|
count: 0,
|
||||||
|
windowStart: currentTime,
|
||||||
|
pendingCount: 0,
|
||||||
|
lastSyncedCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const globalKey = this.getRateLimitKey(clientId);
|
||||||
|
const globalRateLimitData = await redisManager.hgetall(globalKey);
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
for (const [timestamp, countStr] of Object.entries(globalRateLimitData)) {
|
||||||
|
const time = parseInt(timestamp);
|
||||||
|
if (time >= windowStart) {
|
||||||
|
count += parseInt(countStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
count,
|
||||||
|
windowStart: currentTime,
|
||||||
|
pendingCount: 0,
|
||||||
|
lastSyncedCount: count
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to initialize global tracker from Redis:", error);
|
||||||
|
return {
|
||||||
|
count: 0,
|
||||||
|
windowStart: currentTime,
|
||||||
|
pendingCount: 0,
|
||||||
|
lastSyncedCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeMessageTypeTracker(
|
||||||
|
clientId: string,
|
||||||
|
messageType: string
|
||||||
|
): Promise<RateLimitTracker> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const windowStart = currentTime - RATE_LIMIT_WINDOW;
|
||||||
|
|
||||||
|
if (!redisManager.isRedisEnabled()) {
|
||||||
|
return {
|
||||||
|
count: 0,
|
||||||
|
windowStart: currentTime,
|
||||||
|
pendingCount: 0,
|
||||||
|
lastSyncedCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const messageTypeKey = this.getMessageTypeRateLimitKey(clientId, messageType);
|
||||||
|
const messageTypeRateLimitData = await redisManager.hgetall(messageTypeKey);
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
for (const [timestamp, countStr] of Object.entries(messageTypeRateLimitData)) {
|
||||||
|
const time = parseInt(timestamp);
|
||||||
|
if (time >= windowStart) {
|
||||||
|
count += parseInt(countStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
count,
|
||||||
|
windowStart: currentTime,
|
||||||
|
pendingCount: 0,
|
||||||
|
lastSyncedCount: count
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to initialize message type tracker from Redis:", error);
|
||||||
|
return {
|
||||||
|
count: 0,
|
||||||
|
windowStart: currentTime,
|
||||||
|
pendingCount: 0,
|
||||||
|
lastSyncedCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main rate limiting function
|
||||||
|
async checkRateLimit(
|
||||||
|
clientId: string,
|
||||||
|
messageType?: string,
|
||||||
|
maxRequests: number = RATE_LIMIT_MAX_REQUESTS,
|
||||||
|
messageTypeLimit: number = RATE_LIMIT_PER_MESSAGE_TYPE,
|
||||||
|
windowMs: number = RATE_LIMIT_WINDOW * 1000
|
||||||
|
): Promise<RateLimitResult> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const windowStart = currentTime - Math.floor(windowMs / 1000);
|
||||||
|
|
||||||
|
// Check global rate limit
|
||||||
|
let globalTracker = this.localRateLimitTracker.get(clientId);
|
||||||
|
|
||||||
|
if (!globalTracker || globalTracker.windowStart < windowStart) {
|
||||||
|
// New window or first request - initialize from Redis if available
|
||||||
|
globalTracker = await this.initializeLocalTracker(clientId);
|
||||||
|
globalTracker.windowStart = currentTime;
|
||||||
|
this.localRateLimitTracker.set(clientId, globalTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment global counters
|
||||||
|
globalTracker.count++;
|
||||||
|
globalTracker.pendingCount++;
|
||||||
|
this.localRateLimitTracker.set(clientId, globalTracker);
|
||||||
|
|
||||||
|
// Check if global limit would be exceeded
|
||||||
|
if (globalTracker.count >= maxRequests) {
|
||||||
|
return {
|
||||||
|
isLimited: true,
|
||||||
|
reason: "global",
|
||||||
|
totalHits: globalTracker.count,
|
||||||
|
resetTime: new Date((globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to Redis if threshold reached
|
||||||
|
if (globalTracker.pendingCount >= REDIS_SYNC_THRESHOLD) {
|
||||||
|
this.syncRateLimitToRedis(clientId, globalTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check message type specific rate limit if messageType is provided
|
||||||
|
if (messageType) {
|
||||||
|
const messageTypeKey = `${clientId}:${messageType}`;
|
||||||
|
let messageTypeTracker = this.localMessageTypeRateLimitTracker.get(messageTypeKey);
|
||||||
|
|
||||||
|
if (!messageTypeTracker || messageTypeTracker.windowStart < windowStart) {
|
||||||
|
// New window or first request for this message type - initialize from Redis if available
|
||||||
|
messageTypeTracker = await this.initializeMessageTypeTracker(clientId, messageType);
|
||||||
|
messageTypeTracker.windowStart = currentTime;
|
||||||
|
this.localMessageTypeRateLimitTracker.set(messageTypeKey, messageTypeTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment message type counters
|
||||||
|
messageTypeTracker.count++;
|
||||||
|
messageTypeTracker.pendingCount++;
|
||||||
|
this.localMessageTypeRateLimitTracker.set(messageTypeKey, messageTypeTracker);
|
||||||
|
|
||||||
|
// Check if message type limit would be exceeded
|
||||||
|
if (messageTypeTracker.count >= messageTypeLimit) {
|
||||||
|
return {
|
||||||
|
isLimited: true,
|
||||||
|
reason: `message_type:${messageType}`,
|
||||||
|
totalHits: messageTypeTracker.count,
|
||||||
|
resetTime: new Date((messageTypeTracker.windowStart + Math.floor(windowMs / 1000)) * 1000)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to Redis if threshold reached
|
||||||
|
if (messageTypeTracker.pendingCount >= REDIS_SYNC_THRESHOLD) {
|
||||||
|
this.syncMessageTypeRateLimitToRedis(clientId, messageType, messageTypeTracker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLimited: false,
|
||||||
|
totalHits: globalTracker.count,
|
||||||
|
resetTime: new Date((globalTracker.windowStart + Math.floor(windowMs / 1000)) * 1000)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement function for skipSuccessfulRequests/skipFailedRequests functionality
|
||||||
|
async decrementRateLimit(clientId: string, messageType?: string): Promise<void> {
|
||||||
|
// Decrement global counter
|
||||||
|
const globalTracker = this.localRateLimitTracker.get(clientId);
|
||||||
|
if (globalTracker && globalTracker.count > 0) {
|
||||||
|
globalTracker.count--;
|
||||||
|
// We need to account for this in pending count to sync correctly
|
||||||
|
globalTracker.pendingCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement message type counter if provided
|
||||||
|
if (messageType) {
|
||||||
|
const messageTypeKey = `${clientId}:${messageType}`;
|
||||||
|
const messageTypeTracker = this.localMessageTypeRateLimitTracker.get(messageTypeKey);
|
||||||
|
if (messageTypeTracker && messageTypeTracker.count > 0) {
|
||||||
|
messageTypeTracker.count--;
|
||||||
|
messageTypeTracker.pendingCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset key function
|
||||||
|
async resetKey(clientId: string): Promise<void> {
|
||||||
|
// Remove from local tracking
|
||||||
|
this.localRateLimitTracker.delete(clientId);
|
||||||
|
|
||||||
|
// Remove all message type entries for this client
|
||||||
|
for (const [key] of this.localMessageTypeRateLimitTracker) {
|
||||||
|
if (key.startsWith(`${clientId}:`)) {
|
||||||
|
this.localMessageTypeRateLimitTracker.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from Redis if enabled
|
||||||
|
if (redisManager.isRedisEnabled()) {
|
||||||
|
const globalKey = this.getRateLimitKey(clientId);
|
||||||
|
await redisManager.del(globalKey);
|
||||||
|
|
||||||
|
// Get all message type keys for this client and delete them
|
||||||
|
const client = redisManager.getClient();
|
||||||
|
if (client) {
|
||||||
|
const messageTypeKeys = await client.keys(`ratelimit:${clientId}:*`);
|
||||||
|
if (messageTypeKeys.length > 0) {
|
||||||
|
await Promise.all(messageTypeKeys.map(key => redisManager.del(key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup old local rate limit entries and force sync pending data
|
||||||
|
private async cleanupLocalRateLimit(): Promise<void> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const windowStart = currentTime - RATE_LIMIT_WINDOW;
|
||||||
|
|
||||||
|
// Clean up global rate limit tracking and sync pending data
|
||||||
|
for (const [clientId, tracker] of this.localRateLimitTracker.entries()) {
|
||||||
|
if (tracker.windowStart < windowStart) {
|
||||||
|
// Sync any pending data before cleanup
|
||||||
|
if (tracker.pendingCount > 0) {
|
||||||
|
await this.syncRateLimitToRedis(clientId, tracker);
|
||||||
|
}
|
||||||
|
this.localRateLimitTracker.delete(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up message type rate limit tracking and sync pending data
|
||||||
|
for (const [key, tracker] of this.localMessageTypeRateLimitTracker.entries()) {
|
||||||
|
if (tracker.windowStart < windowStart) {
|
||||||
|
// Sync any pending data before cleanup
|
||||||
|
if (tracker.pendingCount > 0) {
|
||||||
|
const [clientId, messageType] = key.split(":", 2);
|
||||||
|
await this.syncMessageTypeRateLimitToRedis(clientId, messageType, tracker);
|
||||||
|
}
|
||||||
|
this.localMessageTypeRateLimitTracker.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force sync all pending rate limit data to Redis
|
||||||
|
async forceSyncAllPendingData(): Promise<void> {
|
||||||
|
if (!redisManager.isRedisEnabled()) return;
|
||||||
|
|
||||||
|
logger.debug("Force syncing all pending rate limit data to Redis...");
|
||||||
|
|
||||||
|
// Sync all pending global rate limits
|
||||||
|
for (const [clientId, tracker] of this.localRateLimitTracker.entries()) {
|
||||||
|
if (tracker.pendingCount > 0) {
|
||||||
|
await this.syncRateLimitToRedis(clientId, tracker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync all pending message type rate limits
|
||||||
|
for (const [key, tracker] of this.localMessageTypeRateLimitTracker.entries()) {
|
||||||
|
if (tracker.pendingCount > 0) {
|
||||||
|
const [clientId, messageType] = key.split(":", 2);
|
||||||
|
await this.syncMessageTypeRateLimitToRedis(clientId, messageType, tracker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Completed force sync of pending rate limit data");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup function for graceful shutdown
|
||||||
|
async cleanup(): Promise<void> {
|
||||||
|
if (build == "oss") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear intervals
|
||||||
|
if (this.cleanupInterval) {
|
||||||
|
clearInterval(this.cleanupInterval);
|
||||||
|
}
|
||||||
|
if (this.forceSyncInterval) {
|
||||||
|
clearInterval(this.forceSyncInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force sync all pending data
|
||||||
|
await this.forceSyncAllPendingData();
|
||||||
|
|
||||||
|
// Clear local data
|
||||||
|
this.localRateLimitTracker.clear();
|
||||||
|
this.localMessageTypeRateLimitTracker.clear();
|
||||||
|
|
||||||
|
logger.info("Rate limit service cleanup completed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export singleton instance
|
||||||
|
export const rateLimitService = new RateLimitService();
|
||||||
|
|
||||||
|
// Handle process termination
|
||||||
|
process.on("SIGTERM", () => rateLimitService.cleanup());
|
||||||
|
process.on("SIGINT", () => rateLimitService.cleanup());
|
||||||
782
server/db/private/redis.ts
Normal file
782
server/db/private/redis.ts
Normal file
@@ -0,0 +1,782 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Redis, { RedisOptions } from "ioredis";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
import { build } from "@server/build";
|
||||||
|
|
||||||
|
class RedisManager {
|
||||||
|
public client: Redis | null = null;
|
||||||
|
private writeClient: Redis | null = null; // Master for writes
|
||||||
|
private readClient: Redis | null = null; // Replica for reads
|
||||||
|
private subscriber: Redis | null = null;
|
||||||
|
private publisher: Redis | null = null;
|
||||||
|
private isEnabled: boolean = false;
|
||||||
|
private isHealthy: boolean = true;
|
||||||
|
private isWriteHealthy: boolean = true;
|
||||||
|
private isReadHealthy: boolean = true;
|
||||||
|
private lastHealthCheck: number = 0;
|
||||||
|
private healthCheckInterval: number = 30000; // 30 seconds
|
||||||
|
private connectionTimeout: number = 15000; // 15 seconds
|
||||||
|
private commandTimeout: number = 15000; // 15 seconds
|
||||||
|
private hasReplicas: boolean = false;
|
||||||
|
private maxRetries: number = 3;
|
||||||
|
private baseRetryDelay: number = 100; // 100ms
|
||||||
|
private maxRetryDelay: number = 2000; // 2 seconds
|
||||||
|
private backoffMultiplier: number = 2;
|
||||||
|
private subscribers: Map<
|
||||||
|
string,
|
||||||
|
Set<(channel: string, message: string) => void>
|
||||||
|
> = new Map();
|
||||||
|
private reconnectionCallbacks: Set<() => Promise<void>> = new Set();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (build == "oss") {
|
||||||
|
this.isEnabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isEnabled = config.getRawPrivateConfig().flags?.enable_redis || false;
|
||||||
|
if (this.isEnabled) {
|
||||||
|
this.initializeClients();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register callback to be called when Redis reconnects
|
||||||
|
public onReconnection(callback: () => Promise<void>): void {
|
||||||
|
this.reconnectionCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister reconnection callback
|
||||||
|
public offReconnection(callback: () => Promise<void>): void {
|
||||||
|
this.reconnectionCallbacks.delete(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async triggerReconnectionCallbacks(): Promise<void> {
|
||||||
|
logger.info(`Triggering ${this.reconnectionCallbacks.size} reconnection callbacks`);
|
||||||
|
|
||||||
|
const promises = Array.from(this.reconnectionCallbacks).map(async (callback) => {
|
||||||
|
try {
|
||||||
|
await callback();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error in reconnection callback:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.allSettled(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resubscribeToChannels(): Promise<void> {
|
||||||
|
if (!this.subscriber || this.subscribers.size === 0) return;
|
||||||
|
|
||||||
|
logger.info(`Re-subscribing to ${this.subscribers.size} channels after Redis reconnection`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const channels = Array.from(this.subscribers.keys());
|
||||||
|
if (channels.length > 0) {
|
||||||
|
await this.subscriber.subscribe(...channels);
|
||||||
|
logger.info(`Successfully re-subscribed to channels: ${channels.join(', ')}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to re-subscribe to channels:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRedisConfig(): RedisOptions {
|
||||||
|
const redisConfig = config.getRawPrivateConfig().redis!;
|
||||||
|
const opts: RedisOptions = {
|
||||||
|
host: redisConfig.host!,
|
||||||
|
port: redisConfig.port!,
|
||||||
|
password: redisConfig.password,
|
||||||
|
db: redisConfig.db,
|
||||||
|
// tls: {
|
||||||
|
// rejectUnauthorized:
|
||||||
|
// redisConfig.tls?.reject_unauthorized || false
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getReplicaRedisConfig(): RedisOptions | null {
|
||||||
|
const redisConfig = config.getRawPrivateConfig().redis!;
|
||||||
|
if (!redisConfig.replicas || redisConfig.replicas.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the first replica for simplicity
|
||||||
|
// In production, you might want to implement load balancing across replicas
|
||||||
|
const replica = redisConfig.replicas[0];
|
||||||
|
const opts: RedisOptions = {
|
||||||
|
host: replica.host!,
|
||||||
|
port: replica.port!,
|
||||||
|
password: replica.password,
|
||||||
|
db: replica.db || redisConfig.db,
|
||||||
|
// tls: {
|
||||||
|
// rejectUnauthorized:
|
||||||
|
// replica.tls?.reject_unauthorized || false
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add reconnection logic in initializeClients
|
||||||
|
private initializeClients(): void {
|
||||||
|
const masterConfig = this.getRedisConfig();
|
||||||
|
const replicaConfig = this.getReplicaRedisConfig();
|
||||||
|
|
||||||
|
this.hasReplicas = replicaConfig !== null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Initialize master connection for writes
|
||||||
|
this.writeClient = new Redis({
|
||||||
|
...masterConfig,
|
||||||
|
enableReadyCheck: false,
|
||||||
|
maxRetriesPerRequest: 3,
|
||||||
|
keepAlive: 30000,
|
||||||
|
connectTimeout: this.connectionTimeout,
|
||||||
|
commandTimeout: this.commandTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize replica connection for reads (if available)
|
||||||
|
if (this.hasReplicas) {
|
||||||
|
this.readClient = new Redis({
|
||||||
|
...replicaConfig!,
|
||||||
|
enableReadyCheck: false,
|
||||||
|
maxRetriesPerRequest: 3,
|
||||||
|
keepAlive: 30000,
|
||||||
|
connectTimeout: this.connectionTimeout,
|
||||||
|
commandTimeout: this.commandTimeout,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Fallback to master for reads if no replicas
|
||||||
|
this.readClient = this.writeClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility - point to write client
|
||||||
|
this.client = this.writeClient;
|
||||||
|
|
||||||
|
// Publisher uses master (writes)
|
||||||
|
this.publisher = new Redis({
|
||||||
|
...masterConfig,
|
||||||
|
enableReadyCheck: false,
|
||||||
|
maxRetriesPerRequest: 3,
|
||||||
|
keepAlive: 30000,
|
||||||
|
connectTimeout: this.connectionTimeout,
|
||||||
|
commandTimeout: this.commandTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscriber uses replica if available (reads)
|
||||||
|
this.subscriber = new Redis({
|
||||||
|
...(this.hasReplicas ? replicaConfig! : masterConfig),
|
||||||
|
enableReadyCheck: false,
|
||||||
|
maxRetriesPerRequest: 3,
|
||||||
|
keepAlive: 30000,
|
||||||
|
connectTimeout: this.connectionTimeout,
|
||||||
|
commandTimeout: this.commandTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add reconnection handlers for write client
|
||||||
|
this.writeClient.on("error", (err) => {
|
||||||
|
logger.error("Redis write client error:", err);
|
||||||
|
this.isWriteHealthy = false;
|
||||||
|
this.isHealthy = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.writeClient.on("reconnecting", () => {
|
||||||
|
logger.info("Redis write client reconnecting...");
|
||||||
|
this.isWriteHealthy = false;
|
||||||
|
this.isHealthy = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.writeClient.on("ready", () => {
|
||||||
|
logger.info("Redis write client ready");
|
||||||
|
this.isWriteHealthy = true;
|
||||||
|
this.updateOverallHealth();
|
||||||
|
|
||||||
|
// Trigger reconnection callbacks when Redis comes back online
|
||||||
|
if (this.isHealthy) {
|
||||||
|
this.triggerReconnectionCallbacks().catch(error => {
|
||||||
|
logger.error("Error triggering reconnection callbacks:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.writeClient.on("connect", () => {
|
||||||
|
logger.info("Redis write client connected");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add reconnection handlers for read client (if different from write)
|
||||||
|
if (this.hasReplicas && this.readClient !== this.writeClient) {
|
||||||
|
this.readClient.on("error", (err) => {
|
||||||
|
logger.error("Redis read client error:", err);
|
||||||
|
this.isReadHealthy = false;
|
||||||
|
this.updateOverallHealth();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.readClient.on("reconnecting", () => {
|
||||||
|
logger.info("Redis read client reconnecting...");
|
||||||
|
this.isReadHealthy = false;
|
||||||
|
this.updateOverallHealth();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.readClient.on("ready", () => {
|
||||||
|
logger.info("Redis read client ready");
|
||||||
|
this.isReadHealthy = true;
|
||||||
|
this.updateOverallHealth();
|
||||||
|
|
||||||
|
// Trigger reconnection callbacks when Redis comes back online
|
||||||
|
if (this.isHealthy) {
|
||||||
|
this.triggerReconnectionCallbacks().catch(error => {
|
||||||
|
logger.error("Error triggering reconnection callbacks:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.readClient.on("connect", () => {
|
||||||
|
logger.info("Redis read client connected");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If using same client for reads and writes
|
||||||
|
this.isReadHealthy = this.isWriteHealthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.publisher.on("error", (err) => {
|
||||||
|
logger.error("Redis publisher error:", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.publisher.on("ready", () => {
|
||||||
|
logger.info("Redis publisher ready");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.publisher.on("connect", () => {
|
||||||
|
logger.info("Redis publisher connected");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriber.on("error", (err) => {
|
||||||
|
logger.error("Redis subscriber error:", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriber.on("ready", () => {
|
||||||
|
logger.info("Redis subscriber ready");
|
||||||
|
// Re-subscribe to all channels after reconnection
|
||||||
|
this.resubscribeToChannels().catch((error: any) => {
|
||||||
|
logger.error("Error re-subscribing to channels:", error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriber.on("connect", () => {
|
||||||
|
logger.info("Redis subscriber connected");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up message handler for subscriber
|
||||||
|
this.subscriber.on(
|
||||||
|
"message",
|
||||||
|
(channel: string, message: string) => {
|
||||||
|
const channelSubscribers = this.subscribers.get(channel);
|
||||||
|
if (channelSubscribers) {
|
||||||
|
channelSubscribers.forEach((callback) => {
|
||||||
|
try {
|
||||||
|
callback(channel, message);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
`Error in subscriber callback for channel ${channel}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const setupMessage = this.hasReplicas
|
||||||
|
? "Redis clients initialized successfully with replica support"
|
||||||
|
: "Redis clients initialized successfully (single instance)";
|
||||||
|
logger.info(setupMessage);
|
||||||
|
|
||||||
|
// Start periodic health monitoring
|
||||||
|
this.startHealthMonitoring();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to initialize Redis clients:", error);
|
||||||
|
this.isEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateOverallHealth(): void {
|
||||||
|
// Overall health is true if write is healthy and (read is healthy OR we don't have replicas)
|
||||||
|
this.isHealthy = this.isWriteHealthy && (this.isReadHealthy || !this.hasReplicas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async executeWithRetry<T>(
|
||||||
|
operation: () => Promise<T>,
|
||||||
|
operationName: string,
|
||||||
|
fallbackOperation?: () => Promise<T>
|
||||||
|
): Promise<T> {
|
||||||
|
let lastError: Error | null = null;
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
return await operation();
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error as Error;
|
||||||
|
|
||||||
|
// If this is the last attempt, try fallback if available
|
||||||
|
if (attempt === this.maxRetries && fallbackOperation) {
|
||||||
|
try {
|
||||||
|
logger.warn(`${operationName} primary operation failed, trying fallback`);
|
||||||
|
return await fallbackOperation();
|
||||||
|
} catch (fallbackError) {
|
||||||
|
logger.error(`${operationName} fallback also failed:`, fallbackError);
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't retry on the last attempt
|
||||||
|
if (attempt === this.maxRetries) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate delay with exponential backoff
|
||||||
|
const delay = Math.min(
|
||||||
|
this.baseRetryDelay * Math.pow(this.backoffMultiplier, attempt),
|
||||||
|
this.maxRetryDelay
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.warn(`${operationName} failed (attempt ${attempt + 1}/${this.maxRetries + 1}), retrying in ${delay}ms:`, error);
|
||||||
|
|
||||||
|
// Wait before retrying
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error(`${operationName} failed after ${this.maxRetries + 1} attempts:`, lastError);
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
private startHealthMonitoring(): void {
|
||||||
|
if (!this.isEnabled) return;
|
||||||
|
|
||||||
|
// Check health every 30 seconds
|
||||||
|
setInterval(async () => {
|
||||||
|
try {
|
||||||
|
await this.checkRedisHealth();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error during Redis health monitoring:", error);
|
||||||
|
}
|
||||||
|
}, this.healthCheckInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isRedisEnabled(): boolean {
|
||||||
|
return this.isEnabled && this.client !== null && this.isHealthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async checkRedisHealth(): Promise<boolean> {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Only check health every 30 seconds
|
||||||
|
if (now - this.lastHealthCheck < this.healthCheckInterval) {
|
||||||
|
return this.isHealthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastHealthCheck = now;
|
||||||
|
|
||||||
|
if (!this.writeClient) {
|
||||||
|
this.isHealthy = false;
|
||||||
|
this.isWriteHealthy = false;
|
||||||
|
this.isReadHealthy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check write client (master) health
|
||||||
|
await Promise.race([
|
||||||
|
this.writeClient.ping(),
|
||||||
|
new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Write client health check timeout')), 2000)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
this.isWriteHealthy = true;
|
||||||
|
|
||||||
|
// Check read client health if it's different from write client
|
||||||
|
if (this.hasReplicas && this.readClient && this.readClient !== this.writeClient) {
|
||||||
|
try {
|
||||||
|
await Promise.race([
|
||||||
|
this.readClient.ping(),
|
||||||
|
new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Read client health check timeout')), 2000)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
this.isReadHealthy = true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis read client health check failed:", error);
|
||||||
|
this.isReadHealthy = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.isReadHealthy = this.isWriteHealthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateOverallHealth();
|
||||||
|
return this.isHealthy;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis write client health check failed:", error);
|
||||||
|
this.isWriteHealthy = false;
|
||||||
|
this.isReadHealthy = false; // If write fails, consider read as failed too for safety
|
||||||
|
this.isHealthy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getClient(): Redis {
|
||||||
|
return this.client!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWriteClient(): Redis | null {
|
||||||
|
return this.writeClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getReadClient(): Redis | null {
|
||||||
|
return this.readClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public hasReplicaSupport(): boolean {
|
||||||
|
return this.hasReplicas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getHealthStatus(): {
|
||||||
|
isEnabled: boolean;
|
||||||
|
isHealthy: boolean;
|
||||||
|
isWriteHealthy: boolean;
|
||||||
|
isReadHealthy: boolean;
|
||||||
|
hasReplicas: boolean;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
isEnabled: this.isEnabled,
|
||||||
|
isHealthy: this.isHealthy,
|
||||||
|
isWriteHealthy: this.isWriteHealthy,
|
||||||
|
isReadHealthy: this.isReadHealthy,
|
||||||
|
hasReplicas: this.hasReplicas
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async set(
|
||||||
|
key: string,
|
||||||
|
value: string,
|
||||||
|
ttl?: number
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.writeClient) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
async () => {
|
||||||
|
if (ttl) {
|
||||||
|
await this.writeClient!.setex(key, ttl, value);
|
||||||
|
} else {
|
||||||
|
await this.writeClient!.set(key, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Redis SET"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis SET error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async get(key: string): Promise<string | null> {
|
||||||
|
if (!this.isRedisEnabled() || !this.readClient) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy)
|
||||||
|
? () => this.writeClient!.get(key)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return await this.executeWithRetry(
|
||||||
|
() => this.readClient!.get(key),
|
||||||
|
"Redis GET",
|
||||||
|
fallbackOperation
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis GET error:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async del(key: string): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.writeClient) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.writeClient!.del(key),
|
||||||
|
"Redis DEL"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis DEL error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sadd(key: string, member: string): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.writeClient) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.writeClient!.sadd(key, member),
|
||||||
|
"Redis SADD"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis SADD error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async srem(key: string, member: string): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.writeClient) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.writeClient!.srem(key, member),
|
||||||
|
"Redis SREM"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis SREM error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async smembers(key: string): Promise<string[]> {
|
||||||
|
if (!this.isRedisEnabled() || !this.readClient) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy)
|
||||||
|
? () => this.writeClient!.smembers(key)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return await this.executeWithRetry(
|
||||||
|
() => this.readClient!.smembers(key),
|
||||||
|
"Redis SMEMBERS",
|
||||||
|
fallbackOperation
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis SMEMBERS error:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async hset(
|
||||||
|
key: string,
|
||||||
|
field: string,
|
||||||
|
value: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.writeClient) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.writeClient!.hset(key, field, value),
|
||||||
|
"Redis HSET"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis HSET error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async hget(key: string, field: string): Promise<string | null> {
|
||||||
|
if (!this.isRedisEnabled() || !this.readClient) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy)
|
||||||
|
? () => this.writeClient!.hget(key, field)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return await this.executeWithRetry(
|
||||||
|
() => this.readClient!.hget(key, field),
|
||||||
|
"Redis HGET",
|
||||||
|
fallbackOperation
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis HGET error:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async hdel(key: string, field: string): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.writeClient) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.writeClient!.hdel(key, field),
|
||||||
|
"Redis HDEL"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis HDEL error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async hgetall(key: string): Promise<Record<string, string>> {
|
||||||
|
if (!this.isRedisEnabled() || !this.readClient) return {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fallbackOperation = (this.hasReplicas && this.writeClient && this.isWriteHealthy)
|
||||||
|
? () => this.writeClient!.hgetall(key)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return await this.executeWithRetry(
|
||||||
|
() => this.readClient!.hgetall(key),
|
||||||
|
"Redis HGETALL",
|
||||||
|
fallbackOperation
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis HGETALL error:", error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async publish(channel: string, message: string): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.publisher) return false;
|
||||||
|
|
||||||
|
// Quick health check before attempting to publish
|
||||||
|
const isHealthy = await this.checkRedisHealth();
|
||||||
|
if (!isHealthy) {
|
||||||
|
logger.warn("Skipping Redis publish due to unhealthy connection");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.executeWithRetry(
|
||||||
|
async () => {
|
||||||
|
// Add timeout to prevent hanging
|
||||||
|
return Promise.race([
|
||||||
|
this.publisher!.publish(channel, message),
|
||||||
|
new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Redis publish timeout')), 3000)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
"Redis PUBLISH"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis PUBLISH error:", error);
|
||||||
|
this.isHealthy = false; // Mark as unhealthy on error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async subscribe(
|
||||||
|
channel: string,
|
||||||
|
callback: (channel: string, message: string) => void
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.subscriber) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Add callback to subscribers map
|
||||||
|
if (!this.subscribers.has(channel)) {
|
||||||
|
this.subscribers.set(channel, new Set());
|
||||||
|
// Only subscribe to the channel if it's the first subscriber
|
||||||
|
await this.executeWithRetry(
|
||||||
|
async () => {
|
||||||
|
return Promise.race([
|
||||||
|
this.subscriber!.subscribe(channel),
|
||||||
|
new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Redis subscribe timeout')), 5000)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
"Redis SUBSCRIBE"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subscribers.get(channel)!.add(callback);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis SUBSCRIBE error:", error);
|
||||||
|
this.isHealthy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async unsubscribe(
|
||||||
|
channel: string,
|
||||||
|
callback?: (channel: string, message: string) => void
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (!this.isRedisEnabled() || !this.subscriber) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const channelSubscribers = this.subscribers.get(channel);
|
||||||
|
if (!channelSubscribers) return true;
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
// Remove specific callback
|
||||||
|
channelSubscribers.delete(callback);
|
||||||
|
if (channelSubscribers.size === 0) {
|
||||||
|
this.subscribers.delete(channel);
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.subscriber!.unsubscribe(channel),
|
||||||
|
"Redis UNSUBSCRIBE"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remove all callbacks for this channel
|
||||||
|
this.subscribers.delete(channel);
|
||||||
|
await this.executeWithRetry(
|
||||||
|
() => this.subscriber!.unsubscribe(channel),
|
||||||
|
"Redis UNSUBSCRIBE"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Redis UNSUBSCRIBE error:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async disconnect(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (this.client) {
|
||||||
|
await this.client.quit();
|
||||||
|
this.client = null;
|
||||||
|
}
|
||||||
|
if (this.writeClient) {
|
||||||
|
await this.writeClient.quit();
|
||||||
|
this.writeClient = null;
|
||||||
|
}
|
||||||
|
if (this.readClient && this.readClient !== this.writeClient) {
|
||||||
|
await this.readClient.quit();
|
||||||
|
this.readClient = null;
|
||||||
|
}
|
||||||
|
if (this.publisher) {
|
||||||
|
await this.publisher.quit();
|
||||||
|
this.publisher = null;
|
||||||
|
}
|
||||||
|
if (this.subscriber) {
|
||||||
|
await this.subscriber.quit();
|
||||||
|
this.subscriber = null;
|
||||||
|
}
|
||||||
|
this.subscribers.clear();
|
||||||
|
logger.info("Redis clients disconnected");
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error disconnecting Redis clients:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const redisManager = new RedisManager();
|
||||||
|
export const redis = redisManager.getClient();
|
||||||
|
export default redisManager;
|
||||||
223
server/db/private/redisStore.ts
Normal file
223
server/db/private/redisStore.ts
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Store, Options, IncrementResponse } from 'express-rate-limit';
|
||||||
|
import { rateLimitService } from './rateLimit';
|
||||||
|
import logger from '@server/logger';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Redis-backed rate limiting store for express-rate-limit that optimizes
|
||||||
|
* for local read performance and batched writes to Redis.
|
||||||
|
*
|
||||||
|
* This store uses the same optimized rate limiting logic as the WebSocket
|
||||||
|
* implementation, providing:
|
||||||
|
* - Local caching for fast reads
|
||||||
|
* - Batched writes to Redis to reduce load
|
||||||
|
* - Automatic cleanup of expired entries
|
||||||
|
* - Graceful fallback when Redis is unavailable
|
||||||
|
*/
|
||||||
|
export default class RedisStore implements Store {
|
||||||
|
/**
|
||||||
|
* The duration of time before which all hit counts are reset (in milliseconds).
|
||||||
|
*/
|
||||||
|
windowMs!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of requests allowed within the window.
|
||||||
|
*/
|
||||||
|
max!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional prefix for Redis keys to avoid collisions.
|
||||||
|
*/
|
||||||
|
prefix: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to skip incrementing on failed requests.
|
||||||
|
*/
|
||||||
|
skipFailedRequests: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to skip incrementing on successful requests.
|
||||||
|
*/
|
||||||
|
skipSuccessfulRequests: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor for RedisStore.
|
||||||
|
*
|
||||||
|
* @param options - Configuration options for the store.
|
||||||
|
*/
|
||||||
|
constructor(options: {
|
||||||
|
prefix?: string;
|
||||||
|
skipFailedRequests?: boolean;
|
||||||
|
skipSuccessfulRequests?: boolean;
|
||||||
|
} = {}) {
|
||||||
|
this.prefix = options.prefix || 'express-rate-limit';
|
||||||
|
this.skipFailedRequests = options.skipFailedRequests || false;
|
||||||
|
this.skipSuccessfulRequests = options.skipSuccessfulRequests || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that actually initializes the store. Must be synchronous.
|
||||||
|
*
|
||||||
|
* @param options - The options used to setup express-rate-limit.
|
||||||
|
*/
|
||||||
|
init(options: Options): void {
|
||||||
|
this.windowMs = options.windowMs;
|
||||||
|
this.max = options.max as number;
|
||||||
|
|
||||||
|
// logger.debug(`RedisStore initialized with windowMs: ${this.windowMs}, max: ${this.max}, prefix: ${this.prefix}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to increment a client's hit counter.
|
||||||
|
*
|
||||||
|
* @param key - The identifier for a client (usually IP address).
|
||||||
|
* @returns Promise resolving to the number of hits and reset time for that client.
|
||||||
|
*/
|
||||||
|
async increment(key: string): Promise<IncrementResponse> {
|
||||||
|
try {
|
||||||
|
const clientId = `${this.prefix}:${key}`;
|
||||||
|
|
||||||
|
const result = await rateLimitService.checkRateLimit(
|
||||||
|
clientId,
|
||||||
|
undefined, // No message type for HTTP requests
|
||||||
|
this.max,
|
||||||
|
undefined, // No message type limit
|
||||||
|
this.windowMs
|
||||||
|
);
|
||||||
|
|
||||||
|
// logger.debug(`Incremented rate limit for key: ${key} with max: ${this.max}, totalHits: ${result.totalHits}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalHits: result.totalHits || 1,
|
||||||
|
resetTime: result.resetTime || new Date(Date.now() + this.windowMs)
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`RedisStore increment error for key ${key}:`, error);
|
||||||
|
|
||||||
|
// Return safe defaults on error to prevent blocking requests
|
||||||
|
return {
|
||||||
|
totalHits: 1,
|
||||||
|
resetTime: new Date(Date.now() + this.windowMs)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to decrement a client's hit counter.
|
||||||
|
* Used when skipSuccessfulRequests or skipFailedRequests is enabled.
|
||||||
|
*
|
||||||
|
* @param key - The identifier for a client.
|
||||||
|
*/
|
||||||
|
async decrement(key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const clientId = `${this.prefix}:${key}`;
|
||||||
|
await rateLimitService.decrementRateLimit(clientId);
|
||||||
|
|
||||||
|
// logger.debug(`Decremented rate limit for key: ${key}`);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`RedisStore decrement error for key ${key}:`, error);
|
||||||
|
// Don't throw - decrement failures shouldn't block requests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to reset a client's hit counter.
|
||||||
|
*
|
||||||
|
* @param key - The identifier for a client.
|
||||||
|
*/
|
||||||
|
async resetKey(key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const clientId = `${this.prefix}:${key}`;
|
||||||
|
await rateLimitService.resetKey(clientId);
|
||||||
|
|
||||||
|
// logger.debug(`Reset rate limit for key: ${key}`);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`RedisStore resetKey error for key ${key}:`, error);
|
||||||
|
// Don't throw - reset failures shouldn't block requests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to reset everyone's hit counter.
|
||||||
|
*
|
||||||
|
* This method is optional and is never called by express-rate-limit.
|
||||||
|
* We implement it for completeness but it's not recommended for production use
|
||||||
|
* as it could be expensive with large datasets.
|
||||||
|
*/
|
||||||
|
async resetAll(): Promise<void> {
|
||||||
|
try {
|
||||||
|
logger.warn('RedisStore resetAll called - this operation can be expensive');
|
||||||
|
|
||||||
|
// Force sync all pending data first
|
||||||
|
await rateLimitService.forceSyncAllPendingData();
|
||||||
|
|
||||||
|
// Note: We don't actually implement full reset as it would require
|
||||||
|
// scanning all Redis keys with our prefix, which could be expensive.
|
||||||
|
// In production, it's better to let entries expire naturally.
|
||||||
|
|
||||||
|
logger.info('RedisStore resetAll completed (pending data synced)');
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('RedisStore resetAll error:', error);
|
||||||
|
// Don't throw - this is an optional method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current hit count for a key without incrementing.
|
||||||
|
* This is a custom method not part of the Store interface.
|
||||||
|
*
|
||||||
|
* @param key - The identifier for a client.
|
||||||
|
* @returns Current hit count and reset time, or null if no data exists.
|
||||||
|
*/
|
||||||
|
async getHits(key: string): Promise<{ totalHits: number; resetTime: Date } | null> {
|
||||||
|
try {
|
||||||
|
const clientId = `${this.prefix}:${key}`;
|
||||||
|
|
||||||
|
// Use checkRateLimit with max + 1 to avoid actually incrementing
|
||||||
|
// but still get the current count
|
||||||
|
const result = await rateLimitService.checkRateLimit(
|
||||||
|
clientId,
|
||||||
|
undefined,
|
||||||
|
this.max + 1000, // Set artificially high to avoid triggering limit
|
||||||
|
undefined,
|
||||||
|
this.windowMs
|
||||||
|
);
|
||||||
|
|
||||||
|
// Decrement since we don't actually want to count this check
|
||||||
|
await rateLimitService.decrementRateLimit(clientId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalHits: Math.max(0, (result.totalHits || 0) - 1), // Adjust for the decrement
|
||||||
|
resetTime: result.resetTime || new Date(Date.now() + this.windowMs)
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`RedisStore getHits error for key ${key}:`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup method for graceful shutdown.
|
||||||
|
* This is not part of the Store interface but is useful for cleanup.
|
||||||
|
*/
|
||||||
|
async shutdown(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// The rateLimitService handles its own cleanup
|
||||||
|
logger.info('RedisStore shutdown completed');
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('RedisStore shutdown error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
345
server/db/queries/verifySessionQueries.ts
Normal file
345
server/db/queries/verifySessionQueries.ts
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
import { db, loginPage, LoginPage, loginPageOrg } from "@server/db";
|
||||||
|
import {
|
||||||
|
Resource,
|
||||||
|
ResourcePassword,
|
||||||
|
ResourcePincode,
|
||||||
|
ResourceRule,
|
||||||
|
resourcePassword,
|
||||||
|
resourcePincode,
|
||||||
|
resourceRules,
|
||||||
|
resources,
|
||||||
|
roleResources,
|
||||||
|
sessions,
|
||||||
|
userOrgs,
|
||||||
|
userResources,
|
||||||
|
users
|
||||||
|
} from "@server/db";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import axios from "axios";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { tokenManager } from "@server/lib/tokenManager";
|
||||||
|
|
||||||
|
export type ResourceWithAuth = {
|
||||||
|
resource: Resource | null;
|
||||||
|
pincode: ResourcePincode | null;
|
||||||
|
password: ResourcePassword | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserSessionWithUser = {
|
||||||
|
session: any;
|
||||||
|
user: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource by domain with pincode and password information
|
||||||
|
*/
|
||||||
|
export async function getResourceByDomain(
|
||||||
|
domain: string
|
||||||
|
): Promise<ResourceWithAuth | null> {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/domain/${domain}`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [result] = await db
|
||||||
|
.select()
|
||||||
|
.from(resources)
|
||||||
|
.leftJoin(
|
||||||
|
resourcePincode,
|
||||||
|
eq(resourcePincode.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourcePassword,
|
||||||
|
eq(resourcePassword.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
|
.where(eq(resources.fullDomain, domain))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
resource: result.resources,
|
||||||
|
pincode: result.resourcePincode,
|
||||||
|
password: result.resourcePassword
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user session with user information
|
||||||
|
*/
|
||||||
|
export async function getUserSessionWithUser(
|
||||||
|
userSessionId: string
|
||||||
|
): Promise<UserSessionWithUser | null> {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/session/${userSessionId}`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [res] = await db
|
||||||
|
.select()
|
||||||
|
.from(sessions)
|
||||||
|
.leftJoin(users, eq(users.userId, sessions.userId))
|
||||||
|
.where(eq(sessions.sessionId, userSessionId));
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
session: res.session,
|
||||||
|
user: res.user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user organization role
|
||||||
|
*/
|
||||||
|
export async function getUserOrgRole(userId: string, orgId: string) {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/org/${orgId}/role`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userOrgRole = await db
|
||||||
|
.select()
|
||||||
|
.from(userOrgs)
|
||||||
|
.where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return userOrgRole.length > 0 ? userOrgRole[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if role has access to resource
|
||||||
|
*/
|
||||||
|
export async function getRoleResourceAccess(
|
||||||
|
resourceId: number,
|
||||||
|
roleId: number
|
||||||
|
) {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/role/${roleId}/resource/${resourceId}/access`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const roleResourceAccess = await db
|
||||||
|
.select()
|
||||||
|
.from(roleResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(roleResources.resourceId, resourceId),
|
||||||
|
eq(roleResources.roleId, roleId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return roleResourceAccess.length > 0 ? roleResourceAccess[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user has direct access to resource
|
||||||
|
*/
|
||||||
|
export async function getUserResourceAccess(
|
||||||
|
userId: string,
|
||||||
|
resourceId: number
|
||||||
|
) {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/user/${userId}/resource/${resourceId}/access`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userResourceAccess = await db
|
||||||
|
.select()
|
||||||
|
.from(userResources)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(userResources.userId, userId),
|
||||||
|
eq(userResources.resourceId, resourceId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return userResourceAccess.length > 0 ? userResourceAccess[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource rules for a given resource
|
||||||
|
*/
|
||||||
|
export async function getResourceRules(
|
||||||
|
resourceId: number
|
||||||
|
): Promise<ResourceRule[]> {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/rules`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = await db
|
||||||
|
.select()
|
||||||
|
.from(resourceRules)
|
||||||
|
.where(eq(resourceRules.resourceId, resourceId));
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get organization login page
|
||||||
|
*/
|
||||||
|
export async function getOrgLoginPage(
|
||||||
|
orgId: string
|
||||||
|
): Promise<LoginPage | null> {
|
||||||
|
if (config.isManagedMode()) {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/org/${orgId}/login-page`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [result] = await db
|
||||||
|
.select()
|
||||||
|
.from(loginPageOrg)
|
||||||
|
.where(eq(loginPageOrg.orgId, orgId))
|
||||||
|
.innerJoin(
|
||||||
|
loginPage,
|
||||||
|
eq(loginPageOrg.loginPageId, loginPage.loginPageId)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result?.loginPage;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user