Compare commits
725 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b4858ae04 | |||
| b0e2fcadd4 | |||
| 65ccd5a89d | |||
| 76a4f50ccf | |||
| 1dfe0124be | |||
| aa52174f96 | |||
| e23ae5707f | |||
| 17053ac2bd | |||
| 2733ae1122 | |||
| 735787b183 | |||
| 2fb3507aa5 | |||
| 262ca8a1d0 | |||
| 66c9bdbfa3 | |||
| 4716a2a647 | |||
| 847f44197b | |||
| 6aacd4d185 | |||
| ab3db3bc68 | |||
| b52baceb50 | |||
| 0d2c8a37ef | |||
| 9298ec7cdb | |||
| 0fdff2feee | |||
| 8e042e6433 | |||
| 883ad14326 | |||
| 3cb41211ee | |||
| ee4a1a6b18 | |||
| ee3f9efa27 | |||
| a10972990a | |||
| c32ef54428 | |||
| 6d6e105711 | |||
| bda09a1b74 | |||
| 178d1d9779 | |||
| d311eb6e8c | |||
| 829680e984 | |||
| be10dcd26e | |||
| cc23e8cf5d | |||
| fc77b5bb3e | |||
| 651acae326 | |||
| 75332a1d1f | |||
| 0f1631f592 | |||
| f1a303de14 | |||
| 191c062cec | |||
| 1839ea7ca3 | |||
| a3c2c98c5e | |||
| 43e42f90f8 | |||
| a954ba349b | |||
| f07aef4ace | |||
| 4afa01aba4 | |||
| 9b15e99cfb | |||
| a322d0479c | |||
| bb9761a171 | |||
| af4d80edec | |||
| da434f63b4 | |||
| 9718c3549b | |||
| d550eef6c2 | |||
| c76b591f9f | |||
| a1b7c69ba5 | |||
| 20e5d537b0 | |||
| 197cfdc339 | |||
| 418bb26ee6 | |||
| 4dfa81416b | |||
| 324f3e50ff | |||
| 64bb6d9f9c | |||
| e54d1aa73e | |||
| c6c12f1dcb | |||
| 4b30911f06 | |||
| c6c443f0e0 | |||
| 24a06dd8ca | |||
| c11bbe670f | |||
| 7ed646c02b | |||
| 9d5dd526bc | |||
| 9e25650f22 | |||
| 143b4cf757 | |||
| 25b151a021 | |||
| c62fb6cbe0 | |||
| e561c9f334 | |||
| 2b970ce6e7 | |||
| e8cb0297a1 | |||
| 4f7e993e70 | |||
| 8dc3a1378a | |||
| 3713b714e4 | |||
| 098157f671 | |||
| b982639dc4 | |||
| 56e758a656 | |||
| ea1b3e5270 | |||
| a84db679b2 | |||
| fb726cc6b2 | |||
| 5d0ceebe0d | |||
| c964821195 | |||
| 5c44c6da73 | |||
| 6e52758d6e | |||
| 0af194550f | |||
| 0d12402662 | |||
| 3fb2e34256 | |||
| ec2b1fbd37 | |||
| 806ae93a12 | |||
| 5d99e0a2f6 | |||
| a75f335791 | |||
| 153d29c47c | |||
| ee8749c292 | |||
| 4f905ddae5 | |||
| 74e686ff3f | |||
| a4ade43380 | |||
| 68821514a0 | |||
| a2652cf692 | |||
| 957144ac3a | |||
| 20c23cdfc5 | |||
| c03e1c5781 | |||
| e69d3eded5 | |||
| 34084cc3a6 | |||
| 91776ad2e4 | |||
| 77eabf41e7 | |||
| d68895e64a | |||
| 1c2d81d77c | |||
| 998778eb72 | |||
| 7be206947a | |||
| 125d33c071 | |||
| 410d26f2df | |||
| 6aa1dafa10 | |||
| 8f1344a665 | |||
| d23028d38e | |||
| 7506a2614b | |||
| b3e5de4a4d | |||
| f9f38fb3f0 | |||
| 888ccce397 | |||
| 49e0a8bd47 | |||
| f59aed8482 | |||
| b6c048c805 | |||
| 018ee17304 | |||
| 5cad796023 | |||
| 5a445817e0 | |||
| fd2c397bfc | |||
| 6c794e0b0c | |||
| 7acb98e245 | |||
| d59ab30c22 | |||
| e4a8e9ac8c | |||
| a874a0b745 | |||
| 9ba2cfdef2 | |||
| bba8d4e7a2 | |||
| 350f8c012a | |||
| 8a97ed5200 | |||
| e66f7fe71b | |||
| a37a59f39a | |||
| d0bf553f6f | |||
| 87e005d32f | |||
| ad343a7453 | |||
| 7e4d38548f | |||
| 3421441635 | |||
| 073bfb32e9 | |||
| 2c5b1e5f0f | |||
| 294830db08 | |||
| fb8d531435 | |||
| 11d947f4ef | |||
| aad18e6501 | |||
| dac8b5a132 | |||
| 162dade852 | |||
| dfe7f60244 | |||
| 07bea71cb9 | |||
| 557c398d0b | |||
| 0a385d1e44 | |||
| 521f78c2f3 | |||
| 0ea4a5fb3d | |||
| e2609f1a0d | |||
| 8b20a88838 | |||
| 7aae51ca9d | |||
| 1d6d885f30 | |||
| fef1476f67 | |||
| e371f26d73 | |||
| 9c8b93d6cc | |||
| aed325f273 | |||
| c7645e5c5b | |||
| c0eed078c8 | |||
| 0f69012c4d | |||
| ba2eb87f20 | |||
| c0ea32863c | |||
| 4d71fcbac8 | |||
| 1d3dcec4c8 | |||
| 899620a890 | |||
| c19cb706e8 | |||
| 938d7f145c | |||
| 17375348b0 | |||
| ea9017ac06 | |||
| 88770ff97b | |||
| 8e75425887 | |||
| 44b0186044 | |||
| 063f6b5ca9 | |||
| e7fdbf9e85 | |||
| 778a840ed7 | |||
| 9d19195089 | |||
| 54bbe82504 | |||
| cddb5ecc3d | |||
| de57df2520 | |||
| 9e392a967d | |||
| 52f7364c35 | |||
| 1bc5fbbf0f | |||
| be0dd65557 | |||
| 11be7b8ee4 | |||
| 9373cc0408 | |||
| e50763c340 | |||
| 8826240548 | |||
| 6d83614482 | |||
| c319b7a65f | |||
| aa7fe7ee0e | |||
| 5fd71df2ae | |||
| c7e1462e46 | |||
| cfb73c9e21 | |||
| 72a9040c2e | |||
| ce53b8c73d | |||
| 6ff2b3e2fe | |||
| 4dada38cb0 | |||
| 732ea034f8 | |||
| 581137c486 | |||
| d75c6da3be | |||
| 11b51202b8 | |||
| c35de18b9b | |||
| 353273a9f2 | |||
| 7c20a292e1 | |||
| 6c13d6e343 | |||
| e641a8ce1b | |||
| a3419f60d6 | |||
| 2db3051a1a | |||
| 3c49b6f3d6 | |||
| 9ad4e36fa9 | |||
| c489ed5724 | |||
| fca3a1e5fa | |||
| b29348d004 | |||
| 337d9a33a3 | |||
| c61e96b1f0 | |||
| 35bc692892 | |||
| 714e7e0ba4 | |||
| 49d5b0ec34 | |||
| e0937a3afa | |||
| 8d7e73afa8 | |||
| a5ce56ea89 | |||
| 2f013335f9 | |||
| b4f6ae74d7 | |||
| f1711ee0b0 | |||
| 780d767a65 | |||
| d6d923e972 | |||
| 41139f2fd0 | |||
| ebcdeab414 | |||
| 2cc7d03ace | |||
| c36cf698c1 | |||
| 34671c6b13 | |||
| 8dfc95347f | |||
| 22db0319c2 | |||
| 39722d30af | |||
| 8aef14cf9f | |||
| 0dece5fef1 | |||
| e7f38c089f | |||
| dd0a5a359a | |||
| 1650ece0c3 | |||
| 7f94d99455 | |||
| 1f9e99219d | |||
| bc56a2bed0 | |||
| 49dcc590ce | |||
| da3e3ff33f | |||
| 69d539f107 | |||
| 872e0f9ae1 | |||
| 5b3713a72f | |||
| f02be1fdbf | |||
| 0bf04cf0cd | |||
| 7cda28d685 | |||
| 60bc74c4df | |||
| 48ab6c501f | |||
| 7a95e543d8 | |||
| b87b7c7e80 | |||
| a47a68d8e1 | |||
| ed0d6fb6b9 | |||
| a02d16fd58 | |||
| 331fee24d4 | |||
| 5bdb12dafe | |||
| e57826d6e0 | |||
| 3d4e143c1f | |||
| 10a25c184d | |||
| 906099d1e1 | |||
| 9a5824900d | |||
| f3474dac98 | |||
| 72d2c79793 | |||
| 23764feb4f | |||
| d2809fbfd1 | |||
| 2d18db3597 | |||
| d00b9478a2 | |||
| 4ddf36ebcc | |||
| 77cab56fa9 | |||
| 2051e5df37 | |||
| 26f9026621 | |||
| 85b40b7164 | |||
| 753cbd45d0 | |||
| 1c2fe44c54 | |||
| 5b782a842c | |||
| 935410b15e | |||
| 89066aa5b1 | |||
| ddf89d0afa | |||
| e4ec6f7cbe | |||
| c1e5769003 | |||
| 4b31326b34 | |||
| 10d2c6438b | |||
| c3140c5da3 | |||
| 0f00a2337d | |||
| 1831b1af58 | |||
| 85257b941b | |||
| 4aa43fd14d | |||
| 90a77ee450 | |||
| d237d6545e | |||
| 262aaa2756 | |||
| 547ac2284a | |||
| 7d2af1837e | |||
| 8ae42e1852 | |||
| 7319bf84f7 | |||
| 9ec9908ed7 | |||
| 9b0e049a21 | |||
| 28b32fe6f7 | |||
| adfb6003d9 | |||
| 19ce236262 | |||
| 6a5ecab013 | |||
| eca1c9044c | |||
| 2c197fab9f | |||
| 4229ef9173 | |||
| d5ea0ecbc1 | |||
| 59f286299e | |||
| 442cefda84 | |||
| e3e1508e8a | |||
| 2e87927b83 | |||
| 65e4fe91b9 | |||
| e65a79cc48 | |||
| 365a905e69 | |||
| bafbf6e096 | |||
| df7e26a444 | |||
| c1051db4a5 | |||
| c1caa30cb9 | |||
| fd0a0818c1 | |||
| 887e13888d | |||
| 114592add8 | |||
| ef051c2f8c | |||
| 5c6da72ec1 | |||
| acd64a049f | |||
| 967deaff88 | |||
| ef8985b0af | |||
| 1f80bdd361 | |||
| 929acc5b1c | |||
| 437ec50019 | |||
| a7bbafd2f0 | |||
| 6824ec6e3e | |||
| 71348f45b2 | |||
| 664b7d52af | |||
| fb6f9c91c6 | |||
| 051c873fdf | |||
| 12224458e7 | |||
| b6719d2a3e | |||
| 7843b56f39 | |||
| 19be6c4b3b | |||
| 4a283710ad | |||
| 1af7c5ee73 | |||
| 77dd47fa8a | |||
| ccfe91f08d | |||
| 624fb8322f | |||
| 6bc3242a29 | |||
| 33f528ef79 | |||
| 6c1afc4453 | |||
| 2a14447f8c | |||
| ad50f7473a | |||
| 5f1218f7b1 | |||
| e377afe9f6 | |||
| 21eb4d2876 | |||
| bb824a7070 | |||
| f98de98288 | |||
| 350de6ad2e | |||
| dc8e724482 | |||
| fb5b564c00 | |||
| 52c078a489 | |||
| 18270381c1 | |||
| b32dc30050 | |||
| 667804043c | |||
| 3fd3d90c10 | |||
| 6e3619fb1a | |||
| 987c0e992b | |||
| bdd7f40688 | |||
| 809bc662e0 | |||
| 195f67c6eb | |||
| a74e1e765a | |||
| c356af3eda | |||
| a4c7121b93 | |||
| 2a3c00045f | |||
| 8e938a2723 | |||
| 197f8f7ba5 | |||
| 10b528642d | |||
| 60d6fff085 | |||
| c664b3da91 | |||
| 492282e758 | |||
| 47f4aefc25 | |||
| 7c0ff9ede7 | |||
| 44e81ea979 | |||
| 56dc10330a | |||
| 6c6e5c0fdf | |||
| 4be73558d5 | |||
| 87ff7650a1 | |||
| ae5af013b4 | |||
| 5adfe65aba | |||
| d3ae06deeb | |||
| ee4a72bcb7 | |||
| f941c23927 | |||
| 3ae6a08b71 | |||
| 620eeaed4c | |||
| 9b08228398 | |||
| eb102a0d23 | |||
| 18317e0028 | |||
| 2ec93c6710 | |||
| 5933eeb05f | |||
| 3fae2d687f | |||
| 285d7c6030 | |||
| 0fd57f5843 | |||
| a296460124 | |||
| 35104f7b29 | |||
| 51375ed8b7 | |||
| 1bb4f604e9 | |||
| 8fd990df93 | |||
| 1f390cabc6 | |||
| 7f1f8c54c1 | |||
| b1515db229 | |||
| d78c226734 | |||
| 52865bc1f5 | |||
| efbb6c9814 | |||
| 1e3dea3dad | |||
| 25f84482ba | |||
| 3f2a116fdb | |||
| dce002c6cc | |||
| dcfbbdc79d | |||
| f8cac37eef | |||
| cb37713c4c | |||
| 7ae4082d7e | |||
| aa8d339d35 | |||
| 7a61b182e0 | |||
| 91b697b80b | |||
| 7d10b63dc5 | |||
| 92916cd9db | |||
| 075d112861 | |||
| 7e9407fbc7 | |||
| 313f777e44 | |||
| 3ba9eb7ad7 | |||
| 328a2d52da | |||
| 697bea814e | |||
| 83cda218b7 | |||
| 76435440af | |||
| 0a8b5e46ca | |||
| a74b0934fc | |||
| c05d2aeafb | |||
| 87a3e5ceea | |||
| 555b36e13e | |||
| 021866011f | |||
| 749abdcb56 | |||
| 7d6f3a2925 | |||
| af515f1072 | |||
| c90c67eab3 | |||
| 51c9507d08 | |||
| 11daf4f927 | |||
| 58195b5959 | |||
| b51aecf45a | |||
| eb8ad6a181 | |||
| 4edd2e4d32 | |||
| 813c3abe54 | |||
| ca7ea72ff1 | |||
| 53b1d8a9f3 | |||
| a3dfb30a42 | |||
| 668a04bcd2 | |||
| 111d4b1b8c | |||
| 32d76ee2ac | |||
| 98ad6fb31b | |||
| 3a18a3994f | |||
| 6d35b20880 | |||
| 173d4a536f | |||
| ea740e12d1 | |||
| d7bff686c0 | |||
| 193f6da8d1 | |||
| 954dc8d1d9 | |||
| 234819f8f2 | |||
| b34a8d116b | |||
| 7375b9efae | |||
| c8f170d197 | |||
| 4989d1e31a | |||
| c568251d8d | |||
| db2de4af89 | |||
| 574ae8f5f9 | |||
| 125091d719 | |||
| 5d37f44241 | |||
| f0f5e9219b | |||
| ee75a09f8c | |||
| c49c6f5837 | |||
| 03118f1ede | |||
| 0ff5ea4f6a | |||
| 978fa8bfc6 | |||
| 36c7c5dc34 | |||
| 8f3628d4ec | |||
| fcd3c72cac | |||
| 3f0be4a39d | |||
| f9ff3a5715 | |||
| ed71c90d73 | |||
| e580c7db7c | |||
| 4a9b0117a2 | |||
| 9b10292e02 | |||
| d9a9ae14fd | |||
| db716f781e | |||
| 46f341f7fd | |||
| 9bb413bb1e | |||
| 2878d5690c | |||
| 1143404a65 | |||
| e92bb9043b | |||
| a2f5d830d6 | |||
| 77e3422e88 | |||
| 9a551c91e5 | |||
| c35ac493ed | |||
| e100645a00 | |||
| 96bfc66a94 | |||
| e21c9eec31 | |||
| a9f32102b5 | |||
| 7291628f86 | |||
| 5fbb205044 | |||
| 02ab24d01e | |||
| 3cfbd66a80 | |||
| 048e4fc73c | |||
| 860fa47b7c | |||
| 71d9d8f010 | |||
| cc58e28e54 | |||
| 653dd920ad | |||
| 49b4fcf063 | |||
| dd78c2cc08 | |||
| 47ae017f94 | |||
| 4b61e12ca6 | |||
| c6bd657ee6 | |||
| c33fa8782b | |||
| 93cba1d098 | |||
| d3a1f9798d | |||
| 115c3cbf07 | |||
| bed5817da3 | |||
| 83035753af | |||
| 379b53bcca | |||
| 355294a2d4 | |||
| 833b27ab4a | |||
| 8a04f13dd4 | |||
| 49020fa6ea | |||
| ac3402a8b3 | |||
| 9369e60695 | |||
| 295e38d2af | |||
| 923371e5b4 | |||
| 81430ba3d3 | |||
| 11595f5f96 | |||
| b08e875b37 | |||
| 0016b8fce7 | |||
| f5b10df7cf | |||
| 1782f31075 | |||
| 21032bc22b | |||
| e4aaadc9f9 | |||
| f7aca85417 | |||
| c42df737b0 | |||
| 75c6af3b11 | |||
| e734cc93a1 | |||
| e3ccc4f8d4 | |||
| 896c362dce | |||
| 864ab97293 | |||
| 899c47e9a3 | |||
| bc2f291352 | |||
| 98f5e39a7f | |||
| e0a66e79bb | |||
| c781be4e70 | |||
| 48c4b44f72 | |||
| 68a84181d9 | |||
| b60390d805 | |||
| b33e776072 | |||
| 2187f23588 | |||
| 732fd4eba1 | |||
| 495c772d6b | |||
| 02e4fe8b48 | |||
| 6d45486bb5 | |||
| 4c1f7f6243 | |||
| 1e3a9fb921 | |||
| 8b8e7913dc | |||
| 88515a7dad | |||
| e012063f5b | |||
| e76377d3c7 | |||
| a357f42c48 | |||
| 2becb15916 | |||
| 186eeed784 | |||
| 216c932cb9 | |||
| 52de5eb539 | |||
| eec3119297 | |||
| f2e7f83b3d | |||
| 93dea0525e | |||
| 7e6e0a8a9c | |||
| 211d3a53f5 | |||
| 187936e5dd | |||
| b180d064d1 | |||
| ed46afd81a | |||
| 0050fad70d | |||
| 3dc9c100e9 | |||
| 969e7c9296 | |||
| 591caab45a | |||
| 02e97d6ae4 | |||
| 996160fadc | |||
| dc1923ab1f | |||
| fe5831eb48 | |||
| ca79abc9d4 | |||
| 9eafa067b9 | |||
| 403b8a12e4 | |||
| 3f305e4d5c | |||
| 5e5e172d39 | |||
| bc7a883f6c | |||
| 12056aebc6 | |||
| 6689a8d93e | |||
| 297cb9c8f2 | |||
| d699455b38 | |||
| 9e7b4afaec | |||
| e91c344e64 | |||
| 07f628b928 | |||
| 184e1425a4 | |||
| 22f2990f56 | |||
| 6c28c5f383 | |||
| e9f9cf54f4 | |||
| 751d1b083d | |||
| a4d77a4fd3 | |||
| f72252552f | |||
| c5d68675c9 | |||
| d04740fede | |||
| 36b8ef5fba | |||
| 6cca5e0472 | |||
| 4048fa274a | |||
| b4d2974e19 | |||
| fb896d6c0f | |||
| 82b86263dc | |||
| 6564bfe8ae | |||
| 75ce7e91d7 | |||
| 656eea5bb1 | |||
| aba27a7bbf | |||
| 3d7e322bf9 | |||
| 1f3fff4a9d | |||
| 39e06f2b6d | |||
| b775c5b674 | |||
| 796bf37141 | |||
| c5b0e1e876 | |||
| 2e8bd7a8c7 | |||
| 790daba796 | |||
| bcf6b86b84 | |||
| 2e9bd50172 | |||
| 4677a0d501 | |||
| c673dce484 | |||
| e1dd8965dc | |||
| 346a0bdc98 | |||
| 8c1169738e | |||
| bc80f91a45 | |||
| 3d062389e9 | |||
| 7afddb5eb5 | |||
| 425a99e5ee | |||
| a5e9339af9 | |||
| b4463f0e1a | |||
| b7c0669c38 | |||
| 835a30cffe | |||
| 83e20c2dfa | |||
| f85d41945a | |||
| 149eb17b27 | |||
| 18b90da6ab | |||
| 72d469b19c | |||
| 6526d7f225 | |||
| 36c0edc62e | |||
| aad26b9ae4 | |||
| 80dcdfe251 | |||
| 1696fc37a8 | |||
| 7759d87835 | |||
| c085de1e9e | |||
| e99cd52e99 | |||
| e38359c74f | |||
| 2cfd7e867b | |||
| ec5a2b0cbe | |||
| 2bf426bc22 | |||
| ed8545f8a2 | |||
| 9811492a0b | |||
| 973925b35d | |||
| 0b30cfc341 | |||
| a7e44944fb | |||
| 9b25ebd6cd | |||
| 33b775e263 | |||
| b0edc6302e | |||
| 1a644b131d | |||
| f7689b7a5a | |||
| 1073011a2a | |||
| 6fa0009ebf | |||
| 42c0abedb7 | |||
| 56e59a93f3 | |||
| 9d581f3897 | |||
| e5ac6ec7cd | |||
| 694fe98131 | |||
| 730078597e | |||
| 32ac8db803 | |||
| aad2ed2719 | |||
| a790fed297 | |||
| deb2d5ce2a | |||
| 093097c619 | |||
| 2cdeb7c104 | |||
| 4dbb04bfb8 | |||
| fa8b921635 | |||
| 33dd10c670 | |||
| ba24e1c4f5 | |||
| f079714caf | |||
| 152d2fb1d6 | |||
| d374b4f66e | |||
| efd2792197 | |||
| efe22c889c | |||
| 7f2b3eb481 | |||
| 81be4a35d9 | |||
| e84da6a8df | |||
| 3ef3ede7df | |||
| 1e521b0b54 | |||
| 59ea701304 | |||
| 7d7c54107d | |||
| f0f6673d69 | |||
| 71561d0e65 | |||
| af87edf3a6 | |||
| 13caad18c7 | |||
| 47522b7e3a | |||
| c8c8d74452 | |||
| e7098963d6 | |||
| f015fb592b | |||
| c099167905 | |||
| b0e274f5a9 | |||
| cb3f0b49a8 | |||
| 192542629f | |||
| e0a8721207 | |||
| 84d5a4b86c |
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: crud-endpoints
|
||||
description: Use whenever asked to add, create, or scaffold a CRUD endpoint, router, or entity in this repo's server (create/list/get/update/delete handlers, new `server/routers/<entity>/` or `server/private/routers/<entity>/` folder). Points to the established file layout, middleware, ActionsEnum, and route-registration conventions before writing any code.
|
||||
---
|
||||
|
||||
Before writing any router/handler/middleware code for a new entity, read
|
||||
`docs/crud-endpoints.md` in full. It documents, with real examples from
|
||||
`server/routers/aiProvider/` (public) and `server/private/routers/alertRule/`
|
||||
(enterprise-only), how this repo structures CRUD endpoints:
|
||||
|
||||
- Directory/file layout per entity (`index.ts`, `types.ts`, `validation.ts`,
|
||||
one file per operation).
|
||||
- The standard handler anatomy (zod parsing, OpenAPI registry, response
|
||||
envelope, error handling).
|
||||
- Where access-control middleware (`verify<Entity>Access`) lives and when
|
||||
it's needed vs. plain `verifyOrgAccess`.
|
||||
- How to wire up `ActionsEnum` entries, `verifyUserHasAction`, and
|
||||
`logActionAudit`.
|
||||
- Which of the four router files (`server/routers/external.ts`,
|
||||
`server/routers/internal.ts`, `server/private/routers/external.ts`,
|
||||
`server/private/routers/internal.ts`) to register routes in, and the
|
||||
middleware chain template per HTTP verb.
|
||||
- The repo's non-standard verb convention: **`PUT` = create, `POST` =
|
||||
update** (backwards from typical REST) — don't "fix" this to standard
|
||||
REST verbs, match the existing convention.
|
||||
- The `#dynamic` import alias, for the rare case of a hook needing different
|
||||
implementations in OSS vs. enterprise builds.
|
||||
|
||||
Follow that doc's checklist (§8) step by step rather than improvising a
|
||||
structure. If the doc and the actual code in `aiProvider`/`alertRule` ever
|
||||
disagree, trust the code and flag the doc as stale.
|
||||
@@ -34,6 +34,14 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: AI Disclosure
|
||||
description: |
|
||||
If you used AI to help write this issue, please disclose it here. This is important for transparency and helps maintain the integrity of the issue tracking process.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
|
||||
@@ -4,6 +4,10 @@ perpetual license to use, modify, and redistribute these contributions under any
|
||||
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.
|
||||
|
||||
## AI Disclosure
|
||||
|
||||
> Please disclose how AI was used in this pull request. The use of AI does not preclude this from being merged but is an important factor in how we review your request.
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
@@ -266,7 +266,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version: 1.25
|
||||
go-version: 1.26
|
||||
|
||||
- name: Update version in package.json
|
||||
run: |
|
||||
@@ -407,7 +407,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Login to GitHub Container Registry (for cosign)
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
||||
@@ -26,7 +26,10 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: '24'
|
||||
# Match the version the app ships on (Dockerfile /
|
||||
# Dockerfile.dev both use 24.18.1) rather than floating
|
||||
# to the latest 24.x.
|
||||
node-version: '24.18.1'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
|
||||
@@ -19,7 +19,13 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: '24'
|
||||
# Pin to the version the app actually ships on (Dockerfile /
|
||||
# Dockerfile.dev both use 24.18.1; .nvmrc pins the 24 major).
|
||||
# A bare '24' floats to the latest 24.x; Node >= 24.19.0 added
|
||||
# node::ObjectWrap cleanup hooks that crash better-sqlite3 on teardown
|
||||
# ("Assertion failed: (env) != nullptr" in RemoveEnvironmentCleanupHook),
|
||||
# which intermittently kills `npm run dev` in the step below.
|
||||
node-version: '24.18.1'
|
||||
|
||||
- name: Copy config file
|
||||
run: cp config/config.example.yml config/config.yml
|
||||
@@ -43,19 +49,34 @@ jobs:
|
||||
run: npx tsc --noEmit
|
||||
|
||||
- name: Start app in background
|
||||
run: nohup npm run dev &
|
||||
run: |
|
||||
nohup npm run dev > app.log 2>&1 &
|
||||
echo $! > app.pid
|
||||
|
||||
- name: Wait for app availability
|
||||
run: |
|
||||
print_log() {
|
||||
if [ -f app.log ]; then cat app.log; else echo "(app.log not found)"; fi
|
||||
}
|
||||
for i in {1..5}; do
|
||||
if curl --silent --fail http://localhost:3002/auth/login; then
|
||||
echo "App is up"
|
||||
exit 0
|
||||
fi
|
||||
# Fail fast (and show why) if the process is gone. Read the PID
|
||||
# safely: if it's missing/empty (e.g. nohup never started), treat
|
||||
# that as "not running" instead of passing a bad arg to kill.
|
||||
pid="$(cat app.pid 2>/dev/null || true)"
|
||||
if [ -z "$pid" ] || ! kill -0 "$pid" 2>/dev/null; then
|
||||
echo "App process exited before becoming available. Output:"
|
||||
print_log
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for the app... attempt $i"
|
||||
sleep 5
|
||||
done
|
||||
echo "App failed to start"
|
||||
echo "App failed to start. Output:"
|
||||
print_log
|
||||
exit 1
|
||||
|
||||
build-sqlite:
|
||||
|
||||
@@ -46,7 +46,6 @@ public/branding
|
||||
server/db/index.ts
|
||||
server/build.ts
|
||||
postgres/
|
||||
dynamic/
|
||||
*.mmdb
|
||||
scratch/
|
||||
tsconfig.json
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# typescript-eslint@8.70.0 declares a peer range of typescript "<6.1.0" and
|
||||
# hasn't caught up to typescript@7.x yet, even though it works fine against it
|
||||
# in practice. Without this, `npm install`/`npm ci` fail with ERESOLVE.
|
||||
legacy-peer-deps=true
|
||||
@@ -1,11 +1,11 @@
|
||||
# FROM node:24-slim AS base
|
||||
FROM public.ecr.aws/docker/library/node:24-slim AS base
|
||||
# FROM node:24.18.1-slim AS base
|
||||
FROM public.ecr.aws/docker/library/node:24.18.1-slim AS base
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY package*.json ./
|
||||
COPY package*.json .npmrc ./
|
||||
|
||||
FROM base AS builder-dev
|
||||
|
||||
@@ -32,8 +32,8 @@ FROM base AS builder
|
||||
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
# FROM node:24-slim AS runner
|
||||
FROM public.ecr.aws/docker/library/node:24-slim AS runner
|
||||
# FROM node:24.18.1-slim AS runner
|
||||
FROM public.ecr.aws/docker/library/node:24.18.1-slim AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
FROM node:24-alpine
|
||||
FROM node:24.18.1-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache python3 make g++
|
||||
|
||||
COPY package*.json ./
|
||||
COPY package*.json .npmrc ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
@@ -37,11 +37,22 @@
|
||||
|
||||
<p align="center">
|
||||
<strong>
|
||||
Get started with Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
|
||||
Get started with Pangolin Cloud at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
Pangolin is an open-source, identity-based remote access platform built on WireGuard® that enables secure connectivity to infrastructure anywhere. It combines reverse-proxy and VPN capabilities into one platform, providing browser-based access to web applications and client-based access to private resources with NAT traversal, all with granular access control.
|
||||
Pangolin is an open-source SASE platform, built on WireGuard®, with a simple mission: connect and protect your users, wherever they are. It brings networking and security together as one system including a zero-trust VPN, zero-trust reverse proxy, privileged access management, and an identity-aware AI gateway, all sharing one identity and policy model. It's the same idea behind platforms like Cloudflare One, Zscaler, and Prisma but open, self-hostable, and built to stay light and easy to deploy.
|
||||
|
||||
### Networking and security that's unified, open, and simple
|
||||
|
||||
Legacy SASE platforms got the idea right: connectivity and security belong together. But they delivered it as a heavyweight, closed, cloud-locked stack assembled from years of patchwork. Pangolin exists to do that unification differently, in the open, self-hostable, and simple enough that administrators actually enjoy running it.
|
||||
|
||||
* **Open source, not a black box**: the code is open and auditable, so you can see exactly how your traffic is handled and how access decisions get made, instead of trusting a closed cloud control plane.
|
||||
* **Networking and security as one platform**: sites, reverse proxy, client access, RBAC, and the AI gateway share one identity and policy model, so protecting users and connecting them are executed together.
|
||||
* **Lightweight by design**: the whole platform is built to stay small and fast: easy to self-host on a small server, with a lightweight, user-space connector that goes in your private networks.
|
||||
* **Enjoyable to use**: a clean, modern interface and a setup flow that gets out of your way, so managing access feels simple instead of like fighting a legacy admin console.
|
||||
* **Zero trust from day one**: access is granted per resource, not per network, with identity provider integration, role-based access control, and full audit logging.
|
||||
* **Run it your way**: self-host the Community Edition for free, step up to the Enterprise Edition for advanced features, or use Pangolin Cloud if you'd rather not manage infrastructure at all.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -53,9 +64,9 @@ Pangolin is an open-source, identity-based remote access platform built on WireG
|
||||
|
||||
## Deployment Options
|
||||
|
||||
- **Pangolin Cloud** - Fully managed service - no infrastructure required.
|
||||
- **Self-Host: Community Edition** - Free, open source, and licensed under AGPL-3.
|
||||
- **Self-Host: Enterprise Edition** - Licensed under Fossorial Commercial License. Free for personal and hobbyist use, and for businesses making less than \$100K USD gross annual revenue.
|
||||
- **Pangolin Cloud** - Fully managed service with no infrastructure required.
|
||||
- **Self-Host: Community Edition** - Free, open-source, and licensed under AGPL-3.
|
||||
- **Self-Host: Enterprise Edition** - Open-core, and licensed under Fossorial Commercial License. Free for personal and hobbyist use, and for businesses making less than \$100K USD gross annual revenue.
|
||||
|
||||
## Key Features
|
||||
|
||||
@@ -99,6 +110,19 @@ Access private resources like SSH servers, databases, RDP, and entire network ra
|
||||
|
||||
<img src="public/screenshots/private-resources.png" alt="Private resources" width="100%" />
|
||||
|
||||
### Identity-aware AI gateway
|
||||
|
||||
Put an identity-aware proxy in front of public cloud (OpenAI, Anthropic, Gemini, etc.) and self-hosted model servers (Ollama, vLLM, Mistral, etc.) so coding agents and AI clients call a single Pangolin URL. Publish it as a public resource with personal API keys, or keep it private on a client tunnel where the connected client is the credential for keyless access. Budgets, session history, and usage analytics sit in front of every call.
|
||||
|
||||
* Access self-hosted models (vLLM, Ollama, etc) alongside cloud models (OpenAI, Anthropic, etc) in one place
|
||||
* Keyless access by authenticating users with the Pangolin desktop client
|
||||
* Or, provide users with personal API keys
|
||||
* Control costs and token usage by setting budgets
|
||||
* Audit with detailed session history and analytics
|
||||
* Integrate AI clients and coding agents (Claude Code, Codex, OpenCode, etc)
|
||||
|
||||
<img src="public/screenshots/expanded-session-logs.png" alt="AI Session Logs" width="100%" />
|
||||
|
||||
### Give users and roles access to resources
|
||||
|
||||
Use Pangolin's built-in users or bring your own identity provider and set up role-based access control (RBAC). Grant users access to specific resources, not entire networks. Unlike traditional VPNs that expose full network access, Pangolin's zero-trust model ensures users can only reach the applications, services, and routes you explicitly define.
|
||||
@@ -118,6 +142,7 @@ Give users a landing page to quickly find and open the resources they can access
|
||||
* Create reusable views for common access patterns
|
||||
|
||||
<img src="public/screenshots/resource-launcher.png" alt="Resource Launcher" width="100%" />
|
||||
<img src="public/screenshots/resource-launcher-expanded.png" alt="Resource Launcher Details Panel" width="100%" />
|
||||
|
||||
## Download Clients
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export const clearExitNodes: CommandModule<
|
||||
// Delete all exit nodes
|
||||
const deletedCount = await db
|
||||
.delete(exitNodes)
|
||||
.where(eq(exitNodes.exitNodeId, exitNodes.exitNodeId)) .returning();; // delete all
|
||||
.where(eq(exitNodes.exitNodeId, exitNodes.exitNodeId)).returning();; // delete all
|
||||
|
||||
console.log(`Deleted ${deletedCount.length} exit node(s) from the database`);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { encrypt } from "@server/lib/crypto";
|
||||
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
|
||||
import { generateCA } from "@server/lib/sshCA";
|
||||
import fs from "fs";
|
||||
import yaml from "js-yaml";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
type GenerateOrgCaKeysArgs = {
|
||||
orgId: string;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { CommandModule } from "yargs";
|
||||
import { db, idpOidcConfig, licenseKey, certificates, eventStreamingDestinations, alertWebhookActions } from "@server/db";
|
||||
import { db, idpOidcConfig, licenseKey, certificates, eventStreamingDestinations, alertWebhookActions, aiProviders, virtualApiKeys } from "@server/db";
|
||||
import { encrypt, decrypt } from "@server/lib/crypto";
|
||||
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
|
||||
import { eq } from "drizzle-orm";
|
||||
import fs from "fs";
|
||||
import yaml from "js-yaml";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
type RotateServerSecretArgs = {
|
||||
"old-secret": string;
|
||||
@@ -132,12 +132,16 @@ export const rotateServerSecret: CommandModule<
|
||||
const certs = await db.select().from(certificates);
|
||||
const streamingDestinations = await db.select().from(eventStreamingDestinations);
|
||||
const webhookActions = await db.select().from(alertWebhookActions);
|
||||
const providers = await db.select().from(aiProviders);
|
||||
const virtualKeys = await db.select().from(virtualApiKeys);
|
||||
|
||||
console.log(`Found ${idpConfigs.length} OIDC IdP configuration(s)`);
|
||||
console.log(`Found ${licenseKeys.length} license key(s)`);
|
||||
console.log(`Found ${certs.length} certificate(s)`);
|
||||
console.log(`Found ${streamingDestinations.length} event streaming destination(s)`);
|
||||
console.log(`Found ${webhookActions.length} alert webhook action(s)`);
|
||||
console.log(`Found ${providers.length} AI provider(s)`);
|
||||
console.log(`Found ${virtualKeys.length} virtual API key(s)`);
|
||||
|
||||
// Prepare all decrypted and re-encrypted values
|
||||
console.log("\nDecrypting and re-encrypting values...");
|
||||
@@ -171,11 +175,24 @@ export const rotateServerSecret: CommandModule<
|
||||
encryptedConfig: string;
|
||||
};
|
||||
|
||||
type AiProviderUpdate = {
|
||||
providerId: number;
|
||||
encryptedApiKey: string | null;
|
||||
encryptedHeaders: string | null;
|
||||
};
|
||||
|
||||
type VirtualApiKeyUpdate = {
|
||||
virtualApiKeyId: string;
|
||||
encryptedToken: string;
|
||||
};
|
||||
|
||||
const idpUpdates: IdpUpdate[] = [];
|
||||
const licenseKeyUpdates: LicenseKeyUpdate[] = [];
|
||||
const certUpdates: CertUpdate[] = [];
|
||||
const streamingDestinationUpdates: StreamingDestinationUpdate[] = [];
|
||||
const webhookActionUpdates: WebhookActionUpdate[] = [];
|
||||
const aiProviderUpdates: AiProviderUpdate[] = [];
|
||||
const virtualApiKeyUpdates: VirtualApiKeyUpdate[] = [];
|
||||
|
||||
// Process idpOidcConfig entries
|
||||
for (const idpConfig of idpConfigs) {
|
||||
@@ -306,6 +323,60 @@ export const rotateServerSecret: CommandModule<
|
||||
}
|
||||
}
|
||||
|
||||
// Process aiProviders entries (apiKey + headers)
|
||||
for (const provider of providers) {
|
||||
try {
|
||||
if (!provider.apiKey && !provider.headers) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const encryptedApiKey = provider.apiKey
|
||||
? encrypt(decrypt(provider.apiKey, oldSecret), newSecret)
|
||||
: null;
|
||||
const encryptedHeaders = provider.headers
|
||||
? encrypt(
|
||||
decrypt(provider.headers, oldSecret),
|
||||
newSecret
|
||||
)
|
||||
: null;
|
||||
|
||||
aiProviderUpdates.push({
|
||||
providerId: provider.providerId,
|
||||
encryptedApiKey,
|
||||
encryptedHeaders
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error processing AI provider ${provider.providerId}:`,
|
||||
error
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Process virtualApiKeys entries (token)
|
||||
for (const key of virtualKeys) {
|
||||
try {
|
||||
if (!key.token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
virtualApiKeyUpdates.push({
|
||||
virtualApiKeyId: key.virtualApiKeyId,
|
||||
encryptedToken: encrypt(
|
||||
decrypt(key.token, oldSecret),
|
||||
newSecret
|
||||
)
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error processing virtual API key ${key.virtualApiKeyId}:`,
|
||||
error
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform all database updates in a single transaction
|
||||
console.log("\nUpdating database in transaction...");
|
||||
await db.transaction(async (trx) => {
|
||||
@@ -376,6 +447,32 @@ export const rotateServerSecret: CommandModule<
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Update AI provider entries
|
||||
for (const update of aiProviderUpdates) {
|
||||
await trx
|
||||
.update(aiProviders)
|
||||
.set({
|
||||
apiKey: update.encryptedApiKey,
|
||||
headers: update.encryptedHeaders
|
||||
})
|
||||
.where(eq(aiProviders.providerId, update.providerId));
|
||||
}
|
||||
|
||||
// Update virtual API key entries
|
||||
for (const update of virtualApiKeyUpdates) {
|
||||
await trx
|
||||
.update(virtualApiKeys)
|
||||
.set({
|
||||
token: update.encryptedToken
|
||||
})
|
||||
.where(
|
||||
eq(
|
||||
virtualApiKeys.virtualApiKeyId,
|
||||
update.virtualApiKeyId
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Rotated ${idpUpdates.length} OIDC IdP configuration(s)`);
|
||||
@@ -383,6 +480,8 @@ export const rotateServerSecret: CommandModule<
|
||||
console.log(`Rotated ${certUpdates.length} certificate(s)`);
|
||||
console.log(`Rotated ${streamingDestinationUpdates.length} event streaming destination(s)`);
|
||||
console.log(`Rotated ${webhookActionUpdates.length} alert webhook action(s)`);
|
||||
console.log(`Rotated ${aiProviderUpdates.length} AI provider(s)`);
|
||||
console.log(`Rotated ${virtualApiKeyUpdates.length} virtual API key(s)`);
|
||||
|
||||
// Update config file with new secret
|
||||
console.log("\nUpdating config file...");
|
||||
@@ -402,6 +501,7 @@ export const rotateServerSecret: CommandModule<
|
||||
console.log(` - Certificates: ${certUpdates.length}`);
|
||||
console.log(` - Event streaming destinations: ${streamingDestinationUpdates.length}`);
|
||||
console.log(` - Alert webhook actions: ${webhookActionUpdates.length}`);
|
||||
console.log(` - AI providers: ${aiProviderUpdates.length}`);
|
||||
console.log(
|
||||
`\n IMPORTANT: Restart the server for the new secret to take effect.`
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ services:
|
||||
POSTGRES_USER: postgres # Default user
|
||||
POSTGRES_PASSWORD: password # Default password (change for production!)
|
||||
volumes:
|
||||
- ./config/postgres:/var/lib/postgresql/data
|
||||
- ${HOME}/.local/share/pangolin-dev/postgres:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432" # Map host port 5432 to container port 5432
|
||||
restart: no
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## Example Docker Reference HA Deployment
|
||||
|
||||
This directory contains a basic reference config for a highly available deployment of Pangolin with two nodes. For more information [refer to the docs](https://docs.pangolin.net/self-host/clustering/understanding-clustering).
|
||||
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: postgres
|
||||
environment:
|
||||
POSTGRES_DB: postgres # Default database name
|
||||
POSTGRES_USER: postgres # Default user
|
||||
POSTGRES_PASSWORD: password # Default password (change for production!)
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
@@ -0,0 +1,38 @@
|
||||
# To see all available options, please visit the docs:
|
||||
# https://docs.pangolin.net/
|
||||
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "<THIS_NODE_EXTERNAL_IP>"
|
||||
exit_node_name: "node1"
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com"
|
||||
log_level: "info"
|
||||
|
||||
postgres:
|
||||
connection_string: postgresql://<POSTGRES_USERNAME>:<POSTGRES_PASSWORD>@<POSTGRES_INTERNAL_HOST>:5432/postgres
|
||||
|
||||
traefik:
|
||||
site_types: ["newt"] # Wireguard and local sites are not support in clustering
|
||||
file_mode: true # Pangolin will generate and save yaml files in a shared volume
|
||||
|
||||
server:
|
||||
secret: "<SECRET>"
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"]
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||
credentials: false
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb" # Make sure to download and place into the config dir
|
||||
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
|
||||
|
||||
flags:
|
||||
require_email_verification: false
|
||||
disable_signup_without_invite: true
|
||||
disable_user_create_org: false
|
||||
allow_raw_resources: false
|
||||
enable_acme_cert_sync: false
|
||||
disable_local_sites: true
|
||||
disable_basic_wireguard_sites: true
|
||||
disable_config_managed_domains: true
|
||||
@@ -0,0 +1,67 @@
|
||||
http:
|
||||
middlewares:
|
||||
badger:
|
||||
plugin:
|
||||
badger:
|
||||
disableForwardAuth: true
|
||||
|
||||
routers:
|
||||
# Next.js router (handles everything except API and WebSocket paths)
|
||||
next-router:
|
||||
rule: "!PathPrefix(`/api/v1`)"
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
# API router (handles /api/v1 paths)
|
||||
api-router:
|
||||
rule: "PathPrefix(`/api/v1`)"
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
# WebSocket router
|
||||
ws-router:
|
||||
rule: "PathPrefix(`/`)"
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002" # Next.js server
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000" # API/WebSocket server
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
|
||||
udp:
|
||||
routers:
|
||||
dns-router:
|
||||
entryPoints:
|
||||
- dns
|
||||
service: dns-service
|
||||
|
||||
services:
|
||||
dns-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- address: "pangolin:53"
|
||||
@@ -0,0 +1,18 @@
|
||||
app:
|
||||
region: "region1"
|
||||
identity_provider_mode: "org"
|
||||
redis:
|
||||
host: "<REDIS_INTERNAL_HOST>"
|
||||
port: 6379
|
||||
flags:
|
||||
enable_redis: true
|
||||
use_pangolin_dns: true
|
||||
acme:
|
||||
cert_mode: "pangolin"
|
||||
contact_email: "<CONTACT_EMAIL>"
|
||||
enable_acme_client: true
|
||||
dns:
|
||||
enabled: true
|
||||
nameserver_name: "ns.example.com"
|
||||
cname_extension: "cname.example.com"
|
||||
site_extension: "site.example.com" # Optional
|
||||
@@ -0,0 +1,45 @@
|
||||
providers:
|
||||
file:
|
||||
directory: "/var/dynamic"
|
||||
watch: true
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.7.0"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
proxyProtocol: # We trust gerbil upstream
|
||||
trustedIPs:
|
||||
- 0.0.0.0/0
|
||||
- ::1/128
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
dashboard:
|
||||
address: ":3000"
|
||||
dns:
|
||||
address: ":53/udp"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
@@ -0,0 +1,62 @@
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: docker.io/fosrl/pangolin:ee-latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./config/certificates:/var/certificates
|
||||
- ./config/dynamic:/var/dynamic
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: docker.io/fosrl/gerbil:latest
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://<NODE1_INTERNAL_IP>:3004
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||
- --trusted-upstreams=<NODE1_EXTERNAL_IP>,<NODE2_EXTERNAL_IP>
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
ports:
|
||||
- 51820:51820/udp # wireguard
|
||||
- 21820:21820/udp # relay
|
||||
- 53:53/udp # DNS
|
||||
- 443:8443 # resources
|
||||
- 80:80 # web
|
||||
- 3004:3004 # gerbil api
|
||||
- 3000:3000 # Pangolin UI
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7.11
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: service:gerbil # Ports appear on the gerbil service
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
- ./config/certificates:/var/certificates:ro
|
||||
- ./config/dynamic:/var/dynamic:ro
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
@@ -0,0 +1,38 @@
|
||||
# To see all available options, please visit the docs:
|
||||
# https://docs.pangolin.net/
|
||||
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "<THIS_NODE_EXTERNAL_IP>"
|
||||
exit_node_name: "node2"
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com"
|
||||
log_level: "info"
|
||||
|
||||
postgres:
|
||||
connection_string: postgresql://<POSTGRES_USERNAME>:<POSTGRES_PASSWORD>@<POSTGRES_INTERNAL_HOST>:5432/postgres
|
||||
|
||||
traefik:
|
||||
site_types: ["newt"] # Wireguard and local sites are not support in clustering
|
||||
file_mode: true # Pangolin will generate and save yaml files in a shared volume
|
||||
|
||||
server:
|
||||
secret: "<SECRET>"
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"]
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||
credentials: false
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb" # Make sure to download and place into the config dir
|
||||
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
|
||||
|
||||
flags:
|
||||
require_email_verification: false
|
||||
disable_signup_without_invite: true
|
||||
disable_user_create_org: false
|
||||
allow_raw_resources: false
|
||||
enable_acme_cert_sync: false
|
||||
disable_local_sites: true
|
||||
disable_basic_wireguard_sites: true
|
||||
disable_config_managed_domains: true
|
||||
@@ -0,0 +1,67 @@
|
||||
http:
|
||||
middlewares:
|
||||
badger:
|
||||
plugin:
|
||||
badger:
|
||||
disableForwardAuth: true
|
||||
|
||||
routers:
|
||||
# Next.js router (handles everything except API and WebSocket paths)
|
||||
next-router:
|
||||
rule: "!PathPrefix(`/api/v1`)"
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
# API router (handles /api/v1 paths)
|
||||
api-router:
|
||||
rule: "PathPrefix(`/api/v1`)"
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
# WebSocket router
|
||||
ws-router:
|
||||
rule: "PathPrefix(`/`)"
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002" # Next.js server
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000" # API/WebSocket server
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
|
||||
udp:
|
||||
routers:
|
||||
dns-router:
|
||||
entryPoints:
|
||||
- dns
|
||||
service: dns-service
|
||||
|
||||
services:
|
||||
dns-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- address: "pangolin:53"
|
||||
@@ -0,0 +1,16 @@
|
||||
app:
|
||||
region: "region1"
|
||||
identity_provider_mode: "org"
|
||||
redis:
|
||||
host: "<REDIS_INTERNAL_HOST>"
|
||||
port: 6379
|
||||
flags:
|
||||
enable_redis: true
|
||||
use_pangolin_dns: true
|
||||
acme:
|
||||
cert_mode: "pangolin"
|
||||
dns:
|
||||
enabled: true
|
||||
nameserver_name: "ns.example.com"
|
||||
cname_extension: "cname.example.com"
|
||||
site_extension: "site.example.com" # Optional
|
||||
@@ -0,0 +1,45 @@
|
||||
providers:
|
||||
file:
|
||||
directory: "/var/dynamic"
|
||||
watch: true
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.7.0"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
proxyProtocol: # We trust gerbil upstream
|
||||
trustedIPs:
|
||||
- 0.0.0.0/0
|
||||
- ::1/128
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
dashboard:
|
||||
address: ":3000"
|
||||
dns:
|
||||
address: ":53/udp"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
@@ -0,0 +1,62 @@
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: docker.io/fosrl/pangolin:ee-latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./config/certificates:/var/certificates
|
||||
- ./config/dynamic:/var/dynamic
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: docker.io/fosrl/gerbil:latest
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://<NODE1_INTERNAL_IP>:3004
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||
- --trusted-upstreams=<NODE1_EXTERNAL_IP>,<NODE2_EXTERNAL_IP>
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
ports:
|
||||
- 51820:51820/udp # wireguard
|
||||
- 21820:21820/udp # relay
|
||||
- 53:53/udp # DNS
|
||||
- 443:8443 # resources
|
||||
- 80:80 # web
|
||||
- 3004:3004 # gerbil api
|
||||
- 3000:3000 # Pangolin UI
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7.11
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: service:gerbil # Ports appear on the gerbil service
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
- ./config/certificates:/var/certificates:ro
|
||||
- ./config/dynamic:/var/dynamic:ro
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
@@ -1,3 +1,3 @@
|
||||
files:
|
||||
- source: /messages/en-US.json
|
||||
translation: /messages/%locale%.json
|
||||
translation: /messages/%locale%.json
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
# AI Gateway Provider Selection
|
||||
|
||||
How the AI gateway picks which attached provider handles a request when an
|
||||
inference resource has more than one AI provider.
|
||||
|
||||
**Code:**
|
||||
|
||||
- Route → capability binding: `server/routers/aiGateway/createAiGatewayRouter.ts`
|
||||
- Request pipeline: `server/routers/aiGateway/pipeline.ts` (`selectProvider`)
|
||||
- Model discovery: `server/routers/aiGateway/v1Models.ts` and
|
||||
`server/lib/aiModelDiscovery.ts`
|
||||
- Tie-break scoring: `server/lib/aiProviderSelection.ts`
|
||||
- Allow/block matching: `server/lib/aiModelKeyMatch.ts`
|
||||
- Model catalog: `server/lib/aiModelCatalog.ts`
|
||||
- Default capabilities per provider type: `server/lib/aiProviderDefaults.ts`
|
||||
|
||||
Overlapping model allows are permitted at save time. Selection happens at
|
||||
request time. If the algorithm cannot confidently pick one provider, the
|
||||
gateway returns `403` with an ambiguous-provider error.
|
||||
|
||||
## Selection Pipeline
|
||||
|
||||
Every gateway request runs through these steps in order. Each step narrows
|
||||
the candidate set. Later steps only run when more than one provider remains.
|
||||
|
||||
```
|
||||
1. Capability filter
|
||||
2. Allow / block lists
|
||||
3. Most specific allow pattern
|
||||
4. Catalog ownership
|
||||
5. Provider class preference
|
||||
6. Ambiguous → error
|
||||
```
|
||||
|
||||
### 1. Capability Filter
|
||||
|
||||
The incoming path selects a capability before any provider logic runs.
|
||||
|
||||
| Path | Capability |
|
||||
|------|------------|
|
||||
| `POST /v1/chat/completions` | `openai_chat` |
|
||||
| `POST /v1/responses` | `openai_responses` |
|
||||
| `POST /v1/messages` | `anthropic_messages` |
|
||||
| `GET /v1/models`, `GET /v1/models/{id}` | `v1_models` |
|
||||
| Gemini / Vertex / Bedrock routes | their respective capability ids |
|
||||
|
||||
Only attached providers that advertise that capability stay in the candidate
|
||||
set. Default capabilities do not overlap for native OpenAI vs Anthropic:
|
||||
|
||||
| Provider type | Default capabilities |
|
||||
|---------------|----------------------|
|
||||
| `openai` | `openai_chat`, `openai_responses` |
|
||||
| `anthropic` | `anthropic_messages`, `v1_models` |
|
||||
| `openRouter` | `openai_chat` |
|
||||
| `vercelAiGateway` | `openai_chat`, `openai_responses` |
|
||||
| `microsoftFoundry` | `openai_chat`, `openai_responses`, `anthropic_messages`, `v1_models` |
|
||||
| `custom` | whatever was configured |
|
||||
|
||||
### 2. Allow / Block Lists
|
||||
|
||||
For each remaining provider, the gateway resolves the effective allow and
|
||||
block patterns:
|
||||
|
||||
- **`inherit`**: use the provider's own model lists
|
||||
- **`select`**: use the resource-selected subset of those lists
|
||||
|
||||
A candidate is kept only if `isAllowedByLists(requestedModel, allows, blocks)`
|
||||
passes:
|
||||
|
||||
1. At least one allow pattern must match
|
||||
2. No block pattern may match
|
||||
|
||||
Patterns support `*` and `?` globs (`gpt-*`, `claude-3-5-sonnet-?`).
|
||||
|
||||
### 3. Most Specific Allow Pattern
|
||||
|
||||
Among providers that allow the model, keep those whose matching allow
|
||||
pattern is most specific:
|
||||
|
||||
1. Exact keys beat patterns
|
||||
2. Fewer wildcard characters win
|
||||
3. Longer literal length wins
|
||||
|
||||
Example: `gpt-4o` beats `gpt-*` beats `*`.
|
||||
|
||||
### 4. Catalog Ownership
|
||||
|
||||
When specificity is tied (common with multiple `*` allows), score each
|
||||
provider against the known model catalog:
|
||||
|
||||
| Score | Meaning |
|
||||
|------:|---------|
|
||||
| 2 | Typed provider whose catalog contains the model (`openai` → openai catalog, `anthropic` → anthropic, etc.) |
|
||||
| 1 | Aggregator or custom (`openRouter`, `vercelAiGateway`, `custom`) and the model exists somewhere in the catalog |
|
||||
| 0 | No ownership signal (typed catalog miss, or unknown model on aggregator/custom) |
|
||||
|
||||
Model id lookup tries the raw id, then a stripped `vendor/model` form
|
||||
(e.g. `openai/gpt-4o` → also try `gpt-4o`).
|
||||
|
||||
Typed providers map to catalog providers as:
|
||||
|
||||
| Provider type | Catalog |
|
||||
|---------------|---------|
|
||||
| `openai` | `openai` |
|
||||
| `anthropic` | `anthropic` |
|
||||
| `googleGemini` | `gemini` |
|
||||
| `vertexAi` | `vertex` |
|
||||
| `bedrock` | `bedrock` |
|
||||
| `microsoftFoundry` | `azure` |
|
||||
| `openRouter` / `vercelAiGateway` / `custom` | none (aggregator/custom path) |
|
||||
|
||||
### 5. Provider Class Preference
|
||||
|
||||
If catalog ownership is still tied, prefer:
|
||||
|
||||
| Rank | Class |
|
||||
|-----:|-------|
|
||||
| 2 | Native typed provider (`openai`, `anthropic`, `googleGemini`, …) |
|
||||
| 1 | Aggregator (`openRouter`, `vercelAiGateway`) |
|
||||
| 0 | `custom` |
|
||||
|
||||
### 6. Ambiguous Error
|
||||
|
||||
If more than one distinct provider remains after all steps, the gateway
|
||||
rejects the request:
|
||||
|
||||
```
|
||||
Model "<id>" is ambiguous across multiple AI providers on this resource
|
||||
```
|
||||
|
||||
Typical remaining ties: two OpenAI-type providers both with `*`, or two
|
||||
customs advertising the same capability for an unknown model.
|
||||
|
||||
## Model Discovery Is Not Selection
|
||||
|
||||
`GET /v1/models` and `GET /v1/models/{id}` (`v1_models`) skip steps 3-6
|
||||
entirely. There is no requested model to disambiguate on, so the gateway does
|
||||
not pick one provider - it returns the **union** of what every attached
|
||||
provider advertising `v1_models` would accept, deduplicated by model id
|
||||
(lowest `providerId` wins a collision).
|
||||
|
||||
Discovery is answered from the gateway's own view of the allow/block lists,
|
||||
never proxied upstream. Providers that expose no `/v1/models` endpoint of their
|
||||
own still get a working listing, and a model an allow/block list forbids is
|
||||
never advertised.
|
||||
|
||||
Each provider's candidate ids come from two places:
|
||||
|
||||
| Source | Contributes |
|
||||
|--------|-------------|
|
||||
| Exact (non-wildcard) allow entries | the model key itself |
|
||||
| The model catalog for the provider's type | every catalog id matching an allow pattern |
|
||||
|
||||
Both sources are then filtered through the same
|
||||
`isAllowedByLists(id, allows, blocks)` check step 2 applies, so a block pattern
|
||||
hides a model from discovery exactly as it would reject it at request time.
|
||||
|
||||
The catalog source is what makes a wildcard allow such as `claude-*`
|
||||
enumerable. Provider types with no catalog mapping (`openRouter`,
|
||||
`vercelAiGateway`, `custom`) have nothing to expand against, so a wildcard
|
||||
allow on those types lists nothing - **add exact allow entries to make their
|
||||
models discoverable.**
|
||||
|
||||
### Where each field comes from
|
||||
|
||||
Token limits and capability flags can't be derived from an allow/block list.
|
||||
They come from the model catalog (`server/lib/aiModelCatalog.ts`), which the
|
||||
Fossorial API builds from LiteLLM:
|
||||
|
||||
| Field | Source |
|
||||
|-------|--------|
|
||||
| `max_input_tokens` | catalog `limits.input` |
|
||||
| `max_tokens` | catalog `limits.output` |
|
||||
| `capabilities` | catalog flags, mapped to the Models API shape by `capabilitiesFromCatalog` |
|
||||
| `display_name` | the configured model row's name, else the model id |
|
||||
| `created_at` | the configured model row's timestamp, else the epoch |
|
||||
|
||||
A model the catalog doesn't know (an exact allow entry for a fine-tune, say)
|
||||
reports `null` for all three metadata fields. The Models API declares them
|
||||
nullable, so that is a valid answer rather than a broken one.
|
||||
|
||||
The catalog's flags are coarser than the Models API describes: it carries a
|
||||
single `reasoning` flag with no way to distinguish adaptive from
|
||||
`budget_tokens`-style thinking, and nothing at all for batch, citations, code
|
||||
execution, PDF input, or context management. Anything it reports as unknown
|
||||
(`null`) is surfaced as unsupported rather than invented, so `capabilities`
|
||||
understates rather than overstates what a model can do.
|
||||
|
||||
The gateway does **not** query the provider's own `/v1/models`. Discovery is
|
||||
answered entirely from local state.
|
||||
|
||||
Results are ordered newest-first with the id as tie-break, and paginated with
|
||||
Anthropic's `limit` / `after_id` / `before_id` semantics (default 20, max
|
||||
1000).
|
||||
|
||||
## Examples
|
||||
|
||||
Assume each provider below is attached and enabled on the same inference
|
||||
resource.
|
||||
|
||||
### Example A: OpenAI + Anthropic, Both `*`
|
||||
|
||||
| Provider | Allow | Capabilities |
|
||||
|----------|-------|--------------|
|
||||
| OpenAI | `*` | `openai_chat`, `openai_responses` |
|
||||
| Anthropic | `*` | `anthropic_messages` |
|
||||
|
||||
**Request:** `POST /v1/chat/completions` with `model: "gpt-4o"`
|
||||
|
||||
1. Capability → only OpenAI remains
|
||||
2. Allow → OpenAI matches `*`
|
||||
3. Result → **OpenAI**
|
||||
|
||||
Anthropic never reaches pattern or catalog scoring. Capability alone decides.
|
||||
|
||||
**Request:** `POST /v1/messages` with `model: "claude-3-5-sonnet-latest"`
|
||||
|
||||
1. Capability → only Anthropic remains
|
||||
2. Result → **Anthropic**
|
||||
|
||||
### Example B: OpenAI + OpenRouter, Both `*`
|
||||
|
||||
| Provider | Allow | Capabilities |
|
||||
|----------|-------|--------------|
|
||||
| OpenAI | `*` | `openai_chat`, … |
|
||||
| OpenRouter | `*` | `openai_chat` |
|
||||
|
||||
**Request:** `POST /v1/chat/completions` with `model: "gpt-4o"`
|
||||
|
||||
1. Capability → both remain (`openai_chat`)
|
||||
2. Allow → both match `*`
|
||||
3. Specificity → tie (`*` vs `*`)
|
||||
4. Catalog → OpenAI scores `2` (owns `gpt-4o`); OpenRouter scores `1`
|
||||
5. Result → **OpenAI**
|
||||
|
||||
### Example C: OpenRouter Only Serving a Claude Model Over OpenAI Chat
|
||||
|
||||
| Provider | Allow | Capabilities |
|
||||
|----------|-------|--------------|
|
||||
| OpenRouter | `*` | `openai_chat` |
|
||||
|
||||
**Request:** `POST /v1/chat/completions` with `model: "anthropic/claude-3.5-sonnet"`
|
||||
|
||||
1. Capability → OpenRouter remains
|
||||
2. Only one candidate → **OpenRouter**
|
||||
|
||||
No tie-breaking needed.
|
||||
|
||||
### Example D: OpenAI (`gpt-*`) + OpenRouter (`*`)
|
||||
|
||||
| Provider | Allow |
|
||||
|----------|-------|
|
||||
| OpenAI | `gpt-*` |
|
||||
| OpenRouter | `*` |
|
||||
|
||||
**Request:** `model: "gpt-4o"` on `openai_chat`
|
||||
|
||||
1. Capability → both
|
||||
2. Allow → both match
|
||||
3. Specificity → OpenAI's `gpt-*` beats OpenRouter's `*`
|
||||
4. Result → **OpenAI**
|
||||
|
||||
Catalog scoring is not needed because specificity already unique'd the set.
|
||||
|
||||
### Example E: OpenAI + Anthropic With Overlapping Custom Capabilities
|
||||
|
||||
Someone grants Anthropic `openai_chat` as well (non-default).
|
||||
|
||||
| Provider | Allow | Capabilities |
|
||||
|----------|-------|--------------|
|
||||
| OpenAI | `*` | `openai_chat`, … |
|
||||
| Anthropic | `*` | `anthropic_messages`, `openai_chat` |
|
||||
|
||||
**Request:** `POST /v1/chat/completions` with `model: "gpt-4o"`
|
||||
|
||||
1. Capability → both remain
|
||||
2. Allow → both match `*`
|
||||
3. Specificity → tie
|
||||
4. Catalog → OpenAI `2`, Anthropic `0` (`gpt-4o` is not in the anthropic catalog)
|
||||
5. Result → **OpenAI**
|
||||
|
||||
### Example F: Two Aggregators, Known Model
|
||||
|
||||
| Provider | Allow |
|
||||
|----------|-------|
|
||||
| OpenRouter | `*` |
|
||||
| Vercel AI Gateway | `*` |
|
||||
|
||||
**Request:** `model: "gpt-4o"` on `openai_chat`
|
||||
|
||||
1. Capability → both
|
||||
2. Allow / specificity → tie
|
||||
3. Catalog → both score `1` (known model, no typed owner in the set)
|
||||
4. Class → both aggregators (rank `1`) → still tied
|
||||
5. Result → **ambiguous error**
|
||||
|
||||
Attach a native OpenAI provider (or narrow one aggregator's allow list) to
|
||||
make this determinable.
|
||||
|
||||
### Example G: Two OpenAI Providers, Both `*`
|
||||
|
||||
| Provider | Type | Allow |
|
||||
|----------|------|-------|
|
||||
| OpenAI Prod | `openai` | `*` |
|
||||
| OpenAI Staging | `openai` | `*` |
|
||||
|
||||
**Request:** `model: "gpt-4o"`
|
||||
|
||||
1–5 all leave both candidates (same capability, same specificity, same
|
||||
catalog ownership, same class).
|
||||
|
||||
Result → **ambiguous error**
|
||||
|
||||
Disambiguate with different allow patterns, disable one attachment, or
|
||||
split across resources.
|
||||
|
||||
### Example H: Unknown Model Across Native + Aggregator
|
||||
|
||||
| Provider | Allow |
|
||||
|----------|-------|
|
||||
| OpenAI | `*` |
|
||||
| OpenRouter | `*` |
|
||||
|
||||
**Request:** `model: "my-fine-tune-v3"` (not in catalog)
|
||||
|
||||
1. Capability → both
|
||||
2. Allow / specificity → tie
|
||||
3. Catalog → both score `0` (typed miss + unknown aggregator model)
|
||||
4. Class → OpenAI (`2`) beats OpenRouter (`1`)
|
||||
5. Result → **OpenAI**
|
||||
|
||||
## Practical Guidance
|
||||
|
||||
- Native OpenAI + Anthropic with `*` is safe. Different default APIs never
|
||||
collide.
|
||||
- OpenAI + OpenRouter with `*` is usually fine for catalog-known OpenAI
|
||||
models. Native wins.
|
||||
- Prefer specific allow patterns (`gpt-4o`, `gpt-*`) when two providers share
|
||||
a capability.
|
||||
- Two providers of the same type both using `*` will stay ambiguous. Narrow
|
||||
at least one allow list.
|
||||
- Custom providers only win ties when no stronger native/aggregator signal
|
||||
remains.
|
||||
|
||||
## Related Behavior
|
||||
|
||||
- **Saving providers on a resource does not reject overlapping allows.**
|
||||
Collisions are resolved (or rejected) per request.
|
||||
- Budgets, auth, and upstream URL / target routing run after a single
|
||||
provider has been selected.
|
||||
@@ -0,0 +1,347 @@
|
||||
# How to build a CRUD endpoint in this repo
|
||||
|
||||
Reference for adding a new CRUD entity to the server. Based on two real
|
||||
examples already in the codebase — read them side by side with this doc:
|
||||
|
||||
- **Public / open-source (Community Edition) pattern**: `server/routers/aiProvider/`
|
||||
- **Enterprise-only pattern**: `server/private/routers/alertRule/`
|
||||
|
||||
The two are structurally identical. The only difference is *where the files
|
||||
live* and *which router they get wired into*.
|
||||
|
||||
## 1. Decide: public or private?
|
||||
|
||||
- `server/routers/<entity>/` — ships in the open-source Community Edition.
|
||||
Anyone running Pangolin gets this.
|
||||
- `server/private/routers/<entity>/` — Enterprise/SaaS only. Gated behind
|
||||
`verifyValidLicense` (and often `verifyValidSubscription(tierMatrix.x)`).
|
||||
Every file here starts with the Fossorial Commercial License header block
|
||||
(copy it verbatim from an existing private file).
|
||||
|
||||
Everything below applies to both — swap `@server/...` for `#private/...`
|
||||
import paths and add license headers when building the private version.
|
||||
|
||||
## 2. Directory layout
|
||||
|
||||
One folder per entity, one file per operation, a barrel `index.ts`:
|
||||
|
||||
```
|
||||
server/routers/<entity>/
|
||||
index.ts # export * from each operation file + ./types
|
||||
types.ts # response payload types + row->public mapper
|
||||
validation.ts # zod schemas/refinements shared by create + update (optional)
|
||||
create<Entity>.ts
|
||||
list<Entities>.ts
|
||||
get<Entity>.ts
|
||||
update<Entity>.ts
|
||||
delete<Entity>.ts
|
||||
```
|
||||
|
||||
`index.ts` is a flat barrel:
|
||||
|
||||
```ts
|
||||
export * from "./createAiProvider";
|
||||
export * from "./listAiProviders";
|
||||
export * from "./getAiProvider";
|
||||
export * from "./updateAiProvider";
|
||||
export * from "./deleteAiProvider";
|
||||
export * from "./types";
|
||||
```
|
||||
|
||||
## 3. Anatomy of a single handler
|
||||
|
||||
Every handler file (`create<Entity>.ts`, etc.) follows the same shape:
|
||||
|
||||
```ts
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { <table>, db } from "@server/db";
|
||||
import response from "@server/lib/response";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { eq } from "drizzle-orm";
|
||||
import type { GetXResponse } from "@server/routers/<entity>/types";
|
||||
|
||||
const paramsSchema = z.strictObject({
|
||||
orgId: z.string().nonempty() // or entityId: z.coerce.number().int().positive()
|
||||
});
|
||||
|
||||
const bodySchema = z.strictObject({ /* ... */ }); // create/update only
|
||||
|
||||
registry.registerPath({
|
||||
method: "get", // put | post | delete
|
||||
path: "/org/{orgId}/x",
|
||||
description: "...",
|
||||
tags: [OpenAPITags.<Entity>],
|
||||
request: { params: paramsSchema, /* body: {...} for write ops, query: for list */ },
|
||||
responses: { 200: { description: "Successful response" } }
|
||||
});
|
||||
|
||||
export async function getX(req: Request, res: Response, next: NextFunction): Promise<any> {
|
||||
try {
|
||||
const parsedParams = paramsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(createHttpError(HttpCode.BAD_REQUEST, fromError(parsedParams.error).toString()));
|
||||
}
|
||||
// parse body too, if present, same pattern
|
||||
|
||||
// ...business logic against db...
|
||||
|
||||
if (!row) {
|
||||
return next(createHttpError(HttpCode.NOT_FOUND, `X with ID ${id} not found`));
|
||||
}
|
||||
|
||||
return response<GetXResponse>(res, {
|
||||
data: { /* ... */ },
|
||||
success: true,
|
||||
error: false,
|
||||
message: "X retrieved successfully",
|
||||
status: HttpCode.OK
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules to keep consistent with the rest of the codebase:
|
||||
|
||||
- `z.strictObject` for params/body — rejects unknown keys.
|
||||
- Params parsed first, then body; each on its own `safeParse` + early
|
||||
`next(createHttpError(...))` — never throw raw errors.
|
||||
- Every handler registers itself with the OpenAPI `registry` even if nobody
|
||||
reads the spec directly — it's how `/api/v1/docs` stays accurate.
|
||||
- Catch-all `try/catch` at the bottom: `logger.error(error)` +
|
||||
generic `500` message. Never leak internal error details to the client.
|
||||
- Use `response<T>(res, { data, success, error, message, status })` from
|
||||
`@server/lib/response` for every response, success or otherwise (errors go
|
||||
through `next(createHttpError(...))` instead, not through `response`).
|
||||
- If the route already ran an access-control middleware that fetched the row
|
||||
(see §5), reuse it instead of re-querying:
|
||||
`req.aiProvider && req.aiProvider.providerId === providerId ? [req.aiProvider] : await db.select()...`
|
||||
|
||||
### List handler specifics
|
||||
|
||||
Pagination is a fixed shape (`page`, `pageSize`, optional `query` for
|
||||
search). See `listAiProviders.ts`:
|
||||
|
||||
```ts
|
||||
const listSchema = z.object({
|
||||
pageSize: z.coerce.number<string>().int().positive().optional().catch(20).default(20),
|
||||
page: z.coerce.number<string>().int().min(0).optional().catch(1).default(1),
|
||||
query: z.string().optional()
|
||||
});
|
||||
```
|
||||
|
||||
Run the count query and the page query in `Promise.all`, and return
|
||||
`PaginatedResponse<{ items: T[] }>` (`@server/types/Pagination`) with
|
||||
`{ total, pageSize, page }`.
|
||||
|
||||
### types.ts specifics
|
||||
|
||||
- Define one response type per operation: `List<Entities>Response`,
|
||||
`Get<Entity>Response`, `CreateOrEdit<Entity>Response` (create and update
|
||||
commonly share a response shape).
|
||||
- If the raw DB row needs to be shaped for clients (decrypting secrets,
|
||||
parsing a serialized column, hiding a column), put a `toPublic<Entity>()`
|
||||
mapper here — see `toPublicAiProvider` for the pattern of stripping
|
||||
`apiKey`/serialized columns and re-adding decrypted/parsed versions.
|
||||
|
||||
### validation.ts specifics
|
||||
|
||||
Only needed when create and update share non-trivial zod pieces (enums,
|
||||
`superRefine` cross-field rules). Export the raw schemas (`z.enum([...])`)
|
||||
and refinement functions, and import them into both `createX.ts` and
|
||||
`updateX.ts` — see `aiProvider/validation.ts`'s
|
||||
`refineProviderUpstreamFields`.
|
||||
|
||||
## 4. Wire up an access-control middleware (for id-scoped routes)
|
||||
|
||||
For routes scoped to a single row (`/x/:xId`, as opposed to
|
||||
`/org/:orgId/x` create/list), add a `verify<Entity>Access` middleware in
|
||||
`server/middlewares/` (or `server/private/middlewares/` for enterprise-only
|
||||
entities) and export it from that directory's `index.ts`.
|
||||
|
||||
Pattern (`verifyAiProviderAccess.ts`):
|
||||
|
||||
1. Read the id param, `Number.parseInt`/validate it.
|
||||
2. Load the row by id.
|
||||
3. `404` if it doesn't exist.
|
||||
4. Resolve the row's `orgId`, then check/attach `req.userOrg` (query
|
||||
`userOrgs` if not already on the request), `403` if the user isn't in
|
||||
that org.
|
||||
5. Run `checkOrgAccessPolicy` if `req.orgPolicyAllowed` hasn't been resolved
|
||||
yet.
|
||||
6. Set `req.userOrgId`, `req.userOrgRoleIds`, and stash the row on the
|
||||
request (e.g. `req.aiProvider = provider`) so downstream handlers and
|
||||
`verifyUserHasAction` don't have to refetch it.
|
||||
|
||||
Org-scoped create/list routes (`/org/:orgId/x`) don't need a bespoke
|
||||
middleware — they use the existing generic `verifyOrgAccess` from
|
||||
`@server/middlewares`.
|
||||
|
||||
## 5. Register an action + permission check
|
||||
|
||||
Add one `ActionsEnum` entry per operation in `server/auth/actions.ts`,
|
||||
grouped near the entity's other actions, named `create<Entity>`,
|
||||
`get<Entity>`, `update<Entity>`, `delete<Entity>`, `list<Entities>`:
|
||||
|
||||
```ts
|
||||
createAiProvider = "createAiProvider",
|
||||
deleteAiProvider = "deleteAiProvider",
|
||||
getAiProvider = "getAiProvider",
|
||||
listAiProviders = "listAiProviders",
|
||||
updateAiProvider = "updateAiProvider",
|
||||
```
|
||||
|
||||
Every route uses `verifyUserHasAction(ActionsEnum.x)` to check the caller's
|
||||
role/permissions for that action, and mutating routes (create/update/delete)
|
||||
follow it with `logActionAudit(ActionsEnum.x)` to record the action in the
|
||||
audit log.
|
||||
|
||||
## 6. Register the routes
|
||||
|
||||
There are four router files; which one(s) you touch depends on public vs.
|
||||
private and user-facing vs. service-to-service:
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `server/routers/external.ts` | Public, user-facing API. Exports `authenticated`, `unauthenticated`, `authRouter` Express routers. |
|
||||
| `server/routers/internal.ts` | Public, internal service-to-service API (gerbil, badger, traefik-config) — no user auth, exports `internalRouter`. |
|
||||
| `server/private/routers/external.ts` | Enterprise-only, user-facing. Imports `authenticated`/`unauthenticated`/`authRouter` **from the public `external.ts`** and re-exports them, then adds more routes on top. |
|
||||
| `server/private/routers/internal.ts` | Enterprise-only, service-to-service. Same re-export trick with `internalRouter`. |
|
||||
|
||||
Private router files always start:
|
||||
|
||||
```ts
|
||||
import {
|
||||
unauthenticated as ua,
|
||||
authenticated as a,
|
||||
authRouter as aa
|
||||
} from "@server/routers/external";
|
||||
|
||||
export const authenticated = a;
|
||||
export const unauthenticated = ua;
|
||||
export const authRouter = aa;
|
||||
```
|
||||
|
||||
...and then call `authenticated.get/put/post/delete(...)` to bolt on
|
||||
additional, enterprise-only routes on the *same* router instances the public
|
||||
build uses. This is why the private build has strictly more routes than the
|
||||
public build, not a divergent copy.
|
||||
|
||||
### Route registration order (mutating vs read)
|
||||
|
||||
Standard middleware chain per verb, using `alertRule`'s registrations as the
|
||||
template:
|
||||
|
||||
```ts
|
||||
// Create — org-scoped, no row exists yet
|
||||
authenticated.put(
|
||||
"/org/:orgId/x",
|
||||
verifyValidLicense, // private/enterprise routes only
|
||||
verifyOrgAccess,
|
||||
verifyLimits, // if the entity counts against a plan limit
|
||||
verifyUserHasAction(ActionsEnum.createX),
|
||||
logActionAudit(ActionsEnum.createX),
|
||||
x.createX
|
||||
);
|
||||
|
||||
// Update — row-scoped
|
||||
authenticated.post(
|
||||
"/org/:orgId/x/:xId", // or "/x/:xId" if id is globally unique
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess, // or verifyXAccess if globally-keyed
|
||||
verifyUserHasAction(ActionsEnum.updateX),
|
||||
logActionAudit(ActionsEnum.updateX),
|
||||
x.updateX
|
||||
);
|
||||
|
||||
// Delete — row-scoped
|
||||
authenticated.delete(
|
||||
"/org/:orgId/x/:xId",
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyUserHasAction(ActionsEnum.deleteX),
|
||||
logActionAudit(ActionsEnum.deleteX),
|
||||
x.deleteX
|
||||
);
|
||||
|
||||
// List — org-scoped, read-only, no audit log
|
||||
authenticated.get(
|
||||
"/org/:orgId/xs",
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyUserHasAction(ActionsEnum.listXs),
|
||||
x.listXs
|
||||
);
|
||||
|
||||
// Get one — row-scoped, read-only, no audit log
|
||||
authenticated.get(
|
||||
"/org/:orgId/x/:xId",
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyUserHasAction(ActionsEnum.getX),
|
||||
x.getX
|
||||
);
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- HTTP verbs: `PUT` = create, `POST` = update, `GET` = read, `DELETE` =
|
||||
delete. This repo does not use `PATCH` for entity updates (site
|
||||
provisioning keys are the one exception, using `PATCH`).
|
||||
- `verifyValidLicense` is only needed on private/enterprise routes; public
|
||||
OSS routes skip it.
|
||||
- Use `verifyValidSubscription(tierMatrix.someFeature)` right after
|
||||
`verifyValidLicense` when a feature is gated to specific SaaS tiers (see
|
||||
`tierMatrix` usages in `server/private/routers/external.ts`).
|
||||
- `verifyLimits` goes on create routes for entities that count against a
|
||||
plan/seat limit.
|
||||
- For entities keyed by a globally-unique id (not nested under `/org/:orgId`),
|
||||
use the dedicated `verify<Entity>Access` middleware from §4 instead of
|
||||
`verifyOrgAccess` on the row-scoped routes (see how `/ai-provider/:providerId`
|
||||
uses `verifyAiProviderAccess`, while `/org/:orgId/ai-provider` create/list
|
||||
use plain `verifyOrgAccess`).
|
||||
- Read-only routes (`get`, `list`) skip `logActionAudit` — only mutations are
|
||||
audited.
|
||||
- `internal*.ts` routes are for trusted internal callers (gerbil/badger
|
||||
sidecars) and generally skip user-facing auth entirely, using
|
||||
`verifySessionUserMiddleware` / `verifyUserFromResourceSessionMiddleware`
|
||||
instead of `verifyOrgAccess`/`verifyUserHasAction`. CRUD entities almost
|
||||
never need internal router entries — only add one if a sidecar process
|
||||
needs direct access to the resource.
|
||||
|
||||
## 7. The `#dynamic` alias (advanced — most CRUD work can ignore this)
|
||||
|
||||
Some middleware (e.g. `logActionAudit`) needs a real implementation in the
|
||||
enterprise/SaaS build but a no-op stub in the open-source build, while
|
||||
being imported by identical code in `server/routers/external.ts` in both
|
||||
builds. That's done via the `#dynamic/*` import alias, which
|
||||
`tsconfig.oss.json` points at `./server/*` and `tsconfig.enterprise.json` /
|
||||
`tsconfig.saas.json` point at `./server/private/*`. You only need this
|
||||
pattern if you're adding a genuinely dual-implementation hook; a normal
|
||||
private-only CRUD entity (like `alertRule`) never touches `#dynamic` — it
|
||||
just lives entirely under `server/private/` and is imported with `#private/*`
|
||||
directly from `server/private/routers/external.ts`.
|
||||
|
||||
## 8. Checklist for a new entity
|
||||
|
||||
1. Add the DB table to `server/db/pg/schema/schema.ts` (and sqlite schema if
|
||||
applicable).
|
||||
2. Add `ActionsEnum` entries in `server/auth/actions.ts`.
|
||||
3. Create `server/routers/<entity>/` (or `server/private/routers/<entity>/`):
|
||||
`types.ts`, optional `validation.ts`, one file per operation, `index.ts`
|
||||
barrel.
|
||||
4. If routes are row-scoped by a global id, add
|
||||
`verify<Entity>Access.ts` to `server/middlewares/` or
|
||||
`server/private/middlewares/`, and export it from that directory's
|
||||
`index.ts`.
|
||||
5. Wire routes into `external.ts` (public or private) following the verb/
|
||||
middleware table in §6. Add to `internal.ts` only if a sidecar needs
|
||||
direct access.
|
||||
6. Add license header block to every new file if it's under `server/private/`.
|
||||
@@ -0,0 +1,2 @@
|
||||
tls:
|
||||
certificates: []
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -16,7 +16,7 @@ experimental:
|
||||
version: "{{.BadgerVersion}}"
|
||||
crowdsec: # CrowdSec plugin configuration added
|
||||
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||
version: "v1.4.4"
|
||||
version: "v1.7.1"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module installer
|
||||
|
||||
go 1.25.0
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/huh v1.0.0
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
golang.org/x/term v0.45.0
|
||||
golang.org/x/term v0.46.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -33,6 +33,6 @@ require (
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
)
|
||||
|
||||
@@ -69,10 +69,10 @@ golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Моля, уверете се, че сте влезли като правилния потребител.",
|
||||
"inviteErrorNoUser": "Съжаляваме, но изглежда, че поканата, до която се опитвате да получите достъп, не е за съществуващ потребител.",
|
||||
"inviteCreateUser": "Моля, първо създайте акаунт.",
|
||||
"inviteErrorOidcNotAllowed": "Поканите могат да се приемат само от вътрешни акаунти. Отпишете се и влезте с вашата парола за този имейл.",
|
||||
"inviteLoginInternalOnly": "Поканите изискват вътрешен акаунт с парола. Създайте акаунт или влезте с вашата парола.",
|
||||
"goHome": "Отиди вкъщи",
|
||||
"inviteLogInOtherUser": "Влезте като друг потребител",
|
||||
"createAnAccount": "Създайте профил",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "Цели на този сайт",
|
||||
"siteSetting": "Настройки на {siteName}",
|
||||
"siteNewtTunnel": "Нов Сайт (Препоръчително)",
|
||||
"pangolinSite": "Сайт Панголиин",
|
||||
"siteNewtTunnelDescription": "Най-лесният начин да създадете точка за достъп до всяка мрежа. Няма нужда от допълнителни настройки.",
|
||||
"siteWg": "Основен WireGuard",
|
||||
"siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required. ONLY WORKS ON SELF HOSTED NODES",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "Не е намерена държава.",
|
||||
"siteSelectionDescription": "Този сайт ще осигури свързаност до целта.",
|
||||
"resourceType": "Тип ресурс",
|
||||
"resourceTypeDescription": "Това контролира протокола на ресурса и как той ще се визуализира в браузъра. Това не може да бъде променено по-късно.",
|
||||
"resourceTypeDescription": "Това контролира протокола на ресурса и не може да се промени по-късно.",
|
||||
"resourceTypeSearch": "Търсене на типове ресурси...",
|
||||
"resourceTypeNotFound": "Не е намерен тип ресурс",
|
||||
"resourceTypeHttpDescription": "Прокси на уеб трафик през HTTPS, използвайки име на домейн",
|
||||
"resourceTypeInferenceDescription": "Направа на AI заявки чрез прикрепени доставчици",
|
||||
"resourceTypeSshDescription": "Достъп до SSH сървър от уеб браузъра",
|
||||
"resourceTypeRdpDescription": "Достъп до отдалечен работен плот от уеб браузъра",
|
||||
"resourceTypeVncDescription": "Достъп до VNC десктоп от уеб браузъра",
|
||||
"resourceTypeTcpDescription": "Прокси на суров TCP трафик, използвайки номер на порт",
|
||||
"resourceTypeUdpDescription": "Прокси на суров UDP трафик, използвайки номер на порт",
|
||||
"privateResourceTypeDescription": "Това контролира как клиентите достигат до ресурса. Това не може да бъде променено по-късно.",
|
||||
"privateResourceTypeHostDescription": "Излагане на единичен хост в мрежата на сайта към свързани клиенти",
|
||||
"privateResourceTypeCidrDescription": "Излагане на CIDR обхват в мрежата на сайта към свързани клиенти",
|
||||
"privateResourceTypeHttpDescription": "Достъп до HTTP или HTTPS услуга чрез домейн",
|
||||
"privateResourceTypeSshDescription": "Достъп до SSH сървър от свързани клиенти",
|
||||
"resourceDomainDescription": "Ресурсът ще се обслужва на това напълно квалифицирано име на домейн.",
|
||||
"resourceHTTPSSettings": "HTTPS настройки",
|
||||
"resourceHTTPSSettingsDescription": "Конфигурирайте как ресурсът ще бъде достъпен по HTTPS",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "Изтрийте API ключа",
|
||||
"apiKeysManage": "Управление на API ключове",
|
||||
"apiKeysDescription": "API ключове се използват за удостоверяване с интеграционния API",
|
||||
"orgsManage": "Управление на организации",
|
||||
"orgsDescription": "Преглед и управление на всички организации в тази инстанция",
|
||||
"provisioningKeysTitle": "Ключ за осигуряване",
|
||||
"provisioningKeysManage": "Управление на ключове за осигуряване",
|
||||
"provisioningKeysDescription": "Ключовете за осигуряване се използват за удостоверяване на автоматичното осигуряване на сайта за вашата организация.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "Име (по избор)",
|
||||
"accessControls": "Контрол на достъпа",
|
||||
"userDescription2": "Управление на настройките на този потребител",
|
||||
"userGeneralSettingsDescription": "Управление на ролите и настройките на този потребител в организацията",
|
||||
"accessRoleErrorAdd": "Неуспешно добавяне на потребител към роля",
|
||||
"accessRoleErrorAddDescription": "Възникна грешка при добавяне на потребителя към ролята.",
|
||||
"userSaved": "Потребителят е запазен",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "Запази Настройки",
|
||||
"policyAuthStackTitle": "Удостоверяване",
|
||||
"policyAuthStackDescription": "Контролирайте кои методи за удостоверяване са необходими за достъп до този ресурс",
|
||||
"policyAuthInferenceStackDescription": "Изберете кои потребители и роли могат да се удостоверят с този AI шлюз",
|
||||
"policyAuthOrLogicTitle": "Множество активни методи за удостоверяване",
|
||||
"policyAuthOrLogicBanner": "Посетителите могат да се удостоверяват чрез който и да е от активните методи по-долу. Не е необходимо да преминават през всичките.",
|
||||
"policyAuthMethodActive": "Активен",
|
||||
"policyAuthMethodOff": "Изключено",
|
||||
"policyAuthSsoTitle": "Платформено SSO",
|
||||
"policyAuthSsoDescription": "Изисква вход чрез удостоверител на вашата организация",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "URL за вход",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Всеки потребител вече има клавиш за API идентичност, така че трябва да създадете виртуални API ключове само за клиенти, които не са потребители или за споделен достъп. Потребителите могат да получат своя ключ, като влязат с доставчик на идентичност на URL адреса на този ресурс, където ще бъде показан след вход.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Всеки потребител вече има клавиш за API идентичност, така че трябва да създадете виртуални API ключове само за клиенти, които не са потребители или за споделен достъп. Потребителите получават своя ключ тук след влизане с доставчик на идентичност.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} потребители, {roles} роли",
|
||||
"policyAuthSsoDefaultIdp": "По подразбиране удостоверител",
|
||||
"policyAuthAddDefaultIdentityProvider": "Добавете удостоверител по подразбиране",
|
||||
@@ -892,7 +916,7 @@
|
||||
"policyAccessRulesFallthroughOff": "Когато правилата са изключени, целият трафик преминава към удостоверяване.",
|
||||
"policyAccessRulesFallthroughOn": "Когато няма съвпадение, трафикът преминава към удостоверяване.",
|
||||
"rulesPlaceholderCidr": "10.0.0.0/8",
|
||||
"rulesPlaceholderPath": "/admin/*",
|
||||
"rulesPlaceholderPath": "/администратор/*",
|
||||
"rulesPlaceholderGeo": "RU, KP",
|
||||
"rulesSave": "Запазете правилата",
|
||||
"resourceErrorCreate": "Грешка при създаване на ресурс",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "Нова роля е необходима",
|
||||
"accessRoleErrorRemove": "Неуспешно премахване на роля",
|
||||
"accessRoleErrorRemoveDescription": "Възникна грешка при премахването на роля.",
|
||||
"accessRoleInferenceBudget": "AI Бюджет",
|
||||
"accessRoleInferenceBudgetDescription": "Конфигурирайте как членовете на тази роля ограничават използването на AI въз основа на лимити на разходи или жетони",
|
||||
"accessRoleName": "Име на роля",
|
||||
"accessRoleQuestionRemove": "Ще изтриете ролята `{name}`. Не можете да отмените това действие.",
|
||||
"accessRoleRemove": "Премахни роля",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "Няма намерени сайтове.",
|
||||
"pangolinServerAdmin": "Администратор на сървър - Панголин",
|
||||
"licenseTierProfessional": "Професионален лиценз",
|
||||
"licenseTierEnterprise": "Предприятие лиценз",
|
||||
"licenseTierPersonal": "Персонален лиценз",
|
||||
"licenseTierEnterprise": "Предприятие",
|
||||
"licenseTierPersonal": "Личен",
|
||||
"licenseTierTier1": "Начален",
|
||||
"licenseTierTier2": "Мащаб",
|
||||
"licensed": "Лицензиран",
|
||||
"yes": "Да",
|
||||
"no": "Не",
|
||||
@@ -1154,6 +1182,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "Научете повече за JMESPath",
|
||||
"idpJmespathLabel": "Идентификатор на пътя",
|
||||
"idpJmespathLabelDescription": "Пътят към идентификатора на потребителя в ID токена",
|
||||
"idpIdentifierChangeTitle": "Предупреждение за промяна на пътя на идентификатора",
|
||||
"idpIdentifierChangeDescription": "Ще промените пътя на идентификатора. Това ще повлияе на начина, по който съществуващите потребители са разпределени. Потребители, които преди са влизали чрез този доставчик на идентичности, може вече да не бъдат разпознавани като същите потребители.",
|
||||
"idpIdentifierChangeConfirmMessage": "Потвърждавам",
|
||||
"idpIdentifierChangeWarningText": "Това ще повлияе на начина, по който съществуващите потребители са разпределени",
|
||||
"idpJmespathEmailPathOptional": "Път за имейл (по избор)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Пътят до имейла на потребителя в ID токена",
|
||||
"idpJmespathNamePathOptional": "Път (по избор) на име",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "Грешка при излизане",
|
||||
"signingAs": "Влезли сте като",
|
||||
"serverAdmin": "Администратор на сървъра",
|
||||
"promoteServerAdmin": "Повишаване до администратор на сървъра",
|
||||
"promoteServerAdminTitle": "Повишаване до Admin на сървъра",
|
||||
"promoteServerAdminQuestion": "Сигурни ли сте, че искате да повишите {selectedUser} до администратор на сървъра?",
|
||||
"promoteServerAdminMessage": "Администраторите на сървъра имат най-високите привилегии и могат да управляват сървъра.",
|
||||
"promoteServerAdminWarning": "Това може да бъде отменено по всяко време посредством понижаване на потребителя.",
|
||||
"promoteServerAdminConfirm": "Повишаване до Admin на сървъра",
|
||||
"promoteServerAdminSuccess": "Потребителят е повишен",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} вече е администратор на сървъра.",
|
||||
"promoteServerAdminError": "Неуспешно повишаване на потребител",
|
||||
"demoteServerAdmin": "Понижаване от администратор на сървъра",
|
||||
"demoteServerAdminTitle": "Понижаване от Admin на сървъра",
|
||||
"demoteServerAdminQuestion": "Сигурни ли сте, че искате да понижите {selectedUser} от администратор на сървъра?",
|
||||
"demoteServerAdminMessage": "{selectedUser} ще загуби всички привилегии на администратор на сървъра.",
|
||||
"demoteServerAdminWarning": "Това може да бъде отменено по всяко време посредством повишаване на потребителя.",
|
||||
"demoteServerAdminConfirm": "Понижаване от администратор на сървъра",
|
||||
"demoteServerAdminSuccess": "Потребителят е понижен",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} вече не е администратор на сървъра.",
|
||||
"demoteServerAdminError": "Неуспешно понижаване на потребител",
|
||||
"managedSelfhosted": "Управлявано Самостоятелно-хоствано",
|
||||
"otpEnable": "Включване на двуфакторен",
|
||||
"otpDisable": "Изключване на двуфакторен",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "Изтриване на сайта",
|
||||
"actionGetSite": "Вземете сайт",
|
||||
"actionListSites": "Изброяване на сайтове",
|
||||
"actionCreateAiProvider": "Създаване на AI Доставчик",
|
||||
"actionDeleteAiProvider": "Изтриване на AI Доставчик",
|
||||
"actionGetAiProvider": "Получаване на AI Доставчик",
|
||||
"actionListAiProviders": "Списък на AI Доставчици",
|
||||
"actionUpdateAiProvider": "Актуализиране на AI Доставчик",
|
||||
"actionCreateAiModel": "Създаване на AI Модел",
|
||||
"actionDeleteAiModel": "Изтриване на AI Модел",
|
||||
"actionGetAiModel": "Получаване на AI Модел",
|
||||
"actionListAiModels": "Списък на AI Модели",
|
||||
"actionUpdateAiModel": "Актуализиране на AI Модел",
|
||||
"actionCreateAiBudget": "Създаване на AI Бюджет",
|
||||
"actionDeleteAiBudget": "Изтриване на AI Бюджет",
|
||||
"actionGetAiBudget": "Получаване на AI Бюджет",
|
||||
"actionListAiBudgets": "Списък на AI Бюджети",
|
||||
"actionUpdateAiBudget": "Актуализиране на AI Бюджет",
|
||||
"actionListResourceAiModels": "Списък на модели на AI ресурс",
|
||||
"actionSetResourceAiModels": "Задаване на модели на AI ресурс",
|
||||
"actionCreateVirtualApiKey": "Създаване на Виртуален API Ключ",
|
||||
"actionDeleteVirtualApiKey": "Изтриване на Виртуален API Ключ",
|
||||
"actionGetVirtualApiKey": "Получаване на Виртуален API Ключ",
|
||||
"actionListVirtualApiKeys": "Списък на Виртуални API Ключове",
|
||||
"actionUpdateVirtualApiKey": "Актуализиране на Виртуален API Ключ",
|
||||
"actionApplyBlueprint": "Приложи Чернова",
|
||||
"actionListBlueprints": "Списък с планове.",
|
||||
"actionGetBlueprint": "Вземи план.",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "Задайте ПИН код на ресурса",
|
||||
"actionSetResourceEmailWhitelist": "Задайте списък на одобрените имейл адреси за ресурса",
|
||||
"actionGetResourceEmailWhitelist": "Вземете списък на одобрените имейл адреси за ресурса",
|
||||
"actionListResourcePolicies": "Списък на ресурсни политики",
|
||||
"actionCreateResourcePolicy": "Създаване на ресурсна политика",
|
||||
"actionGetResourcePolicy": "Получете политика за ресурси",
|
||||
"actionUpdateResourcePolicy": "Актуализирайте политика за ресурси",
|
||||
"actionDeleteResourcePolicy": "Изтриване на ресурсна политика",
|
||||
"actionSetResourcePolicyUsers": "Задайте потребители на ресурсна политика",
|
||||
"actionSetResourcePolicyRoles": "Задайте роли на ресурсна политика",
|
||||
"actionSetResourcePolicyPassword": "Задайте парола на ресурсна политика",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "Търси…",
|
||||
"searchPlaceholder": "Търсене...",
|
||||
"emptySearchOptions": "Няма намерени опции",
|
||||
"ipFilterSearchPlaceholder": "Въведете IP адрес…",
|
||||
"ipFilterEmptyMessage": "Въведете IP адрес, за да филтрирате по него",
|
||||
"create": "Създаване",
|
||||
"orgs": "Организации",
|
||||
"loginError": "Възникна неочаквана грешка. Моля, опитайте отново.",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "Създаване на машинен клиент",
|
||||
"commandPaletteCreateAlertRule": "Създаване на правила за известия",
|
||||
"commandPaletteCreateIdentityProvider": "Създаване на доставчик на идентичност",
|
||||
"commandPaletteCreateAiProvider": "Създайте AI доставчик",
|
||||
"commandPaletteCreateVirtualApiKey": "Създайте виртуален API ключ",
|
||||
"commandPaletteToggleTheme": "Смяна на темата",
|
||||
"commandPaletteChooseOrganization": "Изберете организация",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "Покани",
|
||||
"sidebarRoles": "Роли",
|
||||
"sidebarShareableLinks": "Споделими връзки",
|
||||
"sidebarAiGateway": "AI Шлюз",
|
||||
"sidebarAiProviders": "Доставчици",
|
||||
"commandAiProviders": "AI Доставчици",
|
||||
"sidebarVirtualApiKeys": "Виртуални API Ключове",
|
||||
"sidebarMyApiKeys": "Вашите API Ключове",
|
||||
"sidebarAccount": "Стартер",
|
||||
"commandVirtualApiKeys": "Виртуални API Ключове",
|
||||
"virtualApiKeysTitle": "Управление на Виртуални API Ключове",
|
||||
"virtualApiKeysDescription": "Създаване и управление на ръчни API ключове за достъп до AI шлюза до публични AI шлюзове",
|
||||
"virtualApiKeysTabIdentity": "Идентификационни ключове",
|
||||
"virtualApiKeysTabVirtual": "Виртуални ключове",
|
||||
"virtualApiKeysIdentitySplashTitle": "Идентификационни ключове за всеки потребител",
|
||||
"virtualApiKeysIdentitySplashDescription": "Всеки потребител вече има клавиш за идентичност Pangolin за тази организация. Той е уникален за неговия профил и го удостоверява при достъп до AI шлюзове.",
|
||||
"virtualApiKeysIdentitySplashExample": "Пример",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "Как получават своя ключ потребителите",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Посетете публичен AI шлюз URL адрес в браузъра и влезте с профила си.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "Или отидете на <url></url> докато сте влезли.",
|
||||
"virtualApiKeysIdentitySplashManual": "Можете да създадете допълнителни виртуални API ключове в таба Виртуални ключове. Тези ключове могат да бъдат обхванати до конкретни публични AI шлюзове и при необходимост свързани с потребител. Създаването на ключ веднага предоставя достъп до избраните публични AI шлюзове.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Ръчно създаване на ключ",
|
||||
"virtualApiKeysEmailIdentity": "Имейл идентификационни ключове",
|
||||
"virtualApiKeysEmailIdentityDescription": "Изпратете на всеки избран потребител или роля неговия ключ за идентичност Pangolin.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "Изпратете на всички потребители",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "Изпратете имейл на всеки член на организацията, който има акаунт имейл.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Потребители",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Роли",
|
||||
"virtualApiKeysEmailIdentitySubmit": "Изпращане на имейли",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Идентификационни ключове изпратени",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "Изпратени {sent} имейли.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "{skipped} потребители бяха прескочени, защото нямат имейл адрес.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "Изберете поне един потребител или роля, или изпратете на всички потребители.",
|
||||
"virtualApiKeysEmailIdentityError": "Грешка при изпращането на идентификационни ключове",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "Неуспех при изпращането на идентификационни ключове",
|
||||
"virtualApiKeysBannerTitle": "Идентификационни ключове за всеки потребител",
|
||||
"virtualApiKeysBannerDescription": "Всеки потребител вече има наличен идентификационен ключ на {keysUrl}. Можете също така да генерирате ръчно ключове тук, които предоставят директен достъп до публични AI шлюзове.",
|
||||
"virtualApiKeysBannerButtonText": "Преглеждане на идентификационни ключове",
|
||||
"virtualApiKeys": "Виртуални API Ключове",
|
||||
"virtualApiKeysSearch": "Търсене на ключове...",
|
||||
"virtualApiKeysCreate": "Създаване на Виртуален API Ключ",
|
||||
"virtualApiKeysCreateDescription": "Секретен ключ за всички публични AI шлюзове в тази организация",
|
||||
"virtualApiKeysCreateButton": "Създаване на ключ",
|
||||
"virtualApiKeysEmpty": "Все още няма Виртуални API Ключове",
|
||||
"virtualApiKeysName": "Име",
|
||||
"virtualApiKeysDescriptionOptional": "Описание (по избор)",
|
||||
"virtualApiKeysAssociateUserOptional": "Свързване на потребител (по избор)",
|
||||
"virtualApiKeysAssociateUserDescription": "Свържете този ключ с потребител, за да проследя появата. След създаването този ключ веднага предоставя достъп до избраните публични AI шлюзове. Не е необходимо да свързвате потребител ръчно, за да бъде видим ресурсът. Ключът ще се появи и в профила на потребителя.",
|
||||
"virtualApiKeysAllResources": "Всички публични AI шлюзове",
|
||||
"virtualApiKeysAllResourcesDescription": "Позволете на този ключ да има достъп до всеки публичен AI шлюз в организацията",
|
||||
"virtualApiKeysSelectResources": "Публични AI шлюзове",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Избор на ресурси",
|
||||
"virtualApiKeysSelectResourcesDescription": "Изберете кои публични AI шлюзове този ключ може да има достъп",
|
||||
"virtualApiKeysNoResources": "Няма ресурси",
|
||||
"virtualApiKeysSecret": "Ключ",
|
||||
"virtualApiKeysCopyKey": "Копирайте този ключ. Можете да го видите отново по-късно от таблицата или при редактиране.",
|
||||
"virtualApiKeysViewSecret": "Преглед на секрет",
|
||||
"virtualApiKeysViewSecretTitle": "Секрет за Виртуален API Ключ",
|
||||
"virtualApiKeysViewSecretDescription": "Този секрет предоставя достъп до публичните AI шлюзове, присвоени на този ключ",
|
||||
"virtualApiKeysEdit": "Редактиране на Виртуален API Ключ",
|
||||
"virtualApiKeysEditDescription": "Актуализирайте свързвания потребител и достъпа до публичния AI шлюз за този ключ",
|
||||
"virtualApiKeysSaveButton": "Запазване на промените",
|
||||
"virtualApiKeysSelectResourcesRequired": "Изберете поне един публичен AI шлюз, или активирайте всички публични AI шлюзове",
|
||||
"virtualApiKeysUpdated": "Виртуален API ключ е актуализиран",
|
||||
"virtualApiKeysUpdatedDescription": "Виртуалният API ключ е актуализиран",
|
||||
"virtualApiKeysErrorUpdate": "Грешка при актуализация на виртуален API ключ",
|
||||
"virtualApiKeysErrorUpdateDescription": "Неуспех при актуализация на виртуален API ключ",
|
||||
"virtualApiKeysErrorCreate": "Грешка при създаване на виртуален API ключ",
|
||||
"virtualApiKeysErrorCreateDescription": "Неуспех при създаване на виртуален API ключ",
|
||||
"virtualApiKeysErrorDelete": "Грешка при изтриване на виртуален API ключ",
|
||||
"virtualApiKeysErrorDeleteMessage": "Неуспех при изтриването на виртуален API ключ",
|
||||
"virtualApiKeysDeleted": "Виртуален API ключ е изтрит",
|
||||
"virtualApiKeysDeletedDescription": "Виртуалният API ключ е изтрит",
|
||||
"virtualApiKeysDelete": "Изтриване на Виртуален API Ключ",
|
||||
"virtualApiKeysDeleteConfirm": "Изтриване на ключ",
|
||||
"virtualApiKeysQuestionRemove": "Сигурни ли сте, че искате да изтриете този виртуален API ключ?",
|
||||
"virtualApiKeysMessageRemove": "Клиентите, които използват този ключ, ще загубят достъп веднага.",
|
||||
"virtualApiKeysErrorFetchSecret": "Грешка при зареждане на секрет",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Неуспех при зареждането на секрета на виртуалния API ключ",
|
||||
"virtualApiKeysFilterUnassigned": "Неопределен",
|
||||
"virtualApiKeysInferenceBudget": "Бюджет",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Конфигуриране как този ключ ограничава използването на AI въз основа на лимити на разходи или жетони",
|
||||
"virtualApiKeysEmailOnGenerate": "Имейл ключ при генериране",
|
||||
"virtualApiKeysEmailThisKey": "Имейл този ключ",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Изпратете ключа на свързания потребител и допълнителни адреси след създаването му",
|
||||
"virtualApiKeysEmailThisKeyDescription": "Изпратете текущия ключ на свързания потребител и допълнителни адреси",
|
||||
"virtualApiKeysEmailSmtpRequired": "Имейлът не е конфигуриран на този сървър",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "Конфигурирайте SMTP за изпращане на виртуални API ключове.",
|
||||
"virtualApiKeysEmailSendToUser": "Изпратете на свързан потребител",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Изпратете ключът на акаунтния имейл на свързания потребител",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "Свържете потребител за изпращане на ключа на този потребител",
|
||||
"virtualApiKeysEmailAdditional": "Допълнителни имейли",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "Добавете имейл и натиснете Enter",
|
||||
"virtualApiKeysEmailRecipientsRequired": "Изберете свързания потребител или добавете поне един имейл адрес",
|
||||
"myVirtualApiKeysTitle": "Вашите API Ключове",
|
||||
"myVirtualApiKeysDescription": "Прегледайте вашия идентификационен ключ и всички виртуални API ключове, които ви се отнасят в тази организация",
|
||||
"myVirtualApiKeysResourceTitle": "Вашите API Ключове за {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "Прегледайте вашия идентификационен ключ и виртуални API ключове, които ви се отнасят и могат да достъпят {resourceName}",
|
||||
"myVirtualApiKeysIdentityTitle": "Идентификационен ключ",
|
||||
"myVirtualApiKeysIdentityHeadline": "Вашият Личен API Ключ",
|
||||
"myVirtualApiKeysIdentityDescription": "Вашият личен ключ за тази организация. Той е уникален за вашия акаунт и се използва за идентификация при обаждания на AI Gateway ресурси.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Вашият Личен API Ключ за {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Вашият личен ключ за тази организация. Използвайте го, за да извикате {resourceName}.",
|
||||
"myVirtualApiKeysManualTitle": "Свързани ключове",
|
||||
"myVirtualApiKeysManualDescription": "Ръчни виртуални API ключове, свързани от администратор към вашия акаунт",
|
||||
"myVirtualApiKeysManualResourceDescription": "Ръчни виртуални API ключове, свързани с вашия акаунт, които могат да достъпят {resourceName}",
|
||||
"myVirtualApiKeysManualEmpty": "Все още няма приписани ключове",
|
||||
"myVirtualApiKeysKindUser": "Идентичност",
|
||||
"myVirtualApiKeysKindManual": "Ръчен",
|
||||
"myVirtualApiKeysUnnamed": "Неназован ключ",
|
||||
"myVirtualApiKeysRevealSecret": "Разкриване на секрет",
|
||||
"myVirtualApiKeysViewSecretDescription": "Този секрет ви идентифицира за AI Gateway ресурси",
|
||||
"aiClientConfigTitle": "Конфигуриране на кодиращи агенти",
|
||||
"aiClientConfigDescription": "Копирайте конфигурацията за популярни кодиращи агенти или оставете Pangolin CLI да я настрои автоматично за вас.",
|
||||
"aiClientConfigDescriptionClaude": "Инструментът за кодиране на Anthropic за терминала.",
|
||||
"aiClientConfigDescriptionCodex": "Инструментът за кодиране на OpenAI за терминала.",
|
||||
"aiClientConfigDescriptionOpencode": "Отворен кодиращ агент за терминал.",
|
||||
"aiClientConfigDescriptionGemini": "Агентски инструмент на Google за кодиране на терминал.",
|
||||
"aiClientConfigSetup": "Настройка",
|
||||
"aiClientConfigTabCli": "Автоматичен (CLI)",
|
||||
"aiClientConfigTabManual": "Ръчна конфигурация",
|
||||
"aiClientConfigPreset": "Конфигурация",
|
||||
"aiClientConfigStep": "Стъпка {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "Този фрагмент включва примерни стойности, като име на модел или URL адрес на ресурс. Заменете ги със собствени, преди да го използвате.",
|
||||
"aiClientConfigRevealError": "Не можа да зареди вашия API ключ.",
|
||||
"aiClientConfigRevealRetry": "Опитайте отново",
|
||||
"resourceGeneralAiClientConfigDescription": "Този ресурс може да се използва от общи клиенти като Claude Code и OpenCode. <configLink>Научете повече</configLink>",
|
||||
"aiProvidersTitle": "AI Доставчици",
|
||||
"aiProvidersDescription": "Свързване на модели доставчици за AI задачи в тази организация",
|
||||
"aiProvidersBannerTitle": "Свързване на модели доставчици",
|
||||
"aiProvidersBannerDescription": "Доставчиците са бекендите на модела, които Pangolin използва за AI задачи. Свържете OpenAI, Anthropic и други доставчици тук, след това ги прикачете към публични AI шлюзове, така че потребителите да могат да извикват модели с идентичност-назнаван достъп, бюджети и логиране.",
|
||||
"aiProvidersAdd": "Добавяне на Доставчик",
|
||||
"aiProvidersSearch": "Търсене на доставчици...",
|
||||
"aiProvidersEmpty": "Все още няма AI доставчици",
|
||||
"aiProviderCreate": "Създаване на AI Доставчик",
|
||||
"aiProviderCreateDescription": "Добавяне на модел доставчик за тази организация",
|
||||
"aiProviderSeeAll": "Преглед на всички доставчици",
|
||||
"aiProviderSetting": "Настройки на доставчика за {providerName}",
|
||||
"aiProviderSettingDescription": "Конфигурирайте този AI доставчик",
|
||||
"aiProviderGeneral": "Общи",
|
||||
"aiProviderGeneralDescription": "Основни настройки за този доставчик",
|
||||
"aiProviderConfiguration": "Конфигурация",
|
||||
"aiProviderConfigurationDescription": "Мрежов маршрутизация и удостоверяване за този доставчик",
|
||||
"aiProviderNetworkSettings": "Мрежови настройки",
|
||||
"aiProviderNetworkSettingsDescription": "Избор как трафикът достига до този доставчик",
|
||||
"aiProviderAuthSettings": "Удостоверяване",
|
||||
"aiProviderAuthSettingsDescription": "Конфигурирайте как този доставчик удостоверява заявки до своя URL",
|
||||
"aiProviderBudgetSettings": "Бюджет",
|
||||
"aiProviderBudgetSettingsDescription": "Конфигурирайте как този доставчик ограничава използването въз основа на разходи или жетонни лимити",
|
||||
"aiBudgetAdd": "Добавяне на Бюджет",
|
||||
"aiBudgetEmpty": "Все още няма конфигурирани бюджети. Щракнете върху Добавяне на Бюджет, за да настроите лимит на разходи или жетони.",
|
||||
"aiBudgetUnit": "Вид на разхода",
|
||||
"aiBudgetPeriod": "Период на нулиране",
|
||||
"aiBudgetAmount": "Максимален разход",
|
||||
"aiBudgetAmountPlaceholder": "Максимален разход",
|
||||
"aiBudgetPeriodHourly": "Часов",
|
||||
"aiBudgetPeriodDaily": "Дневен",
|
||||
"aiBudgetPeriodWeekly": "Седмичен",
|
||||
"aiBudgetPeriodMonthly": "Месечен",
|
||||
"aiBudgetPeriodYearly": "Годишен",
|
||||
"aiBudgetPeriodLifetime": "Пожизнен",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "Жетони",
|
||||
"aiBudgetConflictError": "Бюджет за този период на нулиране и вид на разхода вече съществува",
|
||||
"aiBudgetInvalidAmountError": "Въведете максимален разход, по-голям от 0",
|
||||
"aiBudgetUpdated": "Актуализирани бюджети",
|
||||
"aiBudgetErrorSave": "Неуспех при актуализация на бюджети",
|
||||
"aiProviderType": "Вид на доставчика",
|
||||
"aiProviderTypeSearch": "Търсене на доставчици...",
|
||||
"aiProviderTypeNotFound": "Не е намерен вид доставчик",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI Gateway",
|
||||
"aiProviderTypeCustom": "Персонализиран",
|
||||
"aiProviderTypeOpenaiDescription": "OpenAI API",
|
||||
"aiProviderTypeAnthropicDescription": "Anthropic API",
|
||||
"aiProviderTypeGoogleGeminiDescription": "Google Gemini API",
|
||||
"aiProviderTypeVertexAiDescription": "Google Vertex AI API",
|
||||
"aiProviderTypeBedrockDescription": "Amazon Bedrock Рунтайм API",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "Microsoft Foundry API",
|
||||
"aiProviderTypeOpenRouterDescription": "OpenRouter API",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "Vercel AI Gateway API",
|
||||
"aiProviderTypeCustomDescription": "Използвайте собствена крайна точка или маршрутирайте чрез цели на сайта",
|
||||
"aiProviderUpstreamUrl": "Възходящ URL адрес",
|
||||
"aiProviderUpstreamUrlDescription": "Основен URL за API на доставчика",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Оставете празно, за да използвате стандартния възходящ URL за този доставчик",
|
||||
"aiProviderEffectiveUpstreamUrl": "Ефективен възходящ URL адрес",
|
||||
"aiProviderApiKey": "API ключ",
|
||||
"aiProviderApiKeyDescription": "API ключ, използван за удостоверяване на заявки към този доставчик",
|
||||
"aiProviderCustomHeadersDescription": "Заглавки, изпратени при всяка заявка към този доставчик. Отделени със нов ред: Име-на-заглавие: стойност",
|
||||
"aiProviderApiKeyLastChars": "API ключ",
|
||||
"aiProviderAuthType": "Тип удостоверяване",
|
||||
"aiProviderAuthTypeSearch": "Търсене на типове удостоверяване...",
|
||||
"aiProviderAuthTypeNotFound": "Не е намерен тип удостоверяване",
|
||||
"aiProviderAuthTypeBearer": "Bearer",
|
||||
"aiProviderAuthTypeBearerDescription": "Удостоверяване: Bearer ключ. Използва се от OpenAI и повечето доставчици",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "x-api-key заглавие. Използва се от Anthropic",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "x-goog-api-key заглавие. Използва се от Google Gemini",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Удостоверяване: Splunk ключ. Използва се от Splunk HTTP Event Collector",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-authorization: Bearer ключ. Използва се от Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeNone": "Без удостоверяване",
|
||||
"aiProviderAuthTypePassthrough": "Премини",
|
||||
"aiProviderAuthTypeDescription": "Как възходящото API удостоверява заявките",
|
||||
"aiProviderAuthTypePassthroughDescription": "Препрати заглавките на API ключа на повикващия към възходящото",
|
||||
"aiProviderAuthTypeNoneDescription": "Не изпращайте заглавки за удостоверяване към възходящото",
|
||||
"aiProviderRoutingMode": "Режим на маршрутиране",
|
||||
"aiProviderRoutingModeDescription": "Изпрати трафик към възходящ URL или HTTP цели на вашите сайтове",
|
||||
"aiProviderRoutingModeUrl": "Възходящ URL адрес",
|
||||
"aiProviderRoutingModeUrlDescription": "Обадете се на публичен или частен API основен URL",
|
||||
"aiProviderRoutingModeTarget": "Цели на сайта",
|
||||
"aiProviderRoutingModeTargetDescription": "Маршрутиране чрез цели на вашите сайтове",
|
||||
"aiProviderRoutingModeTargetNote": "След създаването на този доставчик, конфигурирайте целите на сайта в раздела Настройки на мрежата.",
|
||||
"aiProviderTargetNoOne": "Този доставчик няма цели. Добавете цел за маршрутиране на заявки чрез вашите сайтове.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Уебсайтовете, свързани с отдалечени възли, са недостъпни за пренасочване към AI Gateway доставчици.",
|
||||
"aiProviderSkipTlsVerification": "Пропуснете проверката на TLS",
|
||||
"aiProviderSkipTlsVerificationDescription": "Деактивирайте проверката на TLS сертификат за възходящото свързване",
|
||||
"aiProviderBudget": "Бюджет",
|
||||
"aiProviderBudgetDescription": "Факултативен лимит за разходи или маркер за този доставчик",
|
||||
"aiProviderBudgetAmount": "Сума на бюджета",
|
||||
"aiProviderBudgetUnit": "Единица на бюджета",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Токени",
|
||||
"aiProviderEnabled": "Активиран",
|
||||
"aiProviderEnabledDescription": "Напълно деактивирайте този доставчик за всички ресурси",
|
||||
"aiProviderErrorCreate": "Неуспешно създаване на AI доставчик",
|
||||
"aiProviderErrorUpdate": "Неуспешно актуализиране на AI доставчик",
|
||||
"aiProviderErrorDelete": "Неуспешно изтриване на AI доставчик",
|
||||
"aiProviderErrorLoad": "Неуспешно зареждане на AI доставчик",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Въведете валиден възходящ URL адрес",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Възходящ URL адрес е задължителен за този доставчик",
|
||||
"aiProviderErrorAuthTypeRequired": "Необходим е тип за удостоверяване",
|
||||
"aiProviderErrorApiKeyRequired": "API ключът е задължителен",
|
||||
"aiProviderErrorRoutingModeTarget": "Маршрутизацията на целите на сайта е налична само за персонализирани доставчици",
|
||||
"aiProviderErrorCapabilitiesRequired": "Изберете поне една възможност на API",
|
||||
"aiProviderCapabilities": "Възможности на API",
|
||||
"aiProviderCapabilitiesDescription": "Изберете, които API формати може да управлява този доставчик. Известни доставчици започват с препоръчани настройки по подразбиране.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Изберете кои формати на API този персонализиран доставчик може да обработва",
|
||||
"aiProviderCapabilitiesSelect": "Изберете възможности",
|
||||
"aiProviderCapabilitiesEmpty": "Няма намерени възможности",
|
||||
"aiProviderCapabilitiesSearch": "Търсене на възможности...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI Чат Завършвания",
|
||||
"aiCapabilityOpenaiChatDescription": "Поддържа /v1/chat/completions",
|
||||
"aiCapabilityOpenaiResponses": "OpenAI Отговори",
|
||||
"aiCapabilityOpenaiResponsesDescription": "Поддържа /v1/responses",
|
||||
"aiCapabilityAnthropicMessages": "Anthropic Съобщения",
|
||||
"aiCapabilityAnthropicMessagesDescription": "Поддържа /v1/messages",
|
||||
"aiCapabilityV1Models": "Списък на модели",
|
||||
"aiCapabilityV1ModelsDescription": "Поддържа /v1/models откриване на модели",
|
||||
"aiCapabilityGeminiGenerateContent": "Gemini Генериране на Съдържание",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Поддържа директния Gemini API",
|
||||
"aiCapabilityBedrockModelInvoke": "Бедрок Модел Активирай",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Поддържа Amazon Bedrock InvokeModel",
|
||||
"aiCapabilityGoogleGenerateContent": "Vertex Генериране на Съдържание",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Поддържа Vertex AI Gemini формат",
|
||||
"aiCapabilityGoogleRawPredict": "Vertex Сурово Предсказване",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Поддържа Vertex AI rawPredict за Anthropic модели",
|
||||
"aiCapabilityBedrockConverse": "Бедрок Разговор",
|
||||
"aiCapabilityBedrockConverseDescription": "Поддържа Amazon Bedrock Converse API",
|
||||
"aiProviderCreated": "AI доставчикът е създаден",
|
||||
"aiProviderUpdated": "AI доставчикът е обновен",
|
||||
"aiProviderDeleted": "AI доставчикът е изтрит",
|
||||
"aiProviderDelete": "Изтриване на Доставчик",
|
||||
"aiProviderDeleteConfirm": "Изтриване на Доставчик",
|
||||
"aiProviderQuestionRemove": "Сигурни ли сте, че искате да изтриете този AI доставчик?",
|
||||
"aiProviderMessageRemove": "Това ще изтрие постоянно доставчика и неговите модели и цели. Това не може да бъде отменено.",
|
||||
"aiProviderErrorNoUpdate": "AI доставчикът не е наличен за обновяване",
|
||||
"aiProviderModels": "Модели",
|
||||
"aiProviderModelsDescription": "Определете позволителните и блокиращите списъци за този доставчик. Заявките трябва да съвпадат с позволителен запис и да не съвпадат с блокиращ запис.",
|
||||
"aiProviderCreateModelsDescription": "Изберете кои модели този доставчик може да обслужва. Празен позволителен списък осъжда целия трафик. Можете да добавите или промените модели по-късно.",
|
||||
"aiProviderModelsPlaceholder": "Търсене на модели или въведете персонален ключ",
|
||||
"aiProviderModelsAllow": "Позволителен Списък",
|
||||
"aiProviderModelsAllowDescription": "Модели, които могат да бъдат използвани чрез този доставчик. Празно означава отричане на всички.",
|
||||
"aiProviderModelsAllowPlaceholder": "Въведете ключ на модела",
|
||||
"aiProviderModelsAllowEmpty": "Все още няма добавени модели.",
|
||||
"aiProviderModelsBlock": "Блокиращ Списък",
|
||||
"aiProviderModelsBlockDescription": "Модели за отричане, дори ако съвпадат с позволителен запис.",
|
||||
"aiProviderModelsBlockPlaceholder": "Въведете ключ на модела",
|
||||
"aiProviderModelsBlockEmpty": "Все още няма добавени модели.",
|
||||
"aiProviderModelsAdd": "Добавяне на Модели",
|
||||
"aiProviderModelsClearAll": "Изчистване на Всички",
|
||||
"aiProviderModelsAddCustom": "Добавяне \"{key}\"",
|
||||
"aiProviderModelsAddCustomHint": "Натиснете Enter, за да добавите този персонален ключ на модел.",
|
||||
"aiProviderModelsAddBulk": "Добавяне на {count} персонални ключове",
|
||||
"aiProviderModelsAddBulkHint": "Натиснете Enter, за да добавите {count} персонални ключове.",
|
||||
"aiProviderModelsAddOne": "Добавяне {key} сега",
|
||||
"aiProviderModelsAddSelected": "Добавяне на Избраните",
|
||||
"aiProviderModelsSelectedCount": "{count} избрани",
|
||||
"aiProviderModelsSelectAll": "Избери всички",
|
||||
"aiProviderModelsClearSelected": "Изчистване",
|
||||
"aiProviderModelsBulkHint": "Изберете известни модели или въведете персонален ключ.",
|
||||
"aiProviderModelsCatalogEmpty": "Няма съвпадение с каталога на модели.",
|
||||
"aiProviderModelsCatalogHeading": "Известни Модели",
|
||||
"aiProviderModelsAllLabel": "Всички модели",
|
||||
"aiProviderModelsAllPatternHint": "Joker: *",
|
||||
"aiProviderModelsAddAllAllow": "Позволете всички модели",
|
||||
"aiProviderModelsAddAllBlock": "Блокирайте всички модели",
|
||||
"aiProviderModelsAddAllDescription": "Използва * joker, така че всеки ключ на модел съвпада.",
|
||||
"aiProviderModelsViewMore": "Вижте още ({count})",
|
||||
"aiProviderModelsViewLess": "Вижте по-малко",
|
||||
"aiProviderModelsRemove": "Премахване на модел",
|
||||
"aiProviderModelsEditHint": "Натиснете, за да редактирате настройките на модела",
|
||||
"aiProviderModelsSourceCatalog": "Известен каталогов модел",
|
||||
"aiProviderModelsSourceCustom": "Персонализиран ключ на модел",
|
||||
"aiProviderModelsSourcePattern": "Модел за съвпадение",
|
||||
"aiProviderModelsSourceAll": "Съвпада с всеки ключ на модел",
|
||||
"aiProviderModelsBudgetConfigured": "Конфигуриран бюджет",
|
||||
"aiProviderModelsEditTitle": "Редактиране на Модел",
|
||||
"aiProviderModelsEditDescription": "Обновете ключа на модела или конфигурирайте бюджета му.",
|
||||
"aiProviderModelsBudgetTab": "Бюджет",
|
||||
"aiProviderModelsKeyLabel": "Ключ на модел",
|
||||
"aiProviderModelsKeyRequired": "Въведете ключ на модел",
|
||||
"aiProviderModelsKeyDuplicate": "Този ключ на модел вече е в списък",
|
||||
"aiProviderModelsOverlapError": "Тези образци не могат да бъдат в двата списъка: {keys}",
|
||||
"aiProviderModelsUpdated": "Моделите са обновени",
|
||||
"aiProviderModelsErrorUpdate": "Неуспешно обновяване на модели",
|
||||
"aiResourceProviders": "Доставчици",
|
||||
"aiResourceProvidersDescription": "Изберете кои AI доставчици този AI портал може да използва",
|
||||
"aiResourceProvidersHelp": "Добавете доставчици и изберете наследяване (използване на списъците на всеки доставчик) или избор (изберете разрешителен списък за този ресурс). Разрешителните образци, които се конфликтират между прикрепени доставчици, не са разрешени.",
|
||||
"aiResourceProvidersSelect": "Изберете доставчици",
|
||||
"aiResourceProvidersEmpty": "Не са намерени AI доставчици",
|
||||
"aiResourceProvidersNoneAttached": "Все още няма прикрепени доставчици.",
|
||||
"aiResourceProvidersAdd": "Добавяне на доставчик",
|
||||
"aiResourceProvidersRemove": "Премахване на доставчик",
|
||||
"aiResourceProviderToggleEnabled": "Разрешете или забранете този доставчик на ресурса",
|
||||
"aiResourceProviderDisabled": "Деактивиран",
|
||||
"aiResourceProvidersUpdated": "Доставчиците са обновени",
|
||||
"aiResourceProvidersErrorUpdate": "Неуспешно обновяване на доставчици",
|
||||
"aiResourceProviderEditDescription": "Изберете как да се изложат моделите на този доставчик върху този ресурс.",
|
||||
"viewProviderSettings": "Преглед на настройки на доставчик",
|
||||
"aiResourceProviderMode": "Режим на достъп",
|
||||
"aiResourceProviderModeInherit": "Наследяване",
|
||||
"aiResourceProviderModeSelect": "Изберете",
|
||||
"aiResourceProviderModeSelectSummary": "Изберете · {count} модели",
|
||||
"aiResourceProviderModeInheritHelp": "Използвайте разрешителните и блокиращите списъци на този доставчик според конфигурацията.",
|
||||
"aiResourceProviderModeSelectHelp": "Изберете подмножество на моделите с разрешителния списък на този доставчик за този ресурс.",
|
||||
"aiResourceProviderAllowModels": "Разрешителен списък",
|
||||
"aiResourceProviderAllowModelsSelect": "Изберете модели",
|
||||
"aiResourceProviderAllowModelsSearch": "Търсене на модели...",
|
||||
"aiResourceProviderAllowModelsEmpty": "Няма намерени модели",
|
||||
"aiResourceProviderAllowModelsHelp": "Само модели от разрешителния списък на този доставчик могат да бъдат избрани.",
|
||||
"aiResourceAliasRequired": "Псевдоним е необходим за AI портали",
|
||||
"aiResourceDomainConfiguration": "Конфигурация на Домейн",
|
||||
"aiResourceDomainConfigurationDescription": "Изберете домейна, който клиентите ще използват, за да достигнат до този AI портал.",
|
||||
"aiUsageAnalyticsTitle": "Аналитика на AI Използването",
|
||||
"aiUsageAnalyticsDescription": "Анализирайте разходите, употребата на токени и активността на AI портал през доставчици, ресурси, роли и потребители",
|
||||
"aiUsageTabOverview": "Общ поглед",
|
||||
"aiUsageTabProviders": "Доставчици",
|
||||
"aiUsageTabResources": "Ресурси",
|
||||
"aiUsageFilterProvider": "Доставчик",
|
||||
"aiUsageFilterModel": "Модел",
|
||||
"aiUsageFilterResource": "Ресурс",
|
||||
"aiUsageFilterRole": "Роля",
|
||||
"aiUsageFilterUser": "Потребител",
|
||||
"aiUsageFilterAllProviders": "Всички Доставчици",
|
||||
"aiUsageFilterAllModels": "Всички Модели",
|
||||
"aiUsageFilterAllResources": "Всички Ресурси",
|
||||
"aiUsageFilterAllRoles": "Всички Роли",
|
||||
"aiUsageFilterAllUsers": "Всички Потребители",
|
||||
"aiUsageFilterSearch": "Търсене...",
|
||||
"aiUsageFilterNotFound": "Не са намерени опции",
|
||||
"aiUsageResetFilters": "Възстановяване на Филтрите",
|
||||
"aiUsageRefresh": "Обновяване",
|
||||
"aiUsageTokenTypePrompt": "Подканя",
|
||||
"aiUsageTokenTypeCacheRead": "Четене от кеш",
|
||||
"aiUsageTokenTypeCacheWrite": "Писане в кеш",
|
||||
"aiUsageTokenTypeCompletion": "Завършване",
|
||||
"aiUsageTokenTypeReasoning": "Разсъждение",
|
||||
"aiUsageRequests": "Запитвания",
|
||||
"aiUsageCost": "Разход",
|
||||
"aiUsageTokens": "Токени",
|
||||
"aiUsageOther": "Друго",
|
||||
"aiUsageTotalRequests": "Общо Запитвания",
|
||||
"aiUsageTotalTokens": "Общо Токени",
|
||||
"aiUsageTotalCost": "Общ Разход",
|
||||
"aiUsageEstimated": "Оценени",
|
||||
"aiUsageRequestVolume": "Обем на Запитване",
|
||||
"aiUsageTokenUsage": "Употреба на Токени",
|
||||
"aiUsageModelCost": "Модел Разход",
|
||||
"aiUsageModelTokens": "Модел Токени",
|
||||
"aiUsageTopModels": "Топ Модели",
|
||||
"aiUsageTopProviders": "Топ Доставчици",
|
||||
"aiUsageProviderCost": "Разход на Доставчик",
|
||||
"aiUsageProviderTokenUsage": "Употреба на Доставчик на Токени",
|
||||
"aiUsageTopResources": "Топ Ресурси",
|
||||
"aiUsageResourceCost": "Разход на Ресурс",
|
||||
"aiUsageResourceTokenUsage": "Употреба на Токени на Ресурс",
|
||||
"aiUsageResourceTypePublic": "Публичен Ресурс",
|
||||
"aiUsageResourceTypeSite": "Частен Ресурс",
|
||||
"aiUsageNoResource": "Няма ресурс",
|
||||
"aiUsageRolesTab": "Роли",
|
||||
"aiUsageUsersTab": "Потребители",
|
||||
"aiUsageTopRoles": "Топ Роли",
|
||||
"aiUsageRoleCost": "Разход на Роля",
|
||||
"aiUsageRoleTokenUsage": "Употреба на Токени на Роля",
|
||||
"aiUsageTopUsers": "Топ Потребители",
|
||||
"aiUsageUserCost": "Разход на Потребител",
|
||||
"aiUsageUserTokenUsage": "Употреба на Токени на Потребител",
|
||||
"aiUsageUnknownUser": "Непознат потребител",
|
||||
"aiUsageVirtualApiKeysTab": "Виртуални API Ключове",
|
||||
"aiUsageFilterVirtualApiKey": "Виртуален API Ключ",
|
||||
"aiUsageFilterAllVirtualApiKeys": "Всички Виртуални API Ключове",
|
||||
"aiUsageTopVirtualApiKeys": "Топ Виртуални API Ключове",
|
||||
"aiUsageVirtualApiKeyCost": "Разход на Виртуален API Ключ",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Употреба на Токени на Виртуален API Ключ",
|
||||
"aiUsageUnknownVirtualApiKey": "Няма виртуален API ключ",
|
||||
"aiUsageUnnamedVirtualApiKey": "Неназован ключ",
|
||||
"aiUsageLoading": "Зареждане...",
|
||||
"aiUsageNoData": "Няма данни",
|
||||
"resourceBudgetSettings": "Бюджет",
|
||||
"resourceBudgetSettingsDescription": "Конфигурирайте как AI порталът ограничава употребата, основавайки се на разходи или лимити на токени",
|
||||
"sidebarApiKeys": "API ключове",
|
||||
"sidebarOrgs": "Организации",
|
||||
"sidebarProvisioning": "Осигуряване",
|
||||
"sidebarSettings": "Настройки",
|
||||
"sidebarAllUsers": "Всички потребители",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "Правилото за предупреждение е запазено",
|
||||
"alertingRuleSavedCreatedDescription": "Вашето ново правило за предупреждение беше създадено. Все още можете да го редактирате на тази страница.",
|
||||
"alertingRuleSavedUpdatedDescription": "Промените, направени по това правило за предупреждение, бяха запазени.",
|
||||
"alertingTestAlertSent": "Изпратено е тестово предупреждение",
|
||||
"alertingTestAlertSentDescription": "Тестово предупреждение беше изпратено до действията, конфигурирани по това правило.",
|
||||
"alertingEditRule": "Редактиране на правило за предупреждение",
|
||||
"alertingCreateRule": "Създаване на правило за предупреждение",
|
||||
"alertingRuleCredenzaDescription": "Изберете какво да наблюдавате, кога да се активира и как да уведомите",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "Всяко правило свързва това, което да се наблюдава (сайт, проверка на състоянието или ресурс), кога да се активира (например офлайн или нездраве) и как да уведомите екипа чрез имейл, уеб куки или интеграции. Използвайте този списък, за да създавате, активирате и управлявате тези правила.",
|
||||
"alertingHealthChecksBannerTitle": "Наблюдавайте здравето и ресурсите",
|
||||
"alertingHealthChecksBannerDescription": "Проверките на състоянието са HTTP или TCP монитори, които определяте веднъж. След това можете да ги използвате като източници в правила за предупреждения, така че да бъдете уведомени, когато целта стане здраве или нездраве. Проверките на състоянието на ресурсите също се появяват тук.",
|
||||
"alertingTestRule": "Правило за тестово предупреждение",
|
||||
"alertingAddActionHeading": "Добавете ново действие",
|
||||
"alertingSelectActionType": "Изберете действие",
|
||||
"alertingNoActionsTitle": "Не са конфигурирани действия",
|
||||
"alertingNoActionsSaveDescription": "Добавете поне едно действие, за да може това правило да уведоми някого, когато се изпълни.",
|
||||
"alertingNoActionsTestDescription": "Добавете поне едно действие, преди да можете да тествате това правило.",
|
||||
"standaloneHcTableTitle": "Проверки на състоянието",
|
||||
"standaloneHcSearchPlaceholder": "Търсене на проверки на състоянието…",
|
||||
"standaloneHcAddButton": "Създаване на проверка на състоянието",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "Тип:",
|
||||
"createDomainName": "Име:",
|
||||
"createDomainValue": "Стойност:",
|
||||
"multiSelectFilterCount": "{count} избрани",
|
||||
"createDomainCnameRecords": "CNAME записи",
|
||||
"createDomainARecords": "A записи",
|
||||
"createDomainRecordNumber": "Запис {number}",
|
||||
@@ -2193,6 +2699,8 @@
|
||||
"subnetPlaceholder": "Мрежа",
|
||||
"addressDescription": "Вътрешният адрес на клиента. Трябва да пада в подмрежата на организацията.",
|
||||
"selectSites": "Избор на сайтове",
|
||||
"selectResources": "Изберете ресурси",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# ресурс} other {# ресурси}}",
|
||||
"selectLabels": "Изберете етикети",
|
||||
"sitesDescription": "Клиентът ще има връзка с избраните сайтове",
|
||||
"clientInstallOlm": "Инсталиране на Olm",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "Метод",
|
||||
"healthSelectScheme": "Избор на метод",
|
||||
"healthCheckPortInvalid": "Портът трябва да бъде между 1 и 65535",
|
||||
"healthCheckHostnameInvalid": "Името на хоста не трябва да съдържа празни символи",
|
||||
"healthCheckPath": "Път",
|
||||
"healthHostname": "IP / Хост",
|
||||
"healthPort": "Порт",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "Времето е в секунди",
|
||||
"requireDeviceApproval": "Изискват одобрение на устройства",
|
||||
"requireDeviceApprovalDescription": "Потребители с тази роля трябва да имат нови устройства одобрени от администратор преди да могат да се свържат и да имат достъп до ресурси.",
|
||||
"sshSettings": "Настройки за SSH",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "AI Портал",
|
||||
"sshAccess": "SSH Достъп",
|
||||
"rdpSettings": "Настройки за RDP",
|
||||
"vncSettings": "Настройки за VNC",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "SSH сървър",
|
||||
"rdpServer": "RDP сървър",
|
||||
"vncServer": "VNC сървър",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "AI Портал",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Метод",
|
||||
"editInternalResourceDialogEnableSsl": "Активиране на TLS",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "Разрешете ICMP Ping",
|
||||
"privateResourceNetworkAccess": "Достъп до мрежата",
|
||||
"privateResourceNetworkAccessDescription": "Контролирайте достъпа до TCP/UDP портовете и дали ICMP пинг е разрешен за този ресурс.",
|
||||
"hostSettings": "Настройки на хоста",
|
||||
"cidrSettings": "Настройки на CIDR",
|
||||
"hostSettings": "Хост",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Свойства на ресурса",
|
||||
"createInternalResourceDialogName": "Име",
|
||||
"createInternalResourceDialogSite": "Сайт",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "AI Портал",
|
||||
"scheme": "Метод",
|
||||
"createInternalResourceDialogScheme": "Метод",
|
||||
"createInternalResourceDialogEnableSsl": "Активиране на TLS",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Добавете CIDR диапазон (напр. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Неуспешно зареждане на подмрежи",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Етикети за Предпочитания",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Сайтове с тези етикети ще бъдат принудени да се свържат чрез този отдалечен край.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Сайтовете с тези етикети ще предпочетат да се свържат чрез този отдалечен изходен възел.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Изберете етикети...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Търсене на етикети...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Неуспешно зареждане на етикети",
|
||||
@@ -2681,7 +3193,7 @@
|
||||
"idpAzureConfiguration": "Конфигурация на Azure Entra ID",
|
||||
"idpAzureConfigurationDescription": "Конфигурирайте OAuth2 идентификационни данни на Azure Entra ID",
|
||||
"idpTenantId": "Идентификационен номер на наемателя",
|
||||
"idpTenantIdPlaceholder": "tenant-id",
|
||||
"idpTenantIdPlaceholder": "идентификационен номер на наемателя",
|
||||
"idpAzureTenantIdDescription": "Идентификационен номер на наемателя на Azure (намира се в прегледа на Azure Active Directory)",
|
||||
"idpAzureClientIdDescription": "Идентификационен код на клиента за регистриране на приложение в Azure",
|
||||
"idpAzureClientSecretDescription": "Секретен код на клиента за регистриране на приложение в Azure",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "Приоритет",
|
||||
"priorityDescription": "По-високите приоритетни маршрути се оценяват първи. Приоритет = 100 означава автоматично подреждане (системата решава). Използвайте друго число, за да наложите ръчен приоритет.",
|
||||
"instanceName": "Име на инстанция",
|
||||
"clearInstanceName": "Reset Server Association",
|
||||
"pathMatchModalTitle": "Конфигурация на съвпадение по пътека",
|
||||
"pathMatchModalDescription": "Настройте как трябва да бъдат съвпадани входящите заявки въз основа на техния път.",
|
||||
"pathMatchType": "Вид на съвпадението",
|
||||
@@ -3051,6 +3564,7 @@
|
||||
"validPassword": "Валидна парола",
|
||||
"validEmail": "Валиден имейл",
|
||||
"validSSO": "Валидно SSO",
|
||||
"validVirtualAPIKey": "Валиден Виртуален API Ключ",
|
||||
"view": "Преглед",
|
||||
"configManaged": "Управлявана конфигурация",
|
||||
"connectedClient": "Свързан клиент",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "Дневници на действията",
|
||||
"logRetention": "Задържане на логове",
|
||||
"logRetentionDescription": "Управлявайте времето за задържане на различни видове логове за тази организация или ги деактивирайте",
|
||||
"logRetentionDisabledWarningTitle": "Деактивирано съхранение на дневници",
|
||||
"logRetentionDisabledWarningDescription": "{logType} не се съхраняват за тази организация, така че новите дейности няма да се показват тук. Активирайте съхранението в настройките за сигурност, за да започнете събирането на тези дневници.",
|
||||
"logRetentionDisabledWarningButton": "Отидете на настройки за сигурност",
|
||||
"requestLogsDescription": "Прегледайте подробни логове на заявки за ресурси в тази организация",
|
||||
"aiSessionLogs": "Журнали на AI Портал Сесиите",
|
||||
"aiSessionLogsDescription": "Прегледайте подканянета и транскрипции на отговори за запитванията към AI портал в тази организация",
|
||||
"sidebarLogsAi": "Журнали на Сесиите",
|
||||
"commandLogsAi": "Журнали на Сесиите",
|
||||
"sidebarLogsAiUsage": "Анализ на Използване",
|
||||
"commandLogsAiUsage": "Анализ на Използване",
|
||||
"provider": "Доставчик",
|
||||
"capability": "Способност",
|
||||
"model": "Модел",
|
||||
"virtualApiKey": "Виртуален API Ключ",
|
||||
"noVirtualApiKey": "Няма виртуален API ключ",
|
||||
"stream": "Поток",
|
||||
"streaming": "Стрийминг",
|
||||
"nonStreaming": "Нестрийминг",
|
||||
"statusCode": "Код на Статуса",
|
||||
"aiSessionId": "ID на Сесията",
|
||||
"aiSessionRequest": "Запитване",
|
||||
"aiSessionResponse": "Отговор",
|
||||
"aiSessionNoData": "Няма събрани данни",
|
||||
"aiSessionCouldNotParse": "(суров, не може да се парсира транскрипт)",
|
||||
"aiSessionLogTruncated": "Тази сесия беше прекъсната преди съхранение и може да е непълна.",
|
||||
"aiSessionViewRaw": "Преглед на Суров JSON",
|
||||
"aiSessionViewChat": "Преглед на Чат",
|
||||
"cost": "Разход",
|
||||
"estimated": "Оценени",
|
||||
"tokenUsage": "Употреба на Токени",
|
||||
"promptTokens": "Подканящи Токени",
|
||||
"cacheReadTokens": "Кеш Прочитане на Токени",
|
||||
"cacheWriteTokens": "Кеш Писане на Токени",
|
||||
"completionTokens": "Токени за Завършване",
|
||||
"reasoningTokens": "Токени за Разсъждение",
|
||||
"totalTokens": "Общо Токени",
|
||||
"requestAnalyticsDescription": "Вижте подробни анализи на заявки за ресурсите в тази организация",
|
||||
"logRetentionRequestLabel": "Задържане на логове за HTTP заявки",
|
||||
"logRetentionRequestDescription": "Колко дълго да се задържат логовете на заявките",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "Колко дълго да се задържат логовете за действия",
|
||||
"logRetentionConnectionLabel": "Запазване на дневниците на връзките",
|
||||
"logRetentionConnectionDescription": "Колко дълго да се съхраняват дневниците на връзките",
|
||||
"logRetentionAISessionsLabel": "Съхранение на Журнали на AI Портал Сесиите",
|
||||
"logRetentionAISessionsDescription": "Колко време да съхраняваме подканящите/отговоряващите журнали на AI портал сесии",
|
||||
"logRetentionDisabled": "Деактивирано",
|
||||
"logRetention3Days": "3 дни",
|
||||
"logRetention7Days": "7 дни",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "Източен адрес",
|
||||
"destinationAddress": "Адрес на дестинация",
|
||||
"duration": "Продължителност",
|
||||
"licenseRequiredToUse": "Изисква се лиценз за <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> или <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>, за да използвате тази функция. <bookADemoLink>Резервирайте безплатна демонстрация или пробен POC, за да научите повече.</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> е необходим за използване на тази функция. Тази функция също е налична в <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Резервирайте безплатна демонстрация или пробен POC, за да научите повече.</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Ентерпрайз Едишън лиценз</enterpriseEditionLink> е необходим. <bookADemoLink>Резервирайте демо или проба</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Ентерпрайз Едишън лиценз</enterpriseEditionLink> е необходим. <bookADemoLink>Резервирайте демо или проба</bookADemoLink>",
|
||||
"certResolver": "Решавач на сертификати",
|
||||
"certResolverDescription": "Изберете решавач на сертификати за използване за този ресурс.",
|
||||
"selectCertResolver": "Изберете решавач на сертификати",
|
||||
@@ -3242,6 +3793,7 @@
|
||||
"noData": "Няма Данни",
|
||||
"machineClients": "Машинни клиенти",
|
||||
"install": "Инсталирай",
|
||||
"downloadInstaller": "Изтегляне на инсталатора",
|
||||
"run": "Изпълни",
|
||||
"envFile": "Файл за среда",
|
||||
"serviceFile": "Файл за услуга",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "Избирайки няколко сайта, се осигурява сигурен път и пренасочване при висока достъпност.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Научете повече",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Ограничете достъпа до конкретни TCP/UDP портове или позволете/блокирайте всички портове.",
|
||||
"createInternalResourceDialogHttpConfiguration": "Конфигурация HTTP",
|
||||
"createInternalResourceDialogHttpConfiguration": "Конфигурация на Домейн",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "Изберете домейна, който клиентите ще използват, за да достигнат този ресурс чрез HTTP или HTTPS.",
|
||||
"editInternalResourceDialogHttpConfiguration": "Конфигурация HTTP",
|
||||
"editInternalResourceDialogHttpConfiguration": "Конфигурация на Домейн",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "Изберете домейна, който клиентите ще използват, за да достигнат този ресурс чрез HTTP или HTTPS.",
|
||||
"editInternalResourceDialogTcp": "TCP.",
|
||||
"editInternalResourceDialogUdp": "UDP.",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "Прекъснат",
|
||||
"approvalsEmptyStateTitle": "Одобрения на устройство не са активирани",
|
||||
"approvalsEmptyStateDescription": "Активирайте одобрения на устройства за роли, така че да изискват администраторско одобрение, преди потребителите да могат да свързват нови устройства.",
|
||||
"approvalsEmptyStateHowToTitle": "Как да го активирате",
|
||||
"approvalsEmptyStateStep1Title": "Отидете на роли",
|
||||
"approvalsEmptyStateStep1Description": "Навигирайте до настройките на ролите на вашата организация, за да конфигурирате одобренията на устройства.",
|
||||
"approvalsEmptyStateStep2Title": "Активирайте одобрения на устройства",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "Събития на свързване и прекъсване на сайта и тунела, включително свръзки и прекъсвания.",
|
||||
"httpDestRequestLogsTitle": "Логове за HTTP заявки",
|
||||
"httpDestRequestLogsDescription": "Регистри за HTTP заявките към проксирани ресурси, включително метод, път и код на отговор.",
|
||||
"httpDestAISessionLogsTitle": "Дневници за AI сесии",
|
||||
"httpDestAISessionLogsDescription": "AI заявки до шлюза и отговори на сесии, включително подканвания, отговори на модели и използване на жетони.",
|
||||
"httpDestSaveChanges": "Запази промените",
|
||||
"httpDestCreateDestination": "Създаване на дестинация",
|
||||
"httpDestUpdatedSuccess": "Дестинацията беше актуализирана успешно",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "Вижте като Админ",
|
||||
"resourceLauncherResourceDetailsDescription": "Информация и статус на връзката за този ресурс.",
|
||||
"resourceLauncherResourceDetails": "Детайли за ресурса",
|
||||
"resourceLauncherSitesDescription": "Ресурсът е достъпен чрез следните сайтове.",
|
||||
"resourceLauncherViewSiteAsAdmin": "Вижте сайта като Админ",
|
||||
"resourceLauncherFilterBySite": "Филтриране по сайт",
|
||||
"resourceLauncherSshCommand": "SSH Команда",
|
||||
"resourceLauncherSshCommandDescription": "Използвайте Pangolin CLI, за да отворите SSH сесия до този ресурс.",
|
||||
"resourceLauncherAuthMethodsDescription": "Методи на автентикация, активирани за този ресурс.",
|
||||
"resourceLauncherPrivateClientRequired": "Свържете се с клиент на вашето устройство, за да получите частен достъп до този ресурс.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "Изисква се връзка с клиента",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Без Етикет",
|
||||
"resourceLauncherAiGateway": "AI Портал",
|
||||
"resourceLauncherNoSite": "Няма Сайт",
|
||||
"resourceLauncherAvailableModels": "Налични Модели",
|
||||
"resourceLauncherAvailableModelsDescription": "Модели, които можете да използвате с този AI портал.",
|
||||
"resourceLauncherAvailableModelsEmpty": "Няма налични модели за този ресурс.",
|
||||
"resourceLauncherAvailableModelsError": "Не можа да се заредят наличните модели.",
|
||||
"resourceLauncherApiKeys": "API Ключове",
|
||||
"resourceLauncherApiKeysDescription": "Използвайте вашия ключ за идентичност или атрибутиран ключ за удостоверяване с този ресурс.",
|
||||
"resourceLauncherApiKeysIdentity": "Ключ за Идентичност",
|
||||
"resourceLauncherApiKeysManual": "Атрибуте Ключове",
|
||||
"resourceLauncherApiKeysEmpty": "Няма налични API ключове за този ресурс.",
|
||||
"resourceLauncherApiKeysError": "Не можа да се заредят API ключовете.",
|
||||
"resourceLauncherNoResourcesInGroup": "Няма ресурси в тази група",
|
||||
"resourceLauncherEmptyStateTitle": "Няма Налични Ресурси",
|
||||
"resourceLauncherEmptyStateDescription": "Все още нямате достъп до никакви ресурси. Свържете се с вашия администратор, за да поискате достъп.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "Не можахте да изтриете изгледа на стартер. Моля, опитайте отново.",
|
||||
"memberPortalPrevious": "Предишен",
|
||||
"memberPortalNext": "Следващ",
|
||||
"httpSettings": "HTTP настройки",
|
||||
"tcpSettings": "TCP настройки",
|
||||
"udpSettings": "UDP настройки",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Установяване на защитена връзка…",
|
||||
"sshConnecting": "Свързване…",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Режим на unicode клавиатура",
|
||||
"sessionToolbarShow": "Показване на лентата с инструменти",
|
||||
"sessionToolbarHide": "Скриване на лентата с инструменти",
|
||||
"actionUpdateSiteApprovals": "Обновяване на одобренията на сайта"
|
||||
"actionUpdateSiteApprovals": "Обновяване на одобренията на сайта",
|
||||
"check": "Проверка"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Venligst tjek at du er loget ind som korrekt bruger.",
|
||||
"inviteErrorNoUser": "Beklager, men det ser ud til, at invitationen du prøver at få adgang til, ikke er til en eksisterende bruger.",
|
||||
"inviteCreateUser": "Opret venligst en konto først.",
|
||||
"inviteErrorOidcNotAllowed": "Invitationer kan kun accepteres af interne konti. Log ud og log ind med din adgangskode til denne e-mail.",
|
||||
"inviteLoginInternalOnly": "Invitationer kræver en intern konto med en adgangskode. Opret en konto eller log ind med din adgangskode.",
|
||||
"goHome": "Gå hjem",
|
||||
"inviteLogInOtherUser": "Log ind som en anden bruger",
|
||||
"createAnAccount": "Opret konto",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "Mål på dette site",
|
||||
"siteSetting": "{siteName} Indstillinger",
|
||||
"siteNewtTunnel": "Newt-site (anbefalet)",
|
||||
"pangolinSite": "Pangolin Site",
|
||||
"siteNewtTunnelDescription": "Lekkeste måte at oprette et indgangspunkt til ethvert netværk. Ingen ekstra opsætning på.",
|
||||
"siteWg": "Grunnleggende WireGuard",
|
||||
"siteWgDescription": "Brug en hvilken som helst WireGuard-klient til at etablere en tunnel. Manuel NAT-opsætning er påkrævet.",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "Ingen land fundet.",
|
||||
"siteSelectionDescription": "Dette site vil give forbindelse til målet.",
|
||||
"resourceType": "Ressourcetype",
|
||||
"resourceTypeDescription": "Dette styrer ressourceprotokolen, og hvordan den vises i browseren. Dette kan ikke ændres senere.",
|
||||
"resourceTypeDescription": "Dette styrer ressourceprotokollen og kan ikke ændres senere.",
|
||||
"resourceTypeSearch": "Søg ressource typer...",
|
||||
"resourceTypeNotFound": "Ingen ressource type fundet",
|
||||
"resourceTypeHttpDescription": "Proxy webtrafik over HTTPS ved brug af et domænenavn",
|
||||
"resourceTypeInferenceDescription": "Rute AI-anmodninger gennem vedhæftede udbydere",
|
||||
"resourceTypeSshDescription": "Adgang til en SSH-server fra browseren",
|
||||
"resourceTypeRdpDescription": "Adgang til et fjernskrivebord fra browseren",
|
||||
"resourceTypeVncDescription": "Adgang til et VNC-skrivebord fra browseren",
|
||||
"resourceTypeTcpDescription": "Proxy rå TCP-trafik ved at bruge et portnummer",
|
||||
"resourceTypeUdpDescription": "Proxy rå UDP-trafik ved at bruge et portnummer",
|
||||
"privateResourceTypeDescription": "Dette styrer, hvordan klienter når ressourcen. Dette kan ikke ændres senere.",
|
||||
"privateResourceTypeHostDescription": "Eksponer en enkelt vært på sitenettet mod tilkoblede klienter",
|
||||
"privateResourceTypeCidrDescription": "Eksponer et CIDR-område på sitenettet mod tilkoblede klienter",
|
||||
"privateResourceTypeHttpDescription": "Adgang til en HTTP eller HTTPS service gennem et domæne",
|
||||
"privateResourceTypeSshDescription": "Adgang til en SSH-server fra tilkoblede klienter",
|
||||
"resourceDomainDescription": "Ressourcen vil blive serveret på dette fuldstændigt kvalificerede domænenavnet.",
|
||||
"resourceHTTPSSettings": "HTTPS-indstillinger",
|
||||
"resourceHTTPSSettingsDescription": "Konfigurer hvordan ressourcen skal nås over HTTPS",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "Slet API-nøgle",
|
||||
"apiKeysManage": "Administrer API-nøgler",
|
||||
"apiKeysDescription": "API-nøgler bruges for at autentificere med integrasjons-API",
|
||||
"orgsManage": "Administrer Organisationer",
|
||||
"orgsDescription": "Vis og administrer alle organisationer i denne instans",
|
||||
"provisioningKeysTitle": "Provisioneringsnøgle",
|
||||
"provisioningKeysManage": "Administrer provisioneringsnøgler",
|
||||
"provisioningKeysDescription": "Provisioneringsnøgler bruges til at godkende automatiseret site-provisionering for din organisation.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "Navn (valgfrit)",
|
||||
"accessControls": "Adgangskontroller",
|
||||
"userDescription2": "Administrer indstillingerne for denne bruger",
|
||||
"userGeneralSettingsDescription": "Administrer denne brugers roller og indstillinger i organisationen",
|
||||
"accessRoleErrorAdd": "Kunne ikke tilføje bruger i rolle",
|
||||
"accessRoleErrorAddDescription": "Det opstod en fejl under tildeling af brugeren til rollen.",
|
||||
"userSaved": "Bruger gemt",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "Gem indstillinger",
|
||||
"policyAuthStackTitle": "Autentificering",
|
||||
"policyAuthStackDescription": "Kontroller hvilke autentificeringsmetoder som kræves for at få adgang til denne ressource",
|
||||
"policyAuthInferenceStackDescription": "Vælg hvilke brugere og roller der kan autentificere til denne AI-gateway",
|
||||
"policyAuthOrLogicTitle": "Flere autentificeringsmetoder aktive",
|
||||
"policyAuthOrLogicBanner": "Besøgende kan autentificere sig med en hvilken som helst af de aktive metoder nedenfor. De behøver ikke at fuldføre dem alle.",
|
||||
"policyAuthMethodActive": "Aktiv",
|
||||
"policyAuthMethodOff": "Af",
|
||||
"policyAuthSsoTitle": "Platform-SSO",
|
||||
"policyAuthSsoDescription": "Kræv påloging gennem din organisations identitetsudbyder",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "Log-in URL",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Hver bruger har allerede en identitets-API-nøgle, du behøver kun at oprette virtuelle API-nøgler til ikke-brugerklienter eller delt adgang. Brugere kan hente deres nøgle ved at logge ind med deres identitetsudbyder på denne ressourcens URL, hvor den vil blive vist efter login.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Hver bruger har allerede en identitets-API-nøgle, du behøver kun at oprette virtuelle API-nøgler til ikke-brugerklienter eller delt adgang. Brugere henter deres nøgle her efter at have logget ind med deres identitetsudbyder.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} brugere, {roles} roller",
|
||||
"policyAuthSsoDefaultIdp": "Standardudbyder",
|
||||
"policyAuthAddDefaultIdentityProvider": "Tilføj standardidentitetsudbyder",
|
||||
@@ -892,7 +916,7 @@
|
||||
"policyAccessRulesFallthroughOff": "Når regler er deaktiveret, går all trafik gennem til autentificering.",
|
||||
"policyAccessRulesFallthroughOn": "Når ingen regler matcher, fortsetter trafikken til autentificering.",
|
||||
"rulesPlaceholderCidr": "10.0.0.0/8",
|
||||
"rulesPlaceholderPath": "/admin/*",
|
||||
"rulesPlaceholderPath": "/administrator/*",
|
||||
"rulesPlaceholderGeo": "RU, KP",
|
||||
"rulesSave": "Gem Regler",
|
||||
"resourceErrorCreate": "Fejl under oprettelse af ressource",
|
||||
@@ -928,7 +952,7 @@
|
||||
"unknownCommand": "Ukjent kommando",
|
||||
"newtErrorFetchReleases": "Mislykkedes at hente utgivelsesinfo: {err}",
|
||||
"newtErrorFetchLatest": "Fejl ved hentning af seneste utgivelse: {err}",
|
||||
"newtEndpoint": "Endpoint",
|
||||
"newtEndpoint": "Slutpunkt",
|
||||
"newtId": "ID",
|
||||
"newtSecretKey": "Sikkerhedsnøgle",
|
||||
"newtVersion": "Version",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "Ny rolle kræves",
|
||||
"accessRoleErrorRemove": "Kunne ikke fjerne rolle",
|
||||
"accessRoleErrorRemoveDescription": "Det opstod en fejl under fjernelse af rollen.",
|
||||
"accessRoleInferenceBudget": "AI-budget",
|
||||
"accessRoleInferenceBudgetDescription": "Konfigurer hvordan medlemmer af denne rolle begrænser AI-brug baseret på forbrug eller token-grænser",
|
||||
"accessRoleName": "Rollenavn",
|
||||
"accessRoleQuestionRemove": "Du er ved at slette rollen `{name}`. Du kan ikke fortryde denne handling.",
|
||||
"accessRoleRemove": "Fjern Rolle",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "Ingen sites fundet.",
|
||||
"pangolinServerAdmin": "Serveradmin - Pangolin",
|
||||
"licenseTierProfessional": "Professionel licens",
|
||||
"licenseTierEnterprise": "Enterprise-licens",
|
||||
"licenseTierPersonal": "Personlig licens",
|
||||
"licenseTierEnterprise": "Bedrift",
|
||||
"licenseTierPersonal": "Personlig",
|
||||
"licenseTierTier1": "Begynder",
|
||||
"licenseTierTier2": "Skala",
|
||||
"licensed": "Licenseret",
|
||||
"yes": "Ja",
|
||||
"no": "Nej",
|
||||
@@ -1154,11 +1182,15 @@
|
||||
"idpJmespathAboutDescriptionLink": "Læs mere om JMESPath",
|
||||
"idpJmespathLabel": "Identifikatorsti",
|
||||
"idpJmespathLabelDescription": "Stien til brugeridentifikatoren i ID-tokenet",
|
||||
"idpIdentifierChangeTitle": "Advarsel om ændring af identifikatorsti",
|
||||
"idpIdentifierChangeDescription": "Du er ved at ændre identifikatorstien. Dette vil påvirke, hvordan eksisterende brugere bliver kortlagt. Brugere, der tidligere har logget ind gennem denne identitetsudbyder, genkendes muligvis ikke længere som de samme brugere.",
|
||||
"idpIdentifierChangeConfirmMessage": "Jeg bekræfter",
|
||||
"idpIdentifierChangeWarningText": "Dette vil påvirke, hvordan eksisterende brugere bliver kortlagt",
|
||||
"idpJmespathEmailPathOptional": "E-mailsti (Valgfrit)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Stien til brugerens e-mailadresse i ID-tokenet",
|
||||
"idpJmespathNamePathOptional": "Navn Sti (Valgfrit)",
|
||||
"idpJmespathNamePathOptionalDescription": "Stien til brugerens navn i ID-tokenet",
|
||||
"idpOidcConfigureScopes": "Scopes",
|
||||
"idpOidcConfigureScopes": "Scope",
|
||||
"idpOidcConfigureScopesDescription": "Mellemrumsepareret liste over OAuth2-scopes at be om",
|
||||
"idpSubmit": "Opret identitetsudbyder",
|
||||
"orgPolicies": "Organisationspolitikker",
|
||||
@@ -1236,7 +1268,7 @@
|
||||
"passwordRequirementsMet": "✓ Adgangskoden opfylder alle krav",
|
||||
"passwordStrength": "Adgangskodestyrke",
|
||||
"passwordStrengthWeak": "Svag",
|
||||
"passwordStrengthMedium": "Medium",
|
||||
"passwordStrengthMedium": "Middel",
|
||||
"passwordStrengthStrong": "Stærk",
|
||||
"passwordRequirements": "Krav:",
|
||||
"passwordRequirementLengthText": "8+ tegn",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "Fejl ved logout",
|
||||
"signingAs": "Logget ind som",
|
||||
"serverAdmin": "Serveradministrator",
|
||||
"promoteServerAdmin": "Fremme til serveradministrator",
|
||||
"promoteServerAdminTitle": "Fremme til Serveradministrator",
|
||||
"promoteServerAdminQuestion": "Er du sikker på at du vil forfremme {selectedUser} til serveradministrator?",
|
||||
"promoteServerAdminMessage": "Serveradministratorer har de højeste privilegier og kan administrere serveren.",
|
||||
"promoteServerAdminWarning": "Dette kan fortrydes når som helst ved at degradere brugeren.",
|
||||
"promoteServerAdminConfirm": "Fremme til Serveradministrator",
|
||||
"promoteServerAdminSuccess": "Bruger Forfremmet",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} er nu serveradministrator.",
|
||||
"promoteServerAdminError": "Kunne ikke forfremme bruger",
|
||||
"demoteServerAdmin": "Degradere fra serveradministrator",
|
||||
"demoteServerAdminTitle": "Degradere fra Serveradministrator",
|
||||
"demoteServerAdminQuestion": "Er du sikker på at du vil degradere {selectedUser} fra serveradministrator?",
|
||||
"demoteServerAdminMessage": "{selectedUser} vil miste alle serveradministrator privilegier.",
|
||||
"demoteServerAdminWarning": "Dette kan fortrydes når som helst ved at forfremme brugeren.",
|
||||
"demoteServerAdminConfirm": "Degradere fra serveradministrator",
|
||||
"demoteServerAdminSuccess": "Bruger degraderet",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} er ikke længere serveradministrator.",
|
||||
"demoteServerAdminError": "Kunne ikke degradere bruger",
|
||||
"managedSelfhosted": "Administreret selvhostet",
|
||||
"otpEnable": "Aktivér tofaktor",
|
||||
"otpDisable": "Deaktivér tofaktor",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "Slet site",
|
||||
"actionGetSite": "Hent site",
|
||||
"actionListSites": "Vis sites",
|
||||
"actionCreateAiProvider": "Opret AI-udbyder",
|
||||
"actionDeleteAiProvider": "Slet AI-udbyder",
|
||||
"actionGetAiProvider": "Få AI-udbyder",
|
||||
"actionListAiProviders": "Vis AI-udbydere",
|
||||
"actionUpdateAiProvider": "Opdater AI-udbyder",
|
||||
"actionCreateAiModel": "Opret AI-model",
|
||||
"actionDeleteAiModel": "Slet AI-model",
|
||||
"actionGetAiModel": "Hent AI-model",
|
||||
"actionListAiModels": "Vis AI-modeller",
|
||||
"actionUpdateAiModel": "Opdater AI-model",
|
||||
"actionCreateAiBudget": "Opret AI Budget",
|
||||
"actionDeleteAiBudget": "Slet AI Budget",
|
||||
"actionGetAiBudget": "Hent AI-budget",
|
||||
"actionListAiBudgets": "Vis AI Budgetter",
|
||||
"actionUpdateAiBudget": "Opdater AI Budget",
|
||||
"actionListResourceAiModels": "Vis Resource AI Modeller",
|
||||
"actionSetResourceAiModels": "Angiv Resource AI Modeller",
|
||||
"actionCreateVirtualApiKey": "Opret Virtuel API-nøgle",
|
||||
"actionDeleteVirtualApiKey": "Slet Virtuel API-nøgle",
|
||||
"actionGetVirtualApiKey": "Hent virtuel API-nøgle",
|
||||
"actionListVirtualApiKeys": "Vis Virtuelle API-nøgler",
|
||||
"actionUpdateVirtualApiKey": "Opdater Virtuel API-nøgle",
|
||||
"actionApplyBlueprint": "Brug blueprint",
|
||||
"actionListBlueprints": "Vis blueprints",
|
||||
"actionGetBlueprint": "Hent blueprint",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "Angiv ressource-PIN-kode",
|
||||
"actionSetResourceEmailWhitelist": "Angiv e-mailwhitelist for ressource",
|
||||
"actionGetResourceEmailWhitelist": "Hent e-mailwhitelist for ressource",
|
||||
"actionListResourcePolicies": "Vis ressourcepolitikker",
|
||||
"actionCreateResourcePolicy": "Opret ressourcepolitik",
|
||||
"actionGetResourcePolicy": "Få ressourceretningslinjen",
|
||||
"actionUpdateResourcePolicy": "Opdater ressourceretningslinjen",
|
||||
"actionDeleteResourcePolicy": "Slet ressourcepolitik",
|
||||
"actionSetResourcePolicyUsers": "Angiv ressourceretningslinjens brugere",
|
||||
"actionSetResourcePolicyRoles": "Angiv ressourceretningslinjens roller",
|
||||
"actionSetResourcePolicyPassword": "Angiv ressourceretningslinjen for adgangskode",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "Søg…",
|
||||
"searchPlaceholder": "Søg...",
|
||||
"emptySearchOptions": "Ingen valg fundet",
|
||||
"ipFilterSearchPlaceholder": "Indtast en IP-adresse…",
|
||||
"ipFilterEmptyMessage": "Indtast en IP-adresse for at filtrere efter",
|
||||
"create": "Opret",
|
||||
"orgs": "Organisationer",
|
||||
"loginError": "Der opstod en uventet fejl. Prøv venligst igen.",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "Opret maskinklient",
|
||||
"commandPaletteCreateAlertRule": "Opret varslingsregel",
|
||||
"commandPaletteCreateIdentityProvider": "Opret identitetsudbyder",
|
||||
"commandPaletteCreateAiProvider": "Opret AI-udbyder",
|
||||
"commandPaletteCreateVirtualApiKey": "Opret Virtuel API-nøgle",
|
||||
"commandPaletteToggleTheme": "Skift tema",
|
||||
"commandPaletteChooseOrganization": "Vælg organisation",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "Invitationer",
|
||||
"sidebarRoles": "Roller",
|
||||
"sidebarShareableLinks": "Delbare links",
|
||||
"sidebarAiGateway": "AI-gateway",
|
||||
"sidebarAiProviders": "Udbydere",
|
||||
"commandAiProviders": "AI-udbydere",
|
||||
"sidebarVirtualApiKeys": "Virtuelle API-nøgler",
|
||||
"sidebarMyApiKeys": "Dine API-nøgler",
|
||||
"sidebarAccount": "Startprogram",
|
||||
"commandVirtualApiKeys": "Virtuelle API-nøgler",
|
||||
"virtualApiKeysTitle": "Administrer Virtuelle API-nøgler",
|
||||
"virtualApiKeysDescription": "Opret og administrer manuelle API-nøgler til adgang til AI Gateway for offentlige AI-gateways",
|
||||
"virtualApiKeysTabIdentity": "Identitetsnøgler",
|
||||
"virtualApiKeysTabVirtual": "Virtuelle nøgler",
|
||||
"virtualApiKeysIdentitySplashTitle": "Identitetsnøgler for hver bruger",
|
||||
"virtualApiKeysIdentitySplashDescription": "Hver bruger har allerede en Pangolin-identitetsnøgle til denne organisation. Den er unik for deres konto og autentificerer dem til AI-gateways, de kan få adgang til.",
|
||||
"virtualApiKeysIdentitySplashExample": "Eksempel",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "Sådan får brugere deres nøgle",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Besøg en offentlig AI-gateway URL i browseren og log ind med deres konto.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "Eller gå til <url></url> mens du er logget ind.",
|
||||
"virtualApiKeysIdentitySplashManual": "Du kan oprette yderligere virtuelle API-nøgler på Virtuelle nøgler fanen. Disse nøgler kan være omfangsbegrænsede til specifikke offentlige AI-gateways og valgfrit være forbundet med en bruger. Oprettelse af en nøgle giver straks adgang til de valgte offentlige AI-gateways.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Opret en nøgle manuelt",
|
||||
"virtualApiKeysEmailIdentity": "E-maile Identitetsnøgler",
|
||||
"virtualApiKeysEmailIdentityDescription": "Send hver udvalgt bruger eller rolle deres Pangolin-identitetsnøgle.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "Send til alle brugere",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "E-maile hvert organisationmedlem, der har en konto-e-mail.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Brugere",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Roller",
|
||||
"virtualApiKeysEmailIdentitySubmit": "Send e-mails",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Identitetsnøgler er sendt",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "Sendte {sent} e-mails.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "{skipped} brugere blev sprunget over, fordi de ikke har en e-mail adresse.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "Vælg mindst en bruger eller rolle, eller send til alle brugere.",
|
||||
"virtualApiKeysEmailIdentityError": "Fejl ved sending af identitetsnøgler",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "Kunne ikke e-maile identitetsnøgler",
|
||||
"virtualApiKeysBannerTitle": "Identitetsnøgler til hver bruger",
|
||||
"virtualApiKeysBannerDescription": "Hver bruger har allerede en identitetsnøgle tilgængelig på {keysUrl}. Du kan også manuelt generere nøgler her, der giver direkte adgang til offentlige AI-gateways.",
|
||||
"virtualApiKeysBannerButtonText": "Vis Identitetsnøgler",
|
||||
"virtualApiKeys": "Virtuelle API-nøgler",
|
||||
"virtualApiKeysSearch": "Søg nøgler...",
|
||||
"virtualApiKeysCreate": "Opret Virtuel API-nøgle",
|
||||
"virtualApiKeysCreateDescription": "Opret en manuel nøgle, der kan kalde offentlige AI-gateways i denne organisation",
|
||||
"virtualApiKeysCreateButton": "Opret nøgle",
|
||||
"virtualApiKeysEmpty": "Ingen Virtuelle API-nøgler endnu",
|
||||
"virtualApiKeysName": "Navn",
|
||||
"virtualApiKeysDescriptionOptional": "Beskrivelse (valgfri)",
|
||||
"virtualApiKeysAssociateUserOptional": "Associer Bruger (valgfri)",
|
||||
"virtualApiKeysAssociateUserDescription": "Associer denne nøgle med en bruger for at spore brugen. Når denne nøgle er oprettet, giver den straks adgang til de valgte offentlige AI-gateways. Du behøver ikke associere en bruger manuelt til ressourcen. Nøglen vil også blive vist i brugerens profil.",
|
||||
"virtualApiKeysAllResources": "Alle offentlige AI-gateways",
|
||||
"virtualApiKeysAllResourcesDescription": "Tillad denne nøgle at få adgang til hver offentlig AI-gateway i organisationen",
|
||||
"virtualApiKeysSelectResources": "Offentlige AI Gateways",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Vælg ressourcer",
|
||||
"virtualApiKeysSelectResourcesDescription": "Vælg hvilke offentlige AI-gateways denne nøgle kan få adgang til",
|
||||
"virtualApiKeysNoResources": "Ingen ressourcer",
|
||||
"virtualApiKeysSecret": "Nøgle",
|
||||
"virtualApiKeysCopyKey": "Kopier denne nøgle. Du kan se den senere igen fra tabellen eller når du redigerer.",
|
||||
"virtualApiKeysViewSecret": "Vis hemmelighed",
|
||||
"virtualApiKeysViewSecretTitle": "Virtuel API Nøglehemmelighed",
|
||||
"virtualApiKeysViewSecretDescription": "Denne hemmelighed giver adgang til de offentlige AI-gateways til denne nøgle",
|
||||
"virtualApiKeysEdit": "Rediger Virtuel API-nøgle",
|
||||
"virtualApiKeysEditDescription": "Opdater den associerede bruger og offentlige AI-gateway adgang for denne nøgle",
|
||||
"virtualApiKeysSaveButton": "Gem ændringer",
|
||||
"virtualApiKeysSelectResourcesRequired": "Vælg mindst en offentlig AI-gateway, eller aktiver alle offentlige AI-gateways",
|
||||
"virtualApiKeysUpdated": "Virtuel API nøgle opdateret",
|
||||
"virtualApiKeysUpdatedDescription": "Den Virtuelle API nøgle er blevet opdateret",
|
||||
"virtualApiKeysErrorUpdate": "Fejl ved opdatering af virtuel API nøgle",
|
||||
"virtualApiKeysErrorUpdateDescription": "Kunne ikke opdatere virtuel API nøgle",
|
||||
"virtualApiKeysErrorCreate": "Fejl ved oprettelse af virtuel API nøgle",
|
||||
"virtualApiKeysErrorCreateDescription": "Kunne ikke oprette virtuel API nøgle",
|
||||
"virtualApiKeysErrorDelete": "Fejl ved sletning af virtuel API nøgle",
|
||||
"virtualApiKeysErrorDeleteMessage": "Kunne ikke slette virtuel API nøgle",
|
||||
"virtualApiKeysDeleted": "Virtuel API nøgle slettet",
|
||||
"virtualApiKeysDeletedDescription": "Den Virtuelle API nøgle er blevet slettet",
|
||||
"virtualApiKeysDelete": "Slet Virtuel API-nøgle",
|
||||
"virtualApiKeysDeleteConfirm": "Slet nøgle",
|
||||
"virtualApiKeysQuestionRemove": "Er du sikker på, at du vil slette denne virtuelle API-nøgle?",
|
||||
"virtualApiKeysMessageRemove": "Klienter, der bruger denne nøgle, vil miste adgang med det samme.",
|
||||
"virtualApiKeysErrorFetchSecret": "Fejl ved indlæsning af hemmelighed",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Kunne ikke indlæse den virtuelle API nøglehemmelighed",
|
||||
"virtualApiKeysFilterUnassigned": "Ikke tildelt",
|
||||
"virtualApiKeysInferenceBudget": "Budget",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Konfigurer hvordan denne nøgle begrænser AI-brug baseret på forbrug eller token-grænser",
|
||||
"virtualApiKeysEmailOnGenerate": "Email nøgle ved generering",
|
||||
"virtualApiKeysEmailThisKey": "Email denne nøgle",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Send nøglen til den associerede bruger og yderligere adresser efter oprettelsen",
|
||||
"virtualApiKeysEmailThisKeyDescription": "Send den aktuelle nøgle til den associerede bruger og yderligere adresser",
|
||||
"virtualApiKeysEmailSmtpRequired": "Email er ikke konfigureret på denne server",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "Konfigurer SMTP for at emaile Virtuelle API-nøgler.",
|
||||
"virtualApiKeysEmailSendToUser": "Send til associeret bruger",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Email nøglen til den associerede brugers konto-email",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "Associer en bruger for at sende nøglen til den bruger",
|
||||
"virtualApiKeysEmailAdditional": "Yderligere e-mails",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "Tilføj email og tryk Enter",
|
||||
"virtualApiKeysEmailRecipientsRequired": "Vælg den associerede bruger eller tilføj mindst én email-adresse",
|
||||
"myVirtualApiKeysTitle": "Dine API-nøgler",
|
||||
"myVirtualApiKeysDescription": "Se din identitetsnøgle og eventuelle Virtuelle API-nøgler tilskrevet dig i denne organisation",
|
||||
"myVirtualApiKeysResourceTitle": "Dine API-nøgler til {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "Se din identitetsnøgle og Virtuelle API-nøgler tilskrevet dig, der kan få adgang til {resourceName}",
|
||||
"myVirtualApiKeysIdentityTitle": "Identitetsnøgle",
|
||||
"myVirtualApiKeysIdentityHeadline": "Din Personlige API Nøgle",
|
||||
"myVirtualApiKeysIdentityDescription": "Din personlige nøgle til denne organisation. Den er unik for din konto og bruges til at identificere dig ved kald til AI Gateway ressourcer.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Din Personlige API Nøgle til {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Din personlige nøgle til denne organisation. Brug den til at kalde {resourceName}.",
|
||||
"myVirtualApiKeysManualTitle": "Tildelte Nøgler",
|
||||
"myVirtualApiKeysManualDescription": "Manuelle Virtuelle API-nøgler, en admin har tilknyttet din konto",
|
||||
"myVirtualApiKeysManualResourceDescription": "Manuelle Virtuelle API-nøgler tilknyttet din konto, der kan få adgang til {resourceName}",
|
||||
"myVirtualApiKeysManualEmpty": "Ingen tildelte nøgler endnu",
|
||||
"myVirtualApiKeysKindUser": "Identitet",
|
||||
"myVirtualApiKeysKindManual": "Manuel",
|
||||
"myVirtualApiKeysUnnamed": "Navnløs nøgle",
|
||||
"myVirtualApiKeysRevealSecret": "Vis hemmelighed",
|
||||
"myVirtualApiKeysViewSecretDescription": "Denne hemmelighed autentificerer dig til AI Gateway ressourcer",
|
||||
"aiClientConfigTitle": "Konfigurer Kodningsagenter",
|
||||
"aiClientConfigDescription": "Kopiér konfiguration for populære kodningsagenter, eller lad Pangolin CLI opsætte det automatisk for dig.",
|
||||
"aiClientConfigDescriptionClaude": "Anthropics agentikodningsværktøj til terminalen.",
|
||||
"aiClientConfigDescriptionCodex": "OpenAIs agentikodningsværktøj til terminalen.",
|
||||
"aiClientConfigDescriptionOpencode": "Open source terminal kodningsagent.",
|
||||
"aiClientConfigDescriptionGemini": "Googles agentiske kodningsværktøj til terminalen.",
|
||||
"aiClientConfigSetup": "Opsætning",
|
||||
"aiClientConfigTabCli": "Automatisk (CLI)",
|
||||
"aiClientConfigTabManual": "Manuel Konfiguration",
|
||||
"aiClientConfigPreset": "Konfiguration",
|
||||
"aiClientConfigStep": "Trin {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "Dette udsnit indeholder eksempelværdier, såsom et modelnavn eller en ressource-URL. Udskift dem med dine egne, før du bruger det.",
|
||||
"aiClientConfigRevealError": "Kunne ikke indlæse din API-nøgle.",
|
||||
"aiClientConfigRevealRetry": "Prøv igen",
|
||||
"resourceGeneralAiClientConfigDescription": "Denne ressource kan bruges fra almindelige klienter som Claude Code og OpenCode. <configLink>Lær mere</configLink>",
|
||||
"aiProvidersTitle": "AI-udbydere",
|
||||
"aiProvidersDescription": "Forbind modeludbydere til AI-arbejdsopgaver i denne organisation",
|
||||
"aiProvidersBannerTitle": "Forbind Modeludbydere",
|
||||
"aiProvidersBannerDescription": "Udbydere er de moduler, Pangolin bruger til AI-arbejdsopgaver. Forbind OpenAI, Anthropic, og andre udbydere her, og tilknyt dem derefter til offentlige AI-gateways, så brugere kan tilkalde modeller med identitetsbevidst adgang, budgetter, og logning.",
|
||||
"aiProvidersAdd": "Tilføj Udbyder",
|
||||
"aiProvidersSearch": "Søg udbydere...",
|
||||
"aiProvidersEmpty": "Ingen AI-udbydere endnu",
|
||||
"aiProviderCreate": "Opret AI-udbyder",
|
||||
"aiProviderCreateDescription": "Tilføj en modeludbyder for denne organisation",
|
||||
"aiProviderSeeAll": "Se Alle Udbydere",
|
||||
"aiProviderSetting": "Udbyderindstillinger for {providerName}",
|
||||
"aiProviderSettingDescription": "Konfigurer denne AI-udbyder",
|
||||
"aiProviderGeneral": "Generelt",
|
||||
"aiProviderGeneralDescription": "Basisindstillinger for denne udbyder",
|
||||
"aiProviderConfiguration": "Konfiguration",
|
||||
"aiProviderConfigurationDescription": "Netværksrouting og autentificering for denne udbyder",
|
||||
"aiProviderNetworkSettings": "Netværksindstillinger",
|
||||
"aiProviderNetworkSettingsDescription": "Vælg hvordan trafik når denne udbyder",
|
||||
"aiProviderAuthSettings": "Autentificering",
|
||||
"aiProviderAuthSettingsDescription": "Konfigurer hvordan denne udbyder autentificerer anmodninger til sin upstream URL",
|
||||
"aiProviderBudgetSettings": "Budget",
|
||||
"aiProviderBudgetSettingsDescription": "Konfigurer hvordan denne udbyder begrænser brug baseret på forbrug eller token-grænser",
|
||||
"aiBudgetAdd": "Tilføj Budget",
|
||||
"aiBudgetEmpty": "Ingen budgetter konfigureret endnu. Klik på Tilføj Budget for at angive et forbrugs- eller tokengränse.",
|
||||
"aiBudgetUnit": "Forbrugstype",
|
||||
"aiBudgetPeriod": "Nulstil periode",
|
||||
"aiBudgetAmount": "Maksimalt Forbrug",
|
||||
"aiBudgetAmountPlaceholder": "Maksimalt forbrug",
|
||||
"aiBudgetPeriodHourly": "Timelig",
|
||||
"aiBudgetPeriodDaily": "Daglig",
|
||||
"aiBudgetPeriodWeekly": "Ugentlig",
|
||||
"aiBudgetPeriodMonthly": "Månedlig",
|
||||
"aiBudgetPeriodYearly": "Årlig",
|
||||
"aiBudgetPeriodLifetime": "Livstid",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "Tokens",
|
||||
"aiBudgetConflictError": "Et budget for denne nulstillingsperiode og forbrugstype eksisterer allerede",
|
||||
"aiBudgetInvalidAmountError": "Indtast et maksimalt forbrug større end 0",
|
||||
"aiBudgetUpdated": "Budgetter opdateret",
|
||||
"aiBudgetErrorSave": "Kunne ikke opdatere budgetter",
|
||||
"aiProviderType": "Udbydertype",
|
||||
"aiProviderTypeSearch": "Søg udbydere...",
|
||||
"aiProviderTypeNotFound": "Ingen udbydertype fundet",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI-gateway",
|
||||
"aiProviderTypeCustom": "Brugerdefineret",
|
||||
"aiProviderTypeOpenaiDescription": "OpenAI API",
|
||||
"aiProviderTypeAnthropicDescription": "Anthropic API",
|
||||
"aiProviderTypeGoogleGeminiDescription": "Google Gemini API",
|
||||
"aiProviderTypeVertexAiDescription": "Google Vertex AI API",
|
||||
"aiProviderTypeBedrockDescription": "Amazon Bedrock Runtime API",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "Microsoft Foundry API",
|
||||
"aiProviderTypeOpenRouterDescription": "OpenRouter API",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "Vercel AI-gateway API",
|
||||
"aiProviderTypeCustomDescription": "Brug din egen endpoint eller ruter via stedmål",
|
||||
"aiProviderUpstreamUrl": "Opstrøms URL",
|
||||
"aiProviderUpstreamUrlDescription": "Basis-URL for udbyderens API",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Lad være tom for at bruge den standard opstrøms URL for denne udbyder",
|
||||
"aiProviderEffectiveUpstreamUrl": "Effektiv opstrøms URL",
|
||||
"aiProviderApiKey": "API-nøgle",
|
||||
"aiProviderApiKeyDescription": "API-nøgle bruges til at godkende forespørgsler til denne udbyder",
|
||||
"aiProviderCustomHeadersDescription": "Headers sendt ved hver forespørgsel til denne udbyder. Newline adskilt: Header-Name: værdi",
|
||||
"aiProviderApiKeyLastChars": "API-nøgle",
|
||||
"aiProviderAuthType": "Godkendelsestype",
|
||||
"aiProviderAuthTypeSearch": "Søg godkendelsestyper...",
|
||||
"aiProviderAuthTypeNotFound": "Ingen godkendelsestype fundet",
|
||||
"aiProviderAuthTypeBearer": "Bearer",
|
||||
"aiProviderAuthTypeBearerDescription": "Autorisation: Bearer nøgle. Bruges af OpenAI og de fleste udbydere",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-nøgle",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "x-api-nøgle header. Bruges af Anthropic",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-nøgle",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "x-goog-api-nøgle header. Bruges af Google Gemini",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Autorisation: Splunk nøgle. Bruges af Splunk HTTP Event Collector",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-autorisation: Bearer nøgle. Bruges af Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeNone": "Ingen godkendelse",
|
||||
"aiProviderAuthTypePassthrough": "Gennemsigtighed",
|
||||
"aiProviderAuthTypeDescription": "Hvordan opstrøms API godkender forespørgsler",
|
||||
"aiProviderAuthTypePassthroughDescription": "Send opkalderens API-nøgle headers til det opstrøms link",
|
||||
"aiProviderAuthTypeNoneDescription": "Send ikke godkendelsesheadere til det opstrøms link",
|
||||
"aiProviderRoutingMode": "Routeringsmodus",
|
||||
"aiProviderRoutingModeDescription": "Send trafik til en opstrøms URL eller til HTTP mål på dine steder",
|
||||
"aiProviderRoutingModeUrl": "Opstrøms URL",
|
||||
"aiProviderRoutingModeUrlDescription": "Kald en offentlig eller privat API basis-URL",
|
||||
"aiProviderRoutingModeTarget": "Stedmål",
|
||||
"aiProviderRoutingModeTargetDescription": "Rute gennem mål på dine steder",
|
||||
"aiProviderRoutingModeTargetNote": "Efter oprettelse af denne udbyder, konfigurer mål på Netværksindstillinger fanen.",
|
||||
"aiProviderTargetNoOne": "Denne udbyder har ingen mål. Tilføj et mål for at rute forespørgsler gennem dine steder.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Websteder, der er forbundet til eksterne noder, kan ikke tilgås for at blive dirigeret til via AI Gateway-udbydere.",
|
||||
"aiProviderSkipTlsVerification": "Spring TLS-verifikation over",
|
||||
"aiProviderSkipTlsVerificationDescription": "Deaktiver TLS-certifikat verifikation for opstrømsforbindelsen",
|
||||
"aiProviderBudget": "Budget",
|
||||
"aiProviderBudgetDescription": "Valgfrit udgifts- eller token budget for denne udbyder",
|
||||
"aiProviderBudgetAmount": "Budgetbeløb",
|
||||
"aiProviderBudgetUnit": "Budgetenhed",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Tokens",
|
||||
"aiProviderEnabled": "Aktiveret",
|
||||
"aiProviderEnabledDescription": "Deaktiver helt denne udbyder på tværs af alle ressourcer",
|
||||
"aiProviderErrorCreate": "Kunne ikke oprette AI-udbyder",
|
||||
"aiProviderErrorUpdate": "Kunne ikke opdatere AI-udbyder",
|
||||
"aiProviderErrorDelete": "Kunne ikke slette AI-udbyder",
|
||||
"aiProviderErrorLoad": "Kunne ikke indlæse AI-udbyder",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Indtast en gyldig opstrøms URL",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Opstrøms URL er påkrævet for denne udbyder",
|
||||
"aiProviderErrorAuthTypeRequired": "Godkendelsestype er påkrævet",
|
||||
"aiProviderErrorApiKeyRequired": "API-nøgle er påkrævet",
|
||||
"aiProviderErrorRoutingModeTarget": "Rute til stedmål er kun tilgængelig for brugerdefinerede udbydere",
|
||||
"aiProviderErrorCapabilitiesRequired": "Vælg mindst en API-kapabilitet",
|
||||
"aiProviderCapabilities": "API-funktioner",
|
||||
"aiProviderCapabilitiesDescription": "Vælg hvilke API-formater denne udbyder kan håndtere. Kendte udbydere starter med anbefalede standarder.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Vælg hvilke API-formater denne specialudbyder kan håndtere",
|
||||
"aiProviderCapabilitiesSelect": "Vælg funktioner",
|
||||
"aiProviderCapabilitiesEmpty": "Ingen funktioner fundet",
|
||||
"aiProviderCapabilitiesSearch": "Søg funktioner...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI Chat Kompletteringer",
|
||||
"aiCapabilityOpenaiChatDescription": "Understøtter /v1/chat/completions",
|
||||
"aiCapabilityOpenaiResponses": "OpenAI Svar",
|
||||
"aiCapabilityOpenaiResponsesDescription": "Understøtter /v1/responses",
|
||||
"aiCapabilityAnthropicMessages": "Anthropic Beskeder",
|
||||
"aiCapabilityAnthropicMessagesDescription": "Understøtter /v1/messages",
|
||||
"aiCapabilityV1Models": "Model Liste",
|
||||
"aiCapabilityV1ModelsDescription": "Understøtter opdagelse af /v1/models modeller",
|
||||
"aiCapabilityGeminiGenerateContent": "Gemini Generer Indhold",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Understøtter den direkte Gemini API",
|
||||
"aiCapabilityBedrockModelInvoke": "Bedrock Modeller Invoker",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Understøtter Amazon Bedrock InvokeModel",
|
||||
"aiCapabilityGoogleGenerateContent": "Vertex Generer Indhold",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Understøtter Vertex AI Gemini format",
|
||||
"aiCapabilityGoogleRawPredict": "Vertex Rå Forudsigelse",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Understøtter Vertex AI rawPredict for Anthropic-modeller",
|
||||
"aiCapabilityBedrockConverse": "Bedrock Samtale",
|
||||
"aiCapabilityBedrockConverseDescription": "Understøtter Amazon Bedrock Converse API",
|
||||
"aiProviderCreated": "AI-udbyder oprettet",
|
||||
"aiProviderUpdated": "AI-udbyder opdateret",
|
||||
"aiProviderDeleted": "AI-udbyder slettet",
|
||||
"aiProviderDelete": "Slet udbyder",
|
||||
"aiProviderDeleteConfirm": "Slet udbyder",
|
||||
"aiProviderQuestionRemove": "Er du sikker på, at du vil slette denne AI-udbyder?",
|
||||
"aiProviderMessageRemove": "Dette vil permanent slette udbyderen og dens modeller og mål. Dette kan ikke fortrydes.",
|
||||
"aiProviderErrorNoUpdate": "AI-udbyder er ikke tilgængelig til opdatering",
|
||||
"aiProviderModels": "Modeller",
|
||||
"aiProviderModelsDescription": "Definer tillad og blokér lister for denne udbyder. Forespørgsler skal matche en tillad post og må ikke matche en blokér post.",
|
||||
"aiProviderCreateModelsDescription": "Vælg hvilke modeller denne udbyder kan betjene. En tom tillad-liste nægter al trafik. Du kan tilføje eller ændre modeller senere.",
|
||||
"aiProviderModelsPlaceholder": "Søg modeller eller skriv en brugerdefineret nøgle",
|
||||
"aiProviderModelsAllow": "Tillad Liste",
|
||||
"aiProviderModelsAllowDescription": "Modeller, der kan bruges gennem denne udbyder. Tom betyder, at alt afvises.",
|
||||
"aiProviderModelsAllowPlaceholder": "Indtast modelnøgle",
|
||||
"aiProviderModelsAllowEmpty": "Ingen modeller tilføjet endnu.",
|
||||
"aiProviderModelsBlock": "Blokér Liste",
|
||||
"aiProviderModelsBlockDescription": "Modeller at afvise, selvom de matcher en tillad post.",
|
||||
"aiProviderModelsBlockPlaceholder": "Indtast modelnøgle",
|
||||
"aiProviderModelsBlockEmpty": "Ingen modeller tilføjet endnu.",
|
||||
"aiProviderModelsAdd": "Tilføj Modeller",
|
||||
"aiProviderModelsClearAll": "Ryd All",
|
||||
"aiProviderModelsAddCustom": "Tilføj \"{key}\"",
|
||||
"aiProviderModelsAddCustomHint": "Tryk på Enter for at tilføje denne brugerdefinerede modelnøgle.",
|
||||
"aiProviderModelsAddBulk": "Tilføj {count} brugerdefinerede nøgler",
|
||||
"aiProviderModelsAddBulkHint": "Tryk på Enter for at tilføje {count} brugerdefinerede nøgler.",
|
||||
"aiProviderModelsAddOne": "Tilføj {key} nu",
|
||||
"aiProviderModelsAddSelected": "Tilføj Valgte",
|
||||
"aiProviderModelsSelectedCount": "{count} valgt",
|
||||
"aiProviderModelsSelectAll": "Vælg alle",
|
||||
"aiProviderModelsClearSelected": "Ryd",
|
||||
"aiProviderModelsBulkHint": "Vælg kendte modeller eller skriv en brugerdefineret nøgle.",
|
||||
"aiProviderModelsCatalogEmpty": "Ingen matchende katalogmodeller.",
|
||||
"aiProviderModelsCatalogHeading": "Kendte Modeller",
|
||||
"aiProviderModelsAllLabel": "Alle modeller",
|
||||
"aiProviderModelsAllPatternHint": "Jokertegn: *",
|
||||
"aiProviderModelsAddAllAllow": "Tillad alle modeller",
|
||||
"aiProviderModelsAddAllBlock": "Blokér alle modeller",
|
||||
"aiProviderModelsAddAllDescription": "Bruger * wildcard så hver modelnøgle matcher.",
|
||||
"aiProviderModelsViewMore": "Se mere ({count})",
|
||||
"aiProviderModelsViewLess": "Se mindre",
|
||||
"aiProviderModelsRemove": "Fjern model",
|
||||
"aiProviderModelsEditHint": "Klik for at redigere modelindstillinger",
|
||||
"aiProviderModelsSourceCatalog": "Kendt katalog model",
|
||||
"aiProviderModelsSourceCustom": "Brugerdefineret modelnøgle",
|
||||
"aiProviderModelsSourcePattern": "Wildcard mønster",
|
||||
"aiProviderModelsSourceAll": "Matcher hver modelnøgle",
|
||||
"aiProviderModelsBudgetConfigured": "Budget konfigureret",
|
||||
"aiProviderModelsEditTitle": "Rediger Model",
|
||||
"aiProviderModelsEditDescription": "Opdater modellenøgle eller konfigurer dens budget.",
|
||||
"aiProviderModelsBudgetTab": "Budget",
|
||||
"aiProviderModelsKeyLabel": "Modelnøgle",
|
||||
"aiProviderModelsKeyRequired": "Indtast en modelnøgle",
|
||||
"aiProviderModelsKeyDuplicate": "Denne modelnøgle er allerede på en liste",
|
||||
"aiProviderModelsOverlapError": "Disse mønstre kan ikke være på begge lister: {keys}",
|
||||
"aiProviderModelsUpdated": "Modeller opdateret",
|
||||
"aiProviderModelsErrorUpdate": "Kunne ikke opdatere modeller",
|
||||
"aiResourceProviders": "Udbydere",
|
||||
"aiResourceProvidersDescription": "Vælg hvilke AI-udbydere denne AI-gateway kan bruge",
|
||||
"aiResourceProvidersHelp": "Vedhæft udbydere og vælg arv (brug hver udbyders lister) eller vælg (vælg en tillad liste for denne ressource). Tillad mønstre, der er i konflikt på tværs af vedhæftede udbydere er ikke tilladt.",
|
||||
"aiResourceProvidersSelect": "Vælg udbydere",
|
||||
"aiResourceProvidersEmpty": "Ingen AI-udbydere fundet",
|
||||
"aiResourceProvidersNoneAttached": "Ingen udbydere tilknyttet endnu.",
|
||||
"aiResourceProvidersAdd": "Tilføj udbyder",
|
||||
"aiResourceProvidersRemove": "Fjern udbyder",
|
||||
"aiResourceProviderToggleEnabled": "Aktivér eller deaktiver denne udbyder på ressourcen",
|
||||
"aiResourceProviderDisabled": "Deaktiveret",
|
||||
"aiResourceProvidersUpdated": "Udbydere opdateret",
|
||||
"aiResourceProvidersErrorUpdate": "Kunne ikke opdatere udbydere",
|
||||
"aiResourceProviderEditDescription": "Vælg, hvordan denne udbyders modeller vises på denne ressource.",
|
||||
"viewProviderSettings": "Se Udbyderindstillinger",
|
||||
"aiResourceProviderMode": "Adgangsmodus",
|
||||
"aiResourceProviderModeInherit": "Arv",
|
||||
"aiResourceProviderModeSelect": "Vælg",
|
||||
"aiResourceProviderModeSelectSummary": "Vælg · {count} modeller",
|
||||
"aiResourceProviderModeInheritHelp": "Brug denne udbyders tillad- og blokér lister som konfigureret på udbyderen.",
|
||||
"aiResourceProviderModeSelectHelp": "Vælg et underindstillet af denne udbyders tillad liste modeller til denne ressource.",
|
||||
"aiResourceProviderAllowModels": "Tillad liste",
|
||||
"aiResourceProviderAllowModelsSelect": "Vælg modeller",
|
||||
"aiResourceProviderAllowModelsSearch": "Søg modeller...",
|
||||
"aiResourceProviderAllowModelsEmpty": "Ingen modeller fundet",
|
||||
"aiResourceProviderAllowModelsHelp": "Kun modeller fra denne udbyders tillad-liste kan vælges.",
|
||||
"aiResourceAliasRequired": "Alias kræves for AI-gateways",
|
||||
"aiResourceDomainConfiguration": "Domænekonfiguration",
|
||||
"aiResourceDomainConfigurationDescription": "Vælg det domæne, klienter vil bruge for at nå denne AI-gateway.",
|
||||
"aiUsageAnalyticsTitle": "AI Brug Analyse",
|
||||
"aiUsageAnalyticsDescription": "Analyser AI-gateway omkostninger, token forbrug, og aktivitet på tværs af udbydere, ressourcer, roller og brugere",
|
||||
"aiUsageTabOverview": "Oversigt",
|
||||
"aiUsageTabProviders": "Udbydere",
|
||||
"aiUsageTabResources": "Ressourcer",
|
||||
"aiUsageFilterProvider": "Udbyder",
|
||||
"aiUsageFilterModel": "Model",
|
||||
"aiUsageFilterResource": "Ressource",
|
||||
"aiUsageFilterRole": "Rolle",
|
||||
"aiUsageFilterUser": "Bruger",
|
||||
"aiUsageFilterAllProviders": "Alle Udbydere",
|
||||
"aiUsageFilterAllModels": "Alle Modeller",
|
||||
"aiUsageFilterAllResources": "Alle Ressourcer",
|
||||
"aiUsageFilterAllRoles": "Alle Roller",
|
||||
"aiUsageFilterAllUsers": "Alle Brugere",
|
||||
"aiUsageFilterSearch": "Søg...",
|
||||
"aiUsageFilterNotFound": "Ingen valg fundet",
|
||||
"aiUsageResetFilters": "Nulstil Filtre",
|
||||
"aiUsageRefresh": "Opdater",
|
||||
"aiUsageTokenTypePrompt": "Vis",
|
||||
"aiUsageTokenTypeCacheRead": "Cache læs",
|
||||
"aiUsageTokenTypeCacheWrite": "Cache skriv",
|
||||
"aiUsageTokenTypeCompletion": "Komplettering",
|
||||
"aiUsageTokenTypeReasoning": "Begrundelse",
|
||||
"aiUsageRequests": "Forespørgsler",
|
||||
"aiUsageCost": "Omkostning",
|
||||
"aiUsageTokens": "Tokens",
|
||||
"aiUsageOther": "Andet",
|
||||
"aiUsageTotalRequests": "Total Forespørgsler",
|
||||
"aiUsageTotalTokens": "Samlede tokens",
|
||||
"aiUsageTotalCost": "Total Omkostning",
|
||||
"aiUsageEstimated": "Estimeret",
|
||||
"aiUsageRequestVolume": "Forespørgselsvolumen",
|
||||
"aiUsageTokenUsage": "Token Forbrug",
|
||||
"aiUsageModelCost": "Model Omkostning",
|
||||
"aiUsageModelTokens": "Model tokens",
|
||||
"aiUsageTopModels": "Top Modeller",
|
||||
"aiUsageTopProviders": "Top Udbydere",
|
||||
"aiUsageProviderCost": "Udbyder Omkostning",
|
||||
"aiUsageProviderTokenUsage": "Udbyder Token Forbrug",
|
||||
"aiUsageTopResources": "Top Ressourcer",
|
||||
"aiUsageResourceCost": "Ressource Omkostning",
|
||||
"aiUsageResourceTokenUsage": "Ressource Token Forbrug",
|
||||
"aiUsageResourceTypePublic": "Offentlig Ressource",
|
||||
"aiUsageResourceTypeSite": "Privat Ressource",
|
||||
"aiUsageNoResource": "Ingen ressource",
|
||||
"aiUsageRolesTab": "Roller",
|
||||
"aiUsageUsersTab": "Brugere",
|
||||
"aiUsageTopRoles": "Top Roller",
|
||||
"aiUsageRoleCost": "Rolle Omkostning",
|
||||
"aiUsageRoleTokenUsage": "Rolle Token Forbrug",
|
||||
"aiUsageTopUsers": "Top Brugere",
|
||||
"aiUsageUserCost": "Bruger Omkostning",
|
||||
"aiUsageUserTokenUsage": "Bruger Token Forbrug",
|
||||
"aiUsageUnknownUser": "Ukendt bruger",
|
||||
"aiUsageVirtualApiKeysTab": "Virtuelle API Nøgler",
|
||||
"aiUsageFilterVirtualApiKey": "Virtuel API Nøgle",
|
||||
"aiUsageFilterAllVirtualApiKeys": "Alle Virtuelle API Nøgler",
|
||||
"aiUsageTopVirtualApiKeys": "Top Virtuelle API Nøgler",
|
||||
"aiUsageVirtualApiKeyCost": "Virtuel API Nøgle Omkostning",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Virtuel API Nøgle Token Forbrug",
|
||||
"aiUsageUnknownVirtualApiKey": "Ingen virtuel API nøgle",
|
||||
"aiUsageUnnamedVirtualApiKey": "Unavngiven nøgle",
|
||||
"aiUsageLoading": "Indlæser...",
|
||||
"aiUsageNoData": "Ingen data",
|
||||
"resourceBudgetSettings": "Budget",
|
||||
"resourceBudgetSettingsDescription": "Konfigurer, hvordan denne AI-gateway begrænser brug baseret på udgifts- eller token-grænser",
|
||||
"sidebarApiKeys": "API-nøgler",
|
||||
"sidebarOrgs": "Organisationer",
|
||||
"sidebarProvisioning": "Provisionering",
|
||||
"sidebarSettings": "Indstillinger",
|
||||
"sidebarAllUsers": "Alle brugere",
|
||||
@@ -1677,7 +2174,7 @@
|
||||
"commandBilling": "Fakturering",
|
||||
"commandEnterpriseLicenses": "Licenser",
|
||||
"commandSettings": "Indstillinger",
|
||||
"commandLauncher": "Launcher",
|
||||
"commandLauncher": "Startprogram",
|
||||
"commandResourceLauncher": "Ressource-launcher",
|
||||
"commandSearchResults": "Søgeresultater",
|
||||
"alertingTitle": "Varsling",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "Varslingsregel gemt",
|
||||
"alertingRuleSavedCreatedDescription": "Din nye varslingsregel blev oprettet. Du kan fortsætte at redigere den på denne side.",
|
||||
"alertingRuleSavedUpdatedDescription": "Dine ændringer i denne varslingsreglen blev gemt.",
|
||||
"alertingTestAlertSent": "Test-alarm sendt",
|
||||
"alertingTestAlertSentDescription": "En testalarm blev sendt til de handlinger, der er konfigureret på denne regel.",
|
||||
"alertingEditRule": "Rediger varslingsregel",
|
||||
"alertingCreateRule": "Opret varslingsregel",
|
||||
"alertingRuleCredenzaDescription": "Vælg hvad som skal overvåges, når det skal varsles, og hvordan du vil blive informert",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "Hver regel binder sammen hvad som skal overvåges (et site, sundhedstjek eller ressource), når det skal varsles (for eksempel offline eller usund), og hvordan varsle teamet dit via e-mail, webhooks eller integrationer. Brug denne listen for at oprette, aktivere og administrer disse reglerne.",
|
||||
"alertingHealthChecksBannerTitle": "Overvåg sundhed & ressourcer",
|
||||
"alertingHealthChecksBannerDescription": "Sundhedstjek er HTTP- eller TCP-monitorer, du definerer én gang. Du kan derefter bruge dem som kilder i varslingsregler, så du bliver varslet, når et mål bliver sundt eller usundt. Sundhedstjek på ressourcer vises også her.",
|
||||
"alertingTestRule": "Test Alarmregel",
|
||||
"alertingAddActionHeading": "Tilføj Ny Handling",
|
||||
"alertingSelectActionType": "Vælg en Handling",
|
||||
"alertingNoActionsTitle": "Ingen handlinger konfigureret",
|
||||
"alertingNoActionsSaveDescription": "Tilføj mindst én handling, så denne regel kan give besked til nogen, når den udløses.",
|
||||
"alertingNoActionsTestDescription": "Tilføj mindst én handling, inden du kan teste denne regel.",
|
||||
"standaloneHcTableTitle": "Sundhedstjek",
|
||||
"standaloneHcSearchPlaceholder": "Søg i sundhedstjek…",
|
||||
"standaloneHcAddButton": "Opret sundhedstjek",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "Type:",
|
||||
"createDomainName": "Navn:",
|
||||
"createDomainValue": "Værdi:",
|
||||
"multiSelectFilterCount": "{count} valgt",
|
||||
"createDomainCnameRecords": "CNAME-poster",
|
||||
"createDomainARecords": "A-poster",
|
||||
"createDomainRecordNumber": "Post {number}",
|
||||
@@ -2193,13 +2699,15 @@
|
||||
"subnetPlaceholder": "Subnet",
|
||||
"addressDescription": "Den interne adressen til klienten. Skal falle inden for organisationens subnet.",
|
||||
"selectSites": "Vælg sites",
|
||||
"selectResources": "Vælg ressourcer",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# ressource} other {# ressourcer}}",
|
||||
"selectLabels": "Vælg etiketter",
|
||||
"sitesDescription": "Klienten vil have forbindelse til de valgte områdene",
|
||||
"clientInstallOlm": "Installer Olm",
|
||||
"clientInstallOlmDescription": "Få Olm til at køre på dit system",
|
||||
"clientOlmCredentials": "Legitimationsoplysninger",
|
||||
"clientOlmCredentialsDescription": "Dette er hvordan klienten vil godkende med serveren",
|
||||
"olmEndpoint": "Endpoint",
|
||||
"olmEndpoint": "Slutpunkt",
|
||||
"olmId": "ID",
|
||||
"olmSecretKey": "Sikkerhedsnøgle",
|
||||
"clientCredentialsSave": "Gem brugeroplysninger",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "Metode",
|
||||
"healthSelectScheme": "Vælg metode",
|
||||
"healthCheckPortInvalid": "Porten skal være mellem 1 og 65535",
|
||||
"healthCheckHostnameInvalid": "Værtsnavnet må ikke indeholde mellemrum",
|
||||
"healthCheckPath": "Sti",
|
||||
"healthHostname": "IP / Vært",
|
||||
"healthPort": "Port",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "Tid er i sekunder",
|
||||
"requireDeviceApproval": "Kræv enhedsgodkendelse",
|
||||
"requireDeviceApprovalDescription": "Brugere med denne rolle skal have nye enheder godkendt af en administrator, før de kan oprette forbindelse og få adgang til ressourcer.",
|
||||
"sshSettings": "SSH Indstillinger",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "AI-gateway",
|
||||
"sshAccess": "SSH-adgang",
|
||||
"rdpSettings": "RDP Indstillinger",
|
||||
"vncSettings": "VNC Indstillinger",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "SSH-server",
|
||||
"rdpServer": "RDP-server",
|
||||
"vncServer": "VNC-server",
|
||||
@@ -2363,7 +2873,7 @@
|
||||
"resourcesTableNoProxyResourcesFound": "Ingen proxy-ressourcer fundet.",
|
||||
"resourcesTableNoInternalResourcesFound": "Ingen private ressourcer fundet.",
|
||||
"resourcesTableDestination": "Destination",
|
||||
"resourcesTableAlias": "Alias",
|
||||
"resourcesTableAlias": "Navn",
|
||||
"resourcesTableAliasAddress": "Alias adresse",
|
||||
"resourcesTableAliasAddressInfo": "Denne adressen er en del af organisationens subnet. Den bruges til at løse aliasposter ved hjælp af intern DNS-opløsning.",
|
||||
"resourcesTableClients": "Klienter",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "AI-gateway",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Skema",
|
||||
"editInternalResourceDialogEnableSsl": "Aktivér TLS",
|
||||
@@ -2409,7 +2920,7 @@
|
||||
"editInternalResourceDialogDestinationHostDescription": "IP-adressen eller værtsnavnet til ressourcen på sitets netværk.",
|
||||
"editInternalResourceDialogDestinationIPDescription": "IP eller hostnavn til ressourcen på sitets netværk.",
|
||||
"editInternalResourceDialogDestinationCidrDescription": "CIDR-området til ressourcen på sitets netværk.",
|
||||
"editInternalResourceDialogAlias": "Alias",
|
||||
"editInternalResourceDialogAlias": "Navn",
|
||||
"editInternalResourceDialogAliasDescription": "Et valgfrit internt DNS-alias for denne ressource.",
|
||||
"createInternalResourceDialogNoSitesAvailable": "Ingen tilgængelige steder",
|
||||
"createInternalResourceDialogNoSitesAvailableDescription": "Du skal have mindst ét Newt-site med et konfigureret subnet for at oprette private ressourcer.",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "Tillad ICMP-ping",
|
||||
"privateResourceNetworkAccess": "Netværksadgang",
|
||||
"privateResourceNetworkAccessDescription": "Styr TCP/UDP-portadgang og om ICMP-ping er tilladt for denne ressource.",
|
||||
"hostSettings": "Værtindstillinger",
|
||||
"cidrSettings": "CIDR-indstillinger",
|
||||
"hostSettings": "Vært",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Ressourceegenskaber",
|
||||
"createInternalResourceDialogName": "Navn",
|
||||
"createInternalResourceDialogSite": "Websted",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "AI-gateway",
|
||||
"scheme": "Skema",
|
||||
"createInternalResourceDialogScheme": "Skema",
|
||||
"createInternalResourceDialogEnableSsl": "Aktivér TLS",
|
||||
@@ -2467,7 +2979,7 @@
|
||||
"createInternalResourceDialogDestination": "Destination",
|
||||
"createInternalResourceDialogDestinationHostDescription": "IP-adressen eller værtsnavnet til ressourcen på sitets netværk.",
|
||||
"createInternalResourceDialogDestinationCidrDescription": "CIDR-området til ressourcen på sitets netværk.",
|
||||
"createInternalResourceDialogAlias": "Alias",
|
||||
"createInternalResourceDialogAlias": "Navn",
|
||||
"createInternalResourceDialogAliasDescription": "Et valgfrit internt DNS-alias for denne ressource.",
|
||||
"internalResourceAliasLocalWarning": "Aliasser, der ender på .local, kan forårsage opløsningsproblemer på grund af mDNS på nogle netværk.",
|
||||
"internalResourceDownstreamSchemeRequired": "Skema er påkrævet for HTTP-ressourcer",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Tilføj et CIDR-område (f.eks. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Kunne ikke indlæse subnets",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Præference Etiketter",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites med disse etiketter vil blive tvunget til at oprette forbindelse gennem denne fjerne exit-node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sider med disse etiketter vil foretrække at forbinde gennem denne fjernudgarnknude.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Vælg etiketter...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Søg efter etiketter...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Kunne ikke indlæse etiketter",
|
||||
@@ -2710,7 +3222,7 @@
|
||||
"authPageBrandingRemoveTitle": "Fjern markedsføring for autentiseringsside",
|
||||
"authPageBrandingQuestionRemove": "Er du sikker på at du vil fjerne merkevarebyggingen for autentiseringssider?",
|
||||
"authPageBrandingDeleteConfirm": "Bekræft sletning af merkevarebygging",
|
||||
"brandingLogoURL": "Logo URL",
|
||||
"brandingLogoURL": "Logo-URL",
|
||||
"brandingLogoURLOrPath": "Logoen URL eller sti",
|
||||
"brandingLogoPathDescription": "Indtast en URL eller en lokal sti.",
|
||||
"brandingLogoURLDescription": "Indtast en offentligt tilgængelig webadresse til din logobillede.",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "Prioritet",
|
||||
"priorityDescription": "Ruter med højere prioritet evalueres først. Prioritet = 100 betyder automatisk rækkefølge (systembeslutninger). Brug et andet tal for at gennemtvinge manuel prioritet.",
|
||||
"instanceName": "Forekomst navn",
|
||||
"clearInstanceName": "Nulstil serverforbindelse",
|
||||
"pathMatchModalTitle": "Konfigurere matching af sti",
|
||||
"pathMatchModalDescription": "Opsæt hvordan indgående forespørgsler skal matches baseret på deres sti.",
|
||||
"pathMatchType": "Matchtype",
|
||||
@@ -3051,6 +3564,7 @@
|
||||
"validPassword": "Gyldig adgangskode",
|
||||
"validEmail": "Gyldig e-mail",
|
||||
"validSSO": "Gyldig SSO",
|
||||
"validVirtualAPIKey": "Gyldig Virtuel API Nøgle",
|
||||
"view": "Vis",
|
||||
"configManaged": "Konfiguration administrert",
|
||||
"connectedClient": "Tilsluttet klient",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "Handlingsloger",
|
||||
"logRetention": "Logopbevaring",
|
||||
"logRetentionDescription": "Håndter hvor længe ulike typer logs beholdes for denne organisation, eller deaktivér dem",
|
||||
"logRetentionDisabledWarningTitle": "Logbevaring deaktiveret",
|
||||
"logRetentionDisabledWarningDescription": "{logType} gemmes ikke for denne organisation, så nye aktiviteter vises ikke her. Aktiver logbevaring i sikkerhedsindstillingerne for at begynde at indsamle disse logs.",
|
||||
"logRetentionDisabledWarningButton": "Gå til sikkerhedsindstillinger",
|
||||
"requestLogsDescription": "Se detaljerede forespørgselslogs for ressourcer i denne organisation",
|
||||
"aiSessionLogs": "AI Gateway Øktsprotokoller",
|
||||
"aiSessionLogsDescription": "Se prompt og svarudskrifter for AI gateway forespørgsler i denne organisation",
|
||||
"sidebarLogsAi": "Øktsprotokoller",
|
||||
"commandLogsAi": "Øktsprotokoller",
|
||||
"sidebarLogsAiUsage": "Bruganalyse",
|
||||
"commandLogsAiUsage": "Bruganalyse",
|
||||
"provider": "Udbyder",
|
||||
"capability": "Kompetence",
|
||||
"model": "Model",
|
||||
"virtualApiKey": "Virtuel API Nøgle",
|
||||
"noVirtualApiKey": "Ingen virtuel API nøgle",
|
||||
"stream": "Stream",
|
||||
"streaming": "Streaming",
|
||||
"nonStreaming": "Ikke-Streaming",
|
||||
"statusCode": "Statuskode",
|
||||
"aiSessionId": "Økts-ID",
|
||||
"aiSessionRequest": "Forespørgsel",
|
||||
"aiSessionResponse": "Svar",
|
||||
"aiSessionNoData": "Ingen data optaget",
|
||||
"aiSessionCouldNotParse": "(rå, kunne ikke analysere udskrift)",
|
||||
"aiSessionLogTruncated": "Denne økt blev afkortet inden lagring og kan være ufuldstændig.",
|
||||
"aiSessionViewRaw": "Se Rå JSON",
|
||||
"aiSessionViewChat": "Se Chat",
|
||||
"cost": "Omkostning",
|
||||
"estimated": "Estimeret",
|
||||
"tokenUsage": "Token Forbrug",
|
||||
"promptTokens": "Prompt tokens",
|
||||
"cacheReadTokens": "Cache Læs Tokens",
|
||||
"cacheWriteTokens": "Cache Skriv Tokens",
|
||||
"completionTokens": "Komplettering Tokens",
|
||||
"reasoningTokens": "Begrundelse Tokens",
|
||||
"totalTokens": "Samlede tokens",
|
||||
"requestAnalyticsDescription": "Se detaljeret anmodningsanalyse for ressourcer i denne organisation",
|
||||
"logRetentionRequestLabel": "Be om logbevaring",
|
||||
"logRetentionRequestDescription": "Hvor længe du vil beholde forespørgselslogs",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "Hvor længe handlingen skal gemmes",
|
||||
"logRetentionConnectionLabel": "Forbindelseslog",
|
||||
"logRetentionConnectionDescription": "Hvor længe du vil beholde forbindelseslogs",
|
||||
"logRetentionAISessionsLabel": "AI Gateway Økt Log Opbevaring",
|
||||
"logRetentionAISessionsDescription": "Hvor længe at opbevare AI gateway prompt/svar øktprotokoller",
|
||||
"logRetentionDisabled": "Deaktiveret",
|
||||
"logRetention3Days": "3 dage",
|
||||
"logRetention7Days": "7 dage",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "Kildeadresse",
|
||||
"destinationAddress": "Måladresse ",
|
||||
"duration": "Varighed",
|
||||
"licenseRequiredToUse": "En <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink>-licens eller <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> er påkrævet for at bruge denne funktion. <bookADemoLink>Book en gratis demo eller POC-prøveversion for at lære mere.</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> er nødvendig for at bruge denne funktion. Denne funktion er også tilgængelig i <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Book en gratis demo eller POC-prøve for at lære mere.</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Enterprise Edition licens</enterpriseEditionLink> er krævet. <bookADemoLink>Book en demo eller prøve</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition licens</enterpriseEditionLink> er krævet. <bookADemoLink>Book en demo eller prøve</bookADemoLink>",
|
||||
"certResolver": "Certifikatløser",
|
||||
"certResolverDescription": "Vælg certifikatløser som skal bruges for denne ressource.",
|
||||
"selectCertResolver": "Vælg certifikatløser",
|
||||
@@ -3225,7 +3776,7 @@
|
||||
"regenerateCredentialsWarning": "Regenerering af legitimationsoplysninger vil ugyldiggøre de forrige og forårsage en frakobling. Sørg for at opdatere alle konfigurationer, der bruger disse legitimationsoplysninger.",
|
||||
"confirm": "Bekræft",
|
||||
"regenerateCredentialsConfirmation": "Er du sikker på at du vil regenerere legetimationerne?",
|
||||
"endpoint": "Endpoint",
|
||||
"endpoint": "Slutpunkt",
|
||||
"Id": "Id",
|
||||
"SecretKey": "Hemmelig nøgle",
|
||||
"niceId": "God ID",
|
||||
@@ -3242,6 +3793,7 @@
|
||||
"noData": "Ingen data",
|
||||
"machineClients": "Maskinklienter",
|
||||
"install": "Installer",
|
||||
"downloadInstaller": "Download Installer",
|
||||
"run": "Kjør",
|
||||
"envFile": "Miljøfil",
|
||||
"serviceFile": "Tjenestefil",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "Valg af flere sites muliggør robust routing og failover for høj tilgængelighed.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Læs mere",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Begrens adgang til specifikke TCP/UDP-porte eller tillade/blokere alle porte.",
|
||||
"createInternalResourceDialogHttpConfiguration": "HTTP-konfiguration",
|
||||
"createInternalResourceDialogHttpConfiguration": "Domænekonfiguration",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "Vælg domænet klienter vil bruge for at nu denne ressource via HTTP eller HTTPS.",
|
||||
"editInternalResourceDialogHttpConfiguration": "HTTP-konfiguration",
|
||||
"editInternalResourceDialogHttpConfiguration": "Domænekonfiguration",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "Vælg domænet klienter vil bruge for at nu denne ressource via HTTP eller HTTPS.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "Offline",
|
||||
"approvalsEmptyStateTitle": "Enhedsgodkendelser er ikke aktiveret",
|
||||
"approvalsEmptyStateDescription": "Aktivere godkendelser af enheder for at roller skal godkendes af admin før brugere kan opret forbindelse til nye enheder.",
|
||||
"approvalsEmptyStateHowToTitle": "Sådan aktiveres",
|
||||
"approvalsEmptyStateStep1Title": "Gå til roller",
|
||||
"approvalsEmptyStateStep1Description": "Naviger til organisationens roller indstillinger for at konfigurere enhedsgodkendelser.",
|
||||
"approvalsEmptyStateStep2Title": "Aktivér enhedsgodkendelser",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "Udstyrs- og tunnelforbindelseshændelser, inklusive forbindelser og frakobling.",
|
||||
"httpDestRequestLogsTitle": "HTTP-forespørgselslogs",
|
||||
"httpDestRequestLogsDescription": "HTTP-forespørgsel logs for bekræftede ressourcer, inklusive metode, sti og responskode.",
|
||||
"httpDestAISessionLogsTitle": "AI-session Logs",
|
||||
"httpDestAISessionLogsDescription": "AI-gateway anmodninger og respons-sessioner, inklusive prompts, modelresponser og tokenforbrug.",
|
||||
"httpDestSaveChanges": "Gem ændringer",
|
||||
"httpDestCreateDestination": "Opret mål",
|
||||
"httpDestUpdatedSuccess": "Målet er opdateret",
|
||||
@@ -3687,7 +4242,7 @@
|
||||
"memberPortalResourceDisabled": "Ressource deaktiveret",
|
||||
"memberPortalShowingResources": "Viser {start}-{end} af {total} ressourcer",
|
||||
"resourceLauncherTitle": "Ressource Starter",
|
||||
"resourceSidebarLauncherTitle": "Launcher",
|
||||
"resourceSidebarLauncherTitle": "Startprogram",
|
||||
"resourceLauncherDescription": "Se alle tilgængelige ressourcer og start dem fra ét centralt knudepunkt",
|
||||
"resourceLauncherSearchPlaceholder": "Søg i dine ressourcer...",
|
||||
"resourceLauncherDefaultView": "Standard",
|
||||
@@ -3703,7 +4258,7 @@
|
||||
"resourceLauncherSaveForEveryone": "Gem for Alle",
|
||||
"resourceLauncherSaveForEveryoneDescription": "Del denne visning med alle organisationsmedlemmer. Når den er ikke markeret, er visningen kun synlig for dig.",
|
||||
"resourceLauncherMakePersonal": "Gør Personlig",
|
||||
"resourceLauncherFilter": "Filter",
|
||||
"resourceLauncherFilter": "Filtre",
|
||||
"resourceLauncherFilterWithCount": "Filter, {count} anvendt",
|
||||
"resourceLauncherSort": "Sortér",
|
||||
"resourceLauncherSortAscending": "Sortér stigende",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "Vis som Admin",
|
||||
"resourceLauncherResourceDetailsDescription": "Forbindelsesoplysninger og status for denne ressource.",
|
||||
"resourceLauncherResourceDetails": "Ressourcedetaljer",
|
||||
"resourceLauncherSitesDescription": "Ressourcen er tilgængelig via følgende sites.",
|
||||
"resourceLauncherViewSiteAsAdmin": "Vis side som Admin",
|
||||
"resourceLauncherFilterBySite": "Filtrer efter site",
|
||||
"resourceLauncherSshCommand": "SSH-kommando",
|
||||
"resourceLauncherSshCommandDescription": "Brug Pangolin CLI til at åbne en SSH-session til denne ressource.",
|
||||
"resourceLauncherAuthMethodsDescription": "Autentificeringsmetoder aktiveret for denne ressource.",
|
||||
"resourceLauncherPrivateClientRequired": "Forbind med en klient på din enhed for at få privat adgang til denne ressource.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "Klientforbindelse påkrævet",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Uden Etiket",
|
||||
"resourceLauncherAiGateway": "AI-gateway",
|
||||
"resourceLauncherNoSite": "Ingen Site",
|
||||
"resourceLauncherAvailableModels": "Tilgængelige Modeller",
|
||||
"resourceLauncherAvailableModelsDescription": "Modeller, du kan bruge med denne AI-gateway.",
|
||||
"resourceLauncherAvailableModelsEmpty": "Ingen modeller er tilgængelige for denne ressource.",
|
||||
"resourceLauncherAvailableModelsError": "Kunne ikke indlæse tilgængelige modeller.",
|
||||
"resourceLauncherApiKeys": "API Nøgler",
|
||||
"resourceLauncherApiKeysDescription": "Brug din identitetsnøgle eller en attributeret nøgle til at godkende med denne ressource.",
|
||||
"resourceLauncherApiKeysIdentity": "Identitetsnøgle",
|
||||
"resourceLauncherApiKeysManual": "Attributerede Nøgler",
|
||||
"resourceLauncherApiKeysEmpty": "Ingen API-nøgler er tilgængelige for denne ressource.",
|
||||
"resourceLauncherApiKeysError": "Kunne ikke indlæse API-nøgler.",
|
||||
"resourceLauncherNoResourcesInGroup": "Ingen ressourcer i denne gruppe",
|
||||
"resourceLauncherEmptyStateTitle": "Ingen ressourcer tilgængelige",
|
||||
"resourceLauncherEmptyStateDescription": "Du har endnu ikke adgang til nogen ressourcer. Kontakt din administrator for at anmode om adgang.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "Kan ikke slette launcher-visningen. Prøv venligst igen.",
|
||||
"memberPortalPrevious": "Forrige",
|
||||
"memberPortalNext": "Næste",
|
||||
"httpSettings": "HTTP Indstillinger",
|
||||
"tcpSettings": "TCP Indstillinger",
|
||||
"udpSettings": "UDP Indstillinger",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Oprette en sikker forbindelse…",
|
||||
"sshConnecting": "Opretter forbindelse til…",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Unicode tastaturtilstand",
|
||||
"sessionToolbarShow": "Vis værktøjslinje",
|
||||
"sessionToolbarHide": "Skjul værktøjslinje",
|
||||
"actionUpdateSiteApprovals": "Opdater godkendelser på site"
|
||||
"actionUpdateSiteApprovals": "Opdater godkendelser på site",
|
||||
"check": "Tjek"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Bitte stelle sicher, dass du als korrekter Benutzer angemeldet bist.",
|
||||
"inviteErrorNoUser": "Es tut uns leid, aber es sieht so aus, als sei die Einladung, auf die du zugreifen möchtest, nicht für einen existierenden Benutzer bestimmt.",
|
||||
"inviteCreateUser": "Bitte erstelle zuerst ein Konto.",
|
||||
"inviteErrorOidcNotAllowed": "Einladungen können nur von internen Konten angenommen werden. Melden Sie sich ab und melden Sie sich mit Ihrem Passwort für diese E-Mail an.",
|
||||
"inviteLoginInternalOnly": "Einladungen erfordern ein internes Konto mit einem Passwort. Erstellen Sie ein Konto oder melden Sie sich mit Ihrem Passwort an.",
|
||||
"goHome": "Zur Startseite",
|
||||
"inviteLogInOtherUser": "Als anderer Benutzer anmelden",
|
||||
"createAnAccount": "Konto erstellen",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "Ziele an diesem Standort",
|
||||
"siteSetting": "{siteName} Einstellungen",
|
||||
"siteNewtTunnel": "Newt Standort (empfohlen)",
|
||||
"pangolinSite": "Pangolin Seite",
|
||||
"siteNewtTunnelDescription": "Einfachster Weg, einen Einstiegspunkt in jedes Netzwerk zu erstellen. Keine zusätzliche Einrichtung.",
|
||||
"siteWg": "Einfacher WireGuard Tunnel",
|
||||
"siteWgDescription": "Verwende jeden WireGuard-Client, um einen Tunnel einzurichten. Manuelles NAT-Setup erforderlich.",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "Kein Land gefunden.",
|
||||
"siteSelectionDescription": "Dieser Standort wird die Verbindung zum Ziel herstellen.",
|
||||
"resourceType": "Ressourcentyp",
|
||||
"resourceTypeDescription": "Dies steuert das Ressourcenprotokoll und wie es im Browser gerendert wird. Dies kann später nicht geändert werden.",
|
||||
"resourceTypeDescription": "Dies steuert das Ressourcenprotokoll und kann später nicht mehr geändert werden.",
|
||||
"resourceTypeSearch": "Suche Ressourcentypen...",
|
||||
"resourceTypeNotFound": "Kein Ressourcentyp gefunden",
|
||||
"resourceTypeHttpDescription": "Leiten Sie Webverkehr über HTTPS unter Verwendung eines Domänennamens weiter",
|
||||
"resourceTypeInferenceDescription": "Leiten Sie KI-Anfragen über angehängte Anbieter weiter",
|
||||
"resourceTypeSshDescription": "Greifen Sie von einem Browser aus auf einen SSH-Server zu",
|
||||
"resourceTypeRdpDescription": "Greifen Sie von einem Browser aus auf einen Remote-Desktop zu",
|
||||
"resourceTypeVncDescription": "Greifen Sie von einem Browser aus auf einen VNC-Desktop zu",
|
||||
"resourceTypeTcpDescription": "Leiten Sie rohe TCP-Datenverkehr durch eine Portnummer weiter",
|
||||
"resourceTypeUdpDescription": "Leiten Sie rohe UDP-Datenverkehr durch eine Portnummer weiter",
|
||||
"privateResourceTypeDescription": "Dies steuert, wie Klienten auf die Ressource zugreifen können. Dies kann später nicht mehr geändert werden.",
|
||||
"privateResourceTypeHostDescription": "Einen einzelnen Host im Netzwerk der Seite für angeschlossene Clients verfügbar machen",
|
||||
"privateResourceTypeCidrDescription": "Einen CIDR-Bereich im Netzwerk der Seite für angeschlossene Clients verfügbar machen",
|
||||
"privateResourceTypeHttpDescription": "Auf einen HTTP- oder HTTPS-Dienst über eine Domain zugreifen",
|
||||
"privateResourceTypeSshDescription": "Auf einen SSH-Server von verbundenen Clients aus zugreifen",
|
||||
"resourceDomainDescription": "Die Ressource wird unter diesem vollständig qualifizierten Domainnamen bereitgestellt.",
|
||||
"resourceHTTPSSettings": "HTTPS-Einstellungen",
|
||||
"resourceHTTPSSettingsDescription": "Konfigurieren Sie den Zugriff auf die Ressource über HTTPS",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "API-Schlüssel löschen",
|
||||
"apiKeysManage": "API-Schlüssel verwalten",
|
||||
"apiKeysDescription": "API-Schlüssel werden zur Authentifizierung mit der Integrations-API verwendet",
|
||||
"orgsManage": "Organisationen verwalten",
|
||||
"orgsDescription": "Alle Organisationen in dieser Instanz anzeigen und verwalten",
|
||||
"provisioningKeysTitle": "Bereitstellungsschlüssel",
|
||||
"provisioningKeysManage": "Bereitstellungsschlüssel verwalten",
|
||||
"provisioningKeysDescription": "Bereitstellungsschlüssel werden verwendet, um die automatisierte Bereitstellung von Standorten für Ihr Unternehmen zu authentifizieren.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "Name (optional)",
|
||||
"accessControls": "Zugriffskontrolle",
|
||||
"userDescription2": "Verwalten Sie die Einstellungen dieses Benutzers",
|
||||
"userGeneralSettingsDescription": "Die Rollen und Einstellungen dieses Benutzers in der Organisation verwalten",
|
||||
"accessRoleErrorAdd": "Fehler beim Hinzufügen des Benutzers zur Rolle",
|
||||
"accessRoleErrorAddDescription": "Beim Hinzufügen des Benutzers zur Rolle ist ein Fehler aufgetreten.",
|
||||
"userSaved": "Benutzer gespeichert",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "Einstellungen speichern",
|
||||
"policyAuthStackTitle": "Authentifizierung",
|
||||
"policyAuthStackDescription": "Kontrollieren Sie, welche Authentifizierungsmethoden erforderlich sind, um auf diese Ressource zuzugreifen",
|
||||
"policyAuthInferenceStackDescription": "Wählen Sie, welche Benutzer und Rollen sich an diesem KI-Gateway authentifizieren können",
|
||||
"policyAuthOrLogicTitle": "Mehrere Authentifizierungsmethoden aktiv",
|
||||
"policyAuthOrLogicBanner": "Besucher können sich mit einer der unten aktiven Methoden authentifizieren. Sie müssen nicht alle abschließen.",
|
||||
"policyAuthMethodActive": "Aktiv",
|
||||
"policyAuthMethodOff": "Aus",
|
||||
"policyAuthSsoTitle": "Plattform SSO",
|
||||
"policyAuthSsoDescription": "Anmeldung über den Identitätsanbieter Ihrer Organisation erforderlich",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "Anmelde-URL",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Jeder Benutzer hat bereits einen Identitäts-API-Schlüssel, sodass Sie nur virtuelle API-Schlüssel für Nicht-Benutzer-Clients oder gemeinsamen Zugriff erstellen müssen. Benutzer können ihren Schlüssel abrufen, indem sie sich bei Ihrem Identitätsanbieter an der URL dieser Ressource anmelden, wo es nach der Anmeldung angezeigt wird.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Jeder Benutzer hat bereits einen Identitäts-API-Schlüssel, sodass Sie nur virtuelle API-Schlüssel für Nicht-Benutzer-Clients oder gemeinsamen Zugriff erstellen müssen. Benutzer rufen ihren Schlüssel hier ab, nachdem sie sich bei ihrem Identitätsanbieter angemeldet haben.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} Benutzer, {roles} Rollen",
|
||||
"policyAuthSsoDefaultIdp": "Standardanbieter",
|
||||
"policyAuthAddDefaultIdentityProvider": "Standardidentitätsanbieter hinzufügen",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "Neue Rolle ist erforderlich",
|
||||
"accessRoleErrorRemove": "Fehler beim Entfernen der Rolle",
|
||||
"accessRoleErrorRemoveDescription": "Beim Entfernen der Rolle ist ein Fehler aufgetreten.",
|
||||
"accessRoleInferenceBudget": "KI-Budget",
|
||||
"accessRoleInferenceBudgetDescription": "Konfigurieren Sie, wie diese Rolle die KI-Nutzung basierend auf Ausgaben oder Tokenlimits einschränkt",
|
||||
"accessRoleName": "Rollenname",
|
||||
"accessRoleQuestionRemove": "Du bist dabei die Rolle `{name}` zu löschen. Du kannst diese Aktion nicht rückgängig machen.",
|
||||
"accessRoleRemove": "Rolle entfernen",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "Keine Standorte gefunden.",
|
||||
"pangolinServerAdmin": "Server-Admin - Pangolin",
|
||||
"licenseTierProfessional": "Professional Lizenz",
|
||||
"licenseTierEnterprise": "Enterprise Lizenz",
|
||||
"licenseTierPersonal": "Persönliche Lizenz",
|
||||
"licenseTierEnterprise": "Firma",
|
||||
"licenseTierPersonal": "Persönlich",
|
||||
"licenseTierTier1": "Starter",
|
||||
"licenseTierTier2": "Maßstab",
|
||||
"licensed": "Lizenziert",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
@@ -1154,6 +1182,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "Mehr über JMESPath erfahren",
|
||||
"idpJmespathLabel": "Identifikationspfad",
|
||||
"idpJmespathLabelDescription": "Der JMESPath zum Benutzeridentifikator im ID-Token",
|
||||
"idpIdentifierChangeTitle": "Warnung zur Änderung des Identifikatorpfads",
|
||||
"idpIdentifierChangeDescription": "Sie stehen kurz davor, den Identifikatorpfad zu ändern. Dies wird beeinflussen, wie bestehende Benutzer zugeordnet werden. Benutzer, die sich zuvor über diesen Identitätsanbieter angemeldet haben, werden möglicherweise nicht mehr als dieselben Benutzer erkannt.",
|
||||
"idpIdentifierChangeConfirmMessage": "Ich bestätige",
|
||||
"idpIdentifierChangeWarningText": "Dies wird beeinflussen, wie bestehende Benutzer zugeordnet werden",
|
||||
"idpJmespathEmailPathOptional": "E-Mail-Pfad (Optional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Der JMESPath zur E-Mail-Adresse des Benutzers im ID-Token",
|
||||
"idpJmespathNamePathOptional": "Namenspfad (Optional)",
|
||||
@@ -1285,7 +1317,7 @@
|
||||
"generatePasswordResetCode": "Passwort zurücksetzen Code generieren",
|
||||
"passwordResetCodeGenerated": "Passwort zurücksetzen Code generiert",
|
||||
"passwordResetCodeGeneratedDescription": "Teilen Sie diesen Code mit dem Benutzer. Sie können ihn verwenden, um ihr Passwort zurückzusetzen.",
|
||||
"passwordResetUrl": "Reset URL",
|
||||
"passwordResetUrl": "URL zurücksetzen",
|
||||
"passwordNew": "Neues Passwort",
|
||||
"passwordNewConfirm": "Neues Passwort bestätigen",
|
||||
"changePassword": "Passwort ändern",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "Fehler beim Abmelden",
|
||||
"signingAs": "Angemeldet als",
|
||||
"serverAdmin": "Server-Administrator",
|
||||
"promoteServerAdmin": "Zum Server-Admin befördern",
|
||||
"promoteServerAdminTitle": "Zum Server-Admin befördern",
|
||||
"promoteServerAdminQuestion": "Sind Sie sicher, dass Sie {selectedUser} zum Server-Admin befördern möchten?",
|
||||
"promoteServerAdminMessage": "Server-Admins haben die höchsten Privilegien und können den Server verwalten.",
|
||||
"promoteServerAdminWarning": "Das kann jederzeit rückgängig gemacht werden, indem der Benutzer degradiert wird.",
|
||||
"promoteServerAdminConfirm": "Zum Server-Admin befördern",
|
||||
"promoteServerAdminSuccess": "Benutzer befördert",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} ist jetzt ein Server-Admin.",
|
||||
"promoteServerAdminError": "Fehler beim Befördern des Benutzers",
|
||||
"demoteServerAdmin": "Vom Server-Admin degradieren",
|
||||
"demoteServerAdminTitle": "Vom Server-Admin degradieren",
|
||||
"demoteServerAdminQuestion": "Sind Sie sicher, dass Sie {selectedUser} als Server-Admin degradieren möchten?",
|
||||
"demoteServerAdminMessage": "{selectedUser} wird alle Server-Admin-Rechte verlieren.",
|
||||
"demoteServerAdminWarning": "Das kann jederzeit rückgängig gemacht werden, indem der Benutzer befördert wird.",
|
||||
"demoteServerAdminConfirm": "Vom Server-Admin degradieren",
|
||||
"demoteServerAdminSuccess": "Benutzer degradiert",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} ist kein Server-Admin mehr.",
|
||||
"demoteServerAdminError": "Fehler beim Degradieren des Benutzers",
|
||||
"managedSelfhosted": "Verwaltetes Selbsthosted",
|
||||
"otpEnable": "Zwei-Faktor aktivieren",
|
||||
"otpDisable": "Zwei-Faktor deaktivieren",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "Standort löschen",
|
||||
"actionGetSite": "Standort abrufen",
|
||||
"actionListSites": "Standorte auflisten",
|
||||
"actionCreateAiProvider": "KI-Anbieter erstellen",
|
||||
"actionDeleteAiProvider": "KI-Anbieter löschen",
|
||||
"actionGetAiProvider": "KI-Anbieter abrufen",
|
||||
"actionListAiProviders": "KI-Anbieter auflisten",
|
||||
"actionUpdateAiProvider": "KI-Anbieter aktualisieren",
|
||||
"actionCreateAiModel": "KI-Modell erstellen",
|
||||
"actionDeleteAiModel": "KI-Modell löschen",
|
||||
"actionGetAiModel": "KI-Modell abrufen",
|
||||
"actionListAiModels": "KI-Modelle auflisten",
|
||||
"actionUpdateAiModel": "KI-Modell aktualisieren",
|
||||
"actionCreateAiBudget": "KI-Budget erstellen",
|
||||
"actionDeleteAiBudget": "KI-Budget löschen",
|
||||
"actionGetAiBudget": "KI-Budget abrufen",
|
||||
"actionListAiBudgets": "KI-Budgets auflisten",
|
||||
"actionUpdateAiBudget": "KI-Budget aktualisieren",
|
||||
"actionListResourceAiModels": "Ressourcen-KI-Modelle auflisten",
|
||||
"actionSetResourceAiModels": "Ressourcen-KI-Modelle festlegen",
|
||||
"actionCreateVirtualApiKey": "Virtuellen API-Schlüssel erstellen",
|
||||
"actionDeleteVirtualApiKey": "Virtuellen API-Schlüssel löschen",
|
||||
"actionGetVirtualApiKey": "Virtuellen API-Schlüssel abrufen",
|
||||
"actionListVirtualApiKeys": "Virtuelle API-Schlüssel auflisten",
|
||||
"actionUpdateVirtualApiKey": "Virtuellen API-Schlüssel aktualisieren",
|
||||
"actionApplyBlueprint": "Blueprint anwenden",
|
||||
"actionListBlueprints": "Blaupausen anzeigen",
|
||||
"actionGetBlueprint": "Erhalte Blaupause",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "Ressourcen-PIN festlegen",
|
||||
"actionSetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist festlegen",
|
||||
"actionGetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist abrufen",
|
||||
"actionListResourcePolicies": "Ressourcen-Richtlinien auflisten",
|
||||
"actionCreateResourcePolicy": "Ressourcen-Richtlinie erstellen",
|
||||
"actionGetResourcePolicy": "Ressourcenrichtlinie abrufen",
|
||||
"actionUpdateResourcePolicy": "Ressourcenrichtlinie aktualisieren",
|
||||
"actionDeleteResourcePolicy": "Ressourcen-Richtlinie löschen",
|
||||
"actionSetResourcePolicyUsers": "Ressourcenrichtlinienbenutzer festlegen",
|
||||
"actionSetResourcePolicyRoles": "Rollen der Ressourcenrichtlinie festlegen",
|
||||
"actionSetResourcePolicyPassword": "Passwort der Ressourcenrichtlinie festlegen",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "Suche…",
|
||||
"searchPlaceholder": "Suche...",
|
||||
"emptySearchOptions": "Keine Optionen gefunden",
|
||||
"ipFilterSearchPlaceholder": "Geben Sie eine IP-Adresse ein…",
|
||||
"ipFilterEmptyMessage": "Geben Sie eine IP-Adresse zur Filterung ein",
|
||||
"create": "Erstellen",
|
||||
"orgs": "Organisationen",
|
||||
"loginError": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "Maschinen-Client erstellen",
|
||||
"commandPaletteCreateAlertRule": "Alarmregel erstellen",
|
||||
"commandPaletteCreateIdentityProvider": "Identitätsanbieter erstellen",
|
||||
"commandPaletteCreateAiProvider": "KI-Anbieter erstellen",
|
||||
"commandPaletteCreateVirtualApiKey": "Virtuellen API-Schlüssel erstellen",
|
||||
"commandPaletteToggleTheme": "Thema umschalten",
|
||||
"commandPaletteChooseOrganization": "Organisation auswählen",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "Einladungen",
|
||||
"sidebarRoles": "Rollen",
|
||||
"sidebarShareableLinks": "Teilbare Links",
|
||||
"sidebarAiGateway": "KI-Gateway",
|
||||
"sidebarAiProviders": "Anbieter",
|
||||
"commandAiProviders": "KI-Anbieter",
|
||||
"sidebarVirtualApiKeys": "Virtuelle API-Schlüssel",
|
||||
"sidebarMyApiKeys": "Ihre API-Schlüssel",
|
||||
"sidebarAccount": "Starter",
|
||||
"commandVirtualApiKeys": "Virtuelle API-Schlüssel",
|
||||
"virtualApiKeysTitle": "Virtuelle API-Schlüssel verwalten",
|
||||
"virtualApiKeysDescription": "Erstellen und verwalten Sie manuelle API-Schlüssel für den Zugang zum KI-Gateway des öffentlichen KI-Gateways",
|
||||
"virtualApiKeysTabIdentity": "Identitätsschlüssel",
|
||||
"virtualApiKeysTabVirtual": "Virtuelle Schlüssel",
|
||||
"virtualApiKeysIdentitySplashTitle": "Identitätsschlüssel für jeden Benutzer",
|
||||
"virtualApiKeysIdentitySplashDescription": "Jeder Benutzer hat bereits einen Pangolin-Identitätsschlüssel für diese Organisation. Es ist einzigartig für ihr Konto und authentifiziert sie bei KI-Gateways, auf die sie zugreifen können.",
|
||||
"virtualApiKeysIdentitySplashExample": "Beispiel",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "Wie Benutzer ihren Schlüssel erhalten",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Besuchen Sie eine URL des öffentlichen KI-Gateways im Browser und melden Sie sich mit ihrem Konto an.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "Oder gehen Sie zu <url></url>, während Sie angemeldet sind.",
|
||||
"virtualApiKeysIdentitySplashManual": "Sie können zusätzliche virtuelle API-Schlüssel auf der Registerkarte \"Virtuelle Schlüssel\" erstellen. Diese Schlüssel können auf bestimmte öffentliche KI-Gateways abgestimmt und optional einem Benutzer zugeordnet werden. Durch das Erstellen eines Schlüssels wird sofortiger Zugang zu den ausgewählten öffentlichen KI-Gateways gewährt.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Schlüssel manuell erstellen",
|
||||
"virtualApiKeysEmailIdentity": "E-Mail-Identitätsschlüssel",
|
||||
"virtualApiKeysEmailIdentityDescription": "Senden Sie jedem ausgewählten Benutzer oder jeder Rolle ihren Pangolin-Identitätsschlüssel.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "An alle Benutzer senden",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "E-Mail an jedes Mitglied der Organisation, das eine Kontoe-Mail hat.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Benutzergeräte",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Rollen",
|
||||
"virtualApiKeysEmailIdentitySubmit": "E-Mails senden",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Identitätsschlüssel versandt",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "{sent} E-Mails wurden gesendet.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "{skipped} Benutzer wurden übersprungen, da sie keine E-Mail-Adresse haben.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "Wählen Sie mindestens einen Benutzer oder eine Rolle aus oder senden Sie an alle Benutzer.",
|
||||
"virtualApiKeysEmailIdentityError": "Fehler beim Senden der Identitätsschlüssel",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "Fehler beim E-Mail-Versand der Identitätsschlüssel",
|
||||
"virtualApiKeysBannerTitle": "Identitätsschlüssel für jeden Benutzer",
|
||||
"virtualApiKeysBannerDescription": "Jeder Benutzer hat bereits einen Identitätsschlüssel verfügbar unter {keysUrl}. Sie können auch manuell Schlüssel generieren, die direkten Zugang zu öffentlichen KI-Gateways gewähren.",
|
||||
"virtualApiKeysBannerButtonText": "Identitätsschlüssel anzeigen",
|
||||
"virtualApiKeys": "Virtuelle API-Schlüssel",
|
||||
"virtualApiKeysSearch": "Schlüssel suchen...",
|
||||
"virtualApiKeysCreate": "Virtuellen API-Schlüssel erstellen",
|
||||
"virtualApiKeysCreateDescription": "Einen manuellen Schlüssel erstellen, der öffentliche KI-Gateways in dieser Organisation aufrufen kann",
|
||||
"virtualApiKeysCreateButton": "Schlüssel erstellen",
|
||||
"virtualApiKeysEmpty": "Noch keine virtuellen API-Schlüssel vorhanden",
|
||||
"virtualApiKeysName": "Name",
|
||||
"virtualApiKeysDescriptionOptional": "Beschreibung (optional)",
|
||||
"virtualApiKeysAssociateUserOptional": "Benutzer zuordnen (optional)",
|
||||
"virtualApiKeysAssociateUserDescription": "Ordnen Sie diesen Schlüssel einem Benutzer zu, um die Nutzung zu verfolgen. Sobald erstellt, gewährt dieser Schlüssel sofort Zugriffe auf die ausgewählten öffentlichen KI-Gateways. Es ist nicht notwendig, manuell eine Verbindung zur Ressource herzustellen. Der Schlüssel wird auch im Benutzerprofil angezeigt.",
|
||||
"virtualApiKeysAllResources": "Alle öffentlichen KI-Gateways",
|
||||
"virtualApiKeysAllResourcesDescription": "Erlauben Sie diesem Schlüssel, auf jedes öffentliche KI-Gateway in der Organisation zuzugreifen",
|
||||
"virtualApiKeysSelectResources": "Öffentliche KI-Gateways",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Ressourcen auswählen",
|
||||
"virtualApiKeysSelectResourcesDescription": "Wählen Sie aus, auf welche öffentlichen KI-Gateways dieser Schlüssel zugreifen kann",
|
||||
"virtualApiKeysNoResources": "Keine Ressourcen",
|
||||
"virtualApiKeysSecret": "Schlüssel",
|
||||
"virtualApiKeysCopyKey": "Kopieren Sie diesen Schlüssel. Sie können ihn später aus der Tabelle oder beim Bearbeiten erneut anzeigen.",
|
||||
"virtualApiKeysViewSecret": "Geheimnis anzeigen",
|
||||
"virtualApiKeysViewSecretTitle": "Virtueller API-Schlüsselgeheimnis",
|
||||
"virtualApiKeysViewSecretDescription": "Dieses Geheimnis gewährt Zugang zu den öffentlichen KI-Gateways, die diesem Schlüssel zugewiesen sind",
|
||||
"virtualApiKeysEdit": "Virtuellen API-Schlüssel bearbeiten",
|
||||
"virtualApiKeysEditDescription": "Aktualisieren Sie den zugeordneten Benutzer und den Zugang zu öffentlichen KI-Gateways für diesen Schlüssel",
|
||||
"virtualApiKeysSaveButton": "Änderungen speichern",
|
||||
"virtualApiKeysSelectResourcesRequired": "Wählen Sie mindestens ein öffentliches KI-Gateway aus oder aktivieren Sie alle öffentlichen KI-Gateways",
|
||||
"virtualApiKeysUpdated": "Virtueller API-Schlüssel aktualisiert",
|
||||
"virtualApiKeysUpdatedDescription": "Der virtuelle API-Schlüssel wurde aktualisiert",
|
||||
"virtualApiKeysErrorUpdate": "Fehler beim Aktualisieren des virtuellen API-Schlüssels",
|
||||
"virtualApiKeysErrorUpdateDescription": "Fehler beim Aktualisieren des virtuellen API-Schlüssels",
|
||||
"virtualApiKeysErrorCreate": "Fehler beim Erstellen des virtuellen API-Schlüssels",
|
||||
"virtualApiKeysErrorCreateDescription": "Fehler beim Erstellen des virtuellen API-Schlüssels",
|
||||
"virtualApiKeysErrorDelete": "Fehler beim Löschen des virtuellen API-Schlüssels",
|
||||
"virtualApiKeysErrorDeleteMessage": "Fehler beim Löschen des virtuellen API-Schlüssels",
|
||||
"virtualApiKeysDeleted": "Virtueller API-Schlüssel gelöscht",
|
||||
"virtualApiKeysDeletedDescription": "Der virtuelle API-Schlüssel wurde gelöscht",
|
||||
"virtualApiKeysDelete": "Virtuellen API-Schlüssel löschen",
|
||||
"virtualApiKeysDeleteConfirm": "Schlüssel löschen",
|
||||
"virtualApiKeysQuestionRemove": "Sind Sie sicher, dass Sie diesen virtuellen API-Schlüssel löschen möchten?",
|
||||
"virtualApiKeysMessageRemove": "Clients, die diesen Schlüssel verwenden, verlieren sofort den Zugriff.",
|
||||
"virtualApiKeysErrorFetchSecret": "Fehler beim Laden des Geheimnisses",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Fehler beim Laden des virtuellen API-Schlüsselgeheimnisses",
|
||||
"virtualApiKeysFilterUnassigned": "Unzugewiesen",
|
||||
"virtualApiKeysInferenceBudget": "Budget",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Konfigurieren Sie, wie dieser Schlüssel die KI-Nutzung basierend auf Ausgaben oder Tokenlimits einschränkt",
|
||||
"virtualApiKeysEmailOnGenerate": "E-Mail-Schlüssel bei Erstellung",
|
||||
"virtualApiKeysEmailThisKey": "Diesen Schlüssel per E-Mail senden",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Senden Sie den Schlüssel an den zugeordneten Benutzer und zusätzliche Adressen, nachdem er erstellt wurde",
|
||||
"virtualApiKeysEmailThisKeyDescription": "Senden Sie den aktuellen Schlüssel an den zugeordneten Benutzer und zusätzliche Adressen",
|
||||
"virtualApiKeysEmailSmtpRequired": "E-Mail ist auf diesem Server nicht konfiguriert",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "Konfigurieren Sie SMTP, um virtuelle API-Schlüssel per E-Mail zu versenden.",
|
||||
"virtualApiKeysEmailSendToUser": "An den zugeordneten Benutzer senden",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Senden Sie den Schlüssel an die Kontoe-Mail des zugeordneten Benutzers",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "Ordnen Sie einen Benutzer zu, um den Schlüssel an diesen Benutzer zu senden",
|
||||
"virtualApiKeysEmailAdditional": "Zusätzliche E-Mails",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "E-Mail-Adresse hinzufügen und Enter drücken",
|
||||
"virtualApiKeysEmailRecipientsRequired": "Wählen Sie den zugeordneten Benutzer aus oder fügen Sie mindestens eine E-Mail-Adresse hinzu",
|
||||
"myVirtualApiKeysTitle": "Ihre API-Schlüssel",
|
||||
"myVirtualApiKeysDescription": "Sehen Sie sich Ihren Identitätsschlüssel und alle Ihnen in dieser Organisation zugewiesenen virtuellen API-Schlüssel an",
|
||||
"myVirtualApiKeysResourceTitle": "Ihre API-Schlüssel für {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "Sehen Sie sich Ihren Identitätsschlüssel und Ihnen zugewiesene virtuelle API-Schlüssel an, die auf {resourceName}",
|
||||
"myVirtualApiKeysIdentityTitle": "Identitätsschlüssel",
|
||||
"myVirtualApiKeysIdentityHeadline": "Ihr persönlicher API-Schlüssel",
|
||||
"myVirtualApiKeysIdentityDescription": "Ihr persönlicher Schlüssel für diese Organisation. Es ist einzigartig für Ihr Konto und wird verwendet, um Sie zu identifizieren, wenn Sie KI-Gateway-Ressourcen aufrufen.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Ihr persönlicher API-Schlüssel für {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Ihr persönlicher Schlüssel für diese Organisation. Verwenden Sie ihn, um {resourceName} anzurufen.",
|
||||
"myVirtualApiKeysManualTitle": "Zugewiesene Schlüssel",
|
||||
"myVirtualApiKeysManualDescription": "Manuelle virtuelle API-Schlüssel, die ein Admin Ihrem Konto zugeordnet hat",
|
||||
"myVirtualApiKeysManualResourceDescription": "Manuelle virtuelle API-Schlüssel, die Ihrem Konto zugeordnet sind und auf {resourceName} zugreifen können",
|
||||
"myVirtualApiKeysManualEmpty": "Noch keine zugewiesenen Schlüssel vorhanden",
|
||||
"myVirtualApiKeysKindUser": "Identität",
|
||||
"myVirtualApiKeysKindManual": "Manuell",
|
||||
"myVirtualApiKeysUnnamed": "Unbenannter Schlüssel",
|
||||
"myVirtualApiKeysRevealSecret": "Geheimnis offenlegen",
|
||||
"myVirtualApiKeysViewSecretDescription": "Dieses Geheimnis authentifiziert Sie zu KI-Gateway-Ressourcen",
|
||||
"aiClientConfigTitle": "Coding-Agenten konfigurieren",
|
||||
"aiClientConfigDescription": "Konfigurationsvorlage für beliebte Coding-Agenten kopieren oder lassen Sie das Pangolin-CLI es automatisch für Sie einrichten.",
|
||||
"aiClientConfigDescriptionClaude": "Agentisches Codierwerkzeug von Anthropic für das Terminal.",
|
||||
"aiClientConfigDescriptionCodex": "Agentisches Codierwerkzeug von OpenAI für das Terminal.",
|
||||
"aiClientConfigDescriptionOpencode": "Open-Source-Coding-Agent für das Terminal.",
|
||||
"aiClientConfigDescriptionGemini": "Googles agentic Coding-Tool für das Terminal.",
|
||||
"aiClientConfigSetup": "Einrichtung",
|
||||
"aiClientConfigTabCli": "Automatisch (CLI)",
|
||||
"aiClientConfigTabManual": "Manuelle Konfiguration",
|
||||
"aiClientConfigPreset": "Konfiguration",
|
||||
"aiClientConfigStep": "Schritt {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "Dieses Snippet enthält Beispielwerte, wie z. B. einen Modellnamen oder eine Ressourcen-URL. Ersetzen Sie sie vor der Verwendung durch Ihre eigenen.",
|
||||
"aiClientConfigRevealError": "Ihr API-Schlüssel konnte nicht geladen werden.",
|
||||
"aiClientConfigRevealRetry": "Versuchen Sie es erneut",
|
||||
"resourceGeneralAiClientConfigDescription": "Diese Ressource kann von gängigen Clients wie Claude Code und OpenCode verwendet werden. <configLink>Erfahren Sie mehr</configLink>",
|
||||
"aiProvidersTitle": "KI-Anbieter",
|
||||
"aiProvidersDescription": "Modellanbieter für KI-Workloads in dieser Organisation verbinden",
|
||||
"aiProvidersBannerTitle": "Modellanbieter verbinden",
|
||||
"aiProvidersBannerDescription": "Anbieter sind die Modell-Backends, die Pangolin für KI-Workloads verwendet. Schließen Sie hier OpenAI, Anthropic und andere Anbieter an und ordnen Sie sie dann öffentlichen KI-Gateways zu, sodass Benutzer Modelle mit identitätsbewusstem Zugang, Budgets und Logging aufrufen können.",
|
||||
"aiProvidersAdd": "Anbieter hinzufügen",
|
||||
"aiProvidersSearch": "Anbieter suchen...",
|
||||
"aiProvidersEmpty": "Noch keine KI-Anbieter vorhanden",
|
||||
"aiProviderCreate": "KI-Anbieter erstellen",
|
||||
"aiProviderCreateDescription": "Fügen Sie einen Modellanbieter für diese Organisation hinzu",
|
||||
"aiProviderSeeAll": "Alle Anbieter anzeigen",
|
||||
"aiProviderSetting": "Anbietereinstellungen für {providerName}",
|
||||
"aiProviderSettingDescription": "Konfigurieren Sie diesen KI-Anbieter",
|
||||
"aiProviderGeneral": "Allgemein",
|
||||
"aiProviderGeneralDescription": "Grundeinstellungen für diesen Anbieter",
|
||||
"aiProviderConfiguration": "Konfiguration",
|
||||
"aiProviderConfigurationDescription": "Netzwerk-Routing und Authentifizierung für diesen Anbieter",
|
||||
"aiProviderNetworkSettings": "Netzwerkeinstellungen",
|
||||
"aiProviderNetworkSettingsDescription": "Wählen Sie aus, wie der Datenverkehr diesen Anbieter erreicht",
|
||||
"aiProviderAuthSettings": "Authentifizierung",
|
||||
"aiProviderAuthSettingsDescription": "Konfigurieren Sie, wie dieser Anbieter Anfragen an seine Upstream-URL authentifiziert",
|
||||
"aiProviderBudgetSettings": "Budget",
|
||||
"aiProviderBudgetSettingsDescription": "Konfigurieren Sie, wie dieser Anbieter die Nutzung basierend auf Ausgabengrenzen oder Tokenlimits einschränkt",
|
||||
"aiBudgetAdd": "Budget hinzufügen",
|
||||
"aiBudgetEmpty": "Noch keine Budgets konfiguriert. Klicken Sie auf \"Budget hinzufügen\", um ein Ausgabe- oder Tokenlimit festzulegen.",
|
||||
"aiBudgetUnit": "Ausgabetyp",
|
||||
"aiBudgetPeriod": "Reset-Zeitraum",
|
||||
"aiBudgetAmount": "Maximaler Verbrauch",
|
||||
"aiBudgetAmountPlaceholder": "Maximaler Verbrauch",
|
||||
"aiBudgetPeriodHourly": "Stündlich",
|
||||
"aiBudgetPeriodDaily": "Täglich",
|
||||
"aiBudgetPeriodWeekly": "Wöchentlich",
|
||||
"aiBudgetPeriodMonthly": "Monatlich",
|
||||
"aiBudgetPeriodYearly": "Jährlich",
|
||||
"aiBudgetPeriodLifetime": "Lebenslang",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "Token",
|
||||
"aiBudgetConflictError": "Ein Budget für diesen Reset-Zeitraum und Ausgabetyp existiert bereits",
|
||||
"aiBudgetInvalidAmountError": "Geben Sie einen maximalen Verbrauch größer als 0 ein",
|
||||
"aiBudgetUpdated": "Budgets aktualisiert",
|
||||
"aiBudgetErrorSave": "Fehler beim Aktualisieren der Budgets",
|
||||
"aiProviderType": "Anbietertyp",
|
||||
"aiProviderTypeSearch": "Anbieter suchen...",
|
||||
"aiProviderTypeNotFound": "Kein Anbietertyp gefunden",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI Gateway",
|
||||
"aiProviderTypeCustom": "Benutzerdefiniert",
|
||||
"aiProviderTypeOpenaiDescription": "OpenAI-API",
|
||||
"aiProviderTypeAnthropicDescription": "Anthropic-API",
|
||||
"aiProviderTypeGoogleGeminiDescription": "Google Gemini API",
|
||||
"aiProviderTypeVertexAiDescription": "Google Vertex AI API",
|
||||
"aiProviderTypeBedrockDescription": "Amazon Bedrock Runtime API",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "Microsoft Foundry API",
|
||||
"aiProviderTypeOpenRouterDescription": "OpenRouter API",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "Vercel AI Gateway API",
|
||||
"aiProviderTypeCustomDescription": "Verwenden Sie Ihren eigenen Endpoint oder leiten Sie über Seitentargets",
|
||||
"aiProviderUpstreamUrl": "Upstream-URL",
|
||||
"aiProviderUpstreamUrlDescription": "Basis-URL für die Provider-API",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Leer lassen, um die Standard-Upstream-URL für diesen Anbieter zu verwenden",
|
||||
"aiProviderEffectiveUpstreamUrl": "Effektive Upstream-URL",
|
||||
"aiProviderApiKey": "API-Schlüssel",
|
||||
"aiProviderApiKeyDescription": "API-Schlüssel, der zur Authentifizierung von Anfragen an diesen Anbieter verwendet wird",
|
||||
"aiProviderCustomHeadersDescription": "Header werden bei jeder Anfrage an diesen Anbieter gesendet. Neue Zeile getrennt: Header-Name: Wert",
|
||||
"aiProviderApiKeyLastChars": "API-Schlüssel",
|
||||
"aiProviderAuthType": "Auth-Typ",
|
||||
"aiProviderAuthTypeSearch": "Auth-Typen durchsuchen...",
|
||||
"aiProviderAuthTypeNotFound": "Kein Auth-Typ gefunden",
|
||||
"aiProviderAuthTypeBearer": "Bearer",
|
||||
"aiProviderAuthTypeBearerDescription": "Autorisierung: Bearer-Schlüssel. Wird von OpenAI und den meisten Anbietern verwendet",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "x-api-key-Header. Wird von Anthropic verwendet",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "x-goog-api-key-Header. Wird von Google Gemini verwendet",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Autorisierung: Splunk-Schlüssel. Wird vom Splunk HTTP-Ereignissammler verwendet",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-authorization: Bearer-Schlüssel. Wird vom Cloudflare AI Gateway verwendet",
|
||||
"aiProviderAuthTypeNone": "Keine Auth",
|
||||
"aiProviderAuthTypePassthrough": "Durchreichen",
|
||||
"aiProviderAuthTypeDescription": "Wie die Upstream-API Anfragen authentifiziert",
|
||||
"aiProviderAuthTypePassthroughDescription": "Leiten Sie die API-Schlüssel-Header des Anrufers an den Upstream weiter",
|
||||
"aiProviderAuthTypeNoneDescription": "Keine Authentifizierungs-Header an den Upstream senden",
|
||||
"aiProviderRoutingMode": "Routing-Modus",
|
||||
"aiProviderRoutingModeDescription": "Senden Sie Traffic an eine Upstream-URL oder an HTTP-Ziele auf Ihren Sites",
|
||||
"aiProviderRoutingModeUrl": "Upstream-URL",
|
||||
"aiProviderRoutingModeUrlDescription": "Rufen Sie eine öffentliche oder private API-Basis-URL auf",
|
||||
"aiProviderRoutingModeTarget": "Site-Ziele",
|
||||
"aiProviderRoutingModeTargetDescription": "Über Ziele auf Ihren Sites routen",
|
||||
"aiProviderRoutingModeTargetNote": "Konfigurieren Sie nach der Erstellung dieses Anbieters Site-Ziele auf der Registerkarte 'Netzwerkeinstellungen'.",
|
||||
"aiProviderTargetNoOne": "Dieser Anbieter hat keine Ziele. Fügen Sie ein Ziel hinzu, um Anfragen über Ihre Sites zu leiten.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Sites, die mit entfernten Knoten verbunden sind, können bei AI-Gateway-Anbietern nicht weitergeleitet werden.",
|
||||
"aiProviderSkipTlsVerification": "TLS-Überprüfung überspringen",
|
||||
"aiProviderSkipTlsVerificationDescription": "TLS-Zertifikatsüberprüfung für die Upstream-Verbindung deaktivieren",
|
||||
"aiProviderBudget": "Budget",
|
||||
"aiProviderBudgetDescription": "Optionales Ausgaben- oder Token-Budget für diesen Anbieter",
|
||||
"aiProviderBudgetAmount": "Budgetbetrag",
|
||||
"aiProviderBudgetUnit": "Budgeteinheit",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Token",
|
||||
"aiProviderEnabled": "Aktiviert",
|
||||
"aiProviderEnabledDescription": "Diesen Anbieter für alle Ressourcen vollständig deaktivieren",
|
||||
"aiProviderErrorCreate": "Fehler beim Erstellen des KI-Anbieters",
|
||||
"aiProviderErrorUpdate": "Fehler beim Aktualisieren des KI-Anbieters",
|
||||
"aiProviderErrorDelete": "Fehler beim Löschen des KI-Anbieters",
|
||||
"aiProviderErrorLoad": "Fehler beim Laden des KI-Anbieters",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Geben Sie eine gültige Upstream-URL ein",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Upstream-URL ist für diesen Anbieter erforderlich",
|
||||
"aiProviderErrorAuthTypeRequired": "Auth-Typ ist erforderlich",
|
||||
"aiProviderErrorApiKeyRequired": "API-Schlüssel ist erforderlich",
|
||||
"aiProviderErrorRoutingModeTarget": "Routen von Site-Zielen ist nur für benutzerdefinierte Anbieter verfügbar",
|
||||
"aiProviderErrorCapabilitiesRequired": "Wählen Sie mindestens eine API-Funktion aus",
|
||||
"aiProviderCapabilities": "API-Funktionen",
|
||||
"aiProviderCapabilitiesDescription": "Wählen Sie aus, welche API-Formate dieser Anbieter verarbeiten kann. Bekannte Anbieter beginnen mit empfohlenen Standardeinstellungen.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Wählen Sie aus, welche API-Formate dieser benutzerdefinierte Anbieter verarbeiten kann",
|
||||
"aiProviderCapabilitiesSelect": "Fähigkeiten auswählen",
|
||||
"aiProviderCapabilitiesEmpty": "Keine Fähigkeiten gefunden",
|
||||
"aiProviderCapabilitiesSearch": "Fähigkeiten suchen...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI Chat-Abschlüsse",
|
||||
"aiCapabilityOpenaiChatDescription": "Unterstützt /v1/chat/Abschlüsse",
|
||||
"aiCapabilityOpenaiResponses": "OpenAI Antworten",
|
||||
"aiCapabilityOpenaiResponsesDescription": "Unterstützt /v1/antworten",
|
||||
"aiCapabilityAnthropicMessages": "Anthropic Nachrichten",
|
||||
"aiCapabilityAnthropicMessagesDescription": "Unterstützt /v1/nachrichten",
|
||||
"aiCapabilityV1Models": "Modellliste",
|
||||
"aiCapabilityV1ModelsDescription": "Unterstützt /v1/models Modellentdeckung",
|
||||
"aiCapabilityGeminiGenerateContent": "Gemini Inhalt erzeugen",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Unterstützt die direkte Gemini-API",
|
||||
"aiCapabilityBedrockModelInvoke": "Bedrock Modell Aufruf",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Unterstützt Amazon Bedrock InvokeModel",
|
||||
"aiCapabilityGoogleGenerateContent": "Vertex Inhalt erzeugen",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Unterstützt Vertex AI Gemini-Format",
|
||||
"aiCapabilityGoogleRawPredict": "Vertex Rohvorhersage",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Unterstützt Vertex AI rawPredict für Anthropic-Modelle",
|
||||
"aiCapabilityBedrockConverse": "Bedrock Gespräch",
|
||||
"aiCapabilityBedrockConverseDescription": "Unterstützt Amazon Bedrock Converse API",
|
||||
"aiProviderCreated": "KI-Anbieter erstellt",
|
||||
"aiProviderUpdated": "KI-Anbieter aktualisiert",
|
||||
"aiProviderDeleted": "KI-Anbieter gelöscht",
|
||||
"aiProviderDelete": "Anbieter löschen",
|
||||
"aiProviderDeleteConfirm": "Anbieter löschen",
|
||||
"aiProviderQuestionRemove": "Sind Sie sicher, dass Sie diesen KI-Anbieter löschen möchten?",
|
||||
"aiProviderMessageRemove": "Dadurch wird der Anbieter und seine Modelle und Ziele dauerhaft gelöscht. Dies kann nicht rückgängig gemacht werden.",
|
||||
"aiProviderErrorNoUpdate": "KI-Anbieter ist nicht verfügbar zur Aktualisierung",
|
||||
"aiProviderModels": "Modelle",
|
||||
"aiProviderModelsDescription": "Definieren Sie Zulassungs- und Sperrlisten für diesen Anbieter. Anfragen müssen mit einem erlaubten Eintrag übereinstimmen und dürfen keinen gesperrten Eintrag treffen.",
|
||||
"aiProviderCreateModelsDescription": "Wählen Sie aus, welche Modelle dieser Anbieter bedienen kann. Eine leere Zulassungsliste verweigert den gesamten Datenverkehr. Sie können später Modelle hinzufügen oder ändern.",
|
||||
"aiProviderModelsPlaceholder": "Modelle suchen oder einen benutzerdefinierten Schlüssel eingeben",
|
||||
"aiProviderModelsAllow": "Zulassungsliste",
|
||||
"aiProviderModelsAllowDescription": "Modelle, die durch diesen Anbieter genutzt werden können. Leer bedeutet, alles verweigern.",
|
||||
"aiProviderModelsAllowPlaceholder": "Modellschlüssel eingeben",
|
||||
"aiProviderModelsAllowEmpty": "Noch keine Modelle hinzugefügt.",
|
||||
"aiProviderModelsBlock": "Sperrliste",
|
||||
"aiProviderModelsBlockDescription": "Modelle, die selbst dann verweigert werden, wenn sie mit einem erlaubten Eintrag übereinstimmen.",
|
||||
"aiProviderModelsBlockPlaceholder": "Modellschlüssel eingeben",
|
||||
"aiProviderModelsBlockEmpty": "Noch keine Modelle hinzugefügt.",
|
||||
"aiProviderModelsAdd": "Modelle hinzufügen",
|
||||
"aiProviderModelsClearAll": "Alle löschen",
|
||||
"aiProviderModelsAddCustom": "\"{key}\" hinzufügen",
|
||||
"aiProviderModelsAddCustomHint": "Drücken Sie die Eingabetaste, um diesen benutzerdefinierten Modellschlüssel hinzuzufügen.",
|
||||
"aiProviderModelsAddBulk": "{count} benutzerdefinierte Schlüssel hinzufügen",
|
||||
"aiProviderModelsAddBulkHint": "Drücken Sie die Eingabetaste, um {count} benutzerdefinierte Schlüssel hinzuzufügen.",
|
||||
"aiProviderModelsAddOne": "{key} jetzt hinzufügen",
|
||||
"aiProviderModelsAddSelected": "Ausgewählte hinzufügen",
|
||||
"aiProviderModelsSelectedCount": "{count} ausgewählt",
|
||||
"aiProviderModelsSelectAll": "Alle auswählen",
|
||||
"aiProviderModelsClearSelected": "Löschen",
|
||||
"aiProviderModelsBulkHint": "Wählen Sie bekannte Modelle aus oder geben Sie einen benutzerdefinierten Schlüssel ein.",
|
||||
"aiProviderModelsCatalogEmpty": "Keine passenden Katalogmodelle.",
|
||||
"aiProviderModelsCatalogHeading": "Bekannte Modelle",
|
||||
"aiProviderModelsAllLabel": "Alle Modelle",
|
||||
"aiProviderModelsAllPatternHint": "Platzhalterzeichen: *",
|
||||
"aiProviderModelsAddAllAllow": "Alle Modelle erlauben",
|
||||
"aiProviderModelsAddAllBlock": "Alle Modelle blockieren",
|
||||
"aiProviderModelsAddAllDescription": "Verwendet das Platzhalterzeichen *, sodass jeder Modellschlüssel übereinstimmt.",
|
||||
"aiProviderModelsViewMore": "Mehr anzeigen ({count})",
|
||||
"aiProviderModelsViewLess": "Weniger anzeigen",
|
||||
"aiProviderModelsRemove": "Modell entfernen",
|
||||
"aiProviderModelsEditHint": "Klicken Sie zum Bearbeiten der Modelleinstellungen",
|
||||
"aiProviderModelsSourceCatalog": "Bekannte Katalogmodelle",
|
||||
"aiProviderModelsSourceCustom": "Benutzerdefinierter Modellschlüssel",
|
||||
"aiProviderModelsSourcePattern": "Wildcardschlüssel",
|
||||
"aiProviderModelsSourceAll": "Übereinstimmung mit jedem Modellschlüssel",
|
||||
"aiProviderModelsBudgetConfigured": "Budget konfiguriert",
|
||||
"aiProviderModelsEditTitle": "Modell bearbeiten",
|
||||
"aiProviderModelsEditDescription": "Aktualisieren Sie den Modellschlüssel oder konfigurieren Sie sein Budget.",
|
||||
"aiProviderModelsBudgetTab": "Budget",
|
||||
"aiProviderModelsKeyLabel": "Modellschlüssel",
|
||||
"aiProviderModelsKeyRequired": "Modellschlüssel eingeben",
|
||||
"aiProviderModelsKeyDuplicate": "Dieser Modellschlüssel befindet sich bereits auf einer Liste",
|
||||
"aiProviderModelsOverlapError": "Diese Muster können nicht auf beiden Listen sein: {keys}",
|
||||
"aiProviderModelsUpdated": "Modelle aktualisiert",
|
||||
"aiProviderModelsErrorUpdate": "Fehler beim Aktualisieren der Modelle",
|
||||
"aiResourceProviders": "Anbieter",
|
||||
"aiResourceProvidersDescription": "Wählen Sie, welche KI-Anbieter dieses KI-Gateway verwenden kann",
|
||||
"aiResourceProvidersHelp": "Fügen Sie Anbieter an und wählen Sie Vererben (Verwendung der Liste des jeweiligen Anbieters) oder auswählen (Wählen Sie eine Zulassungsliste für diese Ressource). Erlaubte Muster, die bei angehängten Anbietern Konflikte verursachen, sind nicht erlaubt.",
|
||||
"aiResourceProvidersSelect": "Anbieter auswählen",
|
||||
"aiResourceProvidersEmpty": "Keine KI-Anbieter gefunden",
|
||||
"aiResourceProvidersNoneAttached": "Noch keine Anbieter angehängt.",
|
||||
"aiResourceProvidersAdd": "Anbieter hinzufügen",
|
||||
"aiResourceProvidersRemove": "Anbieter entfernen",
|
||||
"aiResourceProviderToggleEnabled": "Aktivieren oder Deaktivieren Sie diesen Anbieter auf der Ressource",
|
||||
"aiResourceProviderDisabled": "Deaktiviert",
|
||||
"aiResourceProvidersUpdated": "Anbieter aktualisiert",
|
||||
"aiResourceProvidersErrorUpdate": "Anbieter konnten nicht aktualisiert werden",
|
||||
"aiResourceProviderEditDescription": "Wählen Sie aus, wie die Modelle dieses Anbieters auf dieser Ressource dargestellt werden.",
|
||||
"viewProviderSettings": "Anbietereinstellungen anzeigen",
|
||||
"aiResourceProviderMode": "Zugriffsmodus",
|
||||
"aiResourceProviderModeInherit": "Vererben",
|
||||
"aiResourceProviderModeSelect": "Auswählen",
|
||||
"aiResourceProviderModeSelectSummary": "Auswählen · {count} Modelle",
|
||||
"aiResourceProviderModeInheritHelp": "Verwenden Sie die Zulassungs- und Sperrlisten dieses Anbieters, wie sie am Anbieter konfiguriert sind.",
|
||||
"aiResourceProviderModeSelectHelp": "Wählen Sie ein Teilset von Modellen der Zulassungsliste dieses Anbieters für diese Ressource.",
|
||||
"aiResourceProviderAllowModels": "Zulassungsliste",
|
||||
"aiResourceProviderAllowModelsSelect": "Modelle auswählen",
|
||||
"aiResourceProviderAllowModelsSearch": "Modelle suchen...",
|
||||
"aiResourceProviderAllowModelsEmpty": "Keine Modelle gefunden",
|
||||
"aiResourceProviderAllowModelsHelp": "Es können nur Modelle aus der Zulassungsliste dieses Anbieters ausgewählt werden.",
|
||||
"aiResourceAliasRequired": "Alias ist für KI-Gateways erforderlich",
|
||||
"aiResourceDomainConfiguration": "Domain-Konfiguration",
|
||||
"aiResourceDomainConfigurationDescription": "Wählen Sie die Domain aus, die Clients verwenden sollen, um dieses KI-Gateway zu erreichen.",
|
||||
"aiUsageAnalyticsTitle": "KI Nutzungsanalyse",
|
||||
"aiUsageAnalyticsDescription": "Analysieren Sie die Kosten, die Token-Nutzung und die Aktivität des KI-Gateways über Anbieter, Ressourcen, Rollen und Benutzer hinweg",
|
||||
"aiUsageTabOverview": "Überblick",
|
||||
"aiUsageTabProviders": "Anbieter",
|
||||
"aiUsageTabResources": "Ressourcen",
|
||||
"aiUsageFilterProvider": "Anbieter",
|
||||
"aiUsageFilterModel": "Modell",
|
||||
"aiUsageFilterResource": "Ressource",
|
||||
"aiUsageFilterRole": "Rolle",
|
||||
"aiUsageFilterUser": "Benutzer",
|
||||
"aiUsageFilterAllProviders": "Alle Anbieter",
|
||||
"aiUsageFilterAllModels": "Alle Modelle",
|
||||
"aiUsageFilterAllResources": "Alle Ressourcen",
|
||||
"aiUsageFilterAllRoles": "Alle Rollen",
|
||||
"aiUsageFilterAllUsers": "Alle Benutzer",
|
||||
"aiUsageFilterSearch": "Suche...",
|
||||
"aiUsageFilterNotFound": "Keine Optionen gefunden",
|
||||
"aiUsageResetFilters": "Filter zurücksetzen",
|
||||
"aiUsageRefresh": "Aktualisieren",
|
||||
"aiUsageTokenTypePrompt": "Aufforderung",
|
||||
"aiUsageTokenTypeCacheRead": "Cache-Lesezugriff",
|
||||
"aiUsageTokenTypeCacheWrite": "Cache-Schreibzugriff",
|
||||
"aiUsageTokenTypeCompletion": "Abschluss",
|
||||
"aiUsageTokenTypeReasoning": "Logik",
|
||||
"aiUsageRequests": "Anfragen",
|
||||
"aiUsageCost": "Kosten",
|
||||
"aiUsageTokens": "Token",
|
||||
"aiUsageOther": "Andere",
|
||||
"aiUsageTotalRequests": "Gesamtanfragen",
|
||||
"aiUsageTotalTokens": "Gesamttoken",
|
||||
"aiUsageTotalCost": "Gesamtkosten",
|
||||
"aiUsageEstimated": "Geschätzt",
|
||||
"aiUsageRequestVolume": "Anfragevolumen",
|
||||
"aiUsageTokenUsage": "Token-Nutzung",
|
||||
"aiUsageModelCost": "Modellkosten",
|
||||
"aiUsageModelTokens": "Modell-Token",
|
||||
"aiUsageTopModels": "Top-Modelle",
|
||||
"aiUsageTopProviders": "Top-Anbieter",
|
||||
"aiUsageProviderCost": "Anbieterkosten",
|
||||
"aiUsageProviderTokenUsage": "Anbieter-Token-Nutzung",
|
||||
"aiUsageTopResources": "Top-Ressourcen",
|
||||
"aiUsageResourceCost": "Ressourcenkosten",
|
||||
"aiUsageResourceTokenUsage": "Ressourcen-Token-Nutzung",
|
||||
"aiUsageResourceTypePublic": "Öffentliche Ressource",
|
||||
"aiUsageResourceTypeSite": "Private Ressource",
|
||||
"aiUsageNoResource": "Keine Ressource",
|
||||
"aiUsageRolesTab": "Rollen",
|
||||
"aiUsageUsersTab": "Benutzer",
|
||||
"aiUsageTopRoles": "Top-Rollen",
|
||||
"aiUsageRoleCost": "Rollen-Kosten",
|
||||
"aiUsageRoleTokenUsage": "Rollen-Token-Nutzung",
|
||||
"aiUsageTopUsers": "Top-Benutzer",
|
||||
"aiUsageUserCost": "Benutzerkosten",
|
||||
"aiUsageUserTokenUsage": "Benutzer-Token-Nutzung",
|
||||
"aiUsageUnknownUser": "Unbekannter Benutzer",
|
||||
"aiUsageVirtualApiKeysTab": "Virtuelle API-Schlüssel",
|
||||
"aiUsageFilterVirtualApiKey": "Virtueller API-Schlüssel",
|
||||
"aiUsageFilterAllVirtualApiKeys": "Alle virtuellen API-Schlüssel",
|
||||
"aiUsageTopVirtualApiKeys": "Top virtuelle API-Schlüssel",
|
||||
"aiUsageVirtualApiKeyCost": "Virtueller API-Schlüssel Kost",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Virtuelle API-Schlüssel Token-Nutzung",
|
||||
"aiUsageUnknownVirtualApiKey": "Kein virtueller API-Schlüssel",
|
||||
"aiUsageUnnamedVirtualApiKey": "Unbenannter Schlüssel",
|
||||
"aiUsageLoading": "Laden...",
|
||||
"aiUsageNoData": "Keine Daten",
|
||||
"resourceBudgetSettings": "Budget",
|
||||
"resourceBudgetSettingsDescription": "Konfigurieren Sie, wie dieses KI-Gateway die Nutzung basierend auf Ausgabe- oder Token-Limits einschränkt",
|
||||
"sidebarApiKeys": "API-Schlüssel",
|
||||
"sidebarOrgs": "Organisationen",
|
||||
"sidebarProvisioning": "Bereitstellung",
|
||||
"sidebarSettings": "Einstellungen",
|
||||
"sidebarAllUsers": "Alle Benutzer",
|
||||
@@ -1638,7 +2135,7 @@
|
||||
"sidebarAlerting": "Benachrichtigung",
|
||||
"sidebarHealthChecks": "Gesundheits-Checks",
|
||||
"sidebarOrganization": "Organisation",
|
||||
"sidebarManagement": "Management",
|
||||
"sidebarManagement": "Verwaltung",
|
||||
"sidebarBillingAndLicenses": "Abrechnung & Lizenzen",
|
||||
"sidebarLogsAnalytics": "Analytik",
|
||||
"commandSites": "Seiten",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "Benachrichtigungsregel gespeichert",
|
||||
"alertingRuleSavedCreatedDescription": "Ihre neue Benachrichtigungsregel wurde erstellt. Sie können sie auf dieser Seite weiter bearbeiten.",
|
||||
"alertingRuleSavedUpdatedDescription": "Ihre Änderungen an dieser Benachrichtigungsregel wurden gespeichert.",
|
||||
"alertingTestAlertSent": "Testwarnung gesendet",
|
||||
"alertingTestAlertSentDescription": "Eine Testwarnung wurde an die auf dieser Regel konfigurierten Aktionen gesendet.",
|
||||
"alertingEditRule": "Benachrichtigungsregel bearbeiten",
|
||||
"alertingCreateRule": "Benachrichtigungsregel erstellen",
|
||||
"alertingRuleCredenzaDescription": "Wählen Sie aus, was beobachtet, wann ausgelöst und wie benachrichtigt werden soll",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "Jede Regel verknüpft, was beobachtet werden soll (eine Seite, ein Gesundheits-Check oder eine Ressource), wann es ausgelöst werden soll (zum Beispiel offline oder ungesund), und wie Ihr Team benachrichtigt wird, z. B. per E-Mail, Webhooks oder Integrationen. Verwenden Sie diese Liste, um diese Regeln zu erstellen, zu aktivieren und zu verwalten.",
|
||||
"alertingHealthChecksBannerTitle": "Gesundheit & Ressourcen überwachen",
|
||||
"alertingHealthChecksBannerDescription": "Gesundheits-Checks sind HTTP- oder TCP-Monitore, die Sie einmal definieren. Sie können sie dann als Quellen in Benachrichtigungsregeln verwenden, so dass Sie benachrichtigt werden, wenn ein Ziel gesund oder ungesund wird. Gesundheits-Checks für Ressourcen erscheinen ebenfalls hier.",
|
||||
"alertingTestRule": "Testwarnregel",
|
||||
"alertingAddActionHeading": "Neue Aktion hinzufügen",
|
||||
"alertingSelectActionType": "Wählen Sie eine Aktion aus",
|
||||
"alertingNoActionsTitle": "Keine Aktionen konfiguriert",
|
||||
"alertingNoActionsSaveDescription": "Fügen Sie mindestens eine Aktion hinzu, damit diese Regel jemanden benachrichtigen kann, wenn sie ausgelöst wird.",
|
||||
"alertingNoActionsTestDescription": "Fügen Sie mindestens eine Aktion hinzu, bevor Sie diese Regel testen können.",
|
||||
"standaloneHcTableTitle": "Gesundheits-Checks",
|
||||
"standaloneHcSearchPlaceholder": "Gesundheits-Checks suchen…",
|
||||
"standaloneHcAddButton": "Gesundheits-Check erstellen",
|
||||
@@ -2012,7 +2517,7 @@
|
||||
"billingSites": "Standorte",
|
||||
"billingUsers": "Benutzergeräte",
|
||||
"billingDomains": "Domänen",
|
||||
"billingOrganizations": "Orden",
|
||||
"billingOrganizations": "Organisationen",
|
||||
"billingRemoteExitNodes": "Entfernte Knoten",
|
||||
"billingPublicResources": "Öffentliche Ressourcen",
|
||||
"billingPrivateResources": "Private Ressourcen",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "Typ:",
|
||||
"createDomainName": "Name:",
|
||||
"createDomainValue": "Wert:",
|
||||
"multiSelectFilterCount": "{count} ausgewählt",
|
||||
"createDomainCnameRecords": "CNAME-Einträge",
|
||||
"createDomainARecords": "A-Aufzeichnungen",
|
||||
"createDomainRecordNumber": "Eintrag {number}",
|
||||
@@ -2172,7 +2678,7 @@
|
||||
"billingResolvePaymentIssue": "Bitte beheben Sie Ihr Zahlungsproblem vor dem Upgrade oder Herabstufen",
|
||||
"signUpTerms": {
|
||||
"IAgreeToThe": "Ich stimme den",
|
||||
"termsOfService": "Nutzungsbedingungen zu",
|
||||
"termsOfService": "Nutzungsbedingungen",
|
||||
"and": "und",
|
||||
"privacyPolicy": "datenschutzrichtlinie."
|
||||
},
|
||||
@@ -2193,6 +2699,8 @@
|
||||
"subnetPlaceholder": "Subnetz",
|
||||
"addressDescription": "Die interne Adresse des Clients. Muss in das Subnetz der Organisation fallen.",
|
||||
"selectSites": "Standorte auswählen",
|
||||
"selectResources": "Ressourcen auswählen",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# Ressource} other {# Ressourcen}}",
|
||||
"selectLabels": "Etiketten auswählen",
|
||||
"sitesDescription": "Der Client wird zu den ausgewählten Standorten eine Verbindung haben.",
|
||||
"clientInstallOlm": "Olm installieren",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "Methode",
|
||||
"healthSelectScheme": "Methode auswählen",
|
||||
"healthCheckPortInvalid": "Der Port muss zwischen 1 und 65535 liegen",
|
||||
"healthCheckHostnameInvalid": "Der Hostname darf keinen Leerraum enthalten",
|
||||
"healthCheckPath": "Pfad",
|
||||
"healthHostname": "IP / Host",
|
||||
"healthPort": "Port",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "Zeit ist in Sekunden",
|
||||
"requireDeviceApproval": "Gerätegenehmigungen erforderlich",
|
||||
"requireDeviceApprovalDescription": "Benutzer mit dieser Rolle benötigen neue Geräte, die von einem Administrator genehmigt wurden, bevor sie sich verbinden und auf Ressourcen zugreifen können.",
|
||||
"sshSettings": "SSH-Einstellungen",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "KI-Gateway",
|
||||
"sshAccess": "SSH Zugriff",
|
||||
"rdpSettings": "RDP-Einstellungen",
|
||||
"vncSettings": "VNC-Einstellungen",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "SSH-Server",
|
||||
"rdpServer": "RDP-Server",
|
||||
"vncServer": "VNC-Server",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "KI-Gateway",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Schema",
|
||||
"editInternalResourceDialogEnableSsl": "TLS aktivieren",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "ICMP-Ping zulassen",
|
||||
"privateResourceNetworkAccess": "Netzwerkzugriff",
|
||||
"privateResourceNetworkAccessDescription": "TCP/UDP-Portzugriff kontrollieren und ICMP-Ping für diese Ressource zulassen.",
|
||||
"hostSettings": "Host-Einstellungen",
|
||||
"cidrSettings": "CIDR-Einstellungen",
|
||||
"hostSettings": "Host",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Ressourceneigenschaften",
|
||||
"createInternalResourceDialogName": "Name",
|
||||
"createInternalResourceDialogSite": "Standort",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "KI-Gateway",
|
||||
"scheme": "Schema",
|
||||
"createInternalResourceDialogScheme": "Schema",
|
||||
"createInternalResourceDialogEnableSsl": "TLS aktivieren",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Fügen Sie einen CIDR-Bereich hinzu (z.B. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Fehler beim Laden der Subnetze",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Präferenzetiketten",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Standorte mit diesen Etiketten werden gezwungen, über diesen Remote Exit Node zu verbinden.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Standorte mit diesen Labels bevorzugen die Verbindung über diesen Remote-Exit-Knoten.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Etiketten auswählen...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Etiketten suchen...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Fehler beim Laden der Etiketten",
|
||||
@@ -2676,12 +3188,12 @@
|
||||
"roleMappingRemoveRule": "Entfernen",
|
||||
"idpGoogleConfiguration": "Google-Konfiguration",
|
||||
"idpGoogleConfigurationDescription": "Google OAuth2 Zugangsdaten konfigurieren",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client-ID",
|
||||
"idpGoogleClientSecretDescription": "Google OAuth2 Client Geheimnis",
|
||||
"idpAzureConfiguration": "Azure Entra ID Konfiguration",
|
||||
"idpAzureConfigurationDescription": "Azure Entra ID OAuth2 Zugangsdaten konfigurieren",
|
||||
"idpTenantId": "Mandanten-ID",
|
||||
"idpTenantIdPlaceholder": "tenant-id",
|
||||
"idpTenantIdPlaceholder": "mandanten-ID",
|
||||
"idpAzureTenantIdDescription": "Azure Tenant ID (gefunden in Azure Active Directory Übersicht)",
|
||||
"idpAzureClientIdDescription": "Azure App Registration Client ID",
|
||||
"idpAzureClientSecretDescription": "Azure App Registration Client Geheimnis",
|
||||
@@ -2695,7 +3207,7 @@
|
||||
"idpAzureClientIdDescription2": "Azure App Registration Client ID",
|
||||
"idpAzureClientSecretDescription2": "Azure App Registration Client Geheimnis",
|
||||
"idpGoogleDescription": "Google OAuth2/OIDC Provider",
|
||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC-Anbieter",
|
||||
"subnet": "Subnetz",
|
||||
"utilitySubnet": "Nutzsubnetz",
|
||||
"subnetDescription": "Das Subnetz für die Netzwerkkonfiguration dieser Organisation.",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "Priorität",
|
||||
"priorityDescription": "Die Routen mit höherer Priorität werden zuerst ausgewertet. Priorität = 100 bedeutet automatische Bestellung (Systementscheidung). Verwenden Sie eine andere Nummer, um manuelle Priorität zu erzwingen.",
|
||||
"instanceName": "Instanzname",
|
||||
"clearInstanceName": "Reset Server Association",
|
||||
"pathMatchModalTitle": "Pfad anpassen konfigurieren",
|
||||
"pathMatchModalDescription": "Legen Sie fest, wie eingehende Anfragen basierend auf ihrem Pfad übereinstimmen sollen.",
|
||||
"pathMatchType": "Übereinstimmungstyp",
|
||||
@@ -3051,6 +3564,7 @@
|
||||
"validPassword": "Gültiges Passwort",
|
||||
"validEmail": "Gültige E-Mail-Adresse",
|
||||
"validSSO": "Gültige SSO-Anmeldung",
|
||||
"validVirtualAPIKey": "Gültiger virtueller API-Schlüssel",
|
||||
"view": "Ansehen",
|
||||
"configManaged": "Konfiguration verwaltet",
|
||||
"connectedClient": "Verbundenes Gerät",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "Aktionsprotokolle",
|
||||
"logRetention": "Log-Speicherung",
|
||||
"logRetentionDescription": "Verwalten, wie lange verschiedene Logs für diese Organisation gespeichert werden oder deaktivieren",
|
||||
"logRetentionDisabledWarningTitle": "Protokoll-Aufbewahrung deaktiviert",
|
||||
"logRetentionDisabledWarningDescription": "{logType} werden für diese Organisation nicht aufbewahrt, daher erscheinen neue Aktivitäten hier nicht. Aktivieren Sie die Aufbewahrung in den Sicherheitseinstellungen, um diese Protokolle zu sammeln.",
|
||||
"logRetentionDisabledWarningButton": "Zu den Sicherheitseinstellungen gehen",
|
||||
"requestLogsDescription": "Detaillierte Request-Logs für Ressourcen in dieser Organisation anzeigen",
|
||||
"aiSessionLogs": "AI-Gateway Sitzungsprotokolle",
|
||||
"aiSessionLogsDescription": "Zeigen Sie Aufforderungs- und Antwortprotokolle für Anfragen des KI-Gateways in dieser Organisation an",
|
||||
"sidebarLogsAi": "Sitzungsprotokolle",
|
||||
"commandLogsAi": "Sitzungsprotokolle",
|
||||
"sidebarLogsAiUsage": "Nutzungsanalyse",
|
||||
"commandLogsAiUsage": "Nutzungsanalyse",
|
||||
"provider": "Anbieter",
|
||||
"capability": "Fähigkeit",
|
||||
"model": "Modell",
|
||||
"virtualApiKey": "Virtueller API-Schlüssel",
|
||||
"noVirtualApiKey": "Kein virtueller API-Schlüssel",
|
||||
"stream": "Strom",
|
||||
"streaming": "Streaming",
|
||||
"nonStreaming": "Nicht-Streaming",
|
||||
"statusCode": "Statuscode",
|
||||
"aiSessionId": "Sitzungs-ID",
|
||||
"aiSessionRequest": "Anfrage",
|
||||
"aiSessionResponse": "Antwort",
|
||||
"aiSessionNoData": "Keine Daten erfasst",
|
||||
"aiSessionCouldNotParse": "(Roh, Protokoll konnte nicht geparst werden)",
|
||||
"aiSessionLogTruncated": "Diese Sitzung wurde vor der Speicherung abgeschnitten und könnte unvollständig sein.",
|
||||
"aiSessionViewRaw": "Rohes JSON anzeigen",
|
||||
"aiSessionViewChat": "Chat anzeigen",
|
||||
"cost": "Kosten",
|
||||
"estimated": "Geschätzt",
|
||||
"tokenUsage": "Token-Nutzung",
|
||||
"promptTokens": "Aufforderungstoken",
|
||||
"cacheReadTokens": "Cache-Lese-Token",
|
||||
"cacheWriteTokens": "Cache-Schreib-Token",
|
||||
"completionTokens": "Abschlusstoken",
|
||||
"reasoningTokens": "Logik-Token",
|
||||
"totalTokens": "Gesamttoken",
|
||||
"requestAnalyticsDescription": "Detaillierte Anfrage-Analyse für Ressourcen in dieser Organisation anzeigen",
|
||||
"logRetentionRequestLabel": "HTTP Anforderungsprotokoll Aufbewahrung",
|
||||
"logRetentionRequestDescription": "Wie lange sollen Request-Logs gespeichert werden",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "Dauer des Action-Logs",
|
||||
"logRetentionConnectionLabel": "Verbindungsprotokoll-Speicherung",
|
||||
"logRetentionConnectionDescription": "Wie lange Verbindungsprotokolle gespeichert werden sollen",
|
||||
"logRetentionAISessionsLabel": "Aufbewahrung von Sitzungsprotokollen des KI-Gateways",
|
||||
"logRetentionAISessionsDescription": "Wie lange Sitzunglogs von Aufforderung / Antwort des KI-Gateways aufbewahrt werden",
|
||||
"logRetentionDisabled": "Deaktiviert",
|
||||
"logRetention3Days": "3 Tage",
|
||||
"logRetention7Days": "7 Tage",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "Quelladresse",
|
||||
"destinationAddress": "Zieladresse",
|
||||
"duration": "Dauer",
|
||||
"licenseRequiredToUse": "Eine <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> Lizenz oder <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> wird benötigt, um diese Funktion zu nutzen. <bookADemoLink>Buchen Sie eine kostenlose Demo oder POC Testversion, um mehr zu erfahren.</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "Die <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> ist notwendig, um diese Funktion zu nutzen. Diese Funktion ist auch in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> verfügbar. <bookADemoLink>Buchen Sie eine kostenlose Demo oder POC Testversion, um mehr zu erfahren.</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Enterprise Edition Lizenz</enterpriseEditionLink> ist erforderlich. <bookADemoLink>Einen Test vorführen oder ausprobieren </bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition Lizenz</enterpriseEditionLink> ist erforderlich. <bookADemoLink>Vereinbare eine Demo oder einen Test</bookADemoLink>",
|
||||
"certResolver": "Zertifikatsauflöser",
|
||||
"certResolverDescription": "Wählen Sie den Zertifikatslöser aus, der für diese Ressource verwendet werden soll.",
|
||||
"selectCertResolver": "Zertifikatsauflöser auswählen",
|
||||
@@ -3242,6 +3793,7 @@
|
||||
"noData": "Keine Daten",
|
||||
"machineClients": "Maschinen-Clients",
|
||||
"install": "Installieren",
|
||||
"downloadInstaller": "Installer herunterladen",
|
||||
"run": "Ausführen",
|
||||
"envFile": "Umgebungsdatei",
|
||||
"serviceFile": "Servicedatei",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "Durch die Auswahl mehrerer Seiten wird ein ausfallsicheres Routing und Failover für hohe Verfügbarkeit ermöglicht.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Mehr erfahren",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Den Zugriff auf bestimmte TCP/UDP-Ports beschränken oder alle Ports erlauben/blockieren.",
|
||||
"createInternalResourceDialogHttpConfiguration": "HTTP-Konfiguration",
|
||||
"createInternalResourceDialogHttpConfiguration": "Domain-Konfiguration",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "Wählen Sie die Domain, die Clients verwenden, um über HTTP oder HTTPS auf diese Ressource zuzugreifen.",
|
||||
"editInternalResourceDialogHttpConfiguration": "HTTP-Konfiguration",
|
||||
"editInternalResourceDialogHttpConfiguration": "Domain-Konfiguration",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "Wählen Sie die Domain, die Clients verwenden, um über HTTP oder HTTPS auf diese Ressource zuzugreifen.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3390,7 +3942,7 @@
|
||||
"enterMfaCode": "Geben Sie den Code aus Ihrer Authentifizierungs-App ein",
|
||||
"securityKeyRequired": "Bitte verwenden Sie Ihren Sicherheitsschlüssel zum Anmelden.",
|
||||
"needToUseAnotherAccount": "Benötigen Sie ein anderes Konto?",
|
||||
"loginLegalDisclaimer": "Indem Sie auf die Buttons unten klicken, bestätigen Sie, dass Sie gelesen haben, verstehen, und stimmen den <termsOfService>Nutzungsbedingungen</termsOfService> und <privacyPolicy>Datenschutzrichtlinien</privacyPolicy> zu.",
|
||||
"loginLegalDisclaimer": "Indem Sie auf die Buttons unten klicken, bestätigen Sie, dass Sie die <termsOfService>Nutzungsbedingungen</termsOfService> und <privacyPolicy>Datenschutzrichtlinien</privacyPolicy> gelesen haben, verstehen und ihnen zustimmen.",
|
||||
"termsOfService": "Nutzungsbedingungen",
|
||||
"privacyPolicy": "Datenschutzerklärung",
|
||||
"userNotFoundWithUsername": "Kein Benutzer mit diesem Benutzernamen gefunden.",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "Verbindung getrennt",
|
||||
"approvalsEmptyStateTitle": "Gerätezulassungen nicht aktiviert",
|
||||
"approvalsEmptyStateDescription": "Aktiviere Gerätegenehmigungen für Rollen, um Administratorgenehmigungen zu benötigen, bevor Benutzer neue Geräte verbinden können.",
|
||||
"approvalsEmptyStateHowToTitle": "Aktivieren",
|
||||
"approvalsEmptyStateStep1Title": "Gehe zu Rollen",
|
||||
"approvalsEmptyStateStep1Description": "Navigieren Sie zu den Rolleneinstellungen Ihrer Organisation, um die Gerätefreigaben zu konfigurieren.",
|
||||
"approvalsEmptyStateStep2Title": "Gerätegenehmigungen aktivieren",
|
||||
@@ -3457,12 +4010,12 @@
|
||||
"approvalsEmptyStateButtonText": "Rollen verwalten",
|
||||
"domainErrorTitle": "Wir haben Probleme mit der Überprüfung deiner Domain",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Konfigurieren Sie Rollenzuordnungs- und Organisationsrichtlinien auf der Registerkarte <policiesTabLink>Auto-Bereitstellungseinstellungen</policiesTabLink>.",
|
||||
"streamingTitle": "Event Streaming",
|
||||
"streamingTitle": "Ereignis-Streaming",
|
||||
"streamingDescription": "Streamen Sie Events aus Ihrem Unternehmen in Echtzeit zu externen Zielen.",
|
||||
"streamingUnnamedDestination": "Unbenanntes Ziel",
|
||||
"streamingNoUrlConfigured": "Keine URL konfiguriert",
|
||||
"streamingAddDestination": "Ziel hinzufügen",
|
||||
"streamingHttpWebhookTitle": "HTTP Webhook",
|
||||
"streamingHttpWebhookTitle": "HTTP-Webhook",
|
||||
"streamingHttpWebhookDescription": "Sende Ereignisse an jeden HTTP-Endpunkt mit flexibler Authentifizierung und Vorlage.",
|
||||
"streamingS3Title": "Amazon S3",
|
||||
"streamingS3Description": "Streame Ereignisse in eine S3-kompatible Objekt-Speicher-Eimer. Kommt bald.",
|
||||
@@ -3554,7 +4107,7 @@
|
||||
"httpDestBodyTemplateHint": "Verwenden Sie Template-Variablen, um Ereignisfelder in Ihrer Payload zu referenzieren.",
|
||||
"httpDestPayloadFormatTitle": "Payload-Format",
|
||||
"httpDestPayloadFormatDescription": "Wie Ereignisse in jedes Anfragegremium serialisiert werden.",
|
||||
"httpDestFormatJsonArrayTitle": "JSON Array",
|
||||
"httpDestFormatJsonArrayTitle": "JSON-Array",
|
||||
"httpDestFormatJsonArrayDescription": "Eine Anfrage pro Stapel ist ein JSON-Array. Kompatibel mit den meisten generischen Webhooks und Datadog.",
|
||||
"httpDestFormatNdjsonTitle": "NDJSON",
|
||||
"httpDestFormatNdjsonDescription": "Eine Anfrage pro Batch, der Körper ist newline-getrenntes JSON - ein Objekt pro Zeile, kein äußeres Array. Benötigt von Splunk HEC, Elastic / OpenSearch, und Grafana Loki.",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "Site- und Tunnelverbindungen, einschließlich Verbindungen und Trennungen.",
|
||||
"httpDestRequestLogsTitle": "HTTP Anforderungsprotokolle",
|
||||
"httpDestRequestLogsDescription": "HTTP-Request-Protokolle für proxiierte Ressourcen, einschließlich Methode, Pfad und Antwort-Code.",
|
||||
"httpDestAISessionLogsTitle": "AI-Sitzungsprotokolle",
|
||||
"httpDestAISessionLogsDescription": "AI-Gateway-Anfrage- und Antwortsitzungen, einschließlich Eingabeaufforderungen, Modellantworten und Token-Nutzung.",
|
||||
"httpDestSaveChanges": "Änderungen speichern",
|
||||
"httpDestCreateDestination": "Ziel erstellen",
|
||||
"httpDestUpdatedSuccess": "Ziel erfolgreich aktualisiert",
|
||||
@@ -3687,7 +4242,7 @@
|
||||
"memberPortalResourceDisabled": "Ressource deaktiviert",
|
||||
"memberPortalShowingResources": "Zeige {start}-{end} von {total} Ressourcen",
|
||||
"resourceLauncherTitle": "Ressourcenstarter",
|
||||
"resourceSidebarLauncherTitle": "Launcher",
|
||||
"resourceSidebarLauncherTitle": "Starter",
|
||||
"resourceLauncherDescription": "Alle verfügbaren Ressourcen anzeigen und von einem zentralen Hub aus starten",
|
||||
"resourceLauncherSearchPlaceholder": "Suche deine Ressourcen...",
|
||||
"resourceLauncherDefaultView": "Standard",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "Ansicht als Administrator anzeigen",
|
||||
"resourceLauncherResourceDetailsDescription": "Verbindungsinformationen und Status für diese Ressource.",
|
||||
"resourceLauncherResourceDetails": "Ressourcendetails",
|
||||
"resourceLauncherSitesDescription": "Die Ressource ist über die folgenden Standorte zugänglich.",
|
||||
"resourceLauncherViewSiteAsAdmin": "Seite als Administrator anzeigen",
|
||||
"resourceLauncherFilterBySite": "Nach Standort filtern",
|
||||
"resourceLauncherSshCommand": "SSH-Befehl",
|
||||
"resourceLauncherSshCommandDescription": "Verwenden Sie die Pangolin-CLI, um eine SSH-Sitzung zu dieser Ressource zu öffnen.",
|
||||
"resourceLauncherAuthMethodsDescription": "Aktivierte Authentifizierungsmethoden für diese Ressource.",
|
||||
"resourceLauncherPrivateClientRequired": "Verbinde dich mit einem Client auf deinem Gerät, um privat auf diese Ressource zuzugreifen.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "Client-Verbindung erforderlich",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Nicht etikettiert",
|
||||
"resourceLauncherAiGateway": "KI-Gateway",
|
||||
"resourceLauncherNoSite": "Kein Standort",
|
||||
"resourceLauncherAvailableModels": "Verfügbare Modelle",
|
||||
"resourceLauncherAvailableModelsDescription": "Modelle, die Sie mit diesem KI-Gateway verwenden können.",
|
||||
"resourceLauncherAvailableModelsEmpty": "Für diese Ressource sind keine Modelle verfügbar.",
|
||||
"resourceLauncherAvailableModelsError": "Verfügbare Modelle konnten nicht geladen werden.",
|
||||
"resourceLauncherApiKeys": "API-Schlüssel",
|
||||
"resourceLauncherApiKeysDescription": "Verwenden Sie Ihren Identitätsschlüssel oder einen zugeordneten Schlüssel, um sich bei dieser Ressource zu authentifizieren.",
|
||||
"resourceLauncherApiKeysIdentity": "Identitätsschlüssel",
|
||||
"resourceLauncherApiKeysManual": "Zugeordnete Schlüssel",
|
||||
"resourceLauncherApiKeysEmpty": "Für diese Ressource stehen keine API-Schlüssel zur Verfügung.",
|
||||
"resourceLauncherApiKeysError": "API-Schlüssel konnten nicht geladen werden.",
|
||||
"resourceLauncherNoResourcesInGroup": "Keine Ressourcen in dieser Gruppe",
|
||||
"resourceLauncherEmptyStateTitle": "Keine Ressourcen verfügbar",
|
||||
"resourceLauncherEmptyStateDescription": "Sie haben noch keinen Zugriff auf Ressourcen. Kontaktieren Sie Ihren Administrator, um Zugriff anzufordern.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "Die Startansicht konnte nicht gelöscht werden. Bitte versuchen Sie es erneut.",
|
||||
"memberPortalPrevious": "Vorherige",
|
||||
"memberPortalNext": "Nächste",
|
||||
"httpSettings": "HTTP-Einstellungen",
|
||||
"tcpSettings": "TCP-Einstellungen",
|
||||
"udpSettings": "UDP-Einstellungen",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Aufbau einer sicheren Verbindung…",
|
||||
"sshConnecting": "Verbindung wird hergestellt…",
|
||||
@@ -3816,7 +4387,7 @@
|
||||
"rdpConnectionFailed": "Verbindung fehlgeschlagen",
|
||||
"rdpFit": "Anpassen",
|
||||
"rdpFull": "Vollständig",
|
||||
"rdpReal": "Real",
|
||||
"rdpReal": "Echt",
|
||||
"rdpMeta": "Meta",
|
||||
"rdpUploadFiles": "Dateien hochladen",
|
||||
"rdpFilesReadyToPaste": "Dateien bereit zum Einfügen",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Unicode-Tastaturmodus",
|
||||
"sessionToolbarShow": "Werkzeugleiste zeigen",
|
||||
"sessionToolbarHide": "Werkzeugleiste ausblenden",
|
||||
"actionUpdateSiteApprovals": "Standortgenehmigungen aktualisieren"
|
||||
"actionUpdateSiteApprovals": "Standortgenehmigungen aktualisieren",
|
||||
"check": "Prüfen"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Please make sure you're logged in as the correct user.",
|
||||
"inviteErrorNoUser": "We're sorry, but it looks like the invite you're trying to access is not for a user that exists.",
|
||||
"inviteCreateUser": "Please create an account first.",
|
||||
"inviteErrorOidcNotAllowed": "Invites can only be accepted by internal accounts. Sign out and log in with your password for this email.",
|
||||
"inviteLoginInternalOnly": "Invites require an internal account with a password. Create an account or sign in with your password.",
|
||||
"goHome": "Go Home",
|
||||
"inviteLogInOtherUser": "Log In as a Different User",
|
||||
"createAnAccount": "Create an Account",
|
||||
@@ -78,7 +80,7 @@
|
||||
"siteManageSites": "Manage Sites",
|
||||
"siteDescription": "Create and manage sites to enable connectivity to private networks",
|
||||
"sitesBannerTitle": "Connect Any Network",
|
||||
"sitesBannerDescription": "A site is a connection to a remote network that allows Pangolin to provide access to resources, whether public or private, to users anywhere. Install the site network connector (Newt) anywhere you can run a binary or container to establish the connection.",
|
||||
"sitesBannerDescription": "A site is a connection to a remote network that allows Pangolin to provide access to resources, whether public or private, to users anywhere. Install the site network connector anywhere you can run a binary or container to establish the connection.",
|
||||
"sitesBannerButtonText": "Install Site Connector",
|
||||
"approvalsBannerTitle": "Approve or Deny Device Access",
|
||||
"approvalsBannerDescription": "Review and approve or deny device access requests from users. When device approvals are required, users must get admin approval before their devices can connect to your organization's resources.",
|
||||
@@ -150,7 +152,8 @@
|
||||
"siteResourcesHowToAccess": "How to access",
|
||||
"siteResourcesTargetsOnSite": "Targets on this site",
|
||||
"siteSetting": "{siteName} Settings",
|
||||
"siteNewtTunnel": "Newt Site (Recommended)",
|
||||
"siteNewtTunnel": "Pangolin Site (Recommended)",
|
||||
"pangolinSite": "Pangolin Site",
|
||||
"siteNewtTunnelDescription": "Easiest way to create an entrypoint into any network. No extra setup.",
|
||||
"siteWg": "Basic WireGuard",
|
||||
"siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
||||
@@ -224,13 +227,16 @@
|
||||
"never": "Never",
|
||||
"shareErrorSelectResource": "Please select a resource",
|
||||
"proxyResourceTitle": "Manage Public Resources",
|
||||
"proxyResourceDescription": "Create and manage resources that are publicly accessible through a web browser",
|
||||
"proxyResourceDescription": "Create and manage resources that are publicly accessible via a proxy",
|
||||
"publicResourcesBannerTitle": "Web-based Public Access",
|
||||
"publicResourcesBannerDescription": "Public resources are proxies accessible to anyone on the internet through a web browser and include identity and context-aware access policies. Unlike private resources, they do not require client-side software.",
|
||||
"publicResourcesBannerDescription": "Public resources are proxies accessible to anyone on the internet, like a website or API, and include identity and context-aware access policies. Unlike private resources, they do not require any client-side software to access.",
|
||||
"clientResourceTitle": "Manage Private Resources",
|
||||
"clientResourceDescription": "Create and manage resources that are only accessible through a connected client",
|
||||
"privateResourcesBannerTitle": "Zero-Trust Private Access",
|
||||
"privateResourcesBannerDescription": "Private resources use zero-trust security, ensuring users and machines can only access resources you explicitly grant. Connect user devices or machine clients to access these resources over a secure virtual private network.",
|
||||
"licenseBillingBannerTitle": "Manage License Billing",
|
||||
"licenseBillingBannerDescription": "To manage billing for your license keys, including payment methods and invoices, visit the billing page.",
|
||||
"licenseBillingBannerButton": "Go to Billing",
|
||||
"resourcesSearch": "Search resources...",
|
||||
"resourceAdd": "Add Resource",
|
||||
"resourceErrorDelte": "Error deleting resource",
|
||||
@@ -286,7 +292,21 @@
|
||||
"noCountryFound": "No country found.",
|
||||
"siteSelectionDescription": "This site will provide connectivity to the target.",
|
||||
"resourceType": "Resource Type",
|
||||
"resourceTypeDescription": "This controls the resource protocol and how it will be rendered in the browser. This can’t be changed later.",
|
||||
"resourceTypeDescription": "This controls the resource protocol and can’t be changed later.",
|
||||
"resourceTypeSearch": "Search resource types...",
|
||||
"resourceTypeNotFound": "No resource type found",
|
||||
"resourceTypeHttpDescription": "Proxy web traffic over HTTPS using a domain name",
|
||||
"resourceTypeInferenceDescription": "Route AI requests through attached providers",
|
||||
"resourceTypeSshDescription": "Access an SSH server from the browser",
|
||||
"resourceTypeRdpDescription": "Access a remote desktop from the browser",
|
||||
"resourceTypeVncDescription": "Access a VNC desktop from the browser",
|
||||
"resourceTypeTcpDescription": "Proxy raw TCP traffic using a port number",
|
||||
"resourceTypeUdpDescription": "Proxy raw UDP traffic using a port number",
|
||||
"privateResourceTypeDescription": "This controls how clients reach the resource. This can’t be changed later.",
|
||||
"privateResourceTypeHostDescription": "Expose a single host on the site network to connected clients",
|
||||
"privateResourceTypeCidrDescription": "Expose a CIDR range on the site network to connected clients",
|
||||
"privateResourceTypeHttpDescription": "Access an HTTP or HTTPS service through a domain",
|
||||
"privateResourceTypeSshDescription": "Access an SSH server from connected clients",
|
||||
"resourceDomainDescription": "The resource will be served at this fully qualified domain name.",
|
||||
"resourceHTTPSSettings": "HTTPS Settings",
|
||||
"resourceHTTPSSettingsDescription": "Configure how the resource will be accessed over HTTPS",
|
||||
@@ -449,6 +469,8 @@
|
||||
"apiKeysDelete": "Delete API Key",
|
||||
"apiKeysManage": "Manage API Keys",
|
||||
"apiKeysDescription": "API keys are used to authenticate with the integration API",
|
||||
"orgsManage": "Manage Organizations",
|
||||
"orgsDescription": "View and manage all organizations on this instance",
|
||||
"provisioningKeysTitle": "Provisioning Key",
|
||||
"provisioningKeysManage": "Manage Provisioning Keys",
|
||||
"provisioningKeysDescription": "Provisioning keys are used to authenticate automated site provisioning for your organization.",
|
||||
@@ -504,12 +526,12 @@
|
||||
"pendingSitesBannerDescription": "Sites that connect using a provisioning key appear here for review.",
|
||||
"pendingSitesBannerButtonText": "Learn More",
|
||||
"apiKeysSettings": "{apiKeyName} Settings",
|
||||
"userTitle": "Manage All Users",
|
||||
"userDescription": "View and manage all users in the system",
|
||||
"userTitle": "Manage Users",
|
||||
"userDescription": "View and manage all users in this instance",
|
||||
"userAbount": "About User Management",
|
||||
"userAbountDescription": "This table displays all base user objects in the system. Each user may belong to multiple organizations. Removing a user from an organization does not delete their base user object. They will remain in the system. To completely remove a user from the system, you must delete their base user object using the delete action in this table.",
|
||||
"userServer": "Server Users",
|
||||
"userSearch": "Search server users...",
|
||||
"userSearch": "Search users...",
|
||||
"userErrorDelete": "Error deleting user",
|
||||
"userDeleteConfirm": "Confirm Delete User",
|
||||
"userDeleteServer": "Delete User from Server",
|
||||
@@ -578,7 +600,7 @@
|
||||
"licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ",
|
||||
"invite": "Invitations",
|
||||
"inviteRegenerate": "Regenerate Invitation",
|
||||
"inviteRegenerateDescription": "Revoke previous invitation and create a new one",
|
||||
"inviteRegenerateDescription": "Create a new invite link for this user. The previous invitation will be revoked.",
|
||||
"inviteRemove": "Remove Invitation",
|
||||
"inviteRemoveError": "Failed to remove invitation",
|
||||
"inviteRemoveErrorDescription": "An error occurred while removing the invitation.",
|
||||
@@ -658,11 +680,11 @@
|
||||
"accessUserCreateDescription": "Follow the steps below to create a new user",
|
||||
"userSeeAll": "See All Users",
|
||||
"userTypeTitle": "User Type",
|
||||
"userTypeDescription": "Determine how you want to create the user",
|
||||
"userTypeDescription": "Select the identity provider to use for this user",
|
||||
"userSettings": "User Information",
|
||||
"userSettingsDescription": "Enter the details for the new user",
|
||||
"userSettingsDescription": "Enter the general details for the new user",
|
||||
"inviteEmailSent": "Send invite email to user",
|
||||
"inviteValid": "Invite Valid For (days)",
|
||||
"inviteValid": "Invite Valid For",
|
||||
"selectDuration": "Select duration",
|
||||
"selectResource": "Select Resource",
|
||||
"filterByResource": "Filter By Resource",
|
||||
@@ -700,6 +722,7 @@
|
||||
"nameOptional": "Name (Optional)",
|
||||
"accessControls": "Access Controls",
|
||||
"userDescription2": "Manage the settings on this user",
|
||||
"userGeneralSettingsDescription": "Manage this user's roles and settings in the organization",
|
||||
"accessRoleErrorAdd": "Failed to add user to role",
|
||||
"accessRoleErrorAddDescription": "An error occurred while adding user to the role.",
|
||||
"userSaved": "User saved",
|
||||
@@ -855,12 +878,16 @@
|
||||
"authMethodsSave": "Save Settings",
|
||||
"policyAuthStackTitle": "Authentication",
|
||||
"policyAuthStackDescription": "Control which authentication methods are required to access this resource",
|
||||
"policyAuthInferenceStackDescription": "Choose which users and roles can authenticate to this AI gateway",
|
||||
"policyAuthOrLogicTitle": "Multiple authentication methods active",
|
||||
"policyAuthOrLogicBanner": "Visitors may authenticate using any one of the active methods below. They do not need to complete all of them.",
|
||||
"policyAuthMethodActive": "Active",
|
||||
"policyAuthMethodOff": "Off",
|
||||
"policyAuthSsoTitle": "Platform SSO",
|
||||
"policyAuthSsoDescription": "Require sign-in through your organization's identity provider",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "Sign-in URL",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users can retrieve their key by signing in with their identity provider at this resource's URL, where it will be shown after login.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Every user already has an identity API key, so you only need to create virtual API keys for non-user clients or shared access. Users retrieve their key here after signing in with their identity provider.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} users, {roles} roles",
|
||||
"policyAuthSsoDefaultIdp": "Default provider",
|
||||
"policyAuthAddDefaultIdentityProvider": "Add Default Identity Provider",
|
||||
@@ -1079,6 +1106,8 @@
|
||||
"accessRoleErrorNewRequired": "New role is required",
|
||||
"accessRoleErrorRemove": "Failed to remove role",
|
||||
"accessRoleErrorRemoveDescription": "An error occurred while removing the role.",
|
||||
"accessRoleInferenceBudget": "AI Budget",
|
||||
"accessRoleInferenceBudgetDescription": "Configure how members of this role restrict AI usage based on spending or token limits",
|
||||
"accessRoleName": "Role Name",
|
||||
"accessRoleQuestionRemove": "You're about to delete the `{name}` role. You cannot undo this action.",
|
||||
"accessRoleRemove": "Remove Role",
|
||||
@@ -1092,8 +1121,10 @@
|
||||
"sitesNotFound": "No sites found.",
|
||||
"pangolinServerAdmin": "Server Admin - Pangolin",
|
||||
"licenseTierProfessional": "Professional License",
|
||||
"licenseTierEnterprise": "Enterprise License",
|
||||
"licenseTierPersonal": "Personal License",
|
||||
"licenseTierEnterprise": "Enterprise",
|
||||
"licenseTierPersonal": "Personal",
|
||||
"licenseTierTier1": "Starter",
|
||||
"licenseTierTier2": "Scale",
|
||||
"licensed": "Licensed",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
@@ -1154,6 +1185,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "Learn more about JMESPath",
|
||||
"idpJmespathLabel": "Identifier Path",
|
||||
"idpJmespathLabelDescription": "The path to the user identifier in the ID token",
|
||||
"idpIdentifierChangeTitle": "Identifier Path Change Warning",
|
||||
"idpIdentifierChangeDescription": "You are about to change the identifier path. This will affect how existing users are mapped. Users who previously signed in through this identity provider may no longer be recognized as the same users.",
|
||||
"idpIdentifierChangeConfirmMessage": "I confirm",
|
||||
"idpIdentifierChangeWarningText": "This will affect how existing users are mapped",
|
||||
"idpJmespathEmailPathOptional": "Email Path (Optional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "The path to the user's email in the ID token",
|
||||
"idpJmespathNamePathOptional": "Name Path (Optional)",
|
||||
@@ -1188,7 +1223,7 @@
|
||||
"orgPoliciesEdit": "Edit Organization Policy",
|
||||
"org": "Organization",
|
||||
"orgSelect": "Select organization",
|
||||
"orgSearch": "Search org",
|
||||
"orgSearch": "Search organizations...",
|
||||
"orgNotFound": "No org found.",
|
||||
"roleMappingPathOptional": "Role Mapping Path (Optional)",
|
||||
"orgMappingPathOptional": "Organization Mapping Path (Optional)",
|
||||
@@ -1327,7 +1362,7 @@
|
||||
"siteLabelsDescription": "Manage labels associated with this site.",
|
||||
"labelsNotFound": "No labels found.",
|
||||
"labelsEmptyCreateHint": "Start typing above to create a label.",
|
||||
"labelSearch": "Search labels",
|
||||
"labelSearch": "Search labels...",
|
||||
"labelSearchOrCreate": "Search or create a label",
|
||||
"accessLabelFilterCount": "{count, plural, one {# label} other {# labels}}",
|
||||
"labelOverflowCount": "+{count, plural, one {# label} other {# labels}}",
|
||||
@@ -1398,6 +1433,24 @@
|
||||
"logoutError": "Error logging out",
|
||||
"signingAs": "Signed in as",
|
||||
"serverAdmin": "Server Admin",
|
||||
"promoteServerAdmin": "Promote to Server admin",
|
||||
"promoteServerAdminTitle": "Promote to Server Admin",
|
||||
"promoteServerAdminQuestion": "Are you sure you want to promote {selectedUser} to server admin?",
|
||||
"promoteServerAdminMessage": "Server admins have the highest privileges and can manage the server.",
|
||||
"promoteServerAdminWarning": "This can be undone at any time by demoting the user.",
|
||||
"promoteServerAdminConfirm": "Promote to Server Admin",
|
||||
"promoteServerAdminSuccess": "User Promoted",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} is now a server admin.",
|
||||
"promoteServerAdminError": "Failed to promote user",
|
||||
"demoteServerAdmin": "Demote from Server admin",
|
||||
"demoteServerAdminTitle": "Demote from Server Admin",
|
||||
"demoteServerAdminQuestion": "Are you sure you want to demote {selectedUser} from server admin?",
|
||||
"demoteServerAdminMessage": "{selectedUser} will lose all server admin privileges.",
|
||||
"demoteServerAdminWarning": "This can be undone at any time by promoting the user.",
|
||||
"demoteServerAdminConfirm": "Demote from server admin",
|
||||
"demoteServerAdminSuccess": "User demoted",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} is no longer a server admin.",
|
||||
"demoteServerAdminError": "Failed to demote user",
|
||||
"managedSelfhosted": "Managed Self-Hosted",
|
||||
"otpEnable": "Enable Two-factor",
|
||||
"otpDisable": "Disable Two-factor",
|
||||
@@ -1424,6 +1477,28 @@
|
||||
"actionDeleteSite": "Delete Site",
|
||||
"actionGetSite": "Get Site",
|
||||
"actionListSites": "List Sites",
|
||||
"actionCreateAiProvider": "Create AI Provider",
|
||||
"actionDeleteAiProvider": "Delete AI Provider",
|
||||
"actionGetAiProvider": "Get AI Provider",
|
||||
"actionListAiProviders": "List AI Providers",
|
||||
"actionUpdateAiProvider": "Update AI Provider",
|
||||
"actionCreateAiModel": "Create AI Model",
|
||||
"actionDeleteAiModel": "Delete AI Model",
|
||||
"actionGetAiModel": "Get AI Model",
|
||||
"actionListAiModels": "List AI Models",
|
||||
"actionUpdateAiModel": "Update AI Model",
|
||||
"actionCreateAiBudget": "Create AI Budget",
|
||||
"actionDeleteAiBudget": "Delete AI Budget",
|
||||
"actionGetAiBudget": "Get AI Budget",
|
||||
"actionListAiBudgets": "List AI Budgets",
|
||||
"actionUpdateAiBudget": "Update AI Budget",
|
||||
"actionListResourceAiModels": "List Resource AI Models",
|
||||
"actionSetResourceAiModels": "Set Resource AI Models",
|
||||
"actionCreateVirtualApiKey": "Create Virtual API Key",
|
||||
"actionDeleteVirtualApiKey": "Delete Virtual API Key",
|
||||
"actionGetVirtualApiKey": "Get Virtual API Key",
|
||||
"actionListVirtualApiKeys": "List Virtual API Keys",
|
||||
"actionUpdateVirtualApiKey": "Update Virtual API Key",
|
||||
"actionApplyBlueprint": "Apply Blueprint",
|
||||
"actionListBlueprints": "List Blueprints",
|
||||
"actionGetBlueprint": "Get Blueprint",
|
||||
@@ -1449,8 +1524,11 @@
|
||||
"actionSetResourcePincode": "Set Resource Pincode",
|
||||
"actionSetResourceEmailWhitelist": "Set Resource Email Whitelist",
|
||||
"actionGetResourceEmailWhitelist": "Get Resource Email Whitelist",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Get Resource Policy",
|
||||
"actionUpdateResourcePolicy": "Update Resource Policy",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Set Resource Policy Users",
|
||||
"actionSetResourcePolicyRoles": "Set Resource Policy Roles",
|
||||
"actionSetResourcePolicyPassword": "Set Resource Policy Password",
|
||||
@@ -1526,6 +1604,8 @@
|
||||
"search": "Search…",
|
||||
"searchPlaceholder": "Search...",
|
||||
"emptySearchOptions": "No options found",
|
||||
"ipFilterSearchPlaceholder": "Enter an IP address…",
|
||||
"ipFilterEmptyMessage": "Enter an IP address to filter by",
|
||||
"create": "Create",
|
||||
"orgs": "Organizations",
|
||||
"loginError": "An unexpected error occurred. Please try again.",
|
||||
@@ -1560,6 +1640,8 @@
|
||||
"commandPaletteCreateMachineClient": "Create Machine Client",
|
||||
"commandPaletteCreateAlertRule": "Create Alert Rule",
|
||||
"commandPaletteCreateIdentityProvider": "Create Identity Provider",
|
||||
"commandPaletteCreateAiProvider": "Create AI Provider",
|
||||
"commandPaletteCreateVirtualApiKey": "Create Virtual API Key",
|
||||
"commandPaletteToggleTheme": "Toggle Theme",
|
||||
"commandPaletteChooseOrganization": "Choose Organization",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,10 +1704,428 @@
|
||||
"sidebarInvitations": "Invitations",
|
||||
"sidebarRoles": "Roles",
|
||||
"sidebarShareableLinks": "Shareable Links",
|
||||
"sidebarAiGateway": "AI Gateway",
|
||||
"sidebarAiProviders": "Providers",
|
||||
"commandAiProviders": "AI Providers",
|
||||
"sidebarVirtualApiKeys": "Virtual API Keys",
|
||||
"sidebarMyApiKeys": "Your API Keys",
|
||||
"sidebarAccount": "Launcher",
|
||||
"commandVirtualApiKeys": "Virtual API Keys",
|
||||
"virtualApiKeysTitle": "Manage Virtual API Keys",
|
||||
"virtualApiKeysDescription": "Create and manage manual API keys for AI Gateway access to public AI gateways",
|
||||
"virtualApiKeysTabIdentity": "Identity Keys",
|
||||
"virtualApiKeysTabVirtual": "Virtual Keys",
|
||||
"virtualApiKeysIdentitySplashTitle": "Identity Keys for Every User",
|
||||
"virtualApiKeysIdentitySplashDescription": "Every user already has a Pangolin identity key for this organization. It is unique to their account and authenticates them to AI gateways they can access.",
|
||||
"virtualApiKeysIdentitySplashExample": "Example",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "How Users Get Their Key",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Visit a public AI gateway URL in the browser and log in with their account.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "Or go to <url></url> while signed in.",
|
||||
"virtualApiKeysIdentitySplashManual": "You can create additional virtual API keys on the Virtual Keys tab. Those keys can be scoped to specific public AI gateways and optionally associated with a user. Creating a key immediately grants access to the selected public AI gateways.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Manually Create a Key",
|
||||
"virtualApiKeysEmailIdentity": "Email Identity Keys",
|
||||
"virtualApiKeysEmailIdentityDescription": "Send each selected user or role their Pangolin identity key.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "Send to all users",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "Email every organization member who has an account email.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Users",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Roles",
|
||||
"virtualApiKeysEmailIdentitySubmit": "Send Emails",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Identity keys emailed",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "Sent {sent} emails.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "{skipped} users were skipped because they do not have an email address.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "Select at least one user or role, or send to all users.",
|
||||
"virtualApiKeysEmailIdentityError": "Error sending identity keys",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "Failed to email identity keys",
|
||||
"virtualApiKeysBannerTitle": "Identity Keys for Every User",
|
||||
"virtualApiKeysBannerDescription": "Every user already has an identity key available at {keysUrl}. You can also manually generate keys here that grant direct access to public AI gateways.",
|
||||
"virtualApiKeysBannerButtonText": "View Identity Keys",
|
||||
"virtualApiKeys": "Virtual API Keys",
|
||||
"virtualApiKeysSearch": "Search keys...",
|
||||
"virtualApiKeysCreate": "Create Virtual API Key",
|
||||
"virtualApiKeysCreateDescription": "Mint a manual key that can call public AI gateways in this organization",
|
||||
"virtualApiKeysCreateButton": "Create Key",
|
||||
"virtualApiKeysEmpty": "No virtual API keys yet",
|
||||
"virtualApiKeysName": "Name",
|
||||
"virtualApiKeysDescriptionOptional": "Description (optional)",
|
||||
"virtualApiKeysAssociateUserOptional": "Associate User (optional)",
|
||||
"virtualApiKeysAssociateUserDescription": "Associate this key with a user to track usage. Once created, this key immediately grants access to the selected public AI gateways. You do not need to associate a user to manually to the resource. The key will also show up in the user's profile.",
|
||||
"virtualApiKeysAllResources": "All public AI gateways",
|
||||
"virtualApiKeysAllResourcesDescription": "Allow this key to access every public AI gateway in the organization",
|
||||
"virtualApiKeysSelectResources": "Public AI Gateways",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Select resources",
|
||||
"virtualApiKeysSelectResourcesDescription": "Choose which public AI gateways this key can access",
|
||||
"virtualApiKeysNoResources": "No resources",
|
||||
"virtualApiKeysSecret": "Key",
|
||||
"virtualApiKeysCopyKey": "Copy this key. You can view it again later from the table or when editing.",
|
||||
"virtualApiKeysViewSecret": "View Secret",
|
||||
"virtualApiKeysViewSecretTitle": "Virtual API Key Secret",
|
||||
"virtualApiKeysViewSecretDescription": "This secret grants access to the public AI gateways assigned to this key",
|
||||
"virtualApiKeysEdit": "Edit Virtual API Key",
|
||||
"virtualApiKeysEditDescription": "Update the associated user and public AI gateway access for this key",
|
||||
"virtualApiKeysSaveButton": "Save Changes",
|
||||
"virtualApiKeysSelectResourcesRequired": "Select at least one public AI gateway, or enable all public AI gateways",
|
||||
"virtualApiKeysUpdated": "Virtual API key updated",
|
||||
"virtualApiKeysUpdatedDescription": "The virtual API key has been updated",
|
||||
"virtualApiKeysErrorUpdate": "Error updating virtual API key",
|
||||
"virtualApiKeysErrorUpdateDescription": "Failed to update virtual API key",
|
||||
"virtualApiKeysErrorCreate": "Error creating virtual API key",
|
||||
"virtualApiKeysErrorCreateDescription": "Failed to create virtual API key",
|
||||
"virtualApiKeysErrorDelete": "Error deleting virtual API key",
|
||||
"virtualApiKeysErrorDeleteMessage": "Failed to delete virtual API key",
|
||||
"virtualApiKeysDeleted": "Virtual API key deleted",
|
||||
"virtualApiKeysDeletedDescription": "The virtual API key has been deleted",
|
||||
"virtualApiKeysDelete": "Delete Virtual API Key",
|
||||
"virtualApiKeysDeleteConfirm": "Delete Key",
|
||||
"virtualApiKeysQuestionRemove": "Are you sure you want to delete this virtual API key?",
|
||||
"virtualApiKeysMessageRemove": "Clients using this key will lose access immediately.",
|
||||
"virtualApiKeysErrorFetchSecret": "Error loading secret",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Failed to load the virtual API key secret",
|
||||
"virtualApiKeysFilterUnassigned": "Unassigned",
|
||||
"virtualApiKeysInferenceBudget": "Budget",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Configure how this key restricts AI usage based on spending or token limits",
|
||||
"virtualApiKeysEmailOnGenerate": "Email key upon generation",
|
||||
"virtualApiKeysEmailThisKey": "Email this key",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Send the key to the associated user and additional addresses after it is created",
|
||||
"virtualApiKeysEmailThisKeyDescription": "Send the current key to the associated user and additional addresses",
|
||||
"virtualApiKeysEmailSmtpRequired": "Email is not configured on this server",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "Configure SMTP to email virtual API keys.",
|
||||
"virtualApiKeysEmailSendToUser": "Send to associated user",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Email the key to the associated user's account email",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "Associate a user to send the key to that user",
|
||||
"virtualApiKeysEmailAdditional": "Additional emails",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "Add email and press Enter",
|
||||
"virtualApiKeysEmailRecipientsRequired": "Select the associated user or add at least one email address",
|
||||
"myVirtualApiKeysTitle": "Your API Keys",
|
||||
"myVirtualApiKeysDescription": "View your identity key and any virtual API keys attributed to you in this organization",
|
||||
"myVirtualApiKeysResourceTitle": "Your API Keys for {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "View your identity key and virtual API keys attributed to you that can access {resourceName}",
|
||||
"myVirtualApiKeysIdentityTitle": "Identity Key",
|
||||
"myVirtualApiKeysIdentityHeadline": "Your Personal API Key",
|
||||
"myVirtualApiKeysIdentityDescription": "Your personal key for this organization. It is unique to your account and used to identify you when calling AI Gateway resources.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Your Personal API Key for {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Your personal key for this organization. Use it to call {resourceName}.",
|
||||
"myVirtualApiKeysManualTitle": "Attributed Keys",
|
||||
"myVirtualApiKeysManualDescription": "Manual virtual API keys an admin associated with your account",
|
||||
"myVirtualApiKeysManualResourceDescription": "Manual virtual API keys associated with your account that can access {resourceName}",
|
||||
"myVirtualApiKeysManualEmpty": "No attributed keys yet",
|
||||
"myVirtualApiKeysKindUser": "Identity",
|
||||
"myVirtualApiKeysKindManual": "Manual",
|
||||
"myVirtualApiKeysUnnamed": "Unnamed key",
|
||||
"myVirtualApiKeysRevealSecret": "Reveal Secret",
|
||||
"myVirtualApiKeysViewSecretDescription": "This secret authenticates you to AI Gateway resources",
|
||||
"aiClientConfigTitle": "Configure Coding Agents",
|
||||
"aiClientConfigDescription": "Copy configuration for popular coding agents, or let the Pangolin CLI set it up for you automatically.",
|
||||
"aiClientConfigDescriptionClaude": "Anthropic's agentic coding tool for the terminal.",
|
||||
"aiClientConfigDescriptionCodex": "OpenAI's agentic coding tool for the terminal.",
|
||||
"aiClientConfigDescriptionOpencode": "Open source terminal coding agent.",
|
||||
"aiClientConfigDescriptionGemini": "Google's agentic coding tool for the terminal.",
|
||||
"aiClientConfigSetup": "Setup",
|
||||
"aiClientConfigTabCli": "Automatic (CLI)",
|
||||
"aiClientConfigTabManual": "Manual Configuration",
|
||||
"aiClientConfigPreset": "Configuration",
|
||||
"aiClientConfigStep": "Step {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "This snippet includes example values, such as a model name or resource URL. Replace them with your own before using it.",
|
||||
"aiClientConfigRevealError": "Could not load your API key.",
|
||||
"aiClientConfigRevealRetry": "Try again",
|
||||
"resourceGeneralAiClientConfigDescription": "This resource can be used from common clients like Claude Code and OpenCode. <configLink>Learn more</configLink>",
|
||||
"aiProvidersTitle": "AI Providers",
|
||||
"aiProvidersDescription": "Connect model providers for AI workloads in this organization",
|
||||
"aiProvidersBannerTitle": "Connect Model Providers",
|
||||
"aiProvidersBannerDescription": "Providers are the model backends Pangolin uses for AI workloads. Connect OpenAI, Anthropic, and other providers here, then attach them to public AI gateways so users can call models with identity-aware access, budgets, and logging.",
|
||||
"aiProvidersAdd": "Add Provider",
|
||||
"aiProvidersSearch": "Search providers...",
|
||||
"aiProvidersEmpty": "No AI providers yet",
|
||||
"aiProviderCreate": "Create AI Provider",
|
||||
"aiProviderCreateDescription": "Add a model provider for this organization",
|
||||
"aiProviderSeeAll": "See All Providers",
|
||||
"aiProviderSetting": "Provider Settings for {providerName}",
|
||||
"aiProviderSettingDescription": "Configure this AI provider",
|
||||
"aiProviderGeneral": "General",
|
||||
"aiProviderGeneralDescription": "Basic settings for this provider",
|
||||
"aiProviderConfiguration": "Configuration",
|
||||
"aiProviderConfigurationDescription": "Network routing and authentication for this provider",
|
||||
"aiProviderNetworkSettings": "Network Settings",
|
||||
"aiProviderNetworkSettingsDescription": "Choose how traffic reaches this provider",
|
||||
"aiProviderAuthSettings": "Authentication",
|
||||
"aiProviderAuthSettingsDescription": "Configure how this provider authenticates requests to its upstream URL",
|
||||
"aiProviderBudgetSettings": "Budget",
|
||||
"aiProviderBudgetSettingsDescription": "Configure how this provider restricts usage based on spending or token limits",
|
||||
"aiBudgetAdd": "Add Budget",
|
||||
"aiBudgetEmpty": "No budgets configured yet. Click Add Budget to set a spending or token limit.",
|
||||
"aiBudgetUnit": "Spend Type",
|
||||
"aiBudgetPeriod": "Reset Period",
|
||||
"aiBudgetAmount": "Maximum Spend",
|
||||
"aiBudgetAmountPlaceholder": "Maximum spend",
|
||||
"aiBudgetPeriodHourly": "Hourly",
|
||||
"aiBudgetPeriodDaily": "Daily",
|
||||
"aiBudgetPeriodWeekly": "Weekly",
|
||||
"aiBudgetPeriodMonthly": "Monthly",
|
||||
"aiBudgetPeriodYearly": "Yearly",
|
||||
"aiBudgetPeriodLifetime": "Lifetime",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "Tokens",
|
||||
"aiBudgetConflictError": "A budget for this reset period and spend type already exists",
|
||||
"aiBudgetInvalidAmountError": "Enter a maximum spend greater than 0",
|
||||
"aiBudgetUpdated": "Budgets updated",
|
||||
"aiBudgetErrorSave": "Failed to update budgets",
|
||||
"aiProviderType": "Provider Type",
|
||||
"aiProviderTypeSearch": "Search providers...",
|
||||
"aiProviderTypeNotFound": "No provider type found",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI Gateway",
|
||||
"aiProviderTypeCustom": "Custom",
|
||||
"aiProviderTypeOpenaiDescription": "OpenAI API",
|
||||
"aiProviderTypeAnthropicDescription": "Anthropic API",
|
||||
"aiProviderTypeGoogleGeminiDescription": "Google Gemini API",
|
||||
"aiProviderTypeVertexAiDescription": "Google Vertex AI API",
|
||||
"aiProviderTypeBedrockDescription": "Amazon Bedrock Runtime API",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "Microsoft Foundry API",
|
||||
"aiProviderTypeOpenRouterDescription": "OpenRouter API",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "Vercel AI Gateway API",
|
||||
"aiProviderTypeCustomDescription": "Bring your own endpoint or route via site targets",
|
||||
"aiProviderUpstreamUrl": "Upstream URL",
|
||||
"aiProviderUpstreamUrlDescription": "Base URL for the provider API",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Leave blank to use the default upstream URL for this provider",
|
||||
"aiProviderEffectiveUpstreamUrl": "Effective Upstream URL",
|
||||
"aiProviderApiKey": "API Key",
|
||||
"aiProviderApiKeyDescription": "API key used to authenticate requests to this provider",
|
||||
"aiProviderCustomHeadersDescription": "Headers sent on every request to this provider. Newline separated: Header-Name: value",
|
||||
"aiProviderApiKeyLastChars": "API Key",
|
||||
"aiProviderAuthType": "Auth Type",
|
||||
"aiProviderAuthTypeSearch": "Search auth types...",
|
||||
"aiProviderAuthTypeNotFound": "No auth type found",
|
||||
"aiProviderAuthTypeBearer": "Bearer",
|
||||
"aiProviderAuthTypeBearerDescription": "Authorization: Bearer key. Used by OpenAI and most providers",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "x-api-key header. Used by Anthropic",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "x-goog-api-key header. Used by Google Gemini",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Authorization: Splunk key. Used by Splunk HTTP Event Collector",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-authorization: Bearer key. Used by Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeNone": "No Auth",
|
||||
"aiProviderAuthTypePassthrough": "Passthrough",
|
||||
"aiProviderAuthTypeDescription": "How the upstream API authenticates requests",
|
||||
"aiProviderAuthTypePassthroughDescription": "Forward the caller's API key headers to the upstream",
|
||||
"aiProviderAuthTypeNoneDescription": "Do not send authentication headers to the upstream",
|
||||
"aiProviderRoutingMode": "Routing Mode",
|
||||
"aiProviderRoutingModeDescription": "Send traffic to an upstream URL or to HTTP targets on your sites",
|
||||
"aiProviderRoutingModeUrl": "Upstream URL",
|
||||
"aiProviderRoutingModeUrlDescription": "Call a public or private API base URL",
|
||||
"aiProviderRoutingModeTarget": "Site Targets",
|
||||
"aiProviderRoutingModeTargetDescription": "Route through targets on your sites",
|
||||
"aiProviderRoutingModeTargetNote": "After creating this provider, configure site targets on the Network Settings tab.",
|
||||
"aiProviderTargetNoOne": "This provider doesn't have any targets. Add a target to route requests through your sites.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Sites connected to remote nodes are inaccessable to be routed to on AI Gateway providers.",
|
||||
"aiProviderSkipTlsVerification": "Skip TLS Verification",
|
||||
"aiProviderSkipTlsVerificationDescription": "Disable TLS certificate verification for the upstream connection",
|
||||
"aiProviderBudget": "Budget",
|
||||
"aiProviderBudgetDescription": "Optional spending or token budget for this provider",
|
||||
"aiProviderBudgetAmount": "Budget Amount",
|
||||
"aiProviderBudgetUnit": "Budget Unit",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Tokens",
|
||||
"aiProviderEnabled": "Enabled",
|
||||
"aiProviderEnabledDescription": "Fully disable this provider across all resources",
|
||||
"aiProviderErrorCreate": "Failed to create AI provider",
|
||||
"aiProviderErrorUpdate": "Failed to update AI provider",
|
||||
"aiProviderErrorDelete": "Failed to delete AI provider",
|
||||
"aiProviderErrorLoad": "Failed to load AI provider",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Enter a valid upstream URL",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Upstream URL is required for this provider",
|
||||
"aiProviderErrorAuthTypeRequired": "Auth type is required",
|
||||
"aiProviderErrorApiKeyRequired": "API key is required",
|
||||
"aiProviderErrorRoutingModeTarget": "Site targets routing is only available for custom providers",
|
||||
"aiProviderErrorCapabilitiesRequired": "Select at least one API capability",
|
||||
"aiProviderCapabilities": "API Capabilities",
|
||||
"aiProviderCapabilitiesDescription": "Select which API formats this provider can handle. Known providers start with recommended defaults.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Select which API formats this custom provider can handle",
|
||||
"aiProviderCapabilitiesSelect": "Select capabilities",
|
||||
"aiProviderCapabilitiesEmpty": "No capabilities found",
|
||||
"aiProviderCapabilitiesSearch": "Search capabilities...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI Chat Completions",
|
||||
"aiCapabilityOpenaiChatDescription": "Supports /v1/chat/completions",
|
||||
"aiCapabilityOpenaiResponses": "OpenAI Responses",
|
||||
"aiCapabilityOpenaiResponsesDescription": "Supports /v1/responses",
|
||||
"aiCapabilityAnthropicMessages": "Anthropic Messages",
|
||||
"aiCapabilityAnthropicMessagesDescription": "Supports /v1/messages",
|
||||
"aiCapabilityV1Models": "Models List",
|
||||
"aiCapabilityV1ModelsDescription": "Supports /v1/models model discovery",
|
||||
"aiCapabilityGeminiGenerateContent": "Gemini Generate Content",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Supports the direct Gemini API",
|
||||
"aiCapabilityBedrockModelInvoke": "Bedrock Model Invoke",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Supports Amazon Bedrock InvokeModel",
|
||||
"aiCapabilityGoogleGenerateContent": "Vertex Generate Content",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Supports Vertex AI Gemini format",
|
||||
"aiCapabilityGoogleRawPredict": "Vertex Raw Predict",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Supports Vertex AI rawPredict for Anthropic models",
|
||||
"aiCapabilityBedrockConverse": "Bedrock Converse",
|
||||
"aiCapabilityBedrockConverseDescription": "Supports Amazon Bedrock Converse API",
|
||||
"aiProviderCreated": "AI provider created",
|
||||
"aiProviderUpdated": "AI provider updated",
|
||||
"aiProviderDeleted": "AI provider deleted",
|
||||
"aiProviderDelete": "Delete Provider",
|
||||
"aiProviderDeleteConfirm": "Delete Provider",
|
||||
"aiProviderQuestionRemove": "Are you sure you want to delete this AI provider?",
|
||||
"aiProviderMessageRemove": "This will permanently delete the provider and its models and targets. This cannot be undone.",
|
||||
"aiProviderErrorNoUpdate": "AI provider is not available to update",
|
||||
"aiProviderModels": "Models",
|
||||
"aiProviderModelsDescription": "Define allow and block lists for this provider. Requests must match an allow entry and must not match a block entry.",
|
||||
"aiProviderCreateModelsDescription": "Choose which models this provider can serve. An empty allow list denies all traffic. You can add or change models later.",
|
||||
"aiProviderModelsPlaceholder": "Search models or type a custom key",
|
||||
"aiProviderModelsAllow": "Allow List",
|
||||
"aiProviderModelsAllowDescription": "Models that may be used through this provider. Empty means deny all.",
|
||||
"aiProviderModelsAllowPlaceholder": "Enter model key",
|
||||
"aiProviderModelsAllowEmpty": "No models added yet.",
|
||||
"aiProviderModelsBlock": "Block List",
|
||||
"aiProviderModelsBlockDescription": "Models to deny even if they match an allow entry.",
|
||||
"aiProviderModelsBlockPlaceholder": "Enter model key",
|
||||
"aiProviderModelsBlockEmpty": "No models added yet.",
|
||||
"aiProviderModelsAdd": "Add Models",
|
||||
"aiProviderModelsClearAll": "Clear All",
|
||||
"aiProviderModelsAddCustom": "Add \"{key}\"",
|
||||
"aiProviderModelsAddCustomHint": "Press Enter to add this custom model key.",
|
||||
"aiProviderModelsAddBulk": "Add {count} custom keys",
|
||||
"aiProviderModelsAddBulkHint": "Press Enter to add {count} custom keys.",
|
||||
"aiProviderModelsAddOne": "Add {key} now",
|
||||
"aiProviderModelsAddSelected": "Add Selected",
|
||||
"aiProviderModelsSelectedCount": "{count} selected",
|
||||
"aiProviderModelsSelectAll": "Select all",
|
||||
"aiProviderModelsClearSelected": "Clear",
|
||||
"aiProviderModelsBulkHint": "Select known models or type a custom key.",
|
||||
"aiProviderModelsCatalogEmpty": "No matching catalog models.",
|
||||
"aiProviderModelsCatalogHeading": "Known Models",
|
||||
"aiProviderModelsAllLabel": "All models",
|
||||
"aiProviderModelsAllPatternHint": "Wildcard: *",
|
||||
"aiProviderModelsAddAllAllow": "Allow all models",
|
||||
"aiProviderModelsAddAllBlock": "Block all models",
|
||||
"aiProviderModelsAddAllDescription": "Uses the * wildcard so every model key matches.",
|
||||
"aiProviderModelsViewMore": "View more ({count})",
|
||||
"aiProviderModelsViewLess": "View less",
|
||||
"aiProviderModelsRemove": "Remove model",
|
||||
"aiProviderModelsEditHint": "Click to edit model settings",
|
||||
"aiProviderModelsSourceCatalog": "Known catalog model",
|
||||
"aiProviderModelsSourceCustom": "Custom model key",
|
||||
"aiProviderModelsSourcePattern": "Wildcard pattern",
|
||||
"aiProviderModelsSourceAll": "Matches every model key",
|
||||
"aiProviderModelsBudgetConfigured": "Budget configured",
|
||||
"aiProviderModelsEditTitle": "Edit Model",
|
||||
"aiProviderModelsEditDescription": "Update the model key or configure its budget.",
|
||||
"aiProviderModelsBudgetTab": "Budget",
|
||||
"aiProviderModelsKeyLabel": "Model Key",
|
||||
"aiProviderModelsKeyRequired": "Enter a model key",
|
||||
"aiProviderModelsKeyDuplicate": "This model key is already on a list",
|
||||
"aiProviderModelsOverlapError": "These patterns cannot be on both lists: {keys}",
|
||||
"aiProviderModelsUpdated": "Models updated",
|
||||
"aiProviderModelsErrorUpdate": "Failed to update models",
|
||||
"aiResourceProviders": "Providers",
|
||||
"aiResourceProvidersDescription": "Choose which AI providers this AI gateway can use",
|
||||
"aiResourceProvidersHelp": "Attach providers and choose inherit (use each provider's lists) or select (pick an allow list for this resource). Allow patterns that conflict across attached providers are not allowed.",
|
||||
"aiResourceProvidersSelect": "Select providers",
|
||||
"aiResourceProvidersEmpty": "No AI providers found",
|
||||
"aiResourceProvidersNoneAttached": "No providers attached yet.",
|
||||
"aiResourceProvidersAdd": "Add provider",
|
||||
"aiResourceProvidersRemove": "Remove provider",
|
||||
"aiResourceProviderToggleEnabled": "Enable or disable this provider on the resource",
|
||||
"aiResourceProviderDisabled": "Disabled",
|
||||
"aiResourceProvidersUpdated": "Providers updated",
|
||||
"aiResourceProvidersErrorUpdate": "Failed to update providers",
|
||||
"aiResourceProviderEditDescription": "Choose how this provider's models are exposed on this resource.",
|
||||
"viewProviderSettings": "View Provider Settings",
|
||||
"aiResourceProviderMode": "Access mode",
|
||||
"aiResourceProviderModeInherit": "Inherit",
|
||||
"aiResourceProviderModeSelect": "Select",
|
||||
"aiResourceProviderModeSelectSummary": "Select · {count} models",
|
||||
"aiResourceProviderModeInheritHelp": "Use this provider's allow and block lists as configured on the provider.",
|
||||
"aiResourceProviderModeSelectHelp": "Choose a subset of this provider's allow-list models for this resource.",
|
||||
"aiResourceProviderAllowModels": "Allow list",
|
||||
"aiResourceProviderAllowModelsSelect": "Select models",
|
||||
"aiResourceProviderAllowModelsSearch": "Search models...",
|
||||
"aiResourceProviderAllowModelsEmpty": "No models found",
|
||||
"aiResourceProviderAllowModelsHelp": "Only models from this provider's allow list can be selected.",
|
||||
"aiResourceAliasRequired": "Alias is required for AI gateways",
|
||||
"aiResourceDomainConfiguration": "Domain Configuration",
|
||||
"aiResourceDomainConfigurationDescription": "Choose the domain clients will use to reach this AI gateway.",
|
||||
"aiUsageAnalyticsTitle": "AI Usage Analytics",
|
||||
"aiUsageAnalyticsDescription": "Analyze AI gateway cost, token usage, and activity across providers, resources, roles, and users",
|
||||
"aiUsageTabOverview": "Overview",
|
||||
"aiUsageTabProviders": "Providers",
|
||||
"aiUsageTabResources": "Resources",
|
||||
"aiUsageFilterProvider": "Provider",
|
||||
"aiUsageFilterModel": "Model",
|
||||
"aiUsageFilterResource": "Resource",
|
||||
"aiUsageFilterRole": "Role",
|
||||
"aiUsageFilterUser": "User",
|
||||
"aiUsageFilterAllProviders": "All Providers",
|
||||
"aiUsageFilterAllModels": "All Models",
|
||||
"aiUsageFilterAllResources": "All Resources",
|
||||
"aiUsageFilterAllRoles": "All Roles",
|
||||
"aiUsageFilterAllUsers": "All Users",
|
||||
"aiUsageFilterSearch": "Search...",
|
||||
"aiUsageFilterNotFound": "No options found",
|
||||
"aiUsageResetFilters": "Reset Filters",
|
||||
"aiUsageRefresh": "Refresh",
|
||||
"aiUsageTokenTypePrompt": "Prompt",
|
||||
"aiUsageTokenTypeCacheRead": "Cache read",
|
||||
"aiUsageTokenTypeCacheWrite": "Cache write",
|
||||
"aiUsageTokenTypeCompletion": "Completion",
|
||||
"aiUsageTokenTypeReasoning": "Reasoning",
|
||||
"aiUsageRequests": "Requests",
|
||||
"aiUsageCost": "Cost",
|
||||
"aiUsageTokens": "Tokens",
|
||||
"aiUsageOther": "Other",
|
||||
"aiUsageTotalRequests": "Total Requests",
|
||||
"aiUsageTotalTokens": "Total Tokens",
|
||||
"aiUsageTotalCost": "Total Cost",
|
||||
"aiUsageEstimated": "Estimated",
|
||||
"aiUsageRequestVolume": "Request Volume",
|
||||
"aiUsageTokenUsage": "Token Usage",
|
||||
"aiUsageModelCost": "Model Cost",
|
||||
"aiUsageModelTokens": "Model Tokens",
|
||||
"aiUsageTopModels": "Top Models",
|
||||
"aiUsageTopProviders": "Top Providers",
|
||||
"aiUsageProviderCost": "Provider Cost",
|
||||
"aiUsageProviderTokenUsage": "Provider Token Usage",
|
||||
"aiUsageTopResources": "Top Resources",
|
||||
"aiUsageResourceCost": "Resource Cost",
|
||||
"aiUsageResourceTokenUsage": "Resource Token Usage",
|
||||
"aiUsageResourceTypePublic": "Public Resource",
|
||||
"aiUsageResourceTypeSite": "Private Resource",
|
||||
"aiUsageNoResource": "No resource",
|
||||
"aiUsageRolesTab": "Roles",
|
||||
"aiUsageUsersTab": "Users",
|
||||
"aiUsageTopRoles": "Top Roles",
|
||||
"aiUsageRoleCost": "Role Cost",
|
||||
"aiUsageRoleTokenUsage": "Role Token Usage",
|
||||
"aiUsageTopUsers": "Top Users",
|
||||
"aiUsageUserCost": "User Cost",
|
||||
"aiUsageUserTokenUsage": "User Token Usage",
|
||||
"aiUsageUnknownUser": "Unknown user",
|
||||
"aiUsageVirtualApiKeysTab": "Virtual API Keys",
|
||||
"aiUsageFilterVirtualApiKey": "Virtual API Key",
|
||||
"aiUsageFilterAllVirtualApiKeys": "All Virtual API Keys",
|
||||
"aiUsageTopVirtualApiKeys": "Top Virtual API Keys",
|
||||
"aiUsageVirtualApiKeyCost": "Virtual API Key Cost",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Virtual API Key Token Usage",
|
||||
"aiUsageUnknownVirtualApiKey": "No virtual API key",
|
||||
"aiUsageUnnamedVirtualApiKey": "Unnamed key",
|
||||
"aiUsageLoading": "Loading...",
|
||||
"aiUsageNoData": "No data",
|
||||
"resourceBudgetSettings": "Budget",
|
||||
"resourceBudgetSettingsDescription": "Configure how this AI gateway restricts usage based on spending or token limits",
|
||||
"sidebarApiKeys": "API Keys",
|
||||
"sidebarOrgs": "Organizations",
|
||||
"sidebarProvisioning": "Provisioning",
|
||||
"sidebarSettings": "Settings",
|
||||
"sidebarAllUsers": "All Users",
|
||||
"sidebarAllUsers": "Users",
|
||||
"sidebarIdentityProviders": "Identity Providers",
|
||||
"sidebarLicense": "License",
|
||||
"sidebarClients": "Clients",
|
||||
@@ -1695,6 +2195,8 @@
|
||||
"alertingRuleSaved": "Alert rule saved",
|
||||
"alertingRuleSavedCreatedDescription": "Your new alert rule was created. You can keep editing it on this page.",
|
||||
"alertingRuleSavedUpdatedDescription": "Your changes to this alert rule were saved.",
|
||||
"alertingTestAlertSent": "Test alert sent",
|
||||
"alertingTestAlertSentDescription": "A test alert was sent to the actions configured on this rule.",
|
||||
"alertingEditRule": "Edit Alert Rule",
|
||||
"alertingCreateRule": "Create Alert Rule",
|
||||
"alertingRuleCredenzaDescription": "Choose what to watch, when to fire, and how to notify",
|
||||
@@ -1804,6 +2306,12 @@
|
||||
"alertingRulesBannerDescription": "Each rule ties together what to watch (a site, health check, or resource), when to fire (for example offline or unhealthy), and how to notify your team via email, webhooks, or integrations. Use this list to create, enable, and manage those rules.",
|
||||
"alertingHealthChecksBannerTitle": "Monitor Health & Resources",
|
||||
"alertingHealthChecksBannerDescription": "Health checks are HTTP or TCP monitors you define once. You can then use them as sources in alert rules so you get notified when a target becomes healthy or unhealthy. Health checks on resources also appear here.",
|
||||
"alertingTestRule": "Test Alert Rule",
|
||||
"alertingAddActionHeading": "Add New Action",
|
||||
"alertingSelectActionType": "Select an Action",
|
||||
"alertingNoActionsTitle": "No actions configured",
|
||||
"alertingNoActionsSaveDescription": "Add at least one action so this rule can notify someone when it fires.",
|
||||
"alertingNoActionsTestDescription": "Add at least one action before you can test this rule.",
|
||||
"standaloneHcTableTitle": "Health Checks",
|
||||
"standaloneHcSearchPlaceholder": "Search health checks…",
|
||||
"standaloneHcAddButton": "Create Health Check",
|
||||
@@ -2122,6 +2630,7 @@
|
||||
"createDomainType": "Type:",
|
||||
"createDomainName": "Name:",
|
||||
"createDomainValue": "Value:",
|
||||
"multiSelectFilterCount": "{count} selected",
|
||||
"createDomainCnameRecords": "CNAME Records",
|
||||
"createDomainARecords": "A Records",
|
||||
"createDomainRecordNumber": "Record {number}",
|
||||
@@ -2193,6 +2702,8 @@
|
||||
"subnetPlaceholder": "Subnet",
|
||||
"addressDescription": "The internal address of the client. Must fall within the organization's subnet.",
|
||||
"selectSites": "Select sites",
|
||||
"selectResources": "Select resources",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# resource} other {# resources}}",
|
||||
"selectLabels": "Select labels",
|
||||
"sitesDescription": "The client will have connectivity to the selected sites",
|
||||
"clientInstallOlm": "Install Machine Client",
|
||||
@@ -2234,6 +2745,7 @@
|
||||
"healthScheme": "Method",
|
||||
"healthSelectScheme": "Select Method",
|
||||
"healthCheckPortInvalid": "Port must be between 1 and 65535",
|
||||
"healthCheckHostnameInvalid": "Hostname must not contain whitespace",
|
||||
"healthCheckPath": "Path",
|
||||
"healthHostname": "IP / Host",
|
||||
"healthPort": "Port",
|
||||
@@ -2245,10 +2757,11 @@
|
||||
"timeIsInSeconds": "Time is in seconds",
|
||||
"requireDeviceApproval": "Require Device Approvals",
|
||||
"requireDeviceApprovalDescription": "Users with this role need new devices approved by an admin before they can connect and access resources.",
|
||||
"sshSettings": "SSH Settings",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "AI Gateway",
|
||||
"sshAccess": "SSH Access",
|
||||
"rdpSettings": "RDP Settings",
|
||||
"vncSettings": "VNC Settings",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "SSH Server",
|
||||
"rdpServer": "RDP Server",
|
||||
"vncServer": "VNC Server",
|
||||
@@ -2401,6 +2914,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "AI Gateway",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Scheme",
|
||||
"editInternalResourceDialogEnableSsl": "Enable TLS",
|
||||
@@ -2412,7 +2926,7 @@
|
||||
"editInternalResourceDialogAlias": "Alias",
|
||||
"editInternalResourceDialogAliasDescription": "An optional internal DNS alias for this resource.",
|
||||
"createInternalResourceDialogNoSitesAvailable": "No Sites Available",
|
||||
"createInternalResourceDialogNoSitesAvailableDescription": "You need to have at least one Newt site with a subnet configured to create private resources.",
|
||||
"createInternalResourceDialogNoSitesAvailableDescription": "You need to have at least one site with a subnet configured to create private resources.",
|
||||
"createInternalResourceDialogClose": "Close",
|
||||
"createInternalResourceDialogCreateClientResource": "Create Private Resource",
|
||||
"createInternalResourceDialogCreateClientResourceDescription": "Create a new resource that will only be accessible to clients connected to the organization",
|
||||
@@ -2421,8 +2935,8 @@
|
||||
"privateResourceAllowIcmpPing": "Allow ICMP Ping",
|
||||
"privateResourceNetworkAccess": "Network Access",
|
||||
"privateResourceNetworkAccessDescription": "Control TCP/UDP port access and whether ICMP ping is allowed for this resource.",
|
||||
"hostSettings": "Host Settings",
|
||||
"cidrSettings": "CIDR Settings",
|
||||
"hostSettings": "Host",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Resource Properties",
|
||||
"createInternalResourceDialogName": "Name",
|
||||
"createInternalResourceDialogSite": "Site",
|
||||
@@ -2460,6 +2974,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "AI Gateway",
|
||||
"scheme": "Scheme",
|
||||
"createInternalResourceDialogScheme": "Scheme",
|
||||
"createInternalResourceDialogEnableSsl": "Enable TLS",
|
||||
@@ -2514,7 +3029,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Add a CIDR range (e.g. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Failed to load subnets",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Preference Labels",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will be enforced to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Sites with these labels will prefer to connect through this remote exit node.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Select labels...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Search labels...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Failed to load labels",
|
||||
@@ -2990,7 +3505,8 @@
|
||||
},
|
||||
"priority": "Priority",
|
||||
"priorityDescription": "Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.",
|
||||
"instanceName": "Instance Name",
|
||||
"instanceName": "Server ID",
|
||||
"clearInstanceName": "Reset Server Association",
|
||||
"pathMatchModalTitle": "Configure Path Matching",
|
||||
"pathMatchModalDescription": "Set up how incoming requests should be matched based on their path.",
|
||||
"pathMatchType": "Match Type",
|
||||
@@ -3051,6 +3567,7 @@
|
||||
"validPassword": "Valid Password",
|
||||
"validEmail": "Valid email",
|
||||
"validSSO": "Valid SSO",
|
||||
"validVirtualAPIKey": "Valid Virtual API Key",
|
||||
"view": "View",
|
||||
"configManaged": "Config Managed",
|
||||
"connectedClient": "Connected Client",
|
||||
@@ -3071,7 +3588,42 @@
|
||||
"sidebarLogsAction": "Admin Action Logs",
|
||||
"logRetention": "Log Retention",
|
||||
"logRetentionDescription": "Manage how long different types of logs are retained for this organization or disable them",
|
||||
"logRetentionDisabledWarningTitle": "Log Retention Disabled",
|
||||
"logRetentionDisabledWarningDescription": "{logType} are not being retained for this organization, so new activity will not appear here. Enable retention in security settings to start collecting these logs.",
|
||||
"logRetentionDisabledWarningButton": "Go to Security Settings",
|
||||
"requestLogsDescription": "View detailed request logs for HTTPS resources in this organization",
|
||||
"aiSessionLogs": "AI Gateway Session Logs",
|
||||
"aiSessionLogsDescription": "View prompt and response transcripts for AI gateway requests in this organization",
|
||||
"sidebarLogsAi": "Session Logs",
|
||||
"commandLogsAi": "Session Logs",
|
||||
"sidebarLogsAiUsage": "Usage Analytics",
|
||||
"commandLogsAiUsage": "Usage Analytics",
|
||||
"provider": "Provider",
|
||||
"capability": "Capability",
|
||||
"model": "Model",
|
||||
"virtualApiKey": "Virtual API Key",
|
||||
"noVirtualApiKey": "No virtual API key",
|
||||
"stream": "Stream",
|
||||
"streaming": "Streaming",
|
||||
"nonStreaming": "Non-streaming",
|
||||
"statusCode": "Status Code",
|
||||
"aiSessionId": "Session ID",
|
||||
"aiSessionRequest": "Request",
|
||||
"aiSessionResponse": "Response",
|
||||
"aiSessionNoData": "No data captured",
|
||||
"aiSessionCouldNotParse": "(raw, could not parse transcript)",
|
||||
"aiSessionLogTruncated": "This session was truncated before storage and may be incomplete.",
|
||||
"aiSessionViewRaw": "View Raw JSON",
|
||||
"aiSessionViewChat": "View Chat",
|
||||
"cost": "Cost",
|
||||
"estimated": "Estimated",
|
||||
"tokenUsage": "Token Usage",
|
||||
"promptTokens": "Prompt Tokens",
|
||||
"cacheReadTokens": "Cache Read Tokens",
|
||||
"cacheWriteTokens": "Cache Write Tokens",
|
||||
"completionTokens": "Completion Tokens",
|
||||
"reasoningTokens": "Reasoning Tokens",
|
||||
"totalTokens": "Total Tokens",
|
||||
"requestAnalyticsDescription": "View detailed request analytics for resources in this organization",
|
||||
"logRetentionRequestLabel": "HTTP Request Log Retention",
|
||||
"logRetentionRequestDescription": "How long to retain request logs",
|
||||
@@ -3081,6 +3633,8 @@
|
||||
"logRetentionActionDescription": "How long to retain action logs",
|
||||
"logRetentionConnectionLabel": "Network Log Retention",
|
||||
"logRetentionConnectionDescription": "How long to retain connection logs",
|
||||
"logRetentionAISessionsLabel": "AI Gateway Session Log Retention",
|
||||
"logRetentionAISessionsDescription": "How long to retain AI gateway prompt/response session logs",
|
||||
"logRetentionDisabled": "Disabled",
|
||||
"logRetention3Days": "3 days",
|
||||
"logRetention7Days": "7 days",
|
||||
@@ -3098,8 +3652,8 @@
|
||||
"sourceAddress": "Source Address",
|
||||
"destinationAddress": "Destination Address",
|
||||
"duration": "Duration",
|
||||
"licenseRequiredToUse": "An <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> license or <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is required to use this feature. <bookADemoLink>Book a free demo or POC trial to learn more.</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "The <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Book a free demo or POC trial to learn more.</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Enterprise Edition license</enterpriseEditionLink> is required. <bookADemoLink>Book a demo or trial</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition license</enterpriseEditionLink> is required. <bookADemoLink>Book a demo or trial</bookADemoLink>",
|
||||
"certResolver": "Certificate Resolver",
|
||||
"certResolverDescription": "Select the certificate resolver to use for this resource.",
|
||||
"selectCertResolver": "Select Certificate Resolver",
|
||||
@@ -3242,6 +3796,7 @@
|
||||
"noData": "No Data",
|
||||
"machineClients": "Machine Clients",
|
||||
"install": "Install",
|
||||
"downloadInstaller": "Download Installer",
|
||||
"run": "Run",
|
||||
"envFile": "Environment File",
|
||||
"serviceFile": "Service File",
|
||||
@@ -3297,9 +3852,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "Selecting multiple sites enables resilient routing and failover for high availability.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Learn more",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Restrict access to specific TCP/UDP ports or allow/block all ports.",
|
||||
"createInternalResourceDialogHttpConfiguration": "HTTP configuration",
|
||||
"createInternalResourceDialogHttpConfiguration": "Domain Configuration",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "Choose the domain clients will use to reach this resource over HTTP or HTTPS.",
|
||||
"editInternalResourceDialogHttpConfiguration": "HTTP configuration",
|
||||
"editInternalResourceDialogHttpConfiguration": "Domain Configuration",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "Choose the domain clients will use to reach this resource over HTTP or HTTPS.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3449,11 +4004,12 @@
|
||||
"disconnected": "Disconnected",
|
||||
"approvalsEmptyStateTitle": "Device Approvals Not Enabled",
|
||||
"approvalsEmptyStateDescription": "Enable device approvals for roles to require admin approval before users can connect new devices.",
|
||||
"approvalsEmptyStateHowToTitle": "How to Enable",
|
||||
"approvalsEmptyStateStep1Title": "Go to Roles",
|
||||
"approvalsEmptyStateStep1Description": "Navigate to your organization's roles settings to configure device approvals.",
|
||||
"approvalsEmptyStateStep2Title": "Enable Device Approvals",
|
||||
"approvalsEmptyStateStep2Description": "Edit a role and enable the 'Require Device Approvals' option. Users with this role will need admin approval for new devices.",
|
||||
"approvalsEmptyStatePreviewDescription": "Preview: When enabled, pending device requests will appear here for review",
|
||||
"approvalsEmptyStatePreviewDescription": "When enabled, pending device requests will appear here for review.",
|
||||
"approvalsEmptyStateButtonText": "Manage Roles",
|
||||
"domainErrorTitle": "We are having trouble verifying your domain",
|
||||
"idpAdminAutoProvisionPoliciesTabHint": "Configure role mapping and organization policies on the <policiesTabLink>Auto Provision Settings</policiesTabLink> tab.",
|
||||
@@ -3570,6 +4126,8 @@
|
||||
"httpDestConnectionLogsDescription": "Site and tunnel connection events, including connects and disconnects.",
|
||||
"httpDestRequestLogsTitle": "HTTP Request Logs",
|
||||
"httpDestRequestLogsDescription": "HTTP request logs for proxied resources, including method, path, and response code.",
|
||||
"httpDestAISessionLogsTitle": "AI Session Logs",
|
||||
"httpDestAISessionLogsDescription": "AI gateway request and response sessions, including prompts, model responses, and token usage.",
|
||||
"httpDestSaveChanges": "Save Changes",
|
||||
"httpDestCreateDestination": "Create Destination",
|
||||
"httpDestUpdatedSuccess": "Destination updated successfully",
|
||||
@@ -3726,6 +4284,11 @@
|
||||
"resourceLauncherViewAsAdmin": "View as Admin",
|
||||
"resourceLauncherResourceDetailsDescription": "Connection information and status for this resource.",
|
||||
"resourceLauncherResourceDetails": "Resource Details",
|
||||
"resourceLauncherSitesDescription": "The resource is accessible via the following sites.",
|
||||
"resourceLauncherViewSiteAsAdmin": "View Site as Admin",
|
||||
"resourceLauncherFilterBySite": "Filter by Site",
|
||||
"resourceLauncherSshCommand": "SSH Command",
|
||||
"resourceLauncherSshCommandDescription": "Use the Pangolin CLI to open an SSH session to this resource.",
|
||||
"resourceLauncherAuthMethodsDescription": "Authentication methods enabled for this resource.",
|
||||
"resourceLauncherPrivateClientRequired": "Connect with a client on your device to access this resource privately.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "Client Connection Required",
|
||||
@@ -3735,7 +4298,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Unlabeled",
|
||||
"resourceLauncherAiGateway": "AI Gateway",
|
||||
"resourceLauncherNoSite": "No Site",
|
||||
"resourceLauncherAvailableModels": "Available Models",
|
||||
"resourceLauncherAvailableModelsDescription": "Models you can use with this AI gateway.",
|
||||
"resourceLauncherAvailableModelsEmpty": "No models are available for this resource.",
|
||||
"resourceLauncherAvailableModelsError": "Could not load available models.",
|
||||
"resourceLauncherApiKeys": "API Keys",
|
||||
"resourceLauncherApiKeysDescription": "Use your identity key or an attributed key to authenticate with this resource.",
|
||||
"resourceLauncherApiKeysIdentity": "Identity Key",
|
||||
"resourceLauncherApiKeysManual": "Attributed Keys",
|
||||
"resourceLauncherApiKeysEmpty": "No API keys are available for this resource.",
|
||||
"resourceLauncherApiKeysError": "Could not load API keys.",
|
||||
"resourceLauncherNoResourcesInGroup": "No resources in this group",
|
||||
"resourceLauncherEmptyStateTitle": "No Resources Available",
|
||||
"resourceLauncherEmptyStateDescription": "You don't have access to any resources yet. Contact your administrator to request access.",
|
||||
@@ -3762,9 +4336,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "Could not delete the launcher view. Please try again.",
|
||||
"memberPortalPrevious": "Previous",
|
||||
"memberPortalNext": "Next",
|
||||
"httpSettings": "HTTP Settings",
|
||||
"tcpSettings": "TCP Settings",
|
||||
"udpSettings": "UDP Settings",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Establishing a secure connection…",
|
||||
"sshConnecting": "Connecting…",
|
||||
@@ -3825,5 +4399,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Unicode keyboard mode",
|
||||
"sessionToolbarShow": "Show toolbar",
|
||||
"sessionToolbarHide": "Hide toolbar",
|
||||
"actionUpdateSiteApprovals": "Update Site Approvals"
|
||||
"actionUpdateSiteApprovals": "Update Site Approvals",
|
||||
"check": "Check"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Por favor, asegúrese de que ha iniciado sesión como el usuario correcto.",
|
||||
"inviteErrorNoUser": "Lo sentimos, pero parece que la invitación a la que intentas acceder no es para un usuario que existe.",
|
||||
"inviteCreateUser": "Por favor, cree una cuenta primero.",
|
||||
"inviteErrorOidcNotAllowed": "Las invitaciones solo pueden ser aceptadas por cuentas internas. Cierra sesión e inicia sesión con tu contraseña para este correo electrónico.",
|
||||
"inviteLoginInternalOnly": "Las invitaciones requieren una cuenta interna con una contraseña. Crea una cuenta o inicia sesión con tu contraseña.",
|
||||
"goHome": "Ir a casa",
|
||||
"inviteLogInOtherUser": "Iniciar sesión como un usuario diferente",
|
||||
"createAnAccount": "Crear una cuenta",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "Objetivos en este sitio",
|
||||
"siteSetting": "Ajustes {siteName}",
|
||||
"siteNewtTunnel": "Sitio nuevo (recomendado)",
|
||||
"pangolinSite": "Sitio de Pangolin",
|
||||
"siteNewtTunnelDescription": "La forma más fácil de crear un punto de entrada en cualquier red. Sin configuración extra.",
|
||||
"siteWg": "Wirex Guardia Básica",
|
||||
"siteWgDescription": "Utilice cualquier cliente Wirex Guard para establecer un túnel. Se requiere una configuración manual de NAT.",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "Ningún país encontrado.",
|
||||
"siteSelectionDescription": "Este sitio proporcionará conectividad al objetivo.",
|
||||
"resourceType": "Tipo de recurso",
|
||||
"resourceTypeDescription": "Esto controla el protocolo del recurso y cómo se renderizará en el navegador. Esto no se puede cambiar más tarde.",
|
||||
"resourceTypeDescription": "Esto controla el protocolo del recurso y no se puede cambiar más tarde.",
|
||||
"resourceTypeSearch": "Buscar tipos de recursos...",
|
||||
"resourceTypeNotFound": "No se encontró ningún tipo de recurso",
|
||||
"resourceTypeHttpDescription": "Proxy de tráfico web a través de HTTPS usando un nombre de dominio",
|
||||
"resourceTypeInferenceDescription": "Dirige las solicitudes de IA a través de proveedores adjuntos",
|
||||
"resourceTypeSshDescription": "Accede a un servidor SSH desde el navegador",
|
||||
"resourceTypeRdpDescription": "Accede a un escritorio remoto desde el navegador",
|
||||
"resourceTypeVncDescription": "Accede a un escritorio VNC desde el navegador",
|
||||
"resourceTypeTcpDescription": "Proxy de tráfico TCP crudo usando un número de puerto",
|
||||
"resourceTypeUdpDescription": "Proxy de tráfico UDP crudo usando un número de puerto",
|
||||
"privateResourceTypeDescription": "Esto controla cómo los clientes acceden al recurso. No se puede cambiar más tarde.",
|
||||
"privateResourceTypeHostDescription": "Expone un host único en la red del sitio a los clientes conectados",
|
||||
"privateResourceTypeCidrDescription": "Expón un rango CIDR en la red del sitio para los clientes conectados",
|
||||
"privateResourceTypeHttpDescription": "Accede a un servicio HTTP o HTTPS a través de un dominio",
|
||||
"privateResourceTypeSshDescription": "Accede a un servidor SSH desde clientes conectados",
|
||||
"resourceDomainDescription": "El recurso se servirá en este nombre de dominio completamente calificado.",
|
||||
"resourceHTTPSSettings": "Configuración HTTPS",
|
||||
"resourceHTTPSSettingsDescription": "Configurar cómo se accederá al recurso a través de HTTPS",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "Borrar Clave API",
|
||||
"apiKeysManage": "Administrar claves API",
|
||||
"apiKeysDescription": "Las claves API se utilizan para autenticar con la API de integración",
|
||||
"orgsManage": "Administrar Organizaciones",
|
||||
"orgsDescription": "Ver y gestionar todas las organizaciones en esta instancia",
|
||||
"provisioningKeysTitle": "Clave de aprovisionamiento",
|
||||
"provisioningKeysManage": "Administrar Claves de Aprovisionamiento",
|
||||
"provisioningKeysDescription": "Las claves de aprovisionamiento se utilizan para autenticar la provisión automatizada del sitio para su organización.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "Nombre (opcional)",
|
||||
"accessControls": "Controles de acceso",
|
||||
"userDescription2": "Administrar la configuración de este usuario",
|
||||
"userGeneralSettingsDescription": "Gestionar los roles y configuraciones de este usuario en la organización",
|
||||
"accessRoleErrorAdd": "No se pudo agregar el usuario al rol",
|
||||
"accessRoleErrorAddDescription": "Ocurrió un error mientras se añadía el usuario al rol.",
|
||||
"userSaved": "Usuario guardado",
|
||||
@@ -720,7 +740,7 @@
|
||||
"proxyErrorTls": "Nombre de servidor TLS inválido. Utilice el formato de nombre de dominio o guarde en blanco para eliminar el nombre de servidor TLS.",
|
||||
"proxyEnableSSL": "Activar TLS",
|
||||
"proxyEnableSSLDescription": "Habilita el cifrado SSL/TLS para conexiones seguras HTTPS a los objetivos.",
|
||||
"target": "Target",
|
||||
"target": "Destino",
|
||||
"configureTarget": "Configurar objetivos",
|
||||
"targetErrorFetch": "Error al recuperar los objetivos",
|
||||
"targetErrorFetchDescription": "Se ha producido un error al recuperar los objetivos",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "Guardar ajustes",
|
||||
"policyAuthStackTitle": "Autenticación",
|
||||
"policyAuthStackDescription": "Controla qué métodos de autenticación son necesarios para acceder a este recurso",
|
||||
"policyAuthInferenceStackDescription": "Elige qué usuarios y roles pueden autenticarse en este gateway de IA",
|
||||
"policyAuthOrLogicTitle": "Múltiples métodos de autenticación activos",
|
||||
"policyAuthOrLogicBanner": "Los visitantes pueden autenticarse utilizando cualquier método activo a continuación. No necesitan completar todos ellos.",
|
||||
"policyAuthMethodActive": "Activo",
|
||||
"policyAuthMethodOff": "Apagado",
|
||||
"policyAuthSsoTitle": "SSO de Plataforma",
|
||||
"policyAuthSsoDescription": "Requiere iniciar sesión a través del proveedor de identidad de tu organización",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "URL de inicio de sesión",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Cada usuario ya tiene una clave API de identidad, por lo que solo necesitas crear claves API virtuales para clientes no usuarios o acceso compartido. Los usuarios pueden recuperar su clave iniciando sesión con su proveedor de identidad en la URL de este recurso, donde se mostrará después del inicio de sesión.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Cada usuario ya tiene una clave API de identidad, por lo que solo necesitas crear claves API virtuales para clientes no usuarios o acceso compartido. Los usuarios recuperan su clave aquí después de iniciar sesión con su proveedor de identidad.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} usuarios, {roles} roles",
|
||||
"policyAuthSsoDefaultIdp": "Proveedor por defecto",
|
||||
"policyAuthAddDefaultIdentityProvider": "Añadir Proveedor de Identidad Predeterminado",
|
||||
@@ -928,7 +952,7 @@
|
||||
"unknownCommand": "Comando desconocido",
|
||||
"newtErrorFetchReleases": "No se pudo obtener la información del lanzamiento: {err}",
|
||||
"newtErrorFetchLatest": "Error obteniendo la última versión: {err}",
|
||||
"newtEndpoint": "Endpoint",
|
||||
"newtEndpoint": "Punto final",
|
||||
"newtId": "ID",
|
||||
"newtSecretKey": "Secreto",
|
||||
"newtVersion": "Versión",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "Se requiere un nuevo rol",
|
||||
"accessRoleErrorRemove": "Error al eliminar el rol",
|
||||
"accessRoleErrorRemoveDescription": "Ocurrió un error mientras se eliminaba el rol.",
|
||||
"accessRoleInferenceBudget": "Presupuesto de IA",
|
||||
"accessRoleInferenceBudgetDescription": "Configura cómo los miembros de este rol restringen el uso de IA según los límites de gasto o de tokens",
|
||||
"accessRoleName": "Nombre del Rol",
|
||||
"accessRoleQuestionRemove": "Estás a punto de eliminar el rol `{name}`. No puedes deshacer esta acción.",
|
||||
"accessRoleRemove": "Quitar rol",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "Sitios no encontrados.",
|
||||
"pangolinServerAdmin": "Admin Servidor - Pangolin",
|
||||
"licenseTierProfessional": "Licencia profesional",
|
||||
"licenseTierEnterprise": "Licencia Enterprise",
|
||||
"licenseTierPersonal": "Licencia personal",
|
||||
"licenseTierEnterprise": "Empresa",
|
||||
"licenseTierPersonal": "Uso personal",
|
||||
"licenseTierTier1": "Iniciador",
|
||||
"licenseTierTier2": "Escala",
|
||||
"licensed": "Licenciado",
|
||||
"yes": "Sí",
|
||||
"no": "Nu",
|
||||
@@ -1154,6 +1182,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "Más información sobre JMESPath",
|
||||
"idpJmespathLabel": "Ruta del identificador",
|
||||
"idpJmespathLabelDescription": "La ruta al identificador de usuario en el token de ID",
|
||||
"idpIdentifierChangeTitle": "Advertencia de Cambio de Ruta de Identificador",
|
||||
"idpIdentifierChangeDescription": "Está a punto de cambiar la ruta del identificador. Esto afectará cómo se asignan los usuarios existentes. Los usuarios que anteriormente iniciaron sesión a través de este proveedor de identidad pueden ya no ser reconocidos como los mismos usuarios.",
|
||||
"idpIdentifierChangeConfirmMessage": "Confirmo",
|
||||
"idpIdentifierChangeWarningText": "Esto afectará cómo se asignan los usuarios existentes",
|
||||
"idpJmespathEmailPathOptional": "Ruta de correo (opcional)",
|
||||
"idpJmespathEmailPathOptionalDescription": "La ruta al correo electrónico del usuario en el token de ID",
|
||||
"idpJmespathNamePathOptional": "Ruta del nombre (opcional)",
|
||||
@@ -1285,7 +1317,7 @@
|
||||
"generatePasswordResetCode": "Generar código de restablecimiento de contraseña",
|
||||
"passwordResetCodeGenerated": "Código de restablecimiento de contraseña generado",
|
||||
"passwordResetCodeGeneratedDescription": "Comparte este código con el usuario. Pueden usarlo para restablecer su contraseña.",
|
||||
"passwordResetUrl": "Reset URL",
|
||||
"passwordResetUrl": "URL de reinicio",
|
||||
"passwordNew": "Nueva contraseña",
|
||||
"passwordNewConfirm": "Confirmar nueva contraseña",
|
||||
"changePassword": "Cambiar Contraseña",
|
||||
@@ -1357,8 +1389,8 @@
|
||||
"pangolinDashboard": "Tablero - Pangolin",
|
||||
"noResults": "No se han encontrado resultados.",
|
||||
"terabytes": "TB {count}",
|
||||
"gigabytes": "{count} GB",
|
||||
"megabytes": "{count} MB",
|
||||
"gigabytes": "{count, plural, one {{count} GB} other {{count} GB}}",
|
||||
"megabytes": "{count, plural, one {{count} MB} other {{count} MB}}",
|
||||
"tagsEntered": "Etiquetas introducidas",
|
||||
"tagsEnteredDescription": "Estas son las etiquetas que has introducido.",
|
||||
"tagsWarnCannotBeLessThanZero": "maxTags y minTags no pueden ser menores que 0",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "Error al cerrar sesión",
|
||||
"signingAs": "Conectado como",
|
||||
"serverAdmin": "Admin Servidor",
|
||||
"promoteServerAdmin": "Promover a Admin del Servidor",
|
||||
"promoteServerAdminTitle": "Promover a Admin del Servidor",
|
||||
"promoteServerAdminQuestion": "¿Estás seguro de que quieres promover a {selectedUser} a admin del servidor?",
|
||||
"promoteServerAdminMessage": "Los administradores del servidor tienen los máximos privilegios y pueden gestionar el servidor.",
|
||||
"promoteServerAdminWarning": "Esto se puede deshacer en cualquier momento degradando al usuario.",
|
||||
"promoteServerAdminConfirm": "Promover a Admin del Servidor",
|
||||
"promoteServerAdminSuccess": "Usuario Promovido",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} ahora es administrador del servidor.",
|
||||
"promoteServerAdminError": "Error al promover al usuario",
|
||||
"demoteServerAdmin": "Degradar de Admin del Servidor",
|
||||
"demoteServerAdminTitle": "Degradar de Admin del Servidor",
|
||||
"demoteServerAdminQuestion": "¿Estás seguro de que quieres degradar a {selectedUser} de administrador del servidor?",
|
||||
"demoteServerAdminMessage": "{selectedUser} perderá todos los privilegios de administrador del servidor.",
|
||||
"demoteServerAdminWarning": "Esto se puede deshacer en cualquier momento promoviendo al usuario.",
|
||||
"demoteServerAdminConfirm": "Degradar de Admin del Servidor",
|
||||
"demoteServerAdminSuccess": "Usuario Degradado",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} ya no es administrador del servidor.",
|
||||
"demoteServerAdminError": "Error al degradar al usuario",
|
||||
"managedSelfhosted": "Autogestionado",
|
||||
"otpEnable": "Activar doble factor",
|
||||
"otpDisable": "Desactivar doble factor",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "Eliminar sitio",
|
||||
"actionGetSite": "Obtener sitio",
|
||||
"actionListSites": "Listar sitios",
|
||||
"actionCreateAiProvider": "Crear Proveedor de IA",
|
||||
"actionDeleteAiProvider": "Eliminar Proveedor de IA",
|
||||
"actionGetAiProvider": "Obtener Proveedor de IA",
|
||||
"actionListAiProviders": "Lista de Proveedores de IA",
|
||||
"actionUpdateAiProvider": "Actualizar Proveedor de IA",
|
||||
"actionCreateAiModel": "Crear Modelo de IA",
|
||||
"actionDeleteAiModel": "Eliminar Modelo de IA",
|
||||
"actionGetAiModel": "Obtener Modelo de IA",
|
||||
"actionListAiModels": "Lista de Modelos de IA",
|
||||
"actionUpdateAiModel": "Actualizar Modelo de IA",
|
||||
"actionCreateAiBudget": "Crear Presupuesto de IA",
|
||||
"actionDeleteAiBudget": "Eliminar Presupuesto de IA",
|
||||
"actionGetAiBudget": "Obtener Presupuesto de IA",
|
||||
"actionListAiBudgets": "Lista de Presupuestos de IA",
|
||||
"actionUpdateAiBudget": "Actualizar Presupuesto de IA",
|
||||
"actionListResourceAiModels": "Lista de Modelos de IA de Recursos",
|
||||
"actionSetResourceAiModels": "Establecer Modelos de IA de Recursos",
|
||||
"actionCreateVirtualApiKey": "Crear Clave API Virtual",
|
||||
"actionDeleteVirtualApiKey": "Eliminar Clave API Virtual",
|
||||
"actionGetVirtualApiKey": "Obtener Clave API Virtual",
|
||||
"actionListVirtualApiKeys": "Lista de Claves API Virtuales",
|
||||
"actionUpdateVirtualApiKey": "Actualizar Clave API Virtual",
|
||||
"actionApplyBlueprint": "Aplicar plano",
|
||||
"actionListBlueprints": "Listar blueprints",
|
||||
"actionGetBlueprint": "Obtener blueprint",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "Establecer Pincode del recurso",
|
||||
"actionSetResourceEmailWhitelist": "Establecer lista blanca de correo de recursos",
|
||||
"actionGetResourceEmailWhitelist": "Obtener correo electrónico de recursos",
|
||||
"actionListResourcePolicies": "Lista de Políticas de Recursos",
|
||||
"actionCreateResourcePolicy": "Crear Política de Recursos",
|
||||
"actionGetResourcePolicy": "Obtener política de recursos",
|
||||
"actionUpdateResourcePolicy": "Actualizar política de recursos",
|
||||
"actionDeleteResourcePolicy": "Eliminar Política de Recursos",
|
||||
"actionSetResourcePolicyUsers": "Definir política de recursos para usuarios",
|
||||
"actionSetResourcePolicyRoles": "Definir roles de política de recursos",
|
||||
"actionSetResourcePolicyPassword": "Definir contraseña de política de recursos",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "Buscar…",
|
||||
"searchPlaceholder": "Buscar...",
|
||||
"emptySearchOptions": "No se encontraron opciones",
|
||||
"ipFilterSearchPlaceholder": "Introduzca una dirección IP…",
|
||||
"ipFilterEmptyMessage": "Introduzca una dirección IP para filtrar por",
|
||||
"create": "Crear",
|
||||
"orgs": "Organizaciones",
|
||||
"loginError": "Ocurrió un error inesperado. Por favor, inténtelo de nuevo.",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "Crear Cliente de Máquina",
|
||||
"commandPaletteCreateAlertRule": "Crear Regla de Alerta",
|
||||
"commandPaletteCreateIdentityProvider": "Crear proveedor de identidad",
|
||||
"commandPaletteCreateAiProvider": "Crear proveedor de IA",
|
||||
"commandPaletteCreateVirtualApiKey": "Crear Clave API Virtual",
|
||||
"commandPaletteToggleTheme": "Cambiar tema",
|
||||
"commandPaletteChooseOrganization": "Elegir organización",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "Invitaciones",
|
||||
"sidebarRoles": "Roles",
|
||||
"sidebarShareableLinks": "Enlaces Compartibles",
|
||||
"sidebarAiGateway": "Gateway de IA",
|
||||
"sidebarAiProviders": "Proveedores",
|
||||
"commandAiProviders": "Proveedores de IA",
|
||||
"sidebarVirtualApiKeys": "Claves API Virtuales",
|
||||
"sidebarMyApiKeys": "Tus Claves API",
|
||||
"sidebarAccount": "Lanzador",
|
||||
"commandVirtualApiKeys": "Claves API Virtuales",
|
||||
"virtualApiKeysTitle": "Administrar Claves API Virtuales",
|
||||
"virtualApiKeysDescription": "Crea y gestiona claves API manuales para el acceso al Gateway público de IA",
|
||||
"virtualApiKeysTabIdentity": "Claves de Identidad",
|
||||
"virtualApiKeysTabVirtual": "Claves Virtuales",
|
||||
"virtualApiKeysIdentitySplashTitle": "Claves de Identidad para Cada Usuario",
|
||||
"virtualApiKeysIdentitySplashDescription": "Cada usuario ya tiene una clave de identidad Pangolin para esta organización. Es única para su cuenta y los autentica en gateways de IA a los que pueden acceder.",
|
||||
"virtualApiKeysIdentitySplashExample": "Ejemplo",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "Cómo los Usuarios Obtienen su Clave",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Visita una URL de gateway público de IA en el navegador e inicia sesión con su cuenta.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "O ve a <url></url> mientras estás conectado.",
|
||||
"virtualApiKeysIdentitySplashManual": "Puedes crear claves API virtuales adicionales en la pestaña de Claves Virtuales. Esas claves pueden estar limitadas a gateways públicos de IA específicos y opcionalmente asociadas a un usuario. Crear una clave concede inmediatamente acceso a los gateways públicos de IA seleccionados.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Crear Manualmente una Clave",
|
||||
"virtualApiKeysEmailIdentity": "Claves de Identidad por Correo Electrónico",
|
||||
"virtualApiKeysEmailIdentityDescription": "Envía a cada usuario o rol seleccionado su clave de identidad Pangolin.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "Enviar a todos los usuarios",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "Enviar correo electrónico a cada miembro de la organización que tenga un correo electrónico de cuenta.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Usuarios",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Roles",
|
||||
"virtualApiKeysEmailIdentitySubmit": "Enviar Correos Electrónicos",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Claves de identidad enviadas por correo electrónico",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "Se enviaron {sent} correos electrónicos.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "{skipped} usuarios se omitieron porque no tienen una dirección de correo electrónico.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "Selecciona al menos un usuario o rol, o envía a todos los usuarios.",
|
||||
"virtualApiKeysEmailIdentityError": "Error al enviar claves de identidad",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "No se pudieron enviar por correo electrónico las claves de identidad",
|
||||
"virtualApiKeysBannerTitle": "Claves de Identidad para Cada Usuario",
|
||||
"virtualApiKeysBannerDescription": "Cada usuario ya tiene una clave de identidad disponible en {keysUrl}. También puedes generar manualmente claves aquí que conceden acceso directo a gateways públicos de IA.",
|
||||
"virtualApiKeysBannerButtonText": "Ver Claves de Identidad",
|
||||
"virtualApiKeys": "Claves API Virtuales",
|
||||
"virtualApiKeysSearch": "Buscar claves...",
|
||||
"virtualApiKeysCreate": "Crear Clave API Virtual",
|
||||
"virtualApiKeysCreateDescription": "Genera una clave manual que pueda llamar a gateways públicos de IA en esta organización",
|
||||
"virtualApiKeysCreateButton": "Crear Clave",
|
||||
"virtualApiKeysEmpty": "Aún no hay claves API virtuales",
|
||||
"virtualApiKeysName": "Nombre",
|
||||
"virtualApiKeysDescriptionOptional": "Descripción (opcional)",
|
||||
"virtualApiKeysAssociateUserOptional": "Asociar Usuario (opcional)",
|
||||
"virtualApiKeysAssociateUserDescription": "Asocia esta clave con un usuario para rastrear el uso. Una vez creada, esta clave concede inmediatamente acceso a los gateways públicos de IA seleccionados. No necesitas asociar un usuario manualmente al recurso. La clave también aparecerá en el perfil del usuario.",
|
||||
"virtualApiKeysAllResources": "Todos los gateways públicos de IA",
|
||||
"virtualApiKeysAllResourcesDescription": "Permitir que esta clave acceda a cada gateway público de IA en la organización",
|
||||
"virtualApiKeysSelectResources": "Gateways Públicos de IA",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Seleccionar recursos",
|
||||
"virtualApiKeysSelectResourcesDescription": "Elige a qué gateways públicos de IA puede acceder esta clave",
|
||||
"virtualApiKeysNoResources": "Sin recursos",
|
||||
"virtualApiKeysSecret": "Clave",
|
||||
"virtualApiKeysCopyKey": "Copia esta clave. Puedes verla nuevamente más tarde desde la tabla o al editar.",
|
||||
"virtualApiKeysViewSecret": "Ver Secreto",
|
||||
"virtualApiKeysViewSecretTitle": "Secreto de la Clave API Virtual",
|
||||
"virtualApiKeysViewSecretDescription": "Este secreto concede acceso a los gateways públicos de IA asignados a esta clave",
|
||||
"virtualApiKeysEdit": "Editar Clave API Virtual",
|
||||
"virtualApiKeysEditDescription": "Actualiza el usuario asociado y el acceso al gateway público de IA para esta clave",
|
||||
"virtualApiKeysSaveButton": "Guardar Cambios",
|
||||
"virtualApiKeysSelectResourcesRequired": "Selecciona al menos un gateway público de IA, o habilita todos los gateways públicos de IA",
|
||||
"virtualApiKeysUpdated": "Clave API virtual actualizada",
|
||||
"virtualApiKeysUpdatedDescription": "La clave API virtual ha sido actualizada",
|
||||
"virtualApiKeysErrorUpdate": "Error al actualizar la clave API virtual",
|
||||
"virtualApiKeysErrorUpdateDescription": "Error al actualizar clave API virtual",
|
||||
"virtualApiKeysErrorCreate": "Error al crear clave API virtual",
|
||||
"virtualApiKeysErrorCreateDescription": "Error al crear clave API virtual",
|
||||
"virtualApiKeysErrorDelete": "Error al eliminar clave API virtual",
|
||||
"virtualApiKeysErrorDeleteMessage": "Error al eliminar clave API virtual",
|
||||
"virtualApiKeysDeleted": "Clave API virtual eliminada",
|
||||
"virtualApiKeysDeletedDescription": "La clave API virtual ha sido eliminada",
|
||||
"virtualApiKeysDelete": "Eliminar Clave API Virtual",
|
||||
"virtualApiKeysDeleteConfirm": "Eliminar Clave",
|
||||
"virtualApiKeysQuestionRemove": "¿Está seguro de que desea eliminar esta clave API virtual?",
|
||||
"virtualApiKeysMessageRemove": "Los clientes que usen esta clave perderán acceso inmediatamente.",
|
||||
"virtualApiKeysErrorFetchSecret": "Error al cargar el secreto",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Error al cargar el secreto de la clave API virtual",
|
||||
"virtualApiKeysFilterUnassigned": "No asignado",
|
||||
"virtualApiKeysInferenceBudget": "Presupuesto",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Configura cómo esta clave restringe el uso de IA según los límites de gasto o de tokens",
|
||||
"virtualApiKeysEmailOnGenerate": "Correo electrónico al generar",
|
||||
"virtualApiKeysEmailThisKey": "Envía este correo electrónico",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Envía la clave al usuario asociado y direcciones adicionales después de que se cree",
|
||||
"virtualApiKeysEmailThisKeyDescription": "Envía la clave actual al usuario asociado y direcciones adicionales",
|
||||
"virtualApiKeysEmailSmtpRequired": "El correo electrónico no está configurado en este servidor",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "Configure SMTP para enviar claves API virtuales por correo electrónico.",
|
||||
"virtualApiKeysEmailSendToUser": "Enviar al usuario asociado",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Envía la clave al correo electrónico de la cuenta del usuario asociado",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "Asocia un usuario para enviar la clave a ese usuario",
|
||||
"virtualApiKeysEmailAdditional": "Correos electrónicos adicionales",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "Agrega un correo electrónico y presiona Enter",
|
||||
"virtualApiKeysEmailRecipientsRequired": "Selecciona el usuario asociado o agrega al menos una dirección de correo electrónico",
|
||||
"myVirtualApiKeysTitle": "Tus Claves API",
|
||||
"myVirtualApiKeysDescription": "Ve tu clave de identidad y cualquier clave API virtuales atribuidas a ti en esta organización",
|
||||
"myVirtualApiKeysResourceTitle": "Tus Claves API para {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "Ve tu clave de identidad y claves API virtuales atribuidas a ti que pueden acceder a {resourceName}",
|
||||
"myVirtualApiKeysIdentityTitle": "Clave de Identidad",
|
||||
"myVirtualApiKeysIdentityHeadline": "Tu Clave API Personal",
|
||||
"myVirtualApiKeysIdentityDescription": "Tu clave personal para esta organización. Es única para tu cuenta y se utiliza para identificarte cuando llamas a recursos del Gateway de IA.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Tu Clave API Personal para {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Tu clave personal para esta organización. Úsala para llamar a {resourceName}.",
|
||||
"myVirtualApiKeysManualTitle": "Claves Atribuidas",
|
||||
"myVirtualApiKeysManualDescription": "Claves API virtuales manuales que un administrador ha asociado con tu cuenta",
|
||||
"myVirtualApiKeysManualResourceDescription": "Claves API virtuales manuales asociadas a tu cuenta que pueden acceder a {resourceName}",
|
||||
"myVirtualApiKeysManualEmpty": "Aún no hay claves atribuidas",
|
||||
"myVirtualApiKeysKindUser": "Identidad",
|
||||
"myVirtualApiKeysKindManual": "Manual",
|
||||
"myVirtualApiKeysUnnamed": "Clave sin nombre",
|
||||
"myVirtualApiKeysRevealSecret": "Revelar Secreto",
|
||||
"myVirtualApiKeysViewSecretDescription": "Este secreto te autentica en los recursos del Gateway de IA",
|
||||
"aiClientConfigTitle": "Configura agentes de codificación",
|
||||
"aiClientConfigDescription": "Copie la configuración para los agentes de codificación populares o deje que el CLI de Pangolin lo configure automáticamente.",
|
||||
"aiClientConfigDescriptionClaude": "Herramienta de codificación agentic de Anthropic para el terminal.",
|
||||
"aiClientConfigDescriptionCodex": "Herramienta de codificación agentic de OpenAI para el terminal.",
|
||||
"aiClientConfigDescriptionOpencode": "Agente de codificación de terminal de código abierto.",
|
||||
"aiClientConfigDescriptionGemini": "La herramienta de codificación de agente de Google para el terminal.",
|
||||
"aiClientConfigSetup": "Configuración",
|
||||
"aiClientConfigTabCli": "Automático (CLI)",
|
||||
"aiClientConfigTabManual": "Configuración manual",
|
||||
"aiClientConfigPreset": "Configuración",
|
||||
"aiClientConfigStep": "Paso {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "Este fragmento incluye valores de ejemplo, como un nombre de modelo o URL de recursos. Reemplace con los suyos antes de usarlo.",
|
||||
"aiClientConfigRevealError": "No se pudo cargar su clave API.",
|
||||
"aiClientConfigRevealRetry": "Intentar de nuevo",
|
||||
"resourceGeneralAiClientConfigDescription": "Este recurso se puede usar desde clientes comunes como Claude Code y OpenCode. <configLink>Aprenda más</configLink>",
|
||||
"aiProvidersTitle": "Proveedores de IA",
|
||||
"aiProvidersDescription": "Conecta proveedores de modelos para cargas de trabajo de IA en esta organización",
|
||||
"aiProvidersBannerTitle": "Conecta Proveedores de Modelos",
|
||||
"aiProvidersBannerDescription": "Los proveedores son los backends de modelo que Pangolin usa para cargas de trabajo de IA. Conecta OpenAI, Anthropic y otros proveedores aquí, luego adjúntalos a gateways públicos de IA para que los usuarios puedan llamar a modelos con acceso consciente de identidad, presupuestos y registros.",
|
||||
"aiProvidersAdd": "Agregar Proveedor",
|
||||
"aiProvidersSearch": "Buscar proveedores...",
|
||||
"aiProvidersEmpty": "Aún no hay proveedores de IA",
|
||||
"aiProviderCreate": "Crear Proveedor de IA",
|
||||
"aiProviderCreateDescription": "Agrega un proveedor de modelos para esta organización",
|
||||
"aiProviderSeeAll": "Ver Todos los Proveedores",
|
||||
"aiProviderSetting": "Ajustes del Proveedor para {providerName}",
|
||||
"aiProviderSettingDescription": "Configura este proveedor de IA",
|
||||
"aiProviderGeneral": "General",
|
||||
"aiProviderGeneralDescription": "Configuraciones básicas para este proveedor",
|
||||
"aiProviderConfiguration": "Configuración",
|
||||
"aiProviderConfigurationDescription": "Enrutamiento de red y autenticación para este proveedor",
|
||||
"aiProviderNetworkSettings": "Configuraciones de Red",
|
||||
"aiProviderNetworkSettingsDescription": "Elige cómo llega el tráfico a este proveedor",
|
||||
"aiProviderAuthSettings": "Autenticación",
|
||||
"aiProviderAuthSettingsDescription": "Configura cómo este proveedor autentica solicitudes a su URL upstream",
|
||||
"aiProviderBudgetSettings": "Presupuesto",
|
||||
"aiProviderBudgetSettingsDescription": "Configura cómo este proveedor restringe el uso basado en los límites de gasto o de tokens",
|
||||
"aiBudgetAdd": "Agregar Presupuesto",
|
||||
"aiBudgetEmpty": "Aún no hay presupuestos configurados. Haz clic en Agregar Presupuesto para establecer un límite de gasto o de tokens.",
|
||||
"aiBudgetUnit": "Tipo de Gasto",
|
||||
"aiBudgetPeriod": "Período de Restablecimiento",
|
||||
"aiBudgetAmount": "Gasto Máximo",
|
||||
"aiBudgetAmountPlaceholder": "Gasto máximo",
|
||||
"aiBudgetPeriodHourly": "Por hora",
|
||||
"aiBudgetPeriodDaily": "Diario",
|
||||
"aiBudgetPeriodWeekly": "Semanal",
|
||||
"aiBudgetPeriodMonthly": "Mensual",
|
||||
"aiBudgetPeriodYearly": "Anual",
|
||||
"aiBudgetPeriodLifetime": "De por vida",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "Tokens",
|
||||
"aiBudgetConflictError": "Ya existe un presupuesto para este período de restablecimiento y tipo de gasto",
|
||||
"aiBudgetInvalidAmountError": "Ingresa un gasto máximo mayor a 0",
|
||||
"aiBudgetUpdated": "Presupuestos actualizados",
|
||||
"aiBudgetErrorSave": "Error al actualizar presupuestos",
|
||||
"aiProviderType": "Tipo de Proveedor",
|
||||
"aiProviderTypeSearch": "Buscar proveedores...",
|
||||
"aiProviderTypeNotFound": "No se encontró tipo de proveedor",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI Gateway",
|
||||
"aiProviderTypeCustom": "Personalizado",
|
||||
"aiProviderTypeOpenaiDescription": "API de OpenAI",
|
||||
"aiProviderTypeAnthropicDescription": "API de Anthropic",
|
||||
"aiProviderTypeGoogleGeminiDescription": "API de Google Gemini",
|
||||
"aiProviderTypeVertexAiDescription": "API de Vertex AI de Google",
|
||||
"aiProviderTypeBedrockDescription": "API de Amazon Bedrock Runtime",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "API de Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouterDescription": "API de OpenRouter",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "API de Vercel AI Gateway",
|
||||
"aiProviderTypeCustomDescription": "Lleve su propio endpoint o enrutamiento a través de objetivos del sitio",
|
||||
"aiProviderUpstreamUrl": "URL de upstream",
|
||||
"aiProviderUpstreamUrlDescription": "URL base para la API del proveedor",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Dejar en blanco para usar el URL de upstream predeterminado para este proveedor",
|
||||
"aiProviderEffectiveUpstreamUrl": "URL de upstream efectivo",
|
||||
"aiProviderApiKey": "Clave API",
|
||||
"aiProviderApiKeyDescription": "Clave API usada para autenticar solicitudes a este proveedor",
|
||||
"aiProviderCustomHeadersDescription": "Cabeceras enviadas en cada solicitud a este proveedor. Nueva línea separada: Nombre-Cabecera: valor",
|
||||
"aiProviderApiKeyLastChars": "Clave API",
|
||||
"aiProviderAuthType": "Tipo de autenticación",
|
||||
"aiProviderAuthTypeSearch": "Buscar tipos de autenticación...",
|
||||
"aiProviderAuthTypeNotFound": "No se encontró el tipo de autenticación",
|
||||
"aiProviderAuthTypeBearer": "Portador",
|
||||
"aiProviderAuthTypeBearerDescription": "Autorización: clave portador. Usado por OpenAI y la mayoría de los proveedores",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "Encabezado x-api-key. Usado por Anthropic",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "Encabezado x-goog-api-key. Usado por Google Gemini",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Autorización: Clave Splunk. Usado por el recolector de eventos HTTP Splunk",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Gateway de IA de Cloudflare",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-authorization: Clave Bearer. Usado por el Gateway de IA de Cloudflare",
|
||||
"aiProviderAuthTypeNone": "Sin autenticación",
|
||||
"aiProviderAuthTypePassthrough": "Paso a través",
|
||||
"aiProviderAuthTypeDescription": "Cómo autentica solicitudes la API upstream",
|
||||
"aiProviderAuthTypePassthroughDescription": "Reenviar las cabeceras de clave API del solicitante al upstream",
|
||||
"aiProviderAuthTypeNoneDescription": "No enviar cabeceras de autenticación al upstream",
|
||||
"aiProviderRoutingMode": "Modo de enrutamiento",
|
||||
"aiProviderRoutingModeDescription": "Enviar tráfico a una URL de upstream o a objetivos HTTP en sus sitios",
|
||||
"aiProviderRoutingModeUrl": "URL de upstream",
|
||||
"aiProviderRoutingModeUrlDescription": "Llamar a una URL base de API pública o privada",
|
||||
"aiProviderRoutingModeTarget": "Objetivos de sitio",
|
||||
"aiProviderRoutingModeTargetDescription": "Ruta a través de objetivos en sus sitios",
|
||||
"aiProviderRoutingModeTargetNote": "Después de crear este proveedor, configure objetivos de sitio en la pestaña de Configuración de Red.",
|
||||
"aiProviderTargetNoOne": "Este proveedor no tiene objetivos. Agregue un objetivo para enrutar solicitudes a través de sus sitios.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Los sitios conectados a nodos remotos son inaccesibles para ser enrutados a los proveedores de AI Gateway.",
|
||||
"aiProviderSkipTlsVerification": "Omitir verificación de TLS",
|
||||
"aiProviderSkipTlsVerificationDescription": "Deshabilitar la verificación del certificado TLS para la conexión de upstream",
|
||||
"aiProviderBudget": "Presupuesto",
|
||||
"aiProviderBudgetDescription": "Presupuesto de gastos opcional o de token para este proveedor",
|
||||
"aiProviderBudgetAmount": "Cantidad de presupuesto",
|
||||
"aiProviderBudgetUnit": "Unidad de presupuesto",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Tokens",
|
||||
"aiProviderEnabled": "Activado",
|
||||
"aiProviderEnabledDescription": "Desactivar completamente este proveedor en todos los recursos",
|
||||
"aiProviderErrorCreate": "Error al crear el proveedor de IA",
|
||||
"aiProviderErrorUpdate": "Error al actualizar el proveedor de IA",
|
||||
"aiProviderErrorDelete": "Error al eliminar el proveedor de IA",
|
||||
"aiProviderErrorLoad": "Error al cargar el proveedor de IA",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Introduce una URL de upstream válida",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Se requiere URL de upstream para este proveedor",
|
||||
"aiProviderErrorAuthTypeRequired": "Se requiere tipo de autenticación",
|
||||
"aiProviderErrorApiKeyRequired": "Se requiere clave API",
|
||||
"aiProviderErrorRoutingModeTarget": "El enrutamiento de objetivos del sitio solo está disponible para proveedores personalizados",
|
||||
"aiProviderErrorCapabilitiesRequired": "Seleccione al menos una capacidad de API",
|
||||
"aiProviderCapabilities": "Capacidades de la API",
|
||||
"aiProviderCapabilitiesDescription": "Seleccione qué formatos de API puede manejar este proveedor. Los proveedores conocidos comienzan con valores predeterminados recomendados.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Seleccione qué formatos de API puede manejar este proveedor personalizado",
|
||||
"aiProviderCapabilitiesSelect": "Seleccionar capacidades",
|
||||
"aiProviderCapabilitiesEmpty": "No se encontraron capacidades",
|
||||
"aiProviderCapabilitiesSearch": "Buscar capacidades...",
|
||||
"aiCapabilityOpenaiChat": "Completaciones de Chat OpenAI",
|
||||
"aiCapabilityOpenaiChatDescription": "Admite /v1/chat/completions",
|
||||
"aiCapabilityOpenaiResponses": "Respuestas de OpenAI",
|
||||
"aiCapabilityOpenaiResponsesDescription": "Admite /v1/responses",
|
||||
"aiCapabilityAnthropicMessages": "Mensajes Antropicos",
|
||||
"aiCapabilityAnthropicMessagesDescription": "Admite /v1/messages",
|
||||
"aiCapabilityV1Models": "Lista de Modelos",
|
||||
"aiCapabilityV1ModelsDescription": "Soporta el descubrimiento de modelos /v1/models",
|
||||
"aiCapabilityGeminiGenerateContent": "Generar contenido Gemini",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Admite la API directa de Gemini",
|
||||
"aiCapabilityBedrockModelInvoke": "Invocar modelo de Bedrock",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Admite Amazon Bedrock InvokeModel",
|
||||
"aiCapabilityGoogleGenerateContent": "Generar contenido con Vertex",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Admite el formato Gemini de Vertex AI",
|
||||
"aiCapabilityGoogleRawPredict": "Predicción directa de Vertex",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Admite Vertex AI rawPredict para modelos Antropic",
|
||||
"aiCapabilityBedrockConverse": "Convoque en Bedrock",
|
||||
"aiCapabilityBedrockConverseDescription": "Admite la API de Amazon Bedrock Converse",
|
||||
"aiProviderCreated": "Proveedor de IA creado",
|
||||
"aiProviderUpdated": "Proveedor de IA actualizado",
|
||||
"aiProviderDeleted": "Proveedor de IA eliminado",
|
||||
"aiProviderDelete": "Eliminar proveedor",
|
||||
"aiProviderDeleteConfirm": "Eliminar proveedor",
|
||||
"aiProviderQuestionRemove": "¿Está seguro de que desea eliminar este proveedor de IA?",
|
||||
"aiProviderMessageRemove": "Esto eliminará permanentemente el proveedor y sus modelos y objetivos. Esto no se puede deshacer.",
|
||||
"aiProviderErrorNoUpdate": "El proveedor de IA no está disponible para actualizar",
|
||||
"aiProviderModels": "Modelos",
|
||||
"aiProviderModelsDescription": "Definir las listas de permitidos y bloqueados para este proveedor. Las solicitudes deben coincidir con una entrada permitida y no deben coincidir con una entrada bloqueada.",
|
||||
"aiProviderCreateModelsDescription": "Elija qué modelos puede servir este proveedor. Una lista permitida vacía niega todo el tráfico. Puede agregar o cambiar modelos más tarde.",
|
||||
"aiProviderModelsPlaceholder": "Buscar modelos o escribir una clave personalizada",
|
||||
"aiProviderModelsAllow": "Lista de Permitidos",
|
||||
"aiProviderModelsAllowDescription": "Modelos que pueden utilizarse a través de este proveedor. Vacío significa negar todos.",
|
||||
"aiProviderModelsAllowPlaceholder": "Ingrese la clave del modelo",
|
||||
"aiProviderModelsAllowEmpty": "No se han agregado modelos aún.",
|
||||
"aiProviderModelsBlock": "Lista de Bloqueados",
|
||||
"aiProviderModelsBlockDescription": "Modelos para denegar incluso si coinciden con una entrada permitida.",
|
||||
"aiProviderModelsBlockPlaceholder": "Ingrese la clave del modelo",
|
||||
"aiProviderModelsBlockEmpty": "No se han agregado modelos aún.",
|
||||
"aiProviderModelsAdd": "Agregar Modelos",
|
||||
"aiProviderModelsClearAll": "Borrar todo",
|
||||
"aiProviderModelsAddCustom": "Agregar \"{key}\"",
|
||||
"aiProviderModelsAddCustomHint": "Presione Enter para agregar esta clave de modelo personalizada.",
|
||||
"aiProviderModelsAddBulk": "Agregar {count} claves personalizadas.",
|
||||
"aiProviderModelsAddBulkHint": "Presiona Enter para agregar {count} claves personalizadas.",
|
||||
"aiProviderModelsAddOne": "Agregar {key} ahora",
|
||||
"aiProviderModelsAddSelected": "Agregar seleccionado",
|
||||
"aiProviderModelsSelectedCount": "{count} seleccionados",
|
||||
"aiProviderModelsSelectAll": "Seleccionar todo",
|
||||
"aiProviderModelsClearSelected": "Limpiar",
|
||||
"aiProviderModelsBulkHint": "Seleccione modelos conocidos o escriba una clave personalizada.",
|
||||
"aiProviderModelsCatalogEmpty": "No hay modelos de catálogo coincidentes.",
|
||||
"aiProviderModelsCatalogHeading": "Modelos conocidos",
|
||||
"aiProviderModelsAllLabel": "Todos los modelos",
|
||||
"aiProviderModelsAllPatternHint": "Comodín: *",
|
||||
"aiProviderModelsAddAllAllow": "Permitir todos los modelos",
|
||||
"aiProviderModelsAddAllBlock": "Bloquear todos los modelos",
|
||||
"aiProviderModelsAddAllDescription": "Utiliza el comodín * para que cada clave de modelo coincida.",
|
||||
"aiProviderModelsViewMore": "Ver más ({count})",
|
||||
"aiProviderModelsViewLess": "Ver menos",
|
||||
"aiProviderModelsRemove": "Eliminar modelo",
|
||||
"aiProviderModelsEditHint": "Haga clic para editar la configuración del modelo",
|
||||
"aiProviderModelsSourceCatalog": "Modelo de catálogo conocido",
|
||||
"aiProviderModelsSourceCustom": "Clave de modelo personalizada",
|
||||
"aiProviderModelsSourcePattern": "Patrón de comodín",
|
||||
"aiProviderModelsSourceAll": "Coincide con cada clave de modelo",
|
||||
"aiProviderModelsBudgetConfigured": "Presupuesto configurado",
|
||||
"aiProviderModelsEditTitle": "Editar modelo",
|
||||
"aiProviderModelsEditDescription": "Actualizar la clave del modelo o configurar su presupuesto.",
|
||||
"aiProviderModelsBudgetTab": "Presupuesto",
|
||||
"aiProviderModelsKeyLabel": "Clave del modelo",
|
||||
"aiProviderModelsKeyRequired": "Ingrese una clave de modelo",
|
||||
"aiProviderModelsKeyDuplicate": "Esta clave de modelo ya está en una lista",
|
||||
"aiProviderModelsOverlapError": "Estos patrones no pueden estar en ambas listas: {keys}",
|
||||
"aiProviderModelsUpdated": "Modelos actualizados",
|
||||
"aiProviderModelsErrorUpdate": "No se pudo actualizar los modelos",
|
||||
"aiResourceProviders": "Proveedores",
|
||||
"aiResourceProvidersDescription": "Elija qué proveedores de IA puede usar este portal de IA",
|
||||
"aiResourceProvidersHelp": "Adjunte proveedores y elija heredar (usar las listas de cada proveedor) o seleccionar (elija una lista de permitidos para este recurso). No se permiten patrones de permisos que entren en conflicto entre proveedores adjuntos.",
|
||||
"aiResourceProvidersSelect": "Seleccionar proveedores",
|
||||
"aiResourceProvidersEmpty": "No se encontraron proveedores de IA",
|
||||
"aiResourceProvidersNoneAttached": "No se han adjuntado proveedores aún.",
|
||||
"aiResourceProvidersAdd": "Agregar proveedor",
|
||||
"aiResourceProvidersRemove": "Eliminar proveedor",
|
||||
"aiResourceProviderToggleEnabled": "Habilitar o deshabilitar este proveedor en el recurso",
|
||||
"aiResourceProviderDisabled": "Deshabilitado",
|
||||
"aiResourceProvidersUpdated": "Proveedores actualizados",
|
||||
"aiResourceProvidersErrorUpdate": "Fallo al actualizar proveedores",
|
||||
"aiResourceProviderEditDescription": "Elija cómo se exponen los modelos de este proveedor en este recurso.",
|
||||
"viewProviderSettings": "Ver configuración del proveedor",
|
||||
"aiResourceProviderMode": "Modo de acceso",
|
||||
"aiResourceProviderModeInherit": "Heredar",
|
||||
"aiResourceProviderModeSelect": "Seleccionar",
|
||||
"aiResourceProviderModeSelectSummary": "Seleccionar · {count} modelos",
|
||||
"aiResourceProviderModeInheritHelp": "Use las listas de permitidos y bloqueados de este proveedor según lo configurado en el proveedor.",
|
||||
"aiResourceProviderModeSelectHelp": "Elija un subconjunto de modelos de la lista de permitidos de este proveedor para este recurso.",
|
||||
"aiResourceProviderAllowModels": "Lista de permitidos",
|
||||
"aiResourceProviderAllowModelsSelect": "Seleccionar modelos",
|
||||
"aiResourceProviderAllowModelsSearch": "Buscar modelos...",
|
||||
"aiResourceProviderAllowModelsEmpty": "No se encontraron modelos",
|
||||
"aiResourceProviderAllowModelsHelp": "Solo se pueden seleccionar modelos de la lista de permitidos de este proveedor.",
|
||||
"aiResourceAliasRequired": "Alias es necesario para portales de IA",
|
||||
"aiResourceDomainConfiguration": "Configuración del dominio",
|
||||
"aiResourceDomainConfigurationDescription": "Elija el dominio que los clientes usarán para llegar a este portal de IA.",
|
||||
"aiUsageAnalyticsTitle": "Analítica de uso de IA",
|
||||
"aiUsageAnalyticsDescription": "Analizar el costo del portal de IA, uso de token y actividad a través de proveedores, recursos, roles y usuarios",
|
||||
"aiUsageTabOverview": "Resumen",
|
||||
"aiUsageTabProviders": "Proveedores",
|
||||
"aiUsageTabResources": "Recursos",
|
||||
"aiUsageFilterProvider": "Proveedor",
|
||||
"aiUsageFilterModel": "Modelo",
|
||||
"aiUsageFilterResource": "Recurso",
|
||||
"aiUsageFilterRole": "Rol",
|
||||
"aiUsageFilterUser": "Usuario",
|
||||
"aiUsageFilterAllProviders": "Todos los proveedores",
|
||||
"aiUsageFilterAllModels": "Todos los modelos",
|
||||
"aiUsageFilterAllResources": "Todos los recursos",
|
||||
"aiUsageFilterAllRoles": "Todos los roles",
|
||||
"aiUsageFilterAllUsers": "Todos los usuarios",
|
||||
"aiUsageFilterSearch": "Buscar...",
|
||||
"aiUsageFilterNotFound": "No se encontraron opciones",
|
||||
"aiUsageResetFilters": "Restablecer filtros",
|
||||
"aiUsageRefresh": "Actualizar",
|
||||
"aiUsageTokenTypePrompt": "Indicio",
|
||||
"aiUsageTokenTypeCacheRead": "Lectura de caché",
|
||||
"aiUsageTokenTypeCacheWrite": "Escritura de caché",
|
||||
"aiUsageTokenTypeCompletion": "Completitud",
|
||||
"aiUsageTokenTypeReasoning": "Razonamiento",
|
||||
"aiUsageRequests": "Solicitudes",
|
||||
"aiUsageCost": "Costo",
|
||||
"aiUsageTokens": "Tokens",
|
||||
"aiUsageOther": "Otro",
|
||||
"aiUsageTotalRequests": "Solicitudes Totales",
|
||||
"aiUsageTotalTokens": "Tokens Totales",
|
||||
"aiUsageTotalCost": "Costo Total",
|
||||
"aiUsageEstimated": "Estimado",
|
||||
"aiUsageRequestVolume": "Volumen de Solicitudes",
|
||||
"aiUsageTokenUsage": "Uso de Tokens",
|
||||
"aiUsageModelCost": "Costo del Modelo",
|
||||
"aiUsageModelTokens": "Tokens del Modelo",
|
||||
"aiUsageTopModels": "Modelos Principales",
|
||||
"aiUsageTopProviders": "Proveedores Principales",
|
||||
"aiUsageProviderCost": "Costo del Proveedor",
|
||||
"aiUsageProviderTokenUsage": "Uso de Tokens del Proveedor",
|
||||
"aiUsageTopResources": "Recursos Principales",
|
||||
"aiUsageResourceCost": "Costo del Recurso",
|
||||
"aiUsageResourceTokenUsage": "Uso de Tokens del Recurso",
|
||||
"aiUsageResourceTypePublic": "Recurso Público",
|
||||
"aiUsageResourceTypeSite": "Recurso Privado",
|
||||
"aiUsageNoResource": "Sin recurso",
|
||||
"aiUsageRolesTab": "Roles",
|
||||
"aiUsageUsersTab": "Usuarios",
|
||||
"aiUsageTopRoles": "Roles Principales",
|
||||
"aiUsageRoleCost": "Costo del Rol",
|
||||
"aiUsageRoleTokenUsage": "Uso de Tokens del Rol",
|
||||
"aiUsageTopUsers": "Usuarios principales",
|
||||
"aiUsageUserCost": "Costo del Usuario",
|
||||
"aiUsageUserTokenUsage": "Uso de Tokens del Usuario",
|
||||
"aiUsageUnknownUser": "Usuario desconocido",
|
||||
"aiUsageVirtualApiKeysTab": "Claves API Virtuales",
|
||||
"aiUsageFilterVirtualApiKey": "Clave API Virtual",
|
||||
"aiUsageFilterAllVirtualApiKeys": "Todas las Claves API Virtuales",
|
||||
"aiUsageTopVirtualApiKeys": "Claves API Virtuales Principales",
|
||||
"aiUsageVirtualApiKeyCost": "Costo de la Clave API Virtual",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Uso de Tokens de la Clave API Virtual",
|
||||
"aiUsageUnknownVirtualApiKey": "Sin clave API virtual",
|
||||
"aiUsageUnnamedVirtualApiKey": "Clave sin nombre",
|
||||
"aiUsageLoading": "Cargando...",
|
||||
"aiUsageNoData": "Sin información",
|
||||
"resourceBudgetSettings": "Presupuesto",
|
||||
"resourceBudgetSettingsDescription": "Configure cómo este portal de IA restringe el uso en función de los límites de gasto o tokens",
|
||||
"sidebarApiKeys": "Claves API",
|
||||
"sidebarOrgs": "Organizaciones",
|
||||
"sidebarProvisioning": "Aprovisionamiento",
|
||||
"sidebarSettings": "Ajustes",
|
||||
"sidebarAllUsers": "Todos los usuarios",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "Regla de alerta guardada",
|
||||
"alertingRuleSavedCreatedDescription": "Tu nueva regla de alerta fue creada. Puedes seguir editándola en esta página.",
|
||||
"alertingRuleSavedUpdatedDescription": "Tus cambios a esta regla de alerta fueron guardados.",
|
||||
"alertingTestAlertSent": "Alerta de prueba enviada",
|
||||
"alertingTestAlertSentDescription": "Se envió una alerta de prueba a las acciones configuradas en esta regla.",
|
||||
"alertingEditRule": "Editar regla de alerta",
|
||||
"alertingCreateRule": "Crear regla de alerta",
|
||||
"alertingRuleCredenzaDescription": "Elija qué observar, cuándo disparar y cómo notificar",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "Cada regla vincula lo que se debe observar (un sitio, chequeo de salud o recurso), cuándo disparar (por ejemplo, fuera de línea o no saludable), y cómo notificar a su equipo vía email, webhooks o integraciones. Use esta lista para crear, habilitar y administrar esas reglas.",
|
||||
"alertingHealthChecksBannerTitle": "Monitorear Salud y Recursos",
|
||||
"alertingHealthChecksBannerDescription": "Los chequeos de salud son monitores HTTP o TCP que define una vez. Luego puede usarlos como fuentes en reglas de alerta para que se le notifique cuando un objetivo se vuelva saludable o no saludable. Los chequeos de salud en recursos también aparecen aquí.",
|
||||
"alertingTestRule": "Regla de Alerta de Prueba",
|
||||
"alertingAddActionHeading": "Agregar nueva acción",
|
||||
"alertingSelectActionType": "Seleccione una acción",
|
||||
"alertingNoActionsTitle": "No se han configurado acciones",
|
||||
"alertingNoActionsSaveDescription": "Agregue al menos una acción para que esta regla pueda notificar a alguien cuando se ejecute.",
|
||||
"alertingNoActionsTestDescription": "Agregue al menos una acción antes de poder probar esta regla.",
|
||||
"standaloneHcTableTitle": "Chequeos de salud",
|
||||
"standaloneHcSearchPlaceholder": "Buscar chequeos de salud…",
|
||||
"standaloneHcAddButton": "Crear chequeo de salud",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "Tipo:",
|
||||
"createDomainName": "Nombre:",
|
||||
"createDomainValue": "Valor:",
|
||||
"multiSelectFilterCount": "{count} seleccionado",
|
||||
"createDomainCnameRecords": "Registros CNAME",
|
||||
"createDomainARecords": "Registros A",
|
||||
"createDomainRecordNumber": "Registro {number}",
|
||||
@@ -2193,13 +2699,15 @@
|
||||
"subnetPlaceholder": "Subred",
|
||||
"addressDescription": "La dirección interna del cliente. Debe estar dentro de la subred de la organización.",
|
||||
"selectSites": "Seleccionar sitios",
|
||||
"selectResources": "Seleccionar recursos",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# recurso} other {# recursos}}",
|
||||
"selectLabels": "Seleccionar etiquetas",
|
||||
"sitesDescription": "El cliente tendrá conectividad con los sitios seleccionados",
|
||||
"clientInstallOlm": "Instalar Olm",
|
||||
"clientInstallOlmDescription": "Obtén Olm funcionando en tu sistema",
|
||||
"clientOlmCredentials": "Credenciales",
|
||||
"clientOlmCredentialsDescription": "Así es como el cliente se autentificará con el servidor",
|
||||
"olmEndpoint": "Endpoint",
|
||||
"olmEndpoint": "Punto final",
|
||||
"olmId": "ID",
|
||||
"olmSecretKey": "Secreto",
|
||||
"clientCredentialsSave": "Guardar las credenciales",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "Método",
|
||||
"healthSelectScheme": "Seleccionar método",
|
||||
"healthCheckPortInvalid": "El puerto debe estar entre 1 y 65535",
|
||||
"healthCheckHostnameInvalid": "El nombre de host no debe contener espacios en blanco",
|
||||
"healthCheckPath": "Ruta",
|
||||
"healthHostname": "IP / Nombre del host",
|
||||
"healthPort": "Puerto",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "El tiempo está en segundos",
|
||||
"requireDeviceApproval": "Requiere aprobaciones del dispositivo",
|
||||
"requireDeviceApprovalDescription": "Los usuarios con este rol necesitan nuevos dispositivos aprobados por un administrador antes de poder conectarse y acceder a los recursos.",
|
||||
"sshSettings": "Configuración SSH",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "Portal de IA",
|
||||
"sshAccess": "Acceso SSH",
|
||||
"rdpSettings": "Configuración RDP",
|
||||
"vncSettings": "Configuración VNC",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "Servidor SSH",
|
||||
"rdpServer": "Servidor RDP",
|
||||
"vncServer": "Servidor VNC",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "Portal de IA",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Esquema",
|
||||
"editInternalResourceDialogEnableSsl": "Activar TLS",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "Permitir ping ICMP",
|
||||
"privateResourceNetworkAccess": "Acceso de red",
|
||||
"privateResourceNetworkAccessDescription": "Controlar el acceso a puertos TCP/UDP y si se permite ping ICMP para este recurso.",
|
||||
"hostSettings": "Configuraciones del anfitrión",
|
||||
"cidrSettings": "Configuraciones CIDR",
|
||||
"hostSettings": "Anfitrión",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Propiedades del recurso",
|
||||
"createInternalResourceDialogName": "Nombre",
|
||||
"createInternalResourceDialogSite": "Sitio",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "Portal de IA",
|
||||
"scheme": "Esquema",
|
||||
"createInternalResourceDialogScheme": "Esquema",
|
||||
"createInternalResourceDialogEnableSsl": "Activar TLS",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Añadir un rango CIDR (e.g. 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Error al cargar las subredes",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Etiquetas de Preferencias",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Los sitios con estas etiquetas se verán obligados a conectarse a través de este nodo de salida remoto.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Los sitios con estas etiquetas preferirán conectarse a través de este nodo de salida remoto.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Seleccionar etiquetas...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Buscar etiquetas...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Error al cargar las etiquetas",
|
||||
@@ -2580,7 +3092,7 @@
|
||||
"rulesMatchRegion": "Seleccione una agrupación regional de países",
|
||||
"rulesErrorInvalidRegion": "Región no válida",
|
||||
"rulesErrorInvalidRegionDescription": "Por favor, seleccione una región válida.",
|
||||
"regionAfrica": "Africa",
|
||||
"regionAfrica": "África",
|
||||
"regionNorthernAfrica": "África septentrional",
|
||||
"regionEasternAfrica": "África oriental",
|
||||
"regionMiddleAfrica": "África central",
|
||||
@@ -2602,7 +3114,7 @@
|
||||
"regionNorthernEurope": "Europa septentrional",
|
||||
"regionSouthernEurope": "Europa meridional",
|
||||
"regionWesternEurope": "Europa Occidental",
|
||||
"regionOceania": "Oceania",
|
||||
"regionOceania": "Oceanía",
|
||||
"regionAustraliaAndNewZealand": "Australia y Nueva Zelanda",
|
||||
"regionMelanesia": "Melanesia",
|
||||
"regionMicronesia": "Micronesia",
|
||||
@@ -2676,12 +3188,12 @@
|
||||
"roleMappingRemoveRule": "Eliminar",
|
||||
"idpGoogleConfiguration": "Configuración de Google",
|
||||
"idpGoogleConfigurationDescription": "Configurar las credenciales de Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
"idpGoogleClientIdDescription": "Tu ID de cliente de Google OAuth2",
|
||||
"idpGoogleClientSecretDescription": "Secreto del cliente de Google OAuth2",
|
||||
"idpAzureConfiguration": "Configuración de Azure Entra ID",
|
||||
"idpAzureConfigurationDescription": "Configurar credenciales de Azure Entra ID OAuth2",
|
||||
"idpTenantId": "ID del inquilino",
|
||||
"idpTenantIdPlaceholder": "tenant-id",
|
||||
"idpTenantIdPlaceholder": "iD del inquilino",
|
||||
"idpAzureTenantIdDescription": "ID de inquilino Azure (encontrado en la descripción de Azure Active Directory)",
|
||||
"idpAzureClientIdDescription": "ID de cliente de registro de Azure App",
|
||||
"idpAzureClientSecretDescription": "Azure App Registro Cliente secreto",
|
||||
@@ -2695,7 +3207,7 @@
|
||||
"idpAzureClientIdDescription2": "ID de cliente de registro de Azure App",
|
||||
"idpAzureClientSecretDescription2": "Azure App Registro Cliente secreto",
|
||||
"idpGoogleDescription": "Proveedor OAuth2/OIDC de Google",
|
||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||
"idpAzureDescription": "Proveedor OAuth2/OIDC de Google",
|
||||
"subnet": "Subred",
|
||||
"utilitySubnet": "Subred de Utilidad",
|
||||
"subnetDescription": "La subred para la configuración de red de esta organización.",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "Prioridad",
|
||||
"priorityDescription": "Las rutas de prioridad más alta son evaluadas primero. Prioridad = 100 significa orden automático (decisiones del sistema). Utilice otro número para hacer cumplir la prioridad manual.",
|
||||
"instanceName": "Nombre de instancia",
|
||||
"clearInstanceName": "Reset Server Association",
|
||||
"pathMatchModalTitle": "Configurar ruta coincidente",
|
||||
"pathMatchModalDescription": "Configurar cómo deben coincidir las peticiones entrantes en función de su ruta.",
|
||||
"pathMatchType": "Tipo de partida",
|
||||
@@ -3046,11 +3559,12 @@
|
||||
"allowedByRule": "Permitido por regla",
|
||||
"allowedNoAuth": "No se permite autorización",
|
||||
"validAccessToken": "Token de Acceso Válido",
|
||||
"validHeaderAuth": "Valid header auth",
|
||||
"validPincode": "Valid Pincode",
|
||||
"validHeaderAuth": "Autenticación Básica del Encabezado",
|
||||
"validPincode": "Definir Pincode",
|
||||
"validPassword": "Contraseña válida",
|
||||
"validEmail": "Valid email",
|
||||
"validSSO": "Valid SSO",
|
||||
"validEmail": "Válido hasta",
|
||||
"validSSO": "Clave válida",
|
||||
"validVirtualAPIKey": "Clave API Virtual Válida",
|
||||
"view": "Ver",
|
||||
"configManaged": "Configuración Gestionada",
|
||||
"connectedClient": "Cliente conectado",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "Registros de acción",
|
||||
"logRetention": "Retención de Log",
|
||||
"logRetentionDescription": "Administrar cuánto tiempo se conservan los diferentes tipos de registros para esta organización o desactivarlos",
|
||||
"logRetentionDisabledWarningTitle": "Retención de Registros Deshabilitada",
|
||||
"logRetentionDisabledWarningDescription": "{logType} no se están reteniendo para esta organización, por lo que la nueva actividad no aparecerá aquí. Habilita la retención en la configuración de seguridad para comenzar a recopilar estos registros.",
|
||||
"logRetentionDisabledWarningButton": "Ir a Configuración de Seguridad",
|
||||
"requestLogsDescription": "Ver registros de solicitudes detallados para los recursos de esta organización",
|
||||
"aiSessionLogs": "Registros de Sesiones del Portal de IA",
|
||||
"aiSessionLogsDescription": "Ver transcripciones de solicitud y respuesta para solicitudes del portal de IA en esta organización",
|
||||
"sidebarLogsAi": "Registros de Sesiones",
|
||||
"commandLogsAi": "Registros de Sesiones",
|
||||
"sidebarLogsAiUsage": "Analítica de Uso",
|
||||
"commandLogsAiUsage": "Analítica de Uso",
|
||||
"provider": "Proveedor",
|
||||
"capability": "Capacidad",
|
||||
"model": "Modelo",
|
||||
"virtualApiKey": "Clave API Virtual",
|
||||
"noVirtualApiKey": "Sin clave API virtual",
|
||||
"stream": "Flujo",
|
||||
"streaming": "Flujo de eventos",
|
||||
"nonStreaming": "No-flujo",
|
||||
"statusCode": "Código de estado",
|
||||
"aiSessionId": "ID de Sesión",
|
||||
"aiSessionRequest": "Solicitud",
|
||||
"aiSessionResponse": "Respuesta",
|
||||
"aiSessionNoData": "Sin datos capturados",
|
||||
"aiSessionCouldNotParse": "(sin procesar, no se pudo analizar la transcripción)",
|
||||
"aiSessionLogTruncated": "Esta sesión fue truncada antes del almacenamiento y puede estar incompleta.",
|
||||
"aiSessionViewRaw": "Ver JSON sin procesar",
|
||||
"aiSessionViewChat": "Ver Chat",
|
||||
"cost": "Costo",
|
||||
"estimated": "Estimado",
|
||||
"tokenUsage": "Uso de Tokens",
|
||||
"promptTokens": "Tokens de Indicio",
|
||||
"cacheReadTokens": "Tokens de Lectura de Caché",
|
||||
"cacheWriteTokens": "Tokens de Escritura de Caché",
|
||||
"completionTokens": "Tokens de Completitud",
|
||||
"reasoningTokens": "Tokens de Razonamiento",
|
||||
"totalTokens": "Total de Tokens",
|
||||
"requestAnalyticsDescription": "Ver análisis de solicitudes detalladas de recursos en esta organización",
|
||||
"logRetentionRequestLabel": "Retención de Registro de Solicitud HTTP",
|
||||
"logRetentionRequestDescription": "Cuánto tiempo conservar los registros de solicitudes",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "Cuánto tiempo retener los registros de acción",
|
||||
"logRetentionConnectionLabel": "Retención de Registro de Conexión",
|
||||
"logRetentionConnectionDescription": "Cuánto tiempo conservar los registros de conexión",
|
||||
"logRetentionAISessionsLabel": "Retención de Registro de Sesiones del Portal de IA",
|
||||
"logRetentionAISessionsDescription": "Cuánto tiempo retener los registros de sesión de solicitud/respuesta del portal de IA",
|
||||
"logRetentionDisabled": "Deshabilitado",
|
||||
"logRetention3Days": "3 días",
|
||||
"logRetention7Days": "7 días",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "Dirección de origen",
|
||||
"destinationAddress": "Dirección de destino",
|
||||
"duration": "Duración",
|
||||
"licenseRequiredToUse": "Se requiere una licencia <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> o <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> para usar esta función. <bookADemoLink>Reserve una demostración gratuita o una prueba POC para saber más.</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "La <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> es necesaria para utilizar esta función. Esta función también está disponible en <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Reserva una demostración gratuita o prueba POC para saber más.</bookADemoLink>",
|
||||
"licenseRequiredToUse": "Se requiere una licencia para la <enterpriseEditionLink>Edición Empresarial</enterpriseEditionLink>. <bookADemoLink>Reserve una demostración o prueba</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "Se requiere una licencia para la <enterpriseEditionLink>Edición Empresarial</enterpriseEditionLink>. <bookADemoLink>Reserve una demostración o prueba</bookADemoLink>",
|
||||
"certResolver": "Resolver certificado",
|
||||
"certResolverDescription": "Seleccione la resolución de certificados a utilizar para este recurso.",
|
||||
"selectCertResolver": "Seleccionar Resolver Certificado",
|
||||
@@ -3225,7 +3776,7 @@
|
||||
"regenerateCredentialsWarning": "Regenerar las credenciales invalidará las anteriores y causará una desconexión. Asegúrese de actualizar cualquier configuración que use estas credenciales.",
|
||||
"confirm": "Confirmar",
|
||||
"regenerateCredentialsConfirmation": "¿Está seguro que desea regenerar las credenciales?",
|
||||
"endpoint": "Endpoint",
|
||||
"endpoint": "Punto final",
|
||||
"Id": "Id",
|
||||
"SecretKey": "Clave secreta",
|
||||
"niceId": "ID bonita",
|
||||
@@ -3235,13 +3786,14 @@
|
||||
"niceIdUpdateErrorDescription": "Se ha producido un error al actualizar el ID de Niza.",
|
||||
"niceIdCannotBeEmpty": "El ID de Niza no puede estar vacío",
|
||||
"enterIdentifier": "Introducir identificador",
|
||||
"identifier": "Identifier",
|
||||
"identifier": "Ruta del identificador",
|
||||
"deviceLoginUseDifferentAccount": "¿No tú? Utilice una cuenta diferente.",
|
||||
"deviceLoginDeviceRequestingAccessToAccount": "Un dispositivo está solicitando acceso a esta cuenta.",
|
||||
"loginSelectAuthenticationMethod": "Seleccione un método de autenticación para continuar.",
|
||||
"noData": "Sin datos",
|
||||
"machineClients": "Clientes de la máquina",
|
||||
"install": "Instalar",
|
||||
"downloadInstaller": "Error al descargar",
|
||||
"run": "Ejecutar",
|
||||
"envFile": "Archivo de Entorno",
|
||||
"serviceFile": "Archivo de Servicio",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "Seleccionar múltiples sitios habilita el enrutamiento resistente y la conmutación por error para alta disponibilidad.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Más información",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Restringir el acceso a puertos TCP/UDP específicos o permitir/bloquear todos los puertos.",
|
||||
"createInternalResourceDialogHttpConfiguration": "Configuración HTTP",
|
||||
"createInternalResourceDialogHttpConfiguration": "Configuración del dominio",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "Elija el dominio que los clientes usarán para alcanzar este recurso a través de HTTP o HTTPS.",
|
||||
"editInternalResourceDialogHttpConfiguration": "Configuración HTTP",
|
||||
"editInternalResourceDialogHttpConfiguration": "Configuración del dominio",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "Elija el dominio que los clientes usarán para alcanzar este recurso a través de HTTP o HTTPS.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "Desconectado",
|
||||
"approvalsEmptyStateTitle": "Aprobaciones de dispositivo no habilitadas",
|
||||
"approvalsEmptyStateDescription": "Habilita las aprobaciones de dispositivos para que los roles requieran aprobación del administrador antes de que los usuarios puedan conectar nuevos dispositivos.",
|
||||
"approvalsEmptyStateHowToTitle": "Cómo habilitar",
|
||||
"approvalsEmptyStateStep1Title": "Ir a roles",
|
||||
"approvalsEmptyStateStep1Description": "Navega a la configuración de roles de tu organización para configurar las aprobaciones de dispositivos.",
|
||||
"approvalsEmptyStateStep2Title": "Habilitar aprobaciones de dispositivo",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "Eventos de conexión de sitios y túneles, incluyendo conexiones y desconexiones.",
|
||||
"httpDestRequestLogsTitle": "Registros de Solicitud HTTP",
|
||||
"httpDestRequestLogsDescription": "Registros de peticiones HTTP para recursos proxyficados, incluyendo método, ruta y código de respuesta.",
|
||||
"httpDestAISessionLogsTitle": "Registros de Sesión AI",
|
||||
"httpDestAISessionLogsDescription": "Sesiones de solicitud y respuesta de AI gateway, incluyendo indicaciones, respuestas de modelos, y uso de tokens.",
|
||||
"httpDestSaveChanges": "Guardar Cambios",
|
||||
"httpDestCreateDestination": "Crear destino",
|
||||
"httpDestUpdatedSuccess": "Destino actualizado correctamente",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "Ver como Administrador",
|
||||
"resourceLauncherResourceDetailsDescription": "Información de conexión y estado de este recurso.",
|
||||
"resourceLauncherResourceDetails": "Detalles del recurso",
|
||||
"resourceLauncherSitesDescription": "El recurso es accesible a través de los siguientes sitios.",
|
||||
"resourceLauncherViewSiteAsAdmin": "Ver Sitio como Administrador",
|
||||
"resourceLauncherFilterBySite": "Filtrar por Sitio",
|
||||
"resourceLauncherSshCommand": "Comando SSH",
|
||||
"resourceLauncherSshCommandDescription": "Usa el CLI de Pangolin para abrir una sesión SSH para este recurso.",
|
||||
"resourceLauncherAuthMethodsDescription": "Métodos de autenticación habilitados para este recurso.",
|
||||
"resourceLauncherPrivateClientRequired": "Conéctese con un cliente en su dispositivo para acceder a este recurso en privado.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "Se requiere conexión del cliente",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Sin Etiqueta",
|
||||
"resourceLauncherAiGateway": "Portal de IA",
|
||||
"resourceLauncherNoSite": "Sin Sitio",
|
||||
"resourceLauncherAvailableModels": "Modelos Disponibles",
|
||||
"resourceLauncherAvailableModelsDescription": "Modelos que puede usar con este portal de IA.",
|
||||
"resourceLauncherAvailableModelsEmpty": "No hay modelos disponibles para este recurso.",
|
||||
"resourceLauncherAvailableModelsError": "No se pudieron cargar los modelos disponibles.",
|
||||
"resourceLauncherApiKeys": "Claves API",
|
||||
"resourceLauncherApiKeysDescription": "Use su clave de identidad o una clave atribuida para autenticarse con este recurso.",
|
||||
"resourceLauncherApiKeysIdentity": "Clave de Identidad",
|
||||
"resourceLauncherApiKeysManual": "Claves Atribuidas",
|
||||
"resourceLauncherApiKeysEmpty": "No hay claves API disponibles para este recurso.",
|
||||
"resourceLauncherApiKeysError": "No se pudieron cargar las claves API.",
|
||||
"resourceLauncherNoResourcesInGroup": "No hay recursos en este grupo",
|
||||
"resourceLauncherEmptyStateTitle": "No hay Recursos Disponibles",
|
||||
"resourceLauncherEmptyStateDescription": "Todavía no tienes acceso a ningún recurso. Contacta a tu administrador para solicitar acceso.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "No se pudo eliminar la vista del lanzador. Por favor, intenta de nuevo.",
|
||||
"memberPortalPrevious": "Anterior",
|
||||
"memberPortalNext": "Siguiente",
|
||||
"httpSettings": "Configuración HTTP",
|
||||
"tcpSettings": "Configuración TCP",
|
||||
"udpSettings": "Configuración UDP",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Estableciendo una conexión segura…",
|
||||
"sshConnecting": "Conectando…",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Modo teclado Unicode",
|
||||
"sessionToolbarShow": "Mostrar barra de herramientas",
|
||||
"sessionToolbarHide": "Ocultar barra de herramientas",
|
||||
"actionUpdateSiteApprovals": "Actualizar aprobaciones del sitio"
|
||||
"actionUpdateSiteApprovals": "Actualizar aprobaciones del sitio",
|
||||
"check": "Comprobar"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Veuillez vous assurer que vous êtes connecté avec le bon compte.",
|
||||
"inviteErrorNoUser": "Nous sommes désolés, mais il semble que l'invitation à laquelle vous essayez d'accéder ne concerne pas un utilisateur existant.",
|
||||
"inviteCreateUser": "Veuillez d'abord créer un compte.",
|
||||
"inviteErrorOidcNotAllowed": "Les invitations ne peuvent être acceptées que par des comptes internes. Déconnectez-vous et connectez-vous avec votre mot de passe pour cet email.",
|
||||
"inviteLoginInternalOnly": "Les invitations nécessitent un compte interne avec un mot de passe. Créez un compte ou connectez-vous avec votre mot de passe.",
|
||||
"goHome": "Retour à l'accueil",
|
||||
"inviteLogInOtherUser": "Se connecter en tant qu'utilisateur différent",
|
||||
"createAnAccount": "Créer un compte",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "Cibles sur ce site",
|
||||
"siteSetting": "Paramètres de {siteName}",
|
||||
"siteNewtTunnel": "Site Newt (Recommandé)",
|
||||
"pangolinSite": "Site Pangolin",
|
||||
"siteNewtTunnelDescription": "La façon la plus simple de créer un point d'entrée dans n'importe quel réseau. Pas de configuration supplémentaire.",
|
||||
"siteWg": "WireGuard basique",
|
||||
"siteWgDescription": "Utilisez n'importe quel client WireGuard pour établir un tunnel. Configuration NAT manuelle requise.",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "Aucun pays trouvé.",
|
||||
"siteSelectionDescription": "Ce site fournira la connectivité à la cible.",
|
||||
"resourceType": "Type de ressource",
|
||||
"resourceTypeDescription": "Cela contrôle le protocole de la ressource et comment il sera rendu dans le navigateur. Cela ne peut pas être changé plus tard.",
|
||||
"resourceTypeDescription": "Cela contrôle le protocole de ressource et ne peut pas être modifié ultérieurement.",
|
||||
"resourceTypeSearch": "Rechercher des types de ressource...",
|
||||
"resourceTypeNotFound": "Aucun type de ressource trouvé",
|
||||
"resourceTypeHttpDescription": "Proxy du trafic web via HTTPS en utilisant un nom de domaine",
|
||||
"resourceTypeInferenceDescription": "Acheminer les requêtes AI via les fournisseurs attachés",
|
||||
"resourceTypeSshDescription": "Accéder à un serveur SSH depuis le navigateur",
|
||||
"resourceTypeRdpDescription": "Accéder à un bureau distant depuis le navigateur",
|
||||
"resourceTypeVncDescription": "Accéder à un bureau VNC depuis le navigateur",
|
||||
"resourceTypeTcpDescription": "Proxy du trafic TCP brut utilisant un numéro de port",
|
||||
"resourceTypeUdpDescription": "Proxy du trafic UDP brut utilisant un numéro de port",
|
||||
"privateResourceTypeDescription": "Cela contrôle comment les clients atteignent la ressource. Cela ne peut pas être modifié plus tard.",
|
||||
"privateResourceTypeHostDescription": "Exposer un hôte unique sur le réseau du site aux clients connectés",
|
||||
"privateResourceTypeCidrDescription": "Exposer une plage CIDR sur le réseau du site aux clients connectés",
|
||||
"privateResourceTypeHttpDescription": "Accéder à un service HTTP ou HTTPS via un domaine",
|
||||
"privateResourceTypeSshDescription": "Accéder à un serveur SSH depuis les clients connectés",
|
||||
"resourceDomainDescription": "La ressource sera servie à ce nom de domaine pleinement qualifié.",
|
||||
"resourceHTTPSSettings": "Paramètres HTTPS",
|
||||
"resourceHTTPSSettingsDescription": "Configurer comment la ressource sera accédée via HTTPS",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "Supprimer la clé d'API",
|
||||
"apiKeysManage": "Gérer les clés d'API",
|
||||
"apiKeysDescription": "Les clés d'API sont utilisées pour s'authentifier avec l'API d'intégration",
|
||||
"orgsManage": "Gérer les organisations",
|
||||
"orgsDescription": "Voir et gérer toutes les organisations sur cette instance",
|
||||
"provisioningKeysTitle": "Clé de provisioning",
|
||||
"provisioningKeysManage": "Gérer les clés de provisioning",
|
||||
"provisioningKeysDescription": "Les clés de provisioning sont utilisées pour authentifier la fourniture automatique de sites pour votre organisation.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "Nom (Optionnel)",
|
||||
"accessControls": "Contrôles d'accès",
|
||||
"userDescription2": "Gérer les paramètres de cet utilisateur",
|
||||
"userGeneralSettingsDescription": "Gérer les rôles et paramètres de cet utilisateur dans l'organisation",
|
||||
"accessRoleErrorAdd": "Échec 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é",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "Enregistrer les paramètres",
|
||||
"policyAuthStackTitle": "Authentification",
|
||||
"policyAuthStackDescription": "Contrôlez quelles méthodes d'authentification sont nécessaires pour accéder à cette ressource",
|
||||
"policyAuthInferenceStackDescription": "Choisissez quels utilisateurs et rôles peuvent s'authentifier à cette passerelle AI",
|
||||
"policyAuthOrLogicTitle": "Plusieurs méthodes d'authentification actives",
|
||||
"policyAuthOrLogicBanner": "Les visiteurs peuvent s'authentifier en utilisant l'une des méthodes actives ci-dessous. Ils n'ont pas besoin de toutes les compléter.",
|
||||
"policyAuthMethodActive": "Actif",
|
||||
"policyAuthMethodOff": "Éteint",
|
||||
"policyAuthSsoTitle": "SSO de la plateforme",
|
||||
"policyAuthSsoDescription": "Exigez une connexion via le fournisseur d'identité de votre organisation",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "URL de connexion",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Chaque utilisateur a déjà une clé API d'identité, vous devez donc uniquement créer des clés API virtuelles pour les clients non-utilisateurs ou l'accès partagé. Les utilisateurs peuvent récupérer leur clé en se connectant avec leur fournisseur d'identité à l'URL de cette ressource, où elle sera affichée après la connexion.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Chaque utilisateur a déjà une clé API d'identité, vous devez donc uniquement créer des clés API virtuelles pour les clients non-utilisateurs ou l'accès partagé. Les utilisateurs récupèrent leur clé ici après s'être connectés avec leur fournisseur d'identité.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} utilisateurs, {roles} rôles",
|
||||
"policyAuthSsoDefaultIdp": "Fournisseur par défaut",
|
||||
"policyAuthAddDefaultIdentityProvider": "Ajouter un fournisseur d'identité par défaut",
|
||||
@@ -892,7 +916,7 @@
|
||||
"policyAccessRulesFallthroughOff": "Lorsque les règles sont désactivées, tout le trafic passe par l'authentification.",
|
||||
"policyAccessRulesFallthroughOn": "Lorsqu'aucune règle ne correspond, le trafic passe par l'authentification.",
|
||||
"rulesPlaceholderCidr": "10.0.0.0/8",
|
||||
"rulesPlaceholderPath": "/admin/*",
|
||||
"rulesPlaceholderPath": "/administrateur/*",
|
||||
"rulesPlaceholderGeo": "RU, KP",
|
||||
"rulesSave": "Enregistrer les règles",
|
||||
"resourceErrorCreate": "Erreur lors de la création de la ressource",
|
||||
@@ -928,7 +952,7 @@
|
||||
"unknownCommand": "Commande inconnue",
|
||||
"newtErrorFetchReleases": "Échec de la récupération des informations de version : {err}",
|
||||
"newtErrorFetchLatest": "Erreur lors de la récupération de la dernière version : {err}",
|
||||
"newtEndpoint": "Endpoint",
|
||||
"newtEndpoint": "Point de terminaison",
|
||||
"newtId": "ID",
|
||||
"newtSecretKey": "Secrète",
|
||||
"newtVersion": "Version",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "Un nouveau rôle est requis",
|
||||
"accessRoleErrorRemove": "Échec de la suppression du rôle",
|
||||
"accessRoleErrorRemoveDescription": "Une erreur s'est produite lors de la suppression du rôle.",
|
||||
"accessRoleInferenceBudget": "Budget AI",
|
||||
"accessRoleInferenceBudgetDescription": "Configurez comment les membres de ce rôle restreignent l'utilisation de l'IA en fonction des dépenses ou des limites de jetons",
|
||||
"accessRoleName": "Nom du rôle",
|
||||
"accessRoleQuestionRemove": "Vous êtes sur le point de supprimer le rôle `{name}`. Vous ne pouvez pas annuler cette action.",
|
||||
"accessRoleRemove": "Supprimer le rôle",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "Aucun site trouvé.",
|
||||
"pangolinServerAdmin": "Admin Serveur - Pangolin",
|
||||
"licenseTierProfessional": "Licence Professionnelle",
|
||||
"licenseTierEnterprise": "Licence Entreprise",
|
||||
"licenseTierPersonal": "Licence personnelle",
|
||||
"licenseTierEnterprise": "Entreprise",
|
||||
"licenseTierPersonal": "Personnel",
|
||||
"licenseTierTier1": "Démarrage",
|
||||
"licenseTierTier2": "Échelle",
|
||||
"licensed": "Sous licence",
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
@@ -1154,6 +1182,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "En savoir plus sur JMESPath",
|
||||
"idpJmespathLabel": "Chemin d'identification",
|
||||
"idpJmespathLabelDescription": "Le JMESPath vers l'identifiant de l'utilisateur dans le jeton ID",
|
||||
"idpIdentifierChangeTitle": "Avertissement de changement de chemin d'identification",
|
||||
"idpIdentifierChangeDescription": "Vous êtes sur le point de modifier le chemin d'identification. Cela affectera la manière dont les utilisateurs existants sont mappés. Les utilisateurs qui se sont connectés via ce fournisseur d'identité peuvent ne plus être reconnus comme les mêmes utilisateurs.",
|
||||
"idpIdentifierChangeConfirmMessage": "Je confirme",
|
||||
"idpIdentifierChangeWarningText": "Cela affectera la manière dont les utilisateurs existants sont mappés",
|
||||
"idpJmespathEmailPathOptional": "Chemin de l'email (Optionnel)",
|
||||
"idpJmespathEmailPathOptionalDescription": "Le JMESPath vers l'email de l'utilisateur dans le jeton ID",
|
||||
"idpJmespathNamePathOptional": "Chemin du nom (Optionnel)",
|
||||
@@ -1285,7 +1317,7 @@
|
||||
"generatePasswordResetCode": "Générer le code de réinitialisation du mot de passe",
|
||||
"passwordResetCodeGenerated": "Code de réinitialisation du mot de passe généré",
|
||||
"passwordResetCodeGeneratedDescription": "Partagez ce code avec l'utilisateur. Il peut l'utiliser pour réinitialiser son mot de passe.",
|
||||
"passwordResetUrl": "Reset URL",
|
||||
"passwordResetUrl": "Réinitialiser l'URL",
|
||||
"passwordNew": "Nouveau mot de passe",
|
||||
"passwordNewConfirm": "Confirmer le nouveau mot de passe",
|
||||
"changePassword": "Changer le mot de passe",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "Erreur lors de la déconnexion",
|
||||
"signingAs": "Connecté en tant que",
|
||||
"serverAdmin": "Admin Serveur",
|
||||
"promoteServerAdmin": "Promouvoir en tant qu'administrateur serveur",
|
||||
"promoteServerAdminTitle": "Promouvoir en tant qu'administrateur serveur",
|
||||
"promoteServerAdminQuestion": "Êtes-vous sûr de vouloir promouvoir {selectedUser} en tant qu'administrateur serveur ?",
|
||||
"promoteServerAdminMessage": "Les administrateurs serveurs ont les privilèges les plus élevés et peuvent gérer le serveur.",
|
||||
"promoteServerAdminWarning": "Cela peut être annulé à tout moment en rétrogradant l'utilisateur.",
|
||||
"promoteServerAdminConfirm": "Promouvoir en tant qu'administrateur serveur",
|
||||
"promoteServerAdminSuccess": "Utilisateur promu",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} est maintenant un administrateur serveur.",
|
||||
"promoteServerAdminError": "Échec de la promotion de l'utilisateur",
|
||||
"demoteServerAdmin": "Rétrograder d'administrateur serveur",
|
||||
"demoteServerAdminTitle": "Rétrograder d'administrateur serveur",
|
||||
"demoteServerAdminQuestion": "Êtes-vous sûr de vouloir rétrograder {selectedUser} de l'administrateur serveur ?",
|
||||
"demoteServerAdminMessage": "{selectedUser} perdra tous les privilèges d'administrateur serveur.",
|
||||
"demoteServerAdminWarning": "Cela peut être annulé à tout moment en promouvant l'utilisateur.",
|
||||
"demoteServerAdminConfirm": "Rétrograder d'administrateur serveur",
|
||||
"demoteServerAdminSuccess": "Utilisateur rétrogradé",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} n'est plus un administrateur serveur.",
|
||||
"demoteServerAdminError": "Échec de la rétrogradation de l'utilisateur",
|
||||
"managedSelfhosted": "Gestion autonome",
|
||||
"otpEnable": "Activer l'authentification à deux facteurs",
|
||||
"otpDisable": "Désactiver l'authentification à deux facteurs",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "Supprimer un site",
|
||||
"actionGetSite": "Obtenir un site",
|
||||
"actionListSites": "Lister les sites",
|
||||
"actionCreateAiProvider": "Créer un fournisseur AI",
|
||||
"actionDeleteAiProvider": "Supprimer un fournisseur AI",
|
||||
"actionGetAiProvider": "Obtenir un fournisseur AI",
|
||||
"actionListAiProviders": "Lister les fournisseurs AI",
|
||||
"actionUpdateAiProvider": "Mettre à jour un fournisseur AI",
|
||||
"actionCreateAiModel": "Créer un modèle AI",
|
||||
"actionDeleteAiModel": "Supprimer un modèle AI",
|
||||
"actionGetAiModel": "Obtenir un modèle AI",
|
||||
"actionListAiModels": "Lister les modèles AI",
|
||||
"actionUpdateAiModel": "Mettre à jour un modèle AI",
|
||||
"actionCreateAiBudget": "Créer un budget AI",
|
||||
"actionDeleteAiBudget": "Supprimer un budget AI",
|
||||
"actionGetAiBudget": "Obtenir un budget AI",
|
||||
"actionListAiBudgets": "Lister les budgets AI",
|
||||
"actionUpdateAiBudget": "Mettre à jour un budget AI",
|
||||
"actionListResourceAiModels": "Lister les modèles AI de ressource",
|
||||
"actionSetResourceAiModels": "Définir les modèles AI de ressource",
|
||||
"actionCreateVirtualApiKey": "Créer une clé API virtuelle",
|
||||
"actionDeleteVirtualApiKey": "Supprimer une clé API virtuelle",
|
||||
"actionGetVirtualApiKey": "Obtenir une clé API virtuelle",
|
||||
"actionListVirtualApiKeys": "Lister les clés API virtuelles",
|
||||
"actionUpdateVirtualApiKey": "Mettre à jour une clé API virtuelle",
|
||||
"actionApplyBlueprint": "Appliquer la Config",
|
||||
"actionListBlueprints": "Lister les plans",
|
||||
"actionGetBlueprint": "Obtenez un plan",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "Définir le code PIN de la ressource",
|
||||
"actionSetResourceEmailWhitelist": "Définir la liste blanche des emails de la ressource",
|
||||
"actionGetResourceEmailWhitelist": "Obtenir la liste blanche des emails de la ressource",
|
||||
"actionListResourcePolicies": "Lister les politiques de ressource",
|
||||
"actionCreateResourcePolicy": "Créer une politique de ressource",
|
||||
"actionGetResourcePolicy": "Obtenir une politique de ressources",
|
||||
"actionUpdateResourcePolicy": "Mettre à jour la politique de ressources",
|
||||
"actionDeleteResourcePolicy": "Supprimer une politique de ressource",
|
||||
"actionSetResourcePolicyUsers": "Définir les utilisateurs de la politique de ressources",
|
||||
"actionSetResourcePolicyRoles": "Définir les rôles de la politique de ressources",
|
||||
"actionSetResourcePolicyPassword": "Définir le mot de passe de la politique de ressources",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "Rechercher…",
|
||||
"searchPlaceholder": "Recherche...",
|
||||
"emptySearchOptions": "Aucune option trouvée",
|
||||
"ipFilterSearchPlaceholder": "Entrez une adresse IP…",
|
||||
"ipFilterEmptyMessage": "Entrez une adresse IP pour filtrer",
|
||||
"create": "Créer",
|
||||
"orgs": "Organisations",
|
||||
"loginError": "Une erreur inattendue s'est produite. Veuillez réessayer.",
|
||||
@@ -1547,7 +1624,7 @@
|
||||
"commandPaletteSearching": "Recherche en cours...",
|
||||
"commandPaletteNavigation": "Navigation",
|
||||
"commandPaletteOrganizations": "Organisations",
|
||||
"commandPaletteSites": "Sites",
|
||||
"commandPaletteSites": "Nœuds",
|
||||
"commandPaletteResources": "Ressource",
|
||||
"commandPaletteUsers": "Utilisateurs",
|
||||
"commandPaletteClients": "Liste des clients",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "Créer un client machine",
|
||||
"commandPaletteCreateAlertRule": "Créer une règle d'alerte",
|
||||
"commandPaletteCreateIdentityProvider": "Créer un fournisseur d'identité",
|
||||
"commandPaletteCreateAiProvider": "Créer le fournisseur d'IA",
|
||||
"commandPaletteCreateVirtualApiKey": "Créer une clé API virtuelle",
|
||||
"commandPaletteToggleTheme": "Changer de thème",
|
||||
"commandPaletteChooseOrganization": "Choisir une organisation",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "Invitations",
|
||||
"sidebarRoles": "Rôles",
|
||||
"sidebarShareableLinks": "Liens partageables",
|
||||
"sidebarAiGateway": "Passerelle AI",
|
||||
"sidebarAiProviders": "Fournisseurs",
|
||||
"commandAiProviders": "Fournisseurs AI",
|
||||
"sidebarVirtualApiKeys": "Clés API virtuelles",
|
||||
"sidebarMyApiKeys": "Vos clés API",
|
||||
"sidebarAccount": "Lanceur",
|
||||
"commandVirtualApiKeys": "Clés API virtuelles",
|
||||
"virtualApiKeysTitle": "Gérer les clés API virtuelles",
|
||||
"virtualApiKeysDescription": "Créer et gérer des clés API manuelles pour l'accès à la passerelle AI publique",
|
||||
"virtualApiKeysTabIdentity": "Clés d'identité",
|
||||
"virtualApiKeysTabVirtual": "Clés virtuelles",
|
||||
"virtualApiKeysIdentitySplashTitle": "Clés d'identité pour chaque utilisateur",
|
||||
"virtualApiKeysIdentitySplashDescription": "Chaque utilisateur a déjà une clé d'identité Pangolin pour cette organisation. Elle est unique à leur compte et les authentifie aux passerelles AI qu'ils peuvent utiliser.",
|
||||
"virtualApiKeysIdentitySplashExample": "Exemple",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "Comment les utilisateurs obtiennent leur clé",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Visitez une URL de passerelle AI publique dans le navigateur et connectez-vous avec leur compte.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "Ou accédez à <url></url> en étant connecté.",
|
||||
"virtualApiKeysIdentitySplashManual": "Vous pouvez créer des clés API virtuelles supplémentaires dans l'onglet Clés Virtuelles. Ces clés peuvent être délimitées à des passerelles AI publiques spécifiques et éventuellement associées à un utilisateur. Créer une clé accorde immédiatement l'accès aux passerelles AI publiques sélectionnées.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Créer manuellement une clé",
|
||||
"virtualApiKeysEmailIdentity": "Envoyer les clés d'identité par email",
|
||||
"virtualApiKeysEmailIdentityDescription": "Envoyez à chaque utilisateur sélectionné ou rôle leur clé d'identité Pangolin.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "Envoyer à tous les utilisateurs",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "Envoyez un email à chaque membre de l'organisation possédant une adresse email de compte.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Utilisateurs",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Rôles",
|
||||
"virtualApiKeysEmailIdentitySubmit": "Envoyer des emails",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Clés d'identité envoyées par email",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "{skipped} utilisateurs ont été ignorés car ils n'ont pas d'adresse email.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "Sélectionnez au moins un utilisateur ou rôle, ou envoyez à tous les utilisateurs.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "Erreur lors de l'envoi des clés d'identité",
|
||||
"virtualApiKeysEmailIdentityError": "Échec de l'envoi des clés d'identité par email",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "Clés d'identité pour chaque utilisateur",
|
||||
"virtualApiKeysBannerTitle": "Tous les utilisateurs ont déjà une clé d'identité disponible à {keysUrl}. Vous pouvez également générer manuellement des clés ici pour accorder un accès direct aux passerelles AI publiques.",
|
||||
"virtualApiKeysBannerDescription": "Voir les clés d'identité",
|
||||
"virtualApiKeysBannerButtonText": "Voir les clés d'identité",
|
||||
"virtualApiKeys": "Clés API virtuelles",
|
||||
"virtualApiKeysSearch": "Rechercher des clés...",
|
||||
"virtualApiKeysCreate": "Émettre une clé manuelle pouvant appeler les passerelles AI publiques dans cette organisation",
|
||||
"virtualApiKeysCreateDescription": "Créer une clé",
|
||||
"virtualApiKeysCreateButton": "Créer une clé",
|
||||
"virtualApiKeysEmpty": "Nom",
|
||||
"virtualApiKeysName": "Nom",
|
||||
"virtualApiKeysDescriptionOptional": "Associer un utilisateur (facultatif)",
|
||||
"virtualApiKeysAssociateUserOptional": "Associer un utilisateur (facultatif)",
|
||||
"virtualApiKeysAssociateUserDescription": "Toutes les passerelles AI publiques",
|
||||
"virtualApiKeysAllResources": "Permettre à cette clé d'accéder à chaque passerelle AI publique de l'organisation",
|
||||
"virtualApiKeysAllResourcesDescription": "Passerelles AI publiques",
|
||||
"virtualApiKeysSelectResources": "Sélectionner des ressources",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Sélectionner des ressources",
|
||||
"virtualApiKeysSelectResourcesDescription": "Aucune ressource",
|
||||
"virtualApiKeysNoResources": "Clé",
|
||||
"virtualApiKeysSecret": "Clé",
|
||||
"virtualApiKeysCopyKey": "Voir le secret",
|
||||
"virtualApiKeysViewSecret": "Voir le secret",
|
||||
"virtualApiKeysViewSecretTitle": "Cet accès secret accorde l'accès aux passerelles AI publiques assignées à cette clé",
|
||||
"virtualApiKeysViewSecretDescription": "Modifier la clé API virtuelle",
|
||||
"virtualApiKeysEdit": "Mettre à jour l'utilisateur associé et l'accès à la passerelle AI publique pour cette clé",
|
||||
"virtualApiKeysEditDescription": "Enregistrer les modifications",
|
||||
"virtualApiKeysSaveButton": "Sélectionnez au moins une passerelle AI publique, ou activez toutes les passerelles AI publiques",
|
||||
"virtualApiKeysSelectResourcesRequired": "Clé API virtuelle mise à jour",
|
||||
"virtualApiKeysUpdated": "La clé API virtuelle a été mise à jour",
|
||||
"virtualApiKeysUpdatedDescription": "Erreur lors de la mise à jour de la clé API virtuelle",
|
||||
"virtualApiKeysErrorUpdate": "Échec de la mise à jour de la clé API virtuelle",
|
||||
"virtualApiKeysErrorUpdateDescription": "Erreur lors de la création de la clé API virtuelle",
|
||||
"virtualApiKeysErrorCreate": "Échec de la création de la clé API virtuelle",
|
||||
"virtualApiKeysErrorCreateDescription": "Erreur lors de la suppression de la clé API virtuelle",
|
||||
"virtualApiKeysErrorDelete": "Échec de la suppression de la clé API virtuelle",
|
||||
"virtualApiKeysErrorDeleteMessage": "Clé API virtuelle supprimée",
|
||||
"virtualApiKeysDeleted": "La clé API virtuelle a été supprimée",
|
||||
"virtualApiKeysDeletedDescription": "Supprimer la clé API virtuelle",
|
||||
"virtualApiKeysDelete": "Supprimer la clé",
|
||||
"virtualApiKeysDeleteConfirm": "Supprimer la clé",
|
||||
"virtualApiKeysQuestionRemove": "Êtes-vous sûr de vouloir supprimer cette clé API virtuelle ?",
|
||||
"virtualApiKeysMessageRemove": "Erreur lors du chargement du secret",
|
||||
"virtualApiKeysErrorFetchSecret": "Erreur lors du chargement du secret",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Non assigné",
|
||||
"virtualApiKeysFilterUnassigned": "Budget",
|
||||
"virtualApiKeysInferenceBudget": "Budget",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Email de la clé lors de la génération",
|
||||
"virtualApiKeysEmailOnGenerate": "Envoyer cette clé par email",
|
||||
"virtualApiKeysEmailThisKey": "Envoyer la clé à l'utilisateur associé et à des adresses supplémentaires après sa création",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Envoyez la clé actuelle à l'utilisateur associé et à des adresses supplémentaires",
|
||||
"virtualApiKeysEmailThisKeyDescription": "L'email n'est pas configuré sur ce serveur",
|
||||
"virtualApiKeysEmailSmtpRequired": "L'Email n'est pas configuré sur ce serveur",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "Envoyer à l'utilisateur associé",
|
||||
"virtualApiKeysEmailSendToUser": "Envoyez la clé à l'adresse email du compte de l'utilisateur associé",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Associez un utilisateur pour envoyer la clé à cet utilisateur",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "Emails supplémentaires",
|
||||
"virtualApiKeysEmailAdditional": "Ajoutez un email et appuyez sur Entrée",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "Sélectionnez l'utilisateur associé ou ajoutez au moins une adresse email",
|
||||
"virtualApiKeysEmailRecipientsRequired": "Vos clés API",
|
||||
"myVirtualApiKeysTitle": "Voyez votre clé d'identité et toutes les clés API virtuelles attribuées à vous dans cette organisation",
|
||||
"myVirtualApiKeysDescription": "Vos clés API pour {resourceName}",
|
||||
"myVirtualApiKeysResourceTitle": "Voyez votre clé d'identité et les clés API virtuelles attribuées à vous qui peuvent accéder à {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "Clé d'identité",
|
||||
"myVirtualApiKeysIdentityTitle": "Clé d'identité",
|
||||
"myVirtualApiKeysIdentityHeadline": "Votre clé API personnelle",
|
||||
"myVirtualApiKeysIdentityDescription": "Votre clé API personnelle pour {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Votre clé personnelle pour cette organisation. Utilisez-la pour appeler {resourceName}.",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Clés attribuées",
|
||||
"myVirtualApiKeysManualTitle": "Clés attribuées",
|
||||
"myVirtualApiKeysManualDescription": "Clés API virtuelles manuelles associées à votre compte qui peuvent accéder à {resourceName}",
|
||||
"myVirtualApiKeysManualResourceDescription": "Pas encore de clés attribuées",
|
||||
"myVirtualApiKeysManualEmpty": "Identité",
|
||||
"myVirtualApiKeysKindUser": "Manuel",
|
||||
"myVirtualApiKeysKindManual": "Clé non nommée",
|
||||
"myVirtualApiKeysUnnamed": "Révéler le secret",
|
||||
"myVirtualApiKeysRevealSecret": "Ce secret vous authentifie aux ressources de la passerelle AI",
|
||||
"myVirtualApiKeysViewSecretDescription": "Fournisseurs AI",
|
||||
"aiClientConfigTitle": "Configurer les agents de codage",
|
||||
"aiClientConfigDescription": "Copiez la configuration pour les agents de codage populaires ou laissez le CLI de Pangolin le configurer automatiquement pour vous.",
|
||||
"aiClientConfigDescriptionClaude": "Outil de codage agentique d'Anthropic pour le terminal.",
|
||||
"aiClientConfigDescriptionCodex": "Outil de codage agentique d'OpenAI pour le terminal.",
|
||||
"aiClientConfigDescriptionOpencode": "Agent de codage terminal open source.",
|
||||
"aiClientConfigDescriptionGemini": "Outil de codage agentique de Google pour le terminal.",
|
||||
"aiClientConfigSetup": "Configuration",
|
||||
"aiClientConfigTabCli": "Automatique (CLI)",
|
||||
"aiClientConfigTabManual": "Configuration manuelle",
|
||||
"aiClientConfigPreset": "Configuration",
|
||||
"aiClientConfigStep": "Étape {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "Ce snippet inclut des valeurs d'exemple, telles qu'un nom de modèle ou une URL de ressource. Remplacez-les par les vôtres avant de l'utiliser.",
|
||||
"aiClientConfigRevealError": "Impossible de charger votre clé API.",
|
||||
"aiClientConfigRevealRetry": "Réessayer",
|
||||
"resourceGeneralAiClientConfigDescription": "Cette ressource peut être utilisée à partir de clients communs comme Claude Code et OpenCode. <configLink>En savoir plus</configLink>",
|
||||
"aiProvidersTitle": "Connectez des fournisseurs de modèles pour des charges de travail AI dans cette organisation",
|
||||
"aiProvidersDescription": "Connecter les fournisseurs de modèles",
|
||||
"aiProvidersBannerTitle": "Les fournisseurs sont les backends de modèle que Pangolin utilise pour les charges de travail AI. Connectez OpenAI, Anthropic et d'autres fournisseurs ici, puis attachez-les aux passerelles AI publiques afin que les utilisateurs puissent appeler des modèles avec un accès conscient de l'identité, des budgets et des journaux.",
|
||||
"aiProvidersBannerDescription": "Ajouter un fournisseur",
|
||||
"aiProvidersAdd": "Rechercher des fournisseurs...",
|
||||
"aiProvidersSearch": "Aucun fournisseur AI pour le moment",
|
||||
"aiProvidersEmpty": "Créer un fournisseur AI",
|
||||
"aiProviderCreate": "Ajouter un fournisseur de modèles pour cette organisation",
|
||||
"aiProviderCreateDescription": "Voir tous les fournisseurs",
|
||||
"aiProviderSeeAll": "Paramètres du fournisseur pour {providerName}",
|
||||
"aiProviderSetting": "Configurez ce fournisseur AI",
|
||||
"aiProviderSettingDescription": "Général",
|
||||
"aiProviderGeneral": "Paramètres de base pour ce fournisseur",
|
||||
"aiProviderGeneralDescription": "Configuration",
|
||||
"aiProviderConfiguration": "Routage du réseau et authentification pour ce fournisseur",
|
||||
"aiProviderConfigurationDescription": "Paramètres du réseau",
|
||||
"aiProviderNetworkSettings": "Choisissez comment le trafic atteint ce fournisseur",
|
||||
"aiProviderNetworkSettingsDescription": "Authentification",
|
||||
"aiProviderAuthSettings": "Authentification",
|
||||
"aiProviderAuthSettingsDescription": "Budget",
|
||||
"aiProviderBudgetSettings": "Configurez comment ce fournisseur restreint l'utilisation en fonction des dépenses ou des limites de jetons",
|
||||
"aiProviderBudgetSettingsDescription": "Ajouter un budget",
|
||||
"aiBudgetAdd": "Ajouter un budget",
|
||||
"aiBudgetEmpty": "Type de dépense",
|
||||
"aiBudgetUnit": "Période de réinitialisation",
|
||||
"aiBudgetPeriod": "Dépense maximale",
|
||||
"aiBudgetAmount": "Dépense maximale",
|
||||
"aiBudgetAmountPlaceholder": "Horaire",
|
||||
"aiBudgetPeriodHourly": "Quotidien",
|
||||
"aiBudgetPeriodDaily": "Hebdomadaire",
|
||||
"aiBudgetPeriodWeekly": "Mensuel",
|
||||
"aiBudgetPeriodMonthly": "Annuel",
|
||||
"aiBudgetPeriodYearly": "Durée de vie",
|
||||
"aiBudgetPeriodLifetime": "Durée de vie",
|
||||
"aiBudgetUnitUsd": "Jetons",
|
||||
"aiBudgetUnitTokens": "Un budget pour cette période de réinitialisation et ce type de dépense existe déjà",
|
||||
"aiBudgetConflictError": "Entrez une dépense maximale supérieure à 0",
|
||||
"aiBudgetInvalidAmountError": "Entrez une dépense maximale supérieure à 0",
|
||||
"aiBudgetUpdated": "Échec de la mise à jour des budgets",
|
||||
"aiBudgetErrorSave": "Type de fournisseur",
|
||||
"aiProviderType": "Type de fournisseur",
|
||||
"aiProviderTypeSearch": "Rechercher des fournisseurs...",
|
||||
"aiProviderTypeNotFound": "OpenAI",
|
||||
"aiProviderTypeOpenai": "Anthropic",
|
||||
"aiProviderTypeAnthropic": "Google Gemini",
|
||||
"aiProviderTypeGoogleGemini": "Vertex AI",
|
||||
"aiProviderTypeVertexAi": "Amazon Bedrock",
|
||||
"aiProviderTypeBedrock": "Microsoft Foundry",
|
||||
"aiProviderTypeMicrosoftFoundry": "OpenRouter",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Personnalisé",
|
||||
"aiProviderTypeCustom": "Personnalisé",
|
||||
"aiProviderTypeOpenaiDescription": "API Anthropic",
|
||||
"aiProviderTypeAnthropicDescription": "API Google Gemini",
|
||||
"aiProviderTypeGoogleGeminiDescription": "API Google Vertex AI",
|
||||
"aiProviderTypeVertexAiDescription": "API d'exécution Amazon Bedrock",
|
||||
"aiProviderTypeBedrockDescription": "API Microsoft Foundry",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "API OpenRouter",
|
||||
"aiProviderTypeOpenRouterDescription": "API Passerelle Vercel AI",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "API de passerelle AI Vercel",
|
||||
"aiProviderTypeCustomDescription": "Apportez votre propre point de terminaison ou routez via les cibles du site",
|
||||
"aiProviderUpstreamUrl": "URL amont",
|
||||
"aiProviderUpstreamUrlDescription": "URL de base pour l'API du fournisseur",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Laissez vide pour utiliser l'URL amont par défaut pour ce fournisseur",
|
||||
"aiProviderEffectiveUpstreamUrl": "URL amont effective",
|
||||
"aiProviderApiKey": "Clé API",
|
||||
"aiProviderApiKeyDescription": "Clé API utilisée pour authentifier les requêtes auprès de ce fournisseur",
|
||||
"aiProviderCustomHeadersDescription": "En-têtes envoyés à chaque requête à ce fournisseur. Séparé par une nouvelle ligne : Nom-de-l'en-tête: valeur",
|
||||
"aiProviderApiKeyLastChars": "Clé API",
|
||||
"aiProviderAuthType": "Type d'authentification",
|
||||
"aiProviderAuthTypeSearch": "Rechercher des types d'authentification...",
|
||||
"aiProviderAuthTypeNotFound": "Aucun type d'authentification trouvé",
|
||||
"aiProviderAuthTypeBearer": "Porteur",
|
||||
"aiProviderAuthTypeBearerDescription": "Authorization : Clé Bearer. Utilisé par OpenAI et la plupart des fournisseurs",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "En-tête x-api-key. Utilisé par Anthropic",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "En-tête x-goog-api-key. Utilisé par Google Gemini",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Authorization : Clé Splunk. Utilisé par le collecteur d'événements HTTP Splunk",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Passerelle AI de Cloudflare",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-authorization : Clé Bearer. Utilisé par la passerelle AI de Cloudflare",
|
||||
"aiProviderAuthTypeNone": "Pas d'authentification",
|
||||
"aiProviderAuthTypePassthrough": "Transparence",
|
||||
"aiProviderAuthTypeDescription": "Comment l'API amont authentifie les requêtes",
|
||||
"aiProviderAuthTypePassthroughDescription": "Transmettez les en-têtes de clé API de l'appelant vers l'amont",
|
||||
"aiProviderAuthTypeNoneDescription": "Ne pas envoyer d'en-têtes d'authentification vers l'amont",
|
||||
"aiProviderRoutingMode": "Mode de routage",
|
||||
"aiProviderRoutingModeDescription": "Envoyez le trafic vers une URL amont ou vers des cibles HTTP sur vos sites",
|
||||
"aiProviderRoutingModeUrl": "URL amont",
|
||||
"aiProviderRoutingModeUrlDescription": "Appelez une URL de base d'API publique ou privée",
|
||||
"aiProviderRoutingModeTarget": "Cibles du site",
|
||||
"aiProviderRoutingModeTargetDescription": "Route à travers les cibles sur vos sites",
|
||||
"aiProviderRoutingModeTargetNote": "Après avoir créé ce fournisseur, configurez les cibles du site dans l'onglet Paramètres du réseau.",
|
||||
"aiProviderTargetNoOne": "Ce fournisseur n'a aucune cible. Ajoutez une cible pour acheminer les requêtes via vos sites.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Les sites connectés à des nœuds distants sont inaccessibles pour être routés vers les fournisseurs de passerelles AI.",
|
||||
"aiProviderSkipTlsVerification": "Ignorer la vérification TLS",
|
||||
"aiProviderSkipTlsVerificationDescription": "Désactiver la vérification du certificat TLS pour la connexion amont",
|
||||
"aiProviderBudget": "Budget",
|
||||
"aiProviderBudgetDescription": "Budget de dépenses ou de jetons optionnel pour ce fournisseur",
|
||||
"aiProviderBudgetAmount": "Montant du budget",
|
||||
"aiProviderBudgetUnit": "Unité de budget",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Jetons",
|
||||
"aiProviderEnabled": "Activé",
|
||||
"aiProviderEnabledDescription": "Désactivez complètement ce fournisseur sur toutes les ressources",
|
||||
"aiProviderErrorCreate": "Échec de la création du fournisseur AI",
|
||||
"aiProviderErrorUpdate": "Échec de la mise à jour du fournisseur AI",
|
||||
"aiProviderErrorDelete": "Échec de la suppression du fournisseur AI",
|
||||
"aiProviderErrorLoad": "Échec du chargement du fournisseur AI",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Entrez une URL amont valide",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Une URL amont est requise pour ce fournisseur",
|
||||
"aiProviderErrorAuthTypeRequired": "Le type d'authentification est requis",
|
||||
"aiProviderErrorApiKeyRequired": "La clé API est requise",
|
||||
"aiProviderErrorRoutingModeTarget": "Le routage des cibles du site est uniquement disponible pour les fournisseurs personnalisés",
|
||||
"aiProviderErrorCapabilitiesRequired": "Sélectionnez au moins une capacité API",
|
||||
"aiProviderCapabilities": "Capacités API",
|
||||
"aiProviderCapabilitiesDescription": "Sélectionnez les formats d'API que ce fournisseur peut gérer. Les fournisseurs connus commencent avec des réglages recommandés.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Sélectionnez quels formats d'API ce fournisseur personnalisé peut gérer",
|
||||
"aiProviderCapabilitiesSelect": "Sélectionner les capacités",
|
||||
"aiProviderCapabilitiesEmpty": "Aucune capacité trouvée",
|
||||
"aiProviderCapabilitiesSearch": "Rechercher des capacités...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI Completions de Chat",
|
||||
"aiCapabilityOpenaiChatDescription": "Prend en charge /v1/chat/completions",
|
||||
"aiCapabilityOpenaiResponses": "Réponses OpenAI",
|
||||
"aiCapabilityOpenaiResponsesDescription": "Prend en charge /v1/responses",
|
||||
"aiCapabilityAnthropicMessages": "Messages Anthropiques",
|
||||
"aiCapabilityAnthropicMessagesDescription": "Prend en charge /v1/messages",
|
||||
"aiCapabilityV1Models": "Liste des modèles",
|
||||
"aiCapabilityV1ModelsDescription": "Prise en charge de la découverte de modèles /v1/models",
|
||||
"aiCapabilityGeminiGenerateContent": "Générer du Contenu Gemini",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Prend en charge l'API directe de Gemini",
|
||||
"aiCapabilityBedrockModelInvoke": "Invocation du Modèle Bedrock",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Prend en charge Amazon Bedrock InvokeModel",
|
||||
"aiCapabilityGoogleGenerateContent": "Générer du Contenu Vertex",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Prend en charge le format Gemini de Vertex AI",
|
||||
"aiCapabilityGoogleRawPredict": "Prédiction Brute Vertex",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Prend en charge la prédiction brute Vertex AI pour les modèles anthropiques",
|
||||
"aiCapabilityBedrockConverse": "Converse Bedrock",
|
||||
"aiCapabilityBedrockConverseDescription": "Prend en charge l'API Converse d'Amazon Bedrock",
|
||||
"aiProviderCreated": "Fournisseur d'IA créé",
|
||||
"aiProviderUpdated": "Fournisseur d'IA mis à jour",
|
||||
"aiProviderDeleted": "Fournisseur d'IA supprimé",
|
||||
"aiProviderDelete": "Supprimer le fournisseur",
|
||||
"aiProviderDeleteConfirm": "Supprimer le fournisseur",
|
||||
"aiProviderQuestionRemove": "Êtes-vous sûr de vouloir supprimer ce fournisseur d'IA ?",
|
||||
"aiProviderMessageRemove": "Cela supprimera définitivement le fournisseur ainsi que ses modèles et cibles. Cela ne peut pas être annulé.",
|
||||
"aiProviderErrorNoUpdate": "Le fournisseur d'IA n'est pas disponible pour la mise à jour",
|
||||
"aiProviderModels": "Modèles",
|
||||
"aiProviderModelsDescription": "Définissez les listes d'autorisation et de blocage pour ce fournisseur. Les demandes doivent correspondre à une entrée autorisée et ne doivent pas correspondre à une entrée bloquée.",
|
||||
"aiProviderCreateModelsDescription": "Choisissez quels modèles ce fournisseur peut desservir. Une liste d'autorisation vide bloque tout le trafic. Vous pouvez ajouter ou modifier des modèles plus tard.",
|
||||
"aiProviderModelsPlaceholder": "Recherchez des modèles ou tapez une clé personnalisée",
|
||||
"aiProviderModelsAllow": "Liste d'autorisation",
|
||||
"aiProviderModelsAllowDescription": "Modèles qui peuvent être utilisés via ce fournisseur. Vide signifie tout bloquer.",
|
||||
"aiProviderModelsAllowPlaceholder": "Entrez la clé du modèle",
|
||||
"aiProviderModelsAllowEmpty": "Pas de modèles ajoutés pour le moment.",
|
||||
"aiProviderModelsBlock": "Liste de blocage",
|
||||
"aiProviderModelsBlockDescription": "Modèles à refuser même s'ils correspondent à une entrée autorisée.",
|
||||
"aiProviderModelsBlockPlaceholder": "Entrez la clé du modèle",
|
||||
"aiProviderModelsBlockEmpty": "Pas de modèles ajoutés pour le moment.",
|
||||
"aiProviderModelsAdd": "Ajouter des modèles",
|
||||
"aiProviderModelsClearAll": "Effacer tout",
|
||||
"aiProviderModelsAddCustom": "Ajouter \"{key}\"",
|
||||
"aiProviderModelsAddCustomHint": "Appuyez sur Entrée pour ajouter cette clé de modèle personnalisée.",
|
||||
"aiProviderModelsAddBulk": "Ajouter {count} clés personnalisées",
|
||||
"aiProviderModelsAddBulkHint": "Appuyez sur Entrée pour ajouter {count} clés personnalisées.",
|
||||
"aiProviderModelsAddOne": "Ajouter {key} maintenant",
|
||||
"aiProviderModelsAddSelected": "Ajouter la sélection",
|
||||
"aiProviderModelsSelectedCount": "{count} sélectionné(s)",
|
||||
"aiProviderModelsSelectAll": "Tout sélectionner",
|
||||
"aiProviderModelsClearSelected": "Effacer",
|
||||
"aiProviderModelsBulkHint": "Sélectionnez des modèles connus ou tapez une clé personnalisée.",
|
||||
"aiProviderModelsCatalogEmpty": "Aucun modèle de catalogue correspondant.",
|
||||
"aiProviderModelsCatalogHeading": "Modèles connus",
|
||||
"aiProviderModelsAllLabel": "Tous les modèles",
|
||||
"aiProviderModelsAllPatternHint": "Joker : *",
|
||||
"aiProviderModelsAddAllAllow": "Autoriser tous les modèles",
|
||||
"aiProviderModelsAddAllBlock": "Bloquer tous les modèles",
|
||||
"aiProviderModelsAddAllDescription": "Utilise le modèle * joker pour que chaque clé de modèle corresponde.",
|
||||
"aiProviderModelsViewMore": "Voir plus ({count})",
|
||||
"aiProviderModelsViewLess": "Voir moins",
|
||||
"aiProviderModelsRemove": "Supprimer le modèle",
|
||||
"aiProviderModelsEditHint": "Cliquez pour modifier les paramètres du modèle",
|
||||
"aiProviderModelsSourceCatalog": "Modèle de catalogue connu",
|
||||
"aiProviderModelsSourceCustom": "Clé de modèle personnalisée",
|
||||
"aiProviderModelsSourcePattern": "Modèle de correspondance avec générique",
|
||||
"aiProviderModelsSourceAll": "Correspond à chaque clé de modèle",
|
||||
"aiProviderModelsBudgetConfigured": "Budget configuré",
|
||||
"aiProviderModelsEditTitle": "Modifier le modèle",
|
||||
"aiProviderModelsEditDescription": "Mettez à jour la clé du modèle ou configurez son budget.",
|
||||
"aiProviderModelsBudgetTab": "Configurer le budget de ce fournisseur pour restreindre l'utilisation en fonction des dépenses ou des limites de jetons",
|
||||
"aiProviderModelsKeyLabel": "Clé du modèle",
|
||||
"aiProviderModelsKeyRequired": "Entrez une clé de modèle",
|
||||
"aiProviderModelsKeyDuplicate": "Cette clé de modèle est déjà sur une liste",
|
||||
"aiProviderModelsOverlapError": "Ces modèles ne peuvent être sur les deux listes : {keys}",
|
||||
"aiProviderModelsUpdated": "Modèles mis à jour",
|
||||
"aiProviderModelsErrorUpdate": "Échec de la mise à jour des modèles",
|
||||
"aiResourceProviders": "Fournisseurs",
|
||||
"aiResourceProvidersDescription": "Choisissez quels fournisseurs d'IA ce portail AI peut utiliser",
|
||||
"aiResourceProvidersHelp": "Joignez des fournisseurs et choisissez d'hériter (utiliser les listes de chaque fournisseur) ou de sélectionner (choisir une liste autorisée pour cette ressource). Les modèles conflictuels des fournisseurs attachés ne sont pas autorisés.",
|
||||
"aiResourceProvidersSelect": "Sélectionnez les fournisseurs",
|
||||
"aiResourceProvidersEmpty": "Aucun fournisseur d'IA trouvé",
|
||||
"aiResourceProvidersNoneAttached": "Aucun fournisseur n'est attaché pour le moment.",
|
||||
"aiResourceProvidersAdd": "Ajouter un fournisseur",
|
||||
"aiResourceProvidersRemove": "Supprimer le fournisseur",
|
||||
"aiResourceProviderToggleEnabled": "Activer ou désactiver ce fournisseur sur la ressource",
|
||||
"aiResourceProviderDisabled": "Désactivé",
|
||||
"aiResourceProvidersUpdated": "Fournisseurs mis à jour",
|
||||
"aiResourceProvidersErrorUpdate": "Échec de la mise à jour des fournisseurs",
|
||||
"aiResourceProviderEditDescription": "Choisissez comment les modèles de ce fournisseur sont exposés sur cette ressource.",
|
||||
"viewProviderSettings": "Voir les paramètres du fournisseur",
|
||||
"aiResourceProviderMode": "Mode d'accès",
|
||||
"aiResourceProviderModeInherit": "Hériter",
|
||||
"aiResourceProviderModeSelect": "Sélectionner",
|
||||
"aiResourceProviderModeSelectSummary": "Sélectionner · {count} modèles",
|
||||
"aiResourceProviderModeInheritHelp": "Utilisez les listes d'autorisation et de blocage de ce fournisseur comme configuré sur le fournisseur.",
|
||||
"aiResourceProviderModeSelectHelp": "Choisissez un sous-ensemble des modèles de liste autorisée de ce fournisseur pour cette ressource.",
|
||||
"aiResourceProviderAllowModels": "Liste autorisée",
|
||||
"aiResourceProviderAllowModelsSelect": "Sélectionnez des modèles",
|
||||
"aiResourceProviderAllowModelsSearch": "Rechercher des modèles...",
|
||||
"aiResourceProviderAllowModelsEmpty": "Aucun modèle trouvé",
|
||||
"aiResourceProviderAllowModelsHelp": "Seuls les modèles de la liste autorisée de ce fournisseur peuvent être sélectionnés.",
|
||||
"aiResourceAliasRequired": "L'alias est requis pour les portails AI",
|
||||
"aiResourceDomainConfiguration": "Configuration du Domaine",
|
||||
"aiResourceDomainConfigurationDescription": "Choisissez le domaine que les clients utiliseront pour atteindre ce portail AI.",
|
||||
"aiUsageAnalyticsTitle": "Analytique d'Utilisation d'IA",
|
||||
"aiUsageAnalyticsDescription": "Analysez le coût, l'utilisation des jetons et l'activité du portail AI avec les fournisseurs, les ressources, les rôles et les utilisateurs",
|
||||
"aiUsageTabOverview": "Vue d'ensemble",
|
||||
"aiUsageTabProviders": "Fournisseurs",
|
||||
"aiUsageTabResources": "Ressources",
|
||||
"aiUsageFilterProvider": "Fournisseur",
|
||||
"aiUsageFilterModel": "Modèle",
|
||||
"aiUsageFilterResource": "Ressource",
|
||||
"aiUsageFilterRole": "Rôle",
|
||||
"aiUsageFilterUser": "Utilisateur",
|
||||
"aiUsageFilterAllProviders": "Tous les Fournisseurs",
|
||||
"aiUsageFilterAllModels": "Tous les Modèles",
|
||||
"aiUsageFilterAllResources": "Toutes les Ressources",
|
||||
"aiUsageFilterAllRoles": "Tous les Rôles",
|
||||
"aiUsageFilterAllUsers": "Tous les Utilisateurs",
|
||||
"aiUsageFilterSearch": "Rechercher...",
|
||||
"aiUsageFilterNotFound": "Aucune option trouvée",
|
||||
"aiUsageResetFilters": "Réinitialiser les filtres",
|
||||
"aiUsageRefresh": "Actualiser",
|
||||
"aiUsageTokenTypePrompt": "Invite",
|
||||
"aiUsageTokenTypeCacheRead": "Lecture du cache",
|
||||
"aiUsageTokenTypeCacheWrite": "Écriture du cache",
|
||||
"aiUsageTokenTypeCompletion": "Achèvement",
|
||||
"aiUsageTokenTypeReasoning": "Raisonnement",
|
||||
"aiUsageRequests": "Requêtes",
|
||||
"aiUsageCost": "Coût",
|
||||
"aiUsageTokens": "Jetons",
|
||||
"aiUsageOther": "Autre",
|
||||
"aiUsageTotalRequests": "Total des Requêtes",
|
||||
"aiUsageTotalTokens": "Total des Jetons",
|
||||
"aiUsageTotalCost": "Coût Total",
|
||||
"aiUsageEstimated": "Estimé",
|
||||
"aiUsageRequestVolume": "Volume de Requêtes",
|
||||
"aiUsageTokenUsage": "Usage de Jeton",
|
||||
"aiUsageModelCost": "Coût de Modèle",
|
||||
"aiUsageModelTokens": "Jetons de Modèle",
|
||||
"aiUsageTopModels": "Meilleurs Modèles",
|
||||
"aiUsageTopProviders": "Meilleurs Fournisseurs",
|
||||
"aiUsageProviderCost": "Coût du Fournisseur",
|
||||
"aiUsageProviderTokenUsage": "Usage de Jeton du Fournisseur",
|
||||
"aiUsageTopResources": "Meilleures Ressources",
|
||||
"aiUsageResourceCost": "Coût des Ressources",
|
||||
"aiUsageResourceTokenUsage": "Usage de Jeton des Ressources",
|
||||
"aiUsageResourceTypePublic": "Ressource Publique",
|
||||
"aiUsageResourceTypeSite": "Ressource Privée",
|
||||
"aiUsageNoResource": "Pas de ressource",
|
||||
"aiUsageRolesTab": "Rôles",
|
||||
"aiUsageUsersTab": "Utilisateurs",
|
||||
"aiUsageTopRoles": "Meilleurs Rôles",
|
||||
"aiUsageRoleCost": "Coût du Rôle",
|
||||
"aiUsageRoleTokenUsage": "Usage de Jeton du Rôle",
|
||||
"aiUsageTopUsers": "Meilleurs Utilisateurs",
|
||||
"aiUsageUserCost": "Coût de l'Utilisateur",
|
||||
"aiUsageUserTokenUsage": "Usage de Jeton de l'Utilisateur",
|
||||
"aiUsageUnknownUser": "Utilisateur Inconnu",
|
||||
"aiUsageVirtualApiKeysTab": "API Clés Virtuelles",
|
||||
"aiUsageFilterVirtualApiKey": "Clé API Virtuelle",
|
||||
"aiUsageFilterAllVirtualApiKeys": "Toutes les Clés API Virtuelles",
|
||||
"aiUsageTopVirtualApiKeys": "Meilleures Clés API Virtuelles",
|
||||
"aiUsageVirtualApiKeyCost": "Coût de la Clé API Virtuelle",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Usage de Jeton de la Clé API Virtuelle",
|
||||
"aiUsageUnknownVirtualApiKey": "Pas de clé API virtuelle",
|
||||
"aiUsageUnnamedVirtualApiKey": "Clé sans nom",
|
||||
"aiUsageLoading": "Chargement...",
|
||||
"aiUsageNoData": "Pas de données",
|
||||
"resourceBudgetSettings": "Configurer le budget de ce fournisseur pour restreindre l'utilisation en fonction des dépenses ou des limites de jetons",
|
||||
"resourceBudgetSettingsDescription": "Configurez comment ce portail AI limite l'utilisation en fonction des dépenses ou des limites de jetons",
|
||||
"sidebarApiKeys": "Clés API",
|
||||
"sidebarOrgs": "Organisations",
|
||||
"sidebarProvisioning": "Mise en place",
|
||||
"sidebarSettings": "Réglages",
|
||||
"sidebarAllUsers": "Tous les utilisateurs",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "Règle d'alerte enregistrée",
|
||||
"alertingRuleSavedCreatedDescription": "Votre nouvelle règle d'alerte a été créée. Vous pouvez continuer à la modifier sur cette page.",
|
||||
"alertingRuleSavedUpdatedDescription": "Vos modifications apportées à cette règle d'alerte ont été enregistrées.",
|
||||
"alertingTestAlertSent": "Alerte de test envoyée",
|
||||
"alertingTestAlertSentDescription": "Une alerte de test a été envoyée aux actions configurées sur cette règle.",
|
||||
"alertingEditRule": "Modifier la règle d'alerte",
|
||||
"alertingCreateRule": "Créer une règle d'alerte",
|
||||
"alertingRuleCredenzaDescription": "Choisissez ce qu'il faut surveiller, quand la déclencher et comment notifier",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "Chaque règle associe ce qu'il faut surveiller (un site, une vérification de l'état de santé ou une ressource), quand l'exécuter (par exemple, hors ligne ou non saine), et comment notifier votre équipe par e-mail, webhooks ou intégrations. Utilisez cette liste pour créer, activer et gérer ces règles.",
|
||||
"alertingHealthChecksBannerTitle": "Surveiller la santé et les ressources",
|
||||
"alertingHealthChecksBannerDescription": "Les vérifications de l'état de santé sont des moniteurs HTTP ou TCP que vous définissez une fois. Vous pouvez ensuite les utiliser comme sources dans les règles d'alerte pour être averti lorsqu'une cible devient saine ou non saine. Les vérifications de l'état de santé sur les ressources apparaissent également ici.",
|
||||
"alertingTestRule": "Règle d'alerte de test",
|
||||
"alertingAddActionHeading": "Ajouter une nouvelle action",
|
||||
"alertingSelectActionType": "Sélectionner une action",
|
||||
"alertingNoActionsTitle": "Aucune action configurée",
|
||||
"alertingNoActionsSaveDescription": "Ajoutez au moins une action pour que cette règle puisse notifier quelqu'un lorsqu'elle est déclenchée.",
|
||||
"alertingNoActionsTestDescription": "Ajoutez au moins une action avant de pouvoir tester cette règle.",
|
||||
"standaloneHcTableTitle": "Vérifications de l'état de santé",
|
||||
"standaloneHcSearchPlaceholder": "Rechercher des vérifications de l'état de santé…",
|
||||
"standaloneHcAddButton": "Créer une vérification de l'état de santé",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "Type :",
|
||||
"createDomainName": "Nom :",
|
||||
"createDomainValue": "Valeur :",
|
||||
"multiSelectFilterCount": "{count} sélectionné",
|
||||
"createDomainCnameRecords": "Enregistrements CNAME",
|
||||
"createDomainARecords": "Enregistrements A",
|
||||
"createDomainRecordNumber": "Enregistrement {number}",
|
||||
@@ -2193,13 +2699,15 @@
|
||||
"subnetPlaceholder": "Sous-réseau",
|
||||
"addressDescription": "L'adresse interne du client. Doit être dans le sous-réseau de l'organisation.",
|
||||
"selectSites": "Sélectionner des sites",
|
||||
"selectResources": "Sélectionner des ressources",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# ressource} other {# ressources}}",
|
||||
"selectLabels": "Sélectionner des étiquettes",
|
||||
"sitesDescription": "Le client aura une connectivité vers les sites sélectionnés",
|
||||
"clientInstallOlm": "Installer Olm",
|
||||
"clientInstallOlmDescription": "Faites fonctionner Olm sur votre système",
|
||||
"clientOlmCredentials": "Identifiants",
|
||||
"clientOlmCredentialsDescription": "C'est ainsi que le client s'authentifie avec le serveur",
|
||||
"olmEndpoint": "Endpoint",
|
||||
"olmEndpoint": "Point de terminaison",
|
||||
"olmId": "ID",
|
||||
"olmSecretKey": "Secrète",
|
||||
"clientCredentialsSave": "Enregistrer les informations d'identification",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "Méthode",
|
||||
"healthSelectScheme": "Sélectionnez la méthode",
|
||||
"healthCheckPortInvalid": "Le port doit être compris entre 1 et 65535",
|
||||
"healthCheckHostnameInvalid": "Le nom d'hôte ne doit pas contenir d'espaces blancs",
|
||||
"healthCheckPath": "Chemin d'accès",
|
||||
"healthHostname": "IP / Hôte",
|
||||
"healthPort": "Port",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "Le temps est exprimé en secondes",
|
||||
"requireDeviceApproval": "Exiger les autorisations de l'appareil",
|
||||
"requireDeviceApprovalDescription": "Les utilisateurs ayant ce rôle ont besoin de nouveaux périphériques approuvés par un administrateur avant de pouvoir se connecter et accéder aux ressources.",
|
||||
"sshSettings": "Paramètres SSH",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "Portail AI",
|
||||
"sshAccess": "Accès SSH",
|
||||
"rdpSettings": "Paramètres RDP",
|
||||
"vncSettings": "Paramètres VNC",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "Serveur SSH",
|
||||
"rdpServer": "Serveur RDP",
|
||||
"vncServer": "Serveur VNC",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "Portail AI",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Méthode HTTP",
|
||||
"editInternalResourceDialogEnableSsl": "Activer TLS",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "Autoriser le ping ICMP",
|
||||
"privateResourceNetworkAccess": "Accès au réseau",
|
||||
"privateResourceNetworkAccessDescription": "Contrôler l'accès aux ports TCP/UDP et si le ping ICMP est autorisé pour cette ressource.",
|
||||
"hostSettings": "Paramètres de l'hôte",
|
||||
"cidrSettings": "Paramètres CIDR",
|
||||
"hostSettings": "Hôte",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Propriétés de la ressource",
|
||||
"createInternalResourceDialogName": "Nom",
|
||||
"createInternalResourceDialogSite": "Site",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "Portail AI",
|
||||
"scheme": "Méthode HTTP",
|
||||
"createInternalResourceDialogScheme": "Méthode HTTP",
|
||||
"createInternalResourceDialogEnableSsl": "Activer TLS",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Ajouter une plage CIDR (par exemple 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Échec du chargement des sous-réseaux",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Étiquettes de préférences",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Les sites avec ces étiquettes devront se connecter via ce nœud de sortie distant.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Les sites dotés de ces étiquettes préféreront se connecter via ce nœud de sortie distant.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Sélectionner des étiquettes...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Chercher des étiquettes...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Échec du chargement des étiquettes",
|
||||
@@ -2539,7 +3051,7 @@
|
||||
"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",
|
||||
"secretLabel": "Clé secrète",
|
||||
"secretDescription": "La clé secrète du noeud existant",
|
||||
"submitButton": "Noeud d'Adopt"
|
||||
},
|
||||
@@ -2547,7 +3059,7 @@
|
||||
"title": "Informations d'identification générées",
|
||||
"description": "Utilisez ces identifiants générés pour configurer le noeud",
|
||||
"nodeIdTitle": "Nœud ID",
|
||||
"secretTitle": "Secret",
|
||||
"secretTitle": "Clé secrète",
|
||||
"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"
|
||||
@@ -2676,7 +3188,7 @@
|
||||
"roleMappingRemoveRule": "Supprimer",
|
||||
"idpGoogleConfiguration": "Configuration Google",
|
||||
"idpGoogleConfigurationDescription": "Configurer les identifiants Google OAuth2",
|
||||
"idpGoogleClientIdDescription": "Google OAuth2 Client ID",
|
||||
"idpGoogleClientIdDescription": "Votre identifiant client Google OAuth2",
|
||||
"idpGoogleClientSecretDescription": "Secret client OAuth2 de Google",
|
||||
"idpAzureConfiguration": "Configuration de l'entra ID Azure",
|
||||
"idpAzureConfigurationDescription": "Configurer les identifiants OAuth2 Azure Entra ID",
|
||||
@@ -2695,7 +3207,7 @@
|
||||
"idpAzureClientIdDescription2": "ID client d'enregistrement de l'application Azure",
|
||||
"idpAzureClientSecretDescription2": "Secret du client d'enregistrement de l'application Azure",
|
||||
"idpGoogleDescription": "Fournisseur Google OAuth2/OIDC",
|
||||
"idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider",
|
||||
"idpAzureDescription": "Microsoft Azure OAuth2/Fournisseur OIDC",
|
||||
"subnet": "Sous-réseau",
|
||||
"utilitySubnet": "Routeur utilitaire",
|
||||
"subnetDescription": "Le sous-réseau de la configuration réseau de cette organisation.",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "Priorité",
|
||||
"priorityDescription": "Les routes de haute priorité sont évaluées en premier. La priorité = 100 signifie l'ordre automatique (décision du système). Utilisez un autre nombre pour imposer la priorité manuelle.",
|
||||
"instanceName": "Nom de l'instance",
|
||||
"clearInstanceName": "Réinitialiser l'association de serveur",
|
||||
"pathMatchModalTitle": "Configurer le chemin correspondant",
|
||||
"pathMatchModalDescription": "Définissez comment les requêtes entrantes doivent être trouvées en fonction de leur chemin.",
|
||||
"pathMatchType": "Type de correspondance",
|
||||
@@ -3046,11 +3559,12 @@
|
||||
"allowedByRule": "Autorisé par la règle",
|
||||
"allowedNoAuth": "Aucune authentification autorisée",
|
||||
"validAccessToken": "Jeton d'accès valide",
|
||||
"validHeaderAuth": "Valid header auth",
|
||||
"validHeaderAuth": "Authentification d'en-tête valide",
|
||||
"validPincode": "Valid Pincode",
|
||||
"validPassword": "Mot de passe valide",
|
||||
"validEmail": "Valid email",
|
||||
"validSSO": "Valid SSO",
|
||||
"validEmail": "Email valide",
|
||||
"validSSO": "SSO valide",
|
||||
"validVirtualAPIKey": "Clé API Virtuelle Valide",
|
||||
"view": "Afficher",
|
||||
"configManaged": "Configuration gérée",
|
||||
"connectedClient": "Client connecté",
|
||||
@@ -3058,7 +3572,7 @@
|
||||
"droppedByRule": "Abandonné par la règle",
|
||||
"noSessions": "Aucune session",
|
||||
"temporaryRequestToken": "Jeton de requête temporaire",
|
||||
"noMoreAuthMethods": "No Valid Auth",
|
||||
"noMoreAuthMethods": "Pas d'authentification valide",
|
||||
"ip": "IP",
|
||||
"reason": "Raison",
|
||||
"requestLogs": "Journal des Requêtes HTTP",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "Journaux des actions",
|
||||
"logRetention": "Journaliser la rétention",
|
||||
"logRetentionDescription": "Gérer la durée de conservation des différents types de logs pour cette organisation ou les désactiver",
|
||||
"logRetentionDisabledWarningTitle": "Conservation des journaux désactivée",
|
||||
"logRetentionDisabledWarningDescription": "{logType} ne sont pas conservés pour cette organisation, donc aucune nouvelle activité n’apparaîtra ici. Activez la conservation dans les paramètres de sécurité pour commencer à collecter ces journaux.",
|
||||
"logRetentionDisabledWarningButton": "Aller aux paramètres de sécurité",
|
||||
"requestLogsDescription": "Voir les journaux détaillés des requêtes pour les ressources de cette organisation",
|
||||
"aiSessionLogs": "Journaux de Session du Portail AI",
|
||||
"aiSessionLogsDescription": "Voir les transcriptions de l'invite et de la réponse pour les requêtes de portail AI dans cette organisation",
|
||||
"sidebarLogsAi": "Journaux de Session",
|
||||
"commandLogsAi": "Journaux de Session",
|
||||
"sidebarLogsAiUsage": "Analytique d'Utilisation",
|
||||
"commandLogsAiUsage": "Analytique d'Utilisation",
|
||||
"provider": "Fournisseur",
|
||||
"capability": "Capacité",
|
||||
"model": "Modèle",
|
||||
"virtualApiKey": "Clé API Virtuelle",
|
||||
"noVirtualApiKey": "Pas de clé API virtuelle",
|
||||
"stream": "Flux",
|
||||
"streaming": "En Flux",
|
||||
"nonStreaming": "Non-Flux",
|
||||
"statusCode": "Code d'État",
|
||||
"aiSessionId": "ID de Session",
|
||||
"aiSessionRequest": "Requête",
|
||||
"aiSessionResponse": "Réponse",
|
||||
"aiSessionNoData": "Aucune donnée capturée",
|
||||
"aiSessionCouldNotParse": "(brut, n'a pas pu analyser la transcription)",
|
||||
"aiSessionLogTruncated": "Cette session a été tronquée avant le stockage et peut être incomplète.",
|
||||
"aiSessionViewRaw": "Afficher le JSON Brut",
|
||||
"aiSessionViewChat": "Afficher la Discussion",
|
||||
"cost": "Coût",
|
||||
"estimated": "Estimé",
|
||||
"tokenUsage": "Usage de Jeton",
|
||||
"promptTokens": "Jetons Prompt",
|
||||
"cacheReadTokens": "Jetons Lecture Cache",
|
||||
"cacheWriteTokens": "Jetons Écriture Cache",
|
||||
"completionTokens": "Jetons Completion",
|
||||
"reasoningTokens": "Jetons Raisonnement",
|
||||
"totalTokens": "Total des Jetons",
|
||||
"requestAnalyticsDescription": "Voir les analyses détaillées des demandes pour les ressources de cette organisation",
|
||||
"logRetentionRequestLabel": "Rétention des Journaux de Requêtes HTTP",
|
||||
"logRetentionRequestDescription": "Durée de conservation des journaux de requêtes",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "Durée de conservation du journal des actions",
|
||||
"logRetentionConnectionLabel": "Rétention du journal de connexion",
|
||||
"logRetentionConnectionDescription": "Durée de conservation des logs de connexion",
|
||||
"logRetentionAISessionsLabel": "Rétention des Journaux des Sessions du Portail AI",
|
||||
"logRetentionAISessionsDescription": "Combien de temps conserver les journaux des sessions d'invite/réponse du portail AI",
|
||||
"logRetentionDisabled": "Désactivé",
|
||||
"logRetention3Days": "3 jours",
|
||||
"logRetention7Days": "7 jours",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "Adresse source",
|
||||
"destinationAddress": "Adresse de destination",
|
||||
"duration": "Durée",
|
||||
"licenseRequiredToUse": "Une <enterpriseLicenseLink>licence Enterprise Edition</enterpriseLicenseLink> ou <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> est requise pour utiliser cette fonctionnalité. <bookADemoLink>Réservez une démo gratuite ou un essai POC pour en savoir plus.</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "La version <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> est requise pour utiliser cette fonctionnalité. Cette fonctionnalité est également disponible dans <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Réservez une démo gratuite ou un essai POC pour en savoir plus.</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Licence Enterprise Edition</enterpriseEditionLink> requise. <bookADemoLink>Réserver une démo ou un essai</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Licence Enterprise Edition</enterpriseEditionLink> requise. <bookADemoLink>Réserver une démo ou un essai</bookADemoLink>",
|
||||
"certResolver": "Résolveur de certificat",
|
||||
"certResolverDescription": "Sélectionnez le solveur de certificat à utiliser pour cette ressource.",
|
||||
"selectCertResolver": "Sélectionnez le résolveur de certificat",
|
||||
@@ -3225,7 +3776,7 @@
|
||||
"regenerateCredentialsWarning": "La régénération des identifiants invalidera les identifiants précédents et provoquera une déconnexion. Assurez-vous de mettre à jour toutes les configurations qui utilisent ces identifiants.",
|
||||
"confirm": "Confirmer",
|
||||
"regenerateCredentialsConfirmation": "Voulez-vous vraiment régénérer les identifiants ?",
|
||||
"endpoint": "Endpoint",
|
||||
"endpoint": "Point de terminaison",
|
||||
"Id": "Id",
|
||||
"SecretKey": "Clé privée",
|
||||
"niceId": "Joli ID",
|
||||
@@ -3242,6 +3793,7 @@
|
||||
"noData": "Aucune donnée",
|
||||
"machineClients": "Clients Machines",
|
||||
"install": "Installer",
|
||||
"downloadInstaller": "Télécharger l'installateur",
|
||||
"run": "Exécuter",
|
||||
"envFile": "Fichier Environnement",
|
||||
"serviceFile": "Fichier de Service",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "La sélection de plusieurs sites permet un routage résilient et un basculement pour une haute disponibilité.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "En savoir plus",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Restreindre l'accès à des ports TCP/UDP spécifiques ou autoriser/bloquer tous les ports.",
|
||||
"createInternalResourceDialogHttpConfiguration": "Configuration HTTP",
|
||||
"createInternalResourceDialogHttpConfiguration": "Configuration du Domaine",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "Choisissez le domaine que les clients utiliseront pour atteindre cette ressource via HTTP ou HTTPS.",
|
||||
"editInternalResourceDialogHttpConfiguration": "Configuration HTTP",
|
||||
"editInternalResourceDialogHttpConfiguration": "Configuration du Domaine",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "Choisissez le domaine que les clients utiliseront pour atteindre cette ressource via HTTP ou HTTPS.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3366,7 +3918,7 @@
|
||||
"deviceMessageArchive": "Le périphérique sera archivé et retiré de la liste des périphériques actifs.",
|
||||
"deviceArchiveConfirm": "Dispositif d'archivage",
|
||||
"archiveDevice": "Dispositif d'archivage",
|
||||
"archive": "Archive",
|
||||
"archive": "Archiver",
|
||||
"deviceUnarchived": "Appareil désarchivé",
|
||||
"deviceUnarchivedDescription": "L'appareil a été désarchivé avec succès.",
|
||||
"errorUnarchivingDevice": "Erreur lors de la désarchivage du périphérique",
|
||||
@@ -3419,7 +3971,7 @@
|
||||
"kernelVersion": "Version du noyau",
|
||||
"deviceModel": "Modèle de l'appareil",
|
||||
"serialNumber": "Numéro de série",
|
||||
"hostname": "Hostname",
|
||||
"hostname": "Nom d'hôte",
|
||||
"firstSeen": "Première vue",
|
||||
"lastSeen": "Dernière vue",
|
||||
"biometricsEnabled": "biométrique activée",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "Déconnecté",
|
||||
"approvalsEmptyStateTitle": "Approbations de l'appareil non activées",
|
||||
"approvalsEmptyStateDescription": "Activer les autorisations de l'appareil pour les rôles qui nécessitent l'approbation de l'administrateur avant que les utilisateurs puissent connecter de nouveaux appareils.",
|
||||
"approvalsEmptyStateHowToTitle": "Comment activer",
|
||||
"approvalsEmptyStateStep1Title": "Aller aux Rôles",
|
||||
"approvalsEmptyStateStep1Description": "Accédez aux paramètres de rôles de votre organisation pour configurer les autorisations de l'appareil.",
|
||||
"approvalsEmptyStateStep2Title": "Activer les autorisations de l'appareil",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "Événements de connexion du site et du tunnel, y compris les connexions et les déconnexions.",
|
||||
"httpDestRequestLogsTitle": "Journal des Requêtes HTTP",
|
||||
"httpDestRequestLogsDescription": "Journaux des requêtes HTTP pour les ressources proxiées, y compris la méthode, le chemin et le code de réponse.",
|
||||
"httpDestAISessionLogsTitle": "Journaux de session AI",
|
||||
"httpDestAISessionLogsDescription": "Sessions de requête et de réponse de la passerelle AI, y compris les invites, les réponses du modèle et l'utilisation des jetons.",
|
||||
"httpDestSaveChanges": "Enregistrer les modifications",
|
||||
"httpDestCreateDestination": "Créer une destination",
|
||||
"httpDestUpdatedSuccess": "Destination mise à jour avec succès",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "Voir en tant qu'admin",
|
||||
"resourceLauncherResourceDetailsDescription": "Informations de connexion et statut pour cette ressource.",
|
||||
"resourceLauncherResourceDetails": "Détails de la ressource",
|
||||
"resourceLauncherSitesDescription": "La ressource est accessible via les sites suivants.",
|
||||
"resourceLauncherViewSiteAsAdmin": "Voir le site en tant qu'admin",
|
||||
"resourceLauncherFilterBySite": "Filtrer par site",
|
||||
"resourceLauncherSshCommand": "Commande SSH",
|
||||
"resourceLauncherSshCommandDescription": "Utilisez le CLI Pangolin pour ouvrir une session SSH vers cette ressource.",
|
||||
"resourceLauncherAuthMethodsDescription": "Méthodes d'authentification activées pour cette ressource.",
|
||||
"resourceLauncherPrivateClientRequired": "Connectez-vous avec un client sur votre appareil pour accéder à cette ressource en privé.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "Connexion client requise",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Non étiqueté",
|
||||
"resourceLauncherAiGateway": "Portail AI",
|
||||
"resourceLauncherNoSite": "Aucun nœud",
|
||||
"resourceLauncherAvailableModels": "Modèles Disponibles",
|
||||
"resourceLauncherAvailableModelsDescription": "Modèles que vous pouvez utiliser avec ce portail AI.",
|
||||
"resourceLauncherAvailableModelsEmpty": "Aucun modèle n'est disponible pour cette ressource.",
|
||||
"resourceLauncherAvailableModelsError": "Impossible de charger les modèles disponibles.",
|
||||
"resourceLauncherApiKeys": "Clés API",
|
||||
"resourceLauncherApiKeysDescription": "Utilisez votre clé d'identité ou une clé attribuée pour vous authentifier avec cette ressource.",
|
||||
"resourceLauncherApiKeysIdentity": "Clé d'identité",
|
||||
"resourceLauncherApiKeysManual": "Clés Attribuées",
|
||||
"resourceLauncherApiKeysEmpty": "Pas de clés API disponibles pour cette ressource.",
|
||||
"resourceLauncherApiKeysError": "Impossible de charger les clés API.",
|
||||
"resourceLauncherNoResourcesInGroup": "Aucune ressource dans ce groupe",
|
||||
"resourceLauncherEmptyStateTitle": "Aucune ressource disponible",
|
||||
"resourceLauncherEmptyStateDescription": "Vous n'avez pas encore accès à des ressources. Contactez votre administrateur pour demander l'accès.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "Impossible de supprimer la vue de lancement. Veuillez réessayer.",
|
||||
"memberPortalPrevious": "Précédent",
|
||||
"memberPortalNext": "Suivant",
|
||||
"httpSettings": "Paramètres HTTP",
|
||||
"tcpSettings": "Paramètres TCP",
|
||||
"udpSettings": "Paramètres UDP",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Établissement d'une connexion sécurisée…",
|
||||
"sshConnecting": "Connexion…",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Mode clavier Unicode",
|
||||
"sessionToolbarShow": "Afficher la barre d'outils",
|
||||
"sessionToolbarHide": "Masquer la barre d'outils",
|
||||
"actionUpdateSiteApprovals": "Mettre à jour les approbations de site"
|
||||
"actionUpdateSiteApprovals": "Mettre à jour les approbations de site",
|
||||
"check": "Vérifier"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "올바른 사용자로 로그인했는지 확인하십시오.",
|
||||
"inviteErrorNoUser": "죄송하지만, 접근하려는 초대가 존재하지 않는 사용자에 대한 것인 것 같습니다.",
|
||||
"inviteCreateUser": "먼저 계정을 생성해 주세요.",
|
||||
"inviteErrorOidcNotAllowed": "초대는 내부 계정만 수락할 수 있습니다. 이 이메일의 비밀번호로 로그아웃하고 다시 로그인하십시오.",
|
||||
"inviteLoginInternalOnly": "초대에는 비밀번호가 있는 내부 계정이 필요합니다. 계정을 생성하거나 비밀번호로 로그인하십시오.",
|
||||
"goHome": "홈으로 가기",
|
||||
"inviteLogInOtherUser": "다른 사용자로 로그인",
|
||||
"createAnAccount": "계정 만들기",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "이 사이트의 대상",
|
||||
"siteSetting": "{siteName} 설정",
|
||||
"siteNewtTunnel": "뉴트 사이트 (추천)",
|
||||
"pangolinSite": "판골린 사이트",
|
||||
"siteNewtTunnelDescription": "네트워크의 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.",
|
||||
"siteWg": "기본 WireGuard",
|
||||
"siteWgDescription": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다.",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "국가를 찾을 수 없습니다.",
|
||||
"siteSelectionDescription": "이 사이트는 대상에 대한 연결을 제공합니다.",
|
||||
"resourceType": "리소스 유형",
|
||||
"resourceTypeDescription": "이것은 리소스 프로토콜 및 브라우저에서 렌더링되는 방식에 영향을 줍니다. 나중에 변경할 수 없습니다.",
|
||||
"resourceTypeDescription": "이 리소스 프로토콜을 제어하며 나중에 변경할 수 없습니다.",
|
||||
"resourceTypeSearch": "리소스 유형 검색...",
|
||||
"resourceTypeNotFound": "리소스 유형이 발견되지 않았습니다",
|
||||
"resourceTypeHttpDescription": "도메인 이름을 사용하여 HTTPS를 통한 웹 트래픽 프록시",
|
||||
"resourceTypeInferenceDescription": "연결된 공급자를 통해 AI 요청을 라우팅",
|
||||
"resourceTypeSshDescription": "브라우저에서 SSH 서버에 액세스",
|
||||
"resourceTypeRdpDescription": "브라우저에서 원격 데스크톱에 액세스",
|
||||
"resourceTypeVncDescription": "브라우저에서 VNC 데스크톱에 액세스",
|
||||
"resourceTypeTcpDescription": "포트 번호를 사용하여 원시 TCP 트래픽 프록시",
|
||||
"resourceTypeUdpDescription": "포트 번호를 사용하여 원시 UDP 트래픽 프록시",
|
||||
"privateResourceTypeDescription": "클라이언트가 리소스에 도달하는 방법을 제어합니다. 나중에 변경할 수 없습니다.",
|
||||
"privateResourceTypeHostDescription": "연결된 클라이언트에게 사이트 네트워크에서 단일 호스트를 노출",
|
||||
"privateResourceTypeCidrDescription": "연결된 클라이언트에게 사이트 네트워크에서 CIDR 범위를 노출",
|
||||
"privateResourceTypeHttpDescription": "도메인을 통해 HTTP 또는 HTTPS 서비스에 액세스",
|
||||
"privateResourceTypeSshDescription": "연결된 클라이언트에서 SSH 서버에 액세스",
|
||||
"resourceDomainDescription": "리소스는 숙주 네임에서 제공됩니다.",
|
||||
"resourceHTTPSSettings": "HTTPS 설정",
|
||||
"resourceHTTPSSettingsDescription": "리소스가 HTTPS로 접근할 수 있는 방식을 구성합니다.",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "API 키 삭제",
|
||||
"apiKeysManage": "API 키 관리",
|
||||
"apiKeysDescription": "API 키는 통합 API와 인증하는 데 사용됩니다.",
|
||||
"orgsManage": "조직 관리",
|
||||
"orgsDescription": "이 인스턴스에서 모든 조직을 보고 관리합니다",
|
||||
"provisioningKeysTitle": "프로비저닝 키",
|
||||
"provisioningKeysManage": "프로비저닝 키 관리",
|
||||
"provisioningKeysDescription": "프로비저닝 키는 조직의 자동 사이트 프로비저닝 인증에 사용됩니다.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "이름 (선택 사항)",
|
||||
"accessControls": "접근 제어",
|
||||
"userDescription2": "이 사용자의 설정 관리",
|
||||
"userGeneralSettingsDescription": "조직 내에서 이 사용자의 역할 및 설정을 관리하세요",
|
||||
"accessRoleErrorAdd": "사용자를 역할에 추가하는 데 실패했습니다.",
|
||||
"accessRoleErrorAddDescription": "사용자를 역할에 추가하는 동안 오류가 발생했습니다.",
|
||||
"userSaved": "사용자 저장됨",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "설정 저장",
|
||||
"policyAuthStackTitle": "인증",
|
||||
"policyAuthStackDescription": "이 리소스에 접근하려면 어떤 인증 방법이 필요한지 제어합니다",
|
||||
"policyAuthInferenceStackDescription": "이 AI 게이트웨이에 인증할 수 있는 사용자 및 역할을 선택하십시오",
|
||||
"policyAuthOrLogicTitle": "다수의 인증 방법 활성화",
|
||||
"policyAuthOrLogicBanner": "방문자는 아래 활성화된 방법 중 하나만을 선택하여 인증할 수 있습니다. 모든 방법을 완료할 필요는 없습니다.",
|
||||
"policyAuthMethodActive": "활성화",
|
||||
"policyAuthMethodOff": "비활성화",
|
||||
"policyAuthSsoTitle": "플랫폼 SSO",
|
||||
"policyAuthSsoDescription": "사용자의 아이덴티티 공급자를 통해 로그인 필요",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "로그인 URL",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "모든 사용자는 이미 ID API 키를 보유하고 있으므로 비사용자 클라이언트 또는 공유 액세스를 위한 가상 API 키만 생성하면 됩니다. 사용자는 이 리소스의 URL에서 ID 공급자로 로그인하면 키를 검색할 수 있습니다.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "모든 사용자는 이미 ID API 키를 보유하고 있으므로 비사용자 클라이언트 또는 공유 액세스를 위한 가상 API 키만 생성하면 됩니다. 사용자는 여기서 ID 공급자로 로그인 후 키를 검색할 수 있습니다.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} 사용자, {roles} 역할",
|
||||
"policyAuthSsoDefaultIdp": "기본 공급자",
|
||||
"policyAuthAddDefaultIdentityProvider": "기본 아이덴티티 공급자 추가",
|
||||
@@ -892,7 +916,7 @@
|
||||
"policyAccessRulesFallthroughOff": "규칙이 비활성화되면, 모든 트래픽은 인증으로 넘어갑니다.",
|
||||
"policyAccessRulesFallthroughOn": "매칭되는 규칙이 없으면, 트래픽은 인증으로 넘어갑니다.",
|
||||
"rulesPlaceholderCidr": "10.0.0.0/8",
|
||||
"rulesPlaceholderPath": "/admin/*",
|
||||
"rulesPlaceholderPath": "/관리자/*",
|
||||
"rulesPlaceholderGeo": "RU, KP",
|
||||
"rulesSave": "규칙 저장",
|
||||
"resourceErrorCreate": "리소스 생성 오류",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "새 역할이 필요합니다.",
|
||||
"accessRoleErrorRemove": "역할 제거에 실패했습니다.",
|
||||
"accessRoleErrorRemoveDescription": "역할을 제거하는 동안 오류가 발생했습니다.",
|
||||
"accessRoleInferenceBudget": "AI 예산",
|
||||
"accessRoleInferenceBudgetDescription": "이 역할의 구성원이 AI 사용을 지출 또는 토큰 제한에 따라 제한하는 방법을 구성하십시오",
|
||||
"accessRoleName": "역할 이름",
|
||||
"accessRoleQuestionRemove": "`{name}` 역할을 삭제하려고 합니다. 이 작업은 되돌릴 수 없습니다.",
|
||||
"accessRoleRemove": "역할 제거",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "사이트를 찾을 수 없습니다.",
|
||||
"pangolinServerAdmin": "서버 관리자 - 판골린",
|
||||
"licenseTierProfessional": "전문 라이센스",
|
||||
"licenseTierEnterprise": "기업 라이선스",
|
||||
"licenseTierPersonal": "개인 라이선스",
|
||||
"licenseTierEnterprise": "기업",
|
||||
"licenseTierPersonal": "개인",
|
||||
"licenseTierTier1": "스타터",
|
||||
"licenseTierTier2": "스케일",
|
||||
"licensed": "라이센스",
|
||||
"yes": "예",
|
||||
"no": "아니요",
|
||||
@@ -1154,6 +1182,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "JMESPath에 대해 더 알아보기",
|
||||
"idpJmespathLabel": "식별자 경로",
|
||||
"idpJmespathLabelDescription": "ID 토큰에서 사용자 식별자에 대한 경로",
|
||||
"idpIdentifierChangeTitle": "식별자 경로 변경 경고",
|
||||
"idpIdentifierChangeDescription": "식별자 경로를 변경하려고 합니다. 이는 기존 사용자의 매핑 방법에 영향을 미칩니다. 이전에 이 ID 공급자를 통해 로그인한 사용자는 더 이상 동일한 사용자로 인식되지 않을 수 있습니다. ",
|
||||
"idpIdentifierChangeConfirmMessage": "확인합니다",
|
||||
"idpIdentifierChangeWarningText": "이는 기존 사용자의 매핑 방법에 영향을 미칩니다",
|
||||
"idpJmespathEmailPathOptional": "이메일 경로 (선택 사항)",
|
||||
"idpJmespathEmailPathOptionalDescription": "ID 토큰에서 사용자의 이메일 경로",
|
||||
"idpJmespathNamePathOptional": "이름 경로 (선택 사항)",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "로그아웃 중 오류 발생",
|
||||
"signingAs": "로그인한 사용자",
|
||||
"serverAdmin": "서버 관리자",
|
||||
"promoteServerAdmin": "서버 관리자 승격",
|
||||
"promoteServerAdminTitle": "서버 관리자 승격",
|
||||
"promoteServerAdminQuestion": "{selectedUser}을 서버 관리자로 승격하시겠습니까?",
|
||||
"promoteServerAdminMessage": "서버 관리자는 최고 권한을 갖고 서버를 관리할 수 있습니다.",
|
||||
"promoteServerAdminWarning": "언제든지 사용자를 강등하여 이 작업을 되돌릴 수 있습니다.",
|
||||
"promoteServerAdminConfirm": "서버 관리자 승격",
|
||||
"promoteServerAdminSuccess": "사용자가 승격되었습니다",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser}님이 이제 서버 관리자가 되었습니다.",
|
||||
"promoteServerAdminError": "사용자 승격에 실패했습니다",
|
||||
"demoteServerAdmin": "서버 관리자에서 강등",
|
||||
"demoteServerAdminTitle": "서버 관리자에서 강등",
|
||||
"demoteServerAdminQuestion": "{selectedUser}을 서버 관리자에서 강등하시겠습니까?",
|
||||
"demoteServerAdminMessage": "{selectedUser}은 모든 서버 관리자 권한을 잃게 됩니다",
|
||||
"demoteServerAdminWarning": "언제든지 사용자를 승격하여 이 작업을 되돌릴 수 있습니다.",
|
||||
"demoteServerAdminConfirm": "서버 관리자에서 강등",
|
||||
"demoteServerAdminSuccess": "사용자가 강등되었습니다",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser}님이 더 이상 서버 관리자가 아닙니다.",
|
||||
"demoteServerAdminError": "사용자 강등에 실패했습니다",
|
||||
"managedSelfhosted": "관리 자체 호스팅",
|
||||
"otpEnable": "이중 인증 활성화",
|
||||
"otpDisable": "이중 인증 비활성화",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "사이트 삭제",
|
||||
"actionGetSite": "사이트 가져오기",
|
||||
"actionListSites": "사이트 목록",
|
||||
"actionCreateAiProvider": "AI 공급자 생성",
|
||||
"actionDeleteAiProvider": "AI 공급자 삭제",
|
||||
"actionGetAiProvider": "AI 공급자 가져오기",
|
||||
"actionListAiProviders": "AI 공급자 목록",
|
||||
"actionUpdateAiProvider": "AI 공급자 업데이트",
|
||||
"actionCreateAiModel": "AI 모델 생성",
|
||||
"actionDeleteAiModel": "AI 모델 삭제",
|
||||
"actionGetAiModel": "AI 모델 가져오기",
|
||||
"actionListAiModels": "AI 모델 목록",
|
||||
"actionUpdateAiModel": "AI 모델 업데이트",
|
||||
"actionCreateAiBudget": "AI 예산 생성",
|
||||
"actionDeleteAiBudget": "AI 예산 삭제",
|
||||
"actionGetAiBudget": "AI 예산 가져오기",
|
||||
"actionListAiBudgets": "AI 예산 목록",
|
||||
"actionUpdateAiBudget": "AI 예산 업데이트",
|
||||
"actionListResourceAiModels": "리소스 AI 모델 목록",
|
||||
"actionSetResourceAiModels": "리소스 AI 모델 설정",
|
||||
"actionCreateVirtualApiKey": "가상 API 키 생성",
|
||||
"actionDeleteVirtualApiKey": "가상 API 키 삭제",
|
||||
"actionGetVirtualApiKey": "가상 API 키 가져오기",
|
||||
"actionListVirtualApiKeys": "가상 API 키 목록",
|
||||
"actionUpdateVirtualApiKey": "가상 API 키 업데이트",
|
||||
"actionApplyBlueprint": "청사진 적용",
|
||||
"actionListBlueprints": "청사진 목록",
|
||||
"actionGetBlueprint": "청사진 가져오기",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "리소스 핀코드 설정",
|
||||
"actionSetResourceEmailWhitelist": "리소스 이메일 화이트리스트 설정",
|
||||
"actionGetResourceEmailWhitelist": "리소스 이메일 화이트리스트 가져오기",
|
||||
"actionListResourcePolicies": "리소스 정책 목록",
|
||||
"actionCreateResourcePolicy": "리소스 정책 생성",
|
||||
"actionGetResourcePolicy": "리소스 정책 가져오기",
|
||||
"actionUpdateResourcePolicy": "리소스 정책 업데이트",
|
||||
"actionDeleteResourcePolicy": "리소스 정책 삭제",
|
||||
"actionSetResourcePolicyUsers": "리소스 정책 사용자 설정",
|
||||
"actionSetResourcePolicyRoles": "리소스 정책 역할 설정",
|
||||
"actionSetResourcePolicyPassword": "리소스 정책 비밀번호 설정",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "검색…",
|
||||
"searchPlaceholder": "검색...",
|
||||
"emptySearchOptions": "옵션이 없습니다",
|
||||
"ipFilterSearchPlaceholder": "IP 주소를 입력하세요…",
|
||||
"ipFilterEmptyMessage": "필터링할 IP 주소를 입력하세요",
|
||||
"create": "생성",
|
||||
"orgs": "조직",
|
||||
"loginError": "예기치 않은 오류가 발생했습니다. 다시 시도해주세요.",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "머신 클라이언트 생성",
|
||||
"commandPaletteCreateAlertRule": "경고 규칙 생성",
|
||||
"commandPaletteCreateIdentityProvider": "신원 공급자 생성",
|
||||
"commandPaletteCreateAiProvider": "AI 공급자 생성",
|
||||
"commandPaletteCreateVirtualApiKey": "가상 API 키 생성",
|
||||
"commandPaletteToggleTheme": "테마 전환",
|
||||
"commandPaletteChooseOrganization": "조직 선택",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "초대",
|
||||
"sidebarRoles": "역할",
|
||||
"sidebarShareableLinks": "공유 가능한 링크",
|
||||
"sidebarAiGateway": "AI 게이트웨이",
|
||||
"sidebarAiProviders": "공급자",
|
||||
"commandAiProviders": "AI 공급자",
|
||||
"sidebarVirtualApiKeys": "가상 API 키",
|
||||
"sidebarMyApiKeys": "귀하의 API 키",
|
||||
"sidebarAccount": "실행기",
|
||||
"commandVirtualApiKeys": "가상 API 키",
|
||||
"virtualApiKeysTitle": "가상 API 키 관리",
|
||||
"virtualApiKeysDescription": "공용 AI 게이트웨이에 대한 AI 게이트웨이 액세스를 위한 수동 API 키 생성 및 관리",
|
||||
"virtualApiKeysTabIdentity": "신원 키",
|
||||
"virtualApiKeysTabVirtual": "가상 키",
|
||||
"virtualApiKeysIdentitySplashTitle": "모든 사용자에 대한 신원 키",
|
||||
"virtualApiKeysIdentitySplashDescription": "이 조직을 위한 Pangolin 신원 키가 모든 사용자에게 이미 있습니다. 계정에 고유하며 액세스할 수 있는 AI 게이트웨이에 인증합니다.",
|
||||
"virtualApiKeysIdentitySplashExample": "예시",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "사용자가 키 받는 방법",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "브라우저에서 공용 AI 게이트웨이 URL을 방문하고 계정으로 로그인합니다.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "<url></url>을 방문하여 로그인한 상태에서 이동하십시오.",
|
||||
"virtualApiKeysIdentitySplashManual": "추가 가상 API 키를 가상 키 탭에서 생성할 수 있습니다. 이러한 키는 특정 공용 AI 게이트웨이에 범위를 지정할 수 있으며, 선택적으로 사용자와 연결할 수 있습니다. 키 생성 즉시 선택된 공용 AI 게이트웨이에 대한 액세스 권한이 부여됩니다.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "키 수동 생성",
|
||||
"virtualApiKeysEmailIdentity": "이메일 신원 키",
|
||||
"virtualApiKeysEmailIdentityDescription": "선택한 각 사용자 또는 역할에게 Pangolin 신원 키 보내기.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "모든 사용자에게 보내기",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "계정 이메일이 있는 모든 조직 구성원에게 이메일 발송.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "사용자",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "역할",
|
||||
"virtualApiKeysEmailIdentitySubmit": "이메일 보내기",
|
||||
"virtualApiKeysEmailIdentitySuccess": "신원 키 전송",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "{sent}개의 이메일이 전송되었습니다.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "{skipped}명의 사용자가 이메일 주소가 없어 건너뛰었습니다.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "최소한 하나의 사용자 또는 역할을 선택하거나 모든 사용자에게 보냅니다.",
|
||||
"virtualApiKeysEmailIdentityError": "신원 키 전송 오류",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "신원 키 이메일 전송 실패",
|
||||
"virtualApiKeysBannerTitle": "모든 사용자에 대한 신원 키",
|
||||
"virtualApiKeysBannerDescription": "모든 사용자는 {keysUrl}에서 신원 키를 이미 사용할 수 있습니다. 공용 AI 게이트웨이에 대한 직접 액세스를 부여하는 키를 수동으로 여기에서 생성할 수도 있습니다.",
|
||||
"virtualApiKeysBannerButtonText": "신원 키 보기",
|
||||
"virtualApiKeys": "가상 API 키",
|
||||
"virtualApiKeysSearch": "키 검색...",
|
||||
"virtualApiKeysCreate": "가상 API 키 생성",
|
||||
"virtualApiKeysCreateDescription": "이 조직의 공용 AI 게이트웨이를 호출할 수 있는 수동 키 발급",
|
||||
"virtualApiKeysCreateButton": "키 생성",
|
||||
"virtualApiKeysEmpty": "가상 API 키가 아직 없습니다",
|
||||
"virtualApiKeysName": "이름",
|
||||
"virtualApiKeysDescriptionOptional": "설명 (선택사항)",
|
||||
"virtualApiKeysAssociateUserOptional": "사용자 연결 (선택사항)",
|
||||
"virtualApiKeysAssociateUserDescription": "이 키를 사용자와 연결하여 사용을 추적합니다. 생성되면 선택한 공용 AI 게이트웨이에 즉시 액세스 권한이 부여됩니다. 수동으로 리소스와 연결할 필요가 없습니다. 키는 사용자의 프로필에도 표시됩니다.",
|
||||
"virtualApiKeysAllResources": "모든 공용 AI 게이트웨이",
|
||||
"virtualApiKeysAllResourcesDescription": "이 키가 조직의 모든 공용 AI 게이트웨이에 액세스할 수 있도록 허용",
|
||||
"virtualApiKeysSelectResources": "공용 AI 게이트웨이",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "리소스 선택",
|
||||
"virtualApiKeysSelectResourcesDescription": "이 키가 액세스할 수 있는 공용 AI 게이트웨이를 선택하십시오",
|
||||
"virtualApiKeysNoResources": "리소스 없음",
|
||||
"virtualApiKeysSecret": "키",
|
||||
"virtualApiKeysCopyKey": "이 키를 복사하십시오. 나중에 표에서 또는 편집할 때 다시 볼 수 있습니다.",
|
||||
"virtualApiKeysViewSecret": "비밀 보기",
|
||||
"virtualApiKeysViewSecretTitle": "가상 API 키 비밀",
|
||||
"virtualApiKeysViewSecretDescription": "이 비밀은 이 키에 할당된 공용 AI 게이트웨이에 대한 액세스를 부여합니다",
|
||||
"virtualApiKeysEdit": "가상 API 키 편집",
|
||||
"virtualApiKeysEditDescription": "이 키의 관련 사용자 및 공용 AI 게이트웨이 액세스를 업데이트하십시오",
|
||||
"virtualApiKeysSaveButton": "변경 사항 저장",
|
||||
"virtualApiKeysSelectResourcesRequired": "최소한 하나의 공용 AI 게이트웨이를 선택하거나 모든 공용 AI 게이트웨이를 활성화하십시오",
|
||||
"virtualApiKeysUpdated": "가상 API 키가 업데이트되었습니다",
|
||||
"virtualApiKeysUpdatedDescription": "가상 API 키가 업데이트되었습니다",
|
||||
"virtualApiKeysErrorUpdate": "가상 API 키 업데이트 오류",
|
||||
"virtualApiKeysErrorUpdateDescription": "가상 API 키 업데이트에 실패했습니다",
|
||||
"virtualApiKeysErrorCreate": "가상 API 키 생성 오류",
|
||||
"virtualApiKeysErrorCreateDescription": "가상 API 키 생성에 실패했습니다",
|
||||
"virtualApiKeysErrorDelete": "가상 API 키 삭제 오류",
|
||||
"virtualApiKeysErrorDeleteMessage": "가상 API 키 삭제에 실패했습니다",
|
||||
"virtualApiKeysDeleted": "가상 API 키가 삭제되었습니다",
|
||||
"virtualApiKeysDeletedDescription": "가상 API 키가 삭제되었습니다",
|
||||
"virtualApiKeysDelete": "가상 API 키 삭제",
|
||||
"virtualApiKeysDeleteConfirm": "키 삭제",
|
||||
"virtualApiKeysQuestionRemove": "이 가상 API 키를 삭제하시겠습니까?",
|
||||
"virtualApiKeysMessageRemove": "이 키를 사용하는 클라이언트는 즉시 액세스를 잃습니다.",
|
||||
"virtualApiKeysErrorFetchSecret": "비밀 로딩 오류",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "가상 API 키 비밀 로딩에 실패했습니다",
|
||||
"virtualApiKeysFilterUnassigned": "할당되지 않음",
|
||||
"virtualApiKeysInferenceBudget": "예산",
|
||||
"virtualApiKeysInferenceBudgetDescription": "이 키가 지출 또는 토큰 제한에 따라 AI 사용을 제한하는 방법을 구성하십시오",
|
||||
"virtualApiKeysEmailOnGenerate": "생성 시 이메일 키",
|
||||
"virtualApiKeysEmailThisKey": "이 키를 이메일로 발송",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "생성 후 이 키를 관련 사용자 및 추가 주소로 보냅니다",
|
||||
"virtualApiKeysEmailThisKeyDescription": "현재 키를 관련 사용자 및 추가 주소로 보냅니다",
|
||||
"virtualApiKeysEmailSmtpRequired": "이 서버에 이메일 구성이 되어 있지 않습니다",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "가상 API 키를 이메일로 보내려면 SMTP를 구성하십시오.",
|
||||
"virtualApiKeysEmailSendToUser": "관련 사용자에게 전송",
|
||||
"virtualApiKeysEmailSendToUserDescription": "관련 사용자의 계정 이메일로 키 전송",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "키를 해당 사용자에게 보내기 위해 사용자를 연결하십시오",
|
||||
"virtualApiKeysEmailAdditional": "추가 이메일",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "이메일을 추가하고 Enter를 누르십시오",
|
||||
"virtualApiKeysEmailRecipientsRequired": "관련 사용자를 선택하거나 최소한 하나의 이메일 주소를 추가하십시오",
|
||||
"myVirtualApiKeysTitle": "귀하의 API 키",
|
||||
"myVirtualApiKeysDescription": "이 조직에서 자신에게 할당된 신원 키 및 가상 API 키를 확인하십시오",
|
||||
"myVirtualApiKeysResourceTitle": "{resourceName}에 대한 귀하의 API 키",
|
||||
"myVirtualApiKeysResourceDescription": "{resourceName}에 액세스할 수 있도록 귀하에게 할당된 신원 키 및 가상 API 키를 확인하십시오",
|
||||
"myVirtualApiKeysIdentityTitle": "신원 키",
|
||||
"myVirtualApiKeysIdentityHeadline": "개인 API 키",
|
||||
"myVirtualApiKeysIdentityDescription": "이 조직에 대한 귀하의 개인 키입니다. AI 게이트웨이 리소스를 호출할 때 귀하를 식별하는 데 사용됩니다.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "{resourceName}에 대한 귀하의 개인 API 키",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "이 조직에 대한 귀하의 개인 키입니다. {resourceName}을 호출하는 데 사용하십시오.",
|
||||
"myVirtualApiKeysManualTitle": "귀속 키",
|
||||
"myVirtualApiKeysManualDescription": "관리자가 귀하의 계정에 연결한 수동 가상 API 키",
|
||||
"myVirtualApiKeysManualResourceDescription": "{resourceName}에 액세스할 수 있는 귀하의 계정에 연결된 수동 가상 API 키",
|
||||
"myVirtualApiKeysManualEmpty": "아직 귀속된 키가 없습니다",
|
||||
"myVirtualApiKeysKindUser": "신원",
|
||||
"myVirtualApiKeysKindManual": "수동",
|
||||
"myVirtualApiKeysUnnamed": "이름 없는 키",
|
||||
"myVirtualApiKeysRevealSecret": "비밀 공개",
|
||||
"myVirtualApiKeysViewSecretDescription": "이 비밀은 AI 게이트웨이 리소스에 대한 귀하의 인증을 수행합니다",
|
||||
"aiClientConfigTitle": "코딩 에이전트 구성",
|
||||
"aiClientConfigDescription": "인기 코딩 에이전트의 구성을 복사하거나 Pangolin CLI가 자동으로 설정하도록 하세요.",
|
||||
"aiClientConfigDescriptionClaude": "Anthropic의 터미널 에이전트 코딩 도구입니다.",
|
||||
"aiClientConfigDescriptionCodex": "OpenAI의 터미널 에이전트 코딩 도구입니다.",
|
||||
"aiClientConfigDescriptionOpencode": "오픈 소스 터미널 코딩 에이전트.",
|
||||
"aiClientConfigDescriptionGemini": "터미널용 구글의 에이전시 코딩 도구.",
|
||||
"aiClientConfigSetup": "설정",
|
||||
"aiClientConfigTabCli": "자동 (CLI)",
|
||||
"aiClientConfigTabManual": "수동 구성",
|
||||
"aiClientConfigPreset": "설정",
|
||||
"aiClientConfigStep": "단계 {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "이 스니펫에는 모델 이름이나 리소스 URL과 같은 예제 값이 포함되어 있습니다. 사용하기 전에 이를 고유 값으로 바꾸세요.",
|
||||
"aiClientConfigRevealError": "API 키를 로드할 수 없습니다.",
|
||||
"aiClientConfigRevealRetry": "다시 시도하십시오",
|
||||
"resourceGeneralAiClientConfigDescription": "이 리소스는 Claude Code 및 OpenCode와 같은 일반적인 클라이언트에서 사용할 수 있습니다. <configLink>더 알아보기</configLink>",
|
||||
"aiProvidersTitle": "AI 공급자",
|
||||
"aiProvidersDescription": "이 조직의 AI 작업량에 대한 모델 공급자를 연결하십시오",
|
||||
"aiProvidersBannerTitle": "모델 공급자 연결",
|
||||
"aiProvidersBannerDescription": "공급자는 AI 작업량을 위한 백엔드 모델입니다. 여기에서 OpenAI, Anthropic 및 기타 공급자를 연결한 다음 공용 AI 게이트웨이에 연결하여 사용자가 정체성 인식 액세스, 예산 및 로깅으로 모델을 호출할 수 있도록 하십시오.",
|
||||
"aiProvidersAdd": "공급자 추가",
|
||||
"aiProvidersSearch": "공급자 검색...",
|
||||
"aiProvidersEmpty": "아직 AI 공급자가 없습니다",
|
||||
"aiProviderCreate": "AI 공급자 생성",
|
||||
"aiProviderCreateDescription": "이 조직을 위한 모델 공급자 추가",
|
||||
"aiProviderSeeAll": "모든 공급자 보기",
|
||||
"aiProviderSetting": "{providerName}을 위한 공급자 설정",
|
||||
"aiProviderSettingDescription": "이 AI 공급자 구성을 설정하십시오",
|
||||
"aiProviderGeneral": "일반",
|
||||
"aiProviderGeneralDescription": "이 공급자의 기본 설정",
|
||||
"aiProviderConfiguration": "설정",
|
||||
"aiProviderConfigurationDescription": "이 공급자의 네트워크 라우팅 및 인증 설정",
|
||||
"aiProviderNetworkSettings": "네트워크 설정",
|
||||
"aiProviderNetworkSettingsDescription": "이 공급자로 트래픽을 전달하는 방법을 선택하십시오",
|
||||
"aiProviderAuthSettings": "인증",
|
||||
"aiProviderAuthSettingsDescription": "이 공급자가 업스트림 URL에 대한 요청을 인증하는 방법을 구성하십시오",
|
||||
"aiProviderBudgetSettings": "예산",
|
||||
"aiProviderBudgetSettingsDescription": "이 공급자가 지출 또는 토큰 한도에 따라 사용을 제한하는 방법을 구성하십시오",
|
||||
"aiBudgetAdd": "예산 추가",
|
||||
"aiBudgetEmpty": "아직 예산 구성되지 않았습니다. 예산 추가를 클릭하여 지출 또는 토큰 한도를 설정하십시오.",
|
||||
"aiBudgetUnit": "지출 유형",
|
||||
"aiBudgetPeriod": "재설정 기간",
|
||||
"aiBudgetAmount": "최대 지출",
|
||||
"aiBudgetAmountPlaceholder": "최대 지출",
|
||||
"aiBudgetPeriodHourly": "시간당",
|
||||
"aiBudgetPeriodDaily": "일일",
|
||||
"aiBudgetPeriodWeekly": "주간",
|
||||
"aiBudgetPeriodMonthly": "월간",
|
||||
"aiBudgetPeriodYearly": "연간",
|
||||
"aiBudgetPeriodLifetime": "평생",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "토큰",
|
||||
"aiBudgetConflictError": "이미 이 재설정 기간과 지출 유형에 대한 예산이 존재합니다",
|
||||
"aiBudgetInvalidAmountError": "0보다 큰 최대 지출을 입력하십시오",
|
||||
"aiBudgetUpdated": "예산 업데이트됨",
|
||||
"aiBudgetErrorSave": "예산 업데이트 실패",
|
||||
"aiProviderType": "공급자 유형",
|
||||
"aiProviderTypeSearch": "공급자 검색...",
|
||||
"aiProviderTypeNotFound": "공급자 유형 없음",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI 게이트웨이",
|
||||
"aiProviderTypeCustom": "사용자 정의",
|
||||
"aiProviderTypeOpenaiDescription": "OpenAI API",
|
||||
"aiProviderTypeAnthropicDescription": "Anthropic API",
|
||||
"aiProviderTypeGoogleGeminiDescription": "Google Gemini API",
|
||||
"aiProviderTypeVertexAiDescription": "Google Vertex AI API",
|
||||
"aiProviderTypeBedrockDescription": "Amazon Bedrock Runtime API",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "Microsoft Foundry API",
|
||||
"aiProviderTypeOpenRouterDescription": "OpenRouter API",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "Vercel AI 게이트웨이 API",
|
||||
"aiProviderTypeCustomDescription": "자체 엔드포인트를 가져오거나 사이트 타겟을 통해 라우트하십시오",
|
||||
"aiProviderUpstreamUrl": "상류 URL",
|
||||
"aiProviderUpstreamUrlDescription": "공급자 API의 기본 URL",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "기본 상류 URL 사용을 위해 빈칸으로 두십시오",
|
||||
"aiProviderEffectiveUpstreamUrl": "유효 상류 URL",
|
||||
"aiProviderApiKey": "API 키",
|
||||
"aiProviderApiKeyDescription": "이 공급자에 대한 요청을 인증하기 위해 사용하는 API 키",
|
||||
"aiProviderCustomHeadersDescription": "이 공급자에게 보내는 모든 요청에 대해 전송되는 헤더. 새 줄로 구분된: 헤더-이름: 값",
|
||||
"aiProviderApiKeyLastChars": "API 키",
|
||||
"aiProviderAuthType": "인증 유형",
|
||||
"aiProviderAuthTypeSearch": "인증 유형 검색...",
|
||||
"aiProviderAuthTypeNotFound": "인증 유형을 찾을 수 없습니다",
|
||||
"aiProviderAuthTypeBearer": "Bearer",
|
||||
"aiProviderAuthTypeBearerDescription": "Authorization: Bearer 키. OpenAI 및 대부분의 공급자에서 사용",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "x-api-key 헤더. Anthropic에서 사용",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "x-goog-api-key 헤더. Google Gemini에서 사용",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Authorization: Splunk 키. Splunk HTTP 이벤트 수집기에서 사용",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI 게이트웨이",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-authorization: Bearer 키. Cloudflare AI 게이트웨이에서 사용",
|
||||
"aiProviderAuthTypeNone": "인증 없음",
|
||||
"aiProviderAuthTypePassthrough": "통과",
|
||||
"aiProviderAuthTypeDescription": "상류 API에서 요청을 인증하는 방법",
|
||||
"aiProviderAuthTypePassthroughDescription": "발신자의 API 키 헤더를 상류로 전달",
|
||||
"aiProviderAuthTypeNoneDescription": "상류로 인증 헤더를 보내지 않음",
|
||||
"aiProviderRoutingMode": "라우팅 모드",
|
||||
"aiProviderRoutingModeDescription": "상류 URL로 트래픽을 보내거나 사이트의 HTTP 타겟으로 보냅니다",
|
||||
"aiProviderRoutingModeUrl": "상류 URL",
|
||||
"aiProviderRoutingModeUrlDescription": "공개 또는 개인 API의 기본 URL 호출",
|
||||
"aiProviderRoutingModeTarget": "사이트 타겟",
|
||||
"aiProviderRoutingModeTargetDescription": "사이트의 타겟을 통해 라우트",
|
||||
"aiProviderRoutingModeTargetNote": "이 공급자를 생성한 후 네트워크 설정 탭에 사이트 타겟을 구성합니다.",
|
||||
"aiProviderTargetNoOne": "이 공급자에게 타겟이 없습니다. 사이트를 통해 요청을 라우트하기 위한 타겟을 추가하십시오.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "원격 노드에 연결된 사이트는 AI 게이트웨이 공급자에게 라우팅되지 않습니다.",
|
||||
"aiProviderSkipTlsVerification": "TLS 검증 건너뛰기",
|
||||
"aiProviderSkipTlsVerificationDescription": "상류 연결에 대한 TLS 인증서 검증 비활성화",
|
||||
"aiProviderBudget": "예산",
|
||||
"aiProviderBudgetDescription": "이 공급자에 대한 선택적 지출 또는 토큰 예산",
|
||||
"aiProviderBudgetAmount": "예산 금액",
|
||||
"aiProviderBudgetUnit": "예산 단위",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "토큰",
|
||||
"aiProviderEnabled": "활성화됨",
|
||||
"aiProviderEnabledDescription": "모든 리소스에 대해 이 공급자를 완전히 비활성화합니다",
|
||||
"aiProviderErrorCreate": "AI 공급자 생성에 실패했습니다",
|
||||
"aiProviderErrorUpdate": "AI 공급자 업데이트에 실패했습니다",
|
||||
"aiProviderErrorDelete": "AI 공급자 삭제에 실패했습니다",
|
||||
"aiProviderErrorLoad": "AI 공급자 로드에 실패했습니다",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "유효한 상류 URL을 입력하십시오",
|
||||
"aiProviderErrorUpstreamUrlRequired": "이 공급자에는 상류 URL이 필요합니다",
|
||||
"aiProviderErrorAuthTypeRequired": "인증 유형이 필요합니다",
|
||||
"aiProviderErrorApiKeyRequired": "API 키가 필요합니다",
|
||||
"aiProviderErrorRoutingModeTarget": "사이트 타겟 라우팅은 맞춤형 공급자에게만 가능합니다",
|
||||
"aiProviderErrorCapabilitiesRequired": "최소 한 가지 API 기능을 선택하십시오",
|
||||
"aiProviderCapabilities": "API 기능",
|
||||
"aiProviderCapabilitiesDescription": "이 공급자가 처리할 수 있는 API 형식을 선택하십시오. 알려진 공급자는 권장 기본값으로 시작합니다.",
|
||||
"aiProviderCapabilitiesCustomDescription": "사용자 지정 제공자가 처리할 수 있는 API 형식을 선택하십시오",
|
||||
"aiProviderCapabilitiesSelect": "기능 선택",
|
||||
"aiProviderCapabilitiesEmpty": "능력을 찾을 수 없습니다",
|
||||
"aiProviderCapabilitiesSearch": "능력 검색...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI 채팅 완료",
|
||||
"aiCapabilityOpenaiChatDescription": "/v1/chat/completions 지원",
|
||||
"aiCapabilityOpenaiResponses": "OpenAI 응답",
|
||||
"aiCapabilityOpenaiResponsesDescription": "/v1/responses 지원",
|
||||
"aiCapabilityAnthropicMessages": "Anthropic 메시지",
|
||||
"aiCapabilityAnthropicMessagesDescription": "/v1/messages 지원",
|
||||
"aiCapabilityV1Models": "모델 목록",
|
||||
"aiCapabilityV1ModelsDescription": "/v1/models 모델 검색 지원",
|
||||
"aiCapabilityGeminiGenerateContent": "Gemini 콘텐츠 생성",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "직접 Gemini API 지원",
|
||||
"aiCapabilityBedrockModelInvoke": "Bedrock 모델 실행",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Amazon Bedrock InvokeModel 지원",
|
||||
"aiCapabilityGoogleGenerateContent": "Vertex 콘텐츠 생성",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Vertex AI Gemini 형식 지원",
|
||||
"aiCapabilityGoogleRawPredict": "Vertex Raw 예측",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Anthropic 모델에 대한 Vertex AI rawPredict 지원",
|
||||
"aiCapabilityBedrockConverse": "Bedrock 회화",
|
||||
"aiCapabilityBedrockConverseDescription": "Amazon Bedrock Converse API 지원",
|
||||
"aiProviderCreated": "AI 제공자가 생성되었습니다",
|
||||
"aiProviderUpdated": "AI 제공자가 업데이트되었습니다",
|
||||
"aiProviderDeleted": "AI 제공자가 삭제되었습니다",
|
||||
"aiProviderDelete": "제공자 삭제",
|
||||
"aiProviderDeleteConfirm": "제공자 삭제",
|
||||
"aiProviderQuestionRemove": "이 AI 제공자를 삭제하시겠습니까?",
|
||||
"aiProviderMessageRemove": "이 작업을 실행하면 제공자와 해당 모델 및 대상이 영구적으로 삭제됩니다. 이는 되돌릴 수 없습니다.",
|
||||
"aiProviderErrorNoUpdate": "AI 제공자를 업데이트할 수 없습니다",
|
||||
"aiProviderModels": "모델",
|
||||
"aiProviderModelsDescription": "이 제공자에 대한 허용 및 차단 목록을 정의하십시오. 요청은 허용 항목과 일치해야 하며 차단 항목과 일치하지 않아야 합니다.",
|
||||
"aiProviderCreateModelsDescription": "이 제공자가 제공할 수 있는 모델을 선택합니다. 허용 목록이 비어 있으면 모든 트래픽이 거부됩니다. 나중에 모델을 추가하거나 변경할 수 있습니다.",
|
||||
"aiProviderModelsPlaceholder": "모델을 검색하거나 사용자 지정 키를 입력하세요",
|
||||
"aiProviderModelsAllow": "허용 목록",
|
||||
"aiProviderModelsAllowDescription": "이 제공자를 통해 사용할 수 있는 모델입니다. 비어 있으면 모두 거부를 의미합니다.",
|
||||
"aiProviderModelsAllowPlaceholder": "모델 키 입력",
|
||||
"aiProviderModelsAllowEmpty": "아직 추가된 모델이 없습니다.",
|
||||
"aiProviderModelsBlock": "차단 목록",
|
||||
"aiProviderModelsBlockDescription": "허용 항목과 일치하더라도 사용할 수 없는 모델입니다.",
|
||||
"aiProviderModelsBlockPlaceholder": "모델 키 입력",
|
||||
"aiProviderModelsBlockEmpty": "아직 추가된 모델이 없습니다.",
|
||||
"aiProviderModelsAdd": "모델 추가",
|
||||
"aiProviderModelsClearAll": "모두 지우기",
|
||||
"aiProviderModelsAddCustom": "\"{key}\" 추가",
|
||||
"aiProviderModelsAddCustomHint": "이 사용자 지정 모델 키를 추가하려면 Enter 키를 누르십시오.",
|
||||
"aiProviderModelsAddBulk": "{count}개의 사용자 지정 키 추가",
|
||||
"aiProviderModelsAddBulkHint": "{count}개의 사용자 지정 키를 추가하려면 Enter 키를 누르십시오.",
|
||||
"aiProviderModelsAddOne": "지금 {key} 추가",
|
||||
"aiProviderModelsAddSelected": "선택 항목 추가",
|
||||
"aiProviderModelsSelectedCount": "{count}개 선택됨",
|
||||
"aiProviderModelsSelectAll": "모두 선택",
|
||||
"aiProviderModelsClearSelected": "지우기",
|
||||
"aiProviderModelsBulkHint": "알려진 모델을 선택하거나 사용자 지정 키를 입력하십시오.",
|
||||
"aiProviderModelsCatalogEmpty": "일치하는 카탈로그 모델이 없습니다.",
|
||||
"aiProviderModelsCatalogHeading": "알려진 모델",
|
||||
"aiProviderModelsAllLabel": "모든 모델",
|
||||
"aiProviderModelsAllPatternHint": "와일드카드: *",
|
||||
"aiProviderModelsAddAllAllow": "모든 모델 허용",
|
||||
"aiProviderModelsAddAllBlock": "모델 모두 차단",
|
||||
"aiProviderModelsAddAllDescription": "* 와일드카드를 사용하여 모든 모델 키를 일치시킵니다.",
|
||||
"aiProviderModelsViewMore": "더 보기 ({count})",
|
||||
"aiProviderModelsViewLess": "덜 보기",
|
||||
"aiProviderModelsRemove": "모델 제거",
|
||||
"aiProviderModelsEditHint": "모델 설정을 수정하려면 클릭하세요",
|
||||
"aiProviderModelsSourceCatalog": "알려진 카탈로그 모델",
|
||||
"aiProviderModelsSourceCustom": "사용자 지정 모델 키",
|
||||
"aiProviderModelsSourcePattern": "와일드카드 패턴",
|
||||
"aiProviderModelsSourceAll": "모든 모델 키와 일치",
|
||||
"aiProviderModelsBudgetConfigured": "설정된 예산",
|
||||
"aiProviderModelsEditTitle": "모델 편집",
|
||||
"aiProviderModelsEditDescription": "모델 키를 업데이트하거나 해당 예산을 구성하십시오.",
|
||||
"aiProviderModelsBudgetTab": "예산",
|
||||
"aiProviderModelsKeyLabel": "모델 키",
|
||||
"aiProviderModelsKeyRequired": "모델 키를 입력하십시오",
|
||||
"aiProviderModelsKeyDuplicate": "이 모델 키는 이미 목록에 있습니다",
|
||||
"aiProviderModelsOverlapError": "이 패턴은 두 목록에 모두 있을 수 없습니다: {keys}",
|
||||
"aiProviderModelsUpdated": "모델이 업데이트되었습니다",
|
||||
"aiProviderModelsErrorUpdate": "모델 업데이트에 실패했습니다",
|
||||
"aiResourceProviders": "제공자",
|
||||
"aiResourceProvidersDescription": "이 AI 게이트웨이가 사용할 수 있는 AI 제공자를 선택합니다",
|
||||
"aiResourceProvidersHelp": "공급자를 연결하고 상속(각 공급자의 목록 사용) 또는 선택(이 리소스에 대한 허용 목록 선택)을 선택하십시오. 연결된 공급자 전반에 걸쳐 충돌하는 허용 패턴은 허용되지 않습니다.",
|
||||
"aiResourceProvidersSelect": "공급자 선택",
|
||||
"aiResourceProvidersEmpty": "AI 제공자를 찾을 수 없습니다",
|
||||
"aiResourceProvidersNoneAttached": "아직 연결된 공급자가 없습니다.",
|
||||
"aiResourceProvidersAdd": "제공자 추가",
|
||||
"aiResourceProvidersRemove": "제공자 제거",
|
||||
"aiResourceProviderToggleEnabled": "이 리소스에서 공급자를 활성화하거나 비활성화합니다",
|
||||
"aiResourceProviderDisabled": "사용 중지됨",
|
||||
"aiResourceProvidersUpdated": "공급자가 업데이트되었습니다",
|
||||
"aiResourceProvidersErrorUpdate": "공급자 업데이트에 실패했습니다",
|
||||
"aiResourceProviderEditDescription": "이 공급자의 모델이 리소스에서 어떻게 노출되는지 선택하십시오.",
|
||||
"viewProviderSettings": "제공자 설정 보기",
|
||||
"aiResourceProviderMode": "액세스 모드",
|
||||
"aiResourceProviderModeInherit": "상속",
|
||||
"aiResourceProviderModeSelect": "선택",
|
||||
"aiResourceProviderModeSelectSummary": "선택 · {count} 모델",
|
||||
"aiResourceProviderModeInheritHelp": "공급자에서 구성한 대로 이 공급자의 허용 및 차단 목록을 사용하십시오.",
|
||||
"aiResourceProviderModeSelectHelp": "이 리소스를 위한 공급자의 허용 목록 모델의 하위 집합을 선택하십시오.",
|
||||
"aiResourceProviderAllowModels": "허용 목록",
|
||||
"aiResourceProviderAllowModelsSelect": "모델 선택",
|
||||
"aiResourceProviderAllowModelsSearch": "모델 검색...",
|
||||
"aiResourceProviderAllowModelsEmpty": "모델을 찾을 수 없습니다",
|
||||
"aiResourceProviderAllowModelsHelp": "제공자의 허용 목록에 있는 모델만 선택할 수 있습니다.",
|
||||
"aiResourceAliasRequired": "AI 게이트웨이에 별칭이 필요합니다",
|
||||
"aiResourceDomainConfiguration": "도메인 구성",
|
||||
"aiResourceDomainConfigurationDescription": "고객이 이 AI 게이트웨이를 사용하기 위해 사용할 도메인을 선택하십시오.",
|
||||
"aiUsageAnalyticsTitle": "AI 사용 분석",
|
||||
"aiUsageAnalyticsDescription": "제공자, 리소스, 역할 및 사용자 간의 AI 게이트웨이 비용, 토큰 사용량 및 활동을 분석합니다",
|
||||
"aiUsageTabOverview": "개요",
|
||||
"aiUsageTabProviders": "공급자",
|
||||
"aiUsageTabResources": "리소스",
|
||||
"aiUsageFilterProvider": "제공자",
|
||||
"aiUsageFilterModel": "모델",
|
||||
"aiUsageFilterResource": "리소스",
|
||||
"aiUsageFilterRole": "역할",
|
||||
"aiUsageFilterUser": "사용자",
|
||||
"aiUsageFilterAllProviders": "모든 공급자",
|
||||
"aiUsageFilterAllModels": "모든 모델",
|
||||
"aiUsageFilterAllResources": "모든 리소스",
|
||||
"aiUsageFilterAllRoles": "모든 역할",
|
||||
"aiUsageFilterAllUsers": "모든 사용자",
|
||||
"aiUsageFilterSearch": "검색...",
|
||||
"aiUsageFilterNotFound": "옵션을 찾을 수 없습니다",
|
||||
"aiUsageResetFilters": "필터 재설정",
|
||||
"aiUsageRefresh": "새로 고침",
|
||||
"aiUsageTokenTypePrompt": "프롬프트",
|
||||
"aiUsageTokenTypeCacheRead": "캐시 읽기",
|
||||
"aiUsageTokenTypeCacheWrite": "캐시 쓰기",
|
||||
"aiUsageTokenTypeCompletion": "완료",
|
||||
"aiUsageTokenTypeReasoning": "추론",
|
||||
"aiUsageRequests": "요청",
|
||||
"aiUsageCost": "비용",
|
||||
"aiUsageTokens": "토큰",
|
||||
"aiUsageOther": "기타",
|
||||
"aiUsageTotalRequests": "총 요청 수",
|
||||
"aiUsageTotalTokens": "총 토큰수",
|
||||
"aiUsageTotalCost": "총 비용",
|
||||
"aiUsageEstimated": "추정치",
|
||||
"aiUsageRequestVolume": "요청량",
|
||||
"aiUsageTokenUsage": "토큰 사용",
|
||||
"aiUsageModelCost": "모델 비용",
|
||||
"aiUsageModelTokens": "모델 토큰",
|
||||
"aiUsageTopModels": "상위 모델",
|
||||
"aiUsageTopProviders": "상위 제공자",
|
||||
"aiUsageProviderCost": "제공자 비용",
|
||||
"aiUsageProviderTokenUsage": "제공자 토큰 사용량",
|
||||
"aiUsageTopResources": "최고의 리소스",
|
||||
"aiUsageResourceCost": "리소스 비용",
|
||||
"aiUsageResourceTokenUsage": "리소스 토큰 사용량",
|
||||
"aiUsageResourceTypePublic": "공용 리소스",
|
||||
"aiUsageResourceTypeSite": "개인 리소스",
|
||||
"aiUsageNoResource": "리소스 없음",
|
||||
"aiUsageRolesTab": "역할",
|
||||
"aiUsageUsersTab": "사용자들",
|
||||
"aiUsageTopRoles": "최고의 역할",
|
||||
"aiUsageRoleCost": "역할 비용",
|
||||
"aiUsageRoleTokenUsage": "역할 토큰 사용량",
|
||||
"aiUsageTopUsers": "최고의 사용자",
|
||||
"aiUsageUserCost": "사용자 비용",
|
||||
"aiUsageUserTokenUsage": "사용자 토큰 사용량",
|
||||
"aiUsageUnknownUser": "알 수 없는 사용자",
|
||||
"aiUsageVirtualApiKeysTab": "가상 API 키",
|
||||
"aiUsageFilterVirtualApiKey": "가상 API 키",
|
||||
"aiUsageFilterAllVirtualApiKeys": "모든 가상 API 키",
|
||||
"aiUsageTopVirtualApiKeys": "최고의 가상 API 키",
|
||||
"aiUsageVirtualApiKeyCost": "가상 API 키 비용",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "가상 API 키 사용량",
|
||||
"aiUsageUnknownVirtualApiKey": "가상 API 키 없음",
|
||||
"aiUsageUnnamedVirtualApiKey": "이름 없는 키",
|
||||
"aiUsageLoading": "로딩 중...",
|
||||
"aiUsageNoData": "데이터 없음",
|
||||
"resourceBudgetSettings": "예산",
|
||||
"resourceBudgetSettingsDescription": "이 AI 게이트웨이가 지출 또는 토큰 제한에 따라 사용을 제한하는 방법을 구성하십시오",
|
||||
"sidebarApiKeys": "API 키",
|
||||
"sidebarOrgs": "조직",
|
||||
"sidebarProvisioning": "프로비저닝",
|
||||
"sidebarSettings": "설정",
|
||||
"sidebarAllUsers": "모든 사용자",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "알림 규칙 저장됨",
|
||||
"alertingRuleSavedCreatedDescription": "새 알림 규칙이 생성되었습니다. 이 페이지에서 계속 편집할 수 있습니다.",
|
||||
"alertingRuleSavedUpdatedDescription": "이 알림 규칙에 대한 변경 사항이 저장되었습니다.",
|
||||
"alertingTestAlertSent": "테스트 경고 전송됨",
|
||||
"alertingTestAlertSentDescription": "이 규칙에 구성된 조치에 테스트 경고가 전송되었습니다.",
|
||||
"alertingEditRule": "알림 규칙 편집",
|
||||
"alertingCreateRule": "알림 규칙 생성",
|
||||
"alertingRuleCredenzaDescription": "무엇을 감시할지, 언제 알릴지, 어떻게 알릴지를 선택하세요.",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "각 규칙은 무엇을 감시할지(사이트, 상태 확인, 리소스), 언제 발동할지(예: 오프라인 또는 비정상), 이메일, 웹훅 또는 통합을 통해 팀에 어떻게 알릴지를 연결합니다. 이 목록을 사용하여 규칙을 생성, 활성화 및 관리하세요.",
|
||||
"alertingHealthChecksBannerTitle": "건강 및 리소스 모니터링",
|
||||
"alertingHealthChecksBannerDescription": "상태 확인은 한 번 정의한 HTTP 또는 TCP 모니터링입니다. 그런 다음 이를 알림 규칙의 소스로 사용하여 타겟이 정상 또는 비정상이 되었을 때 알림을 받을 수 있습니다. 리소스의 상태 확인도 여기에 나타납니다.",
|
||||
"alertingTestRule": "테스트 경고 규칙",
|
||||
"alertingAddActionHeading": "새 작업 추가",
|
||||
"alertingSelectActionType": "작업 선택",
|
||||
"alertingNoActionsTitle": "구성된 작업이 없습니다",
|
||||
"alertingNoActionsSaveDescription": "이 규칙이 작동할 때 누군가에게 알리기 위해 적어도 하나의 작업을 추가하십시오.",
|
||||
"alertingNoActionsTestDescription": "이 규칙을 테스트하기 전에 적어도 하나의 작업을 추가하십시오.",
|
||||
"standaloneHcTableTitle": "상태 확인",
|
||||
"standaloneHcSearchPlaceholder": "상태 확인 검색…",
|
||||
"standaloneHcAddButton": "상태 확인 생성",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "유형:",
|
||||
"createDomainName": "이름:",
|
||||
"createDomainValue": "값:",
|
||||
"multiSelectFilterCount": "{count} 선택됨",
|
||||
"createDomainCnameRecords": "CNAME 레코드",
|
||||
"createDomainARecords": "A 레코드",
|
||||
"createDomainRecordNumber": "레코드 {number}",
|
||||
@@ -2193,6 +2699,8 @@
|
||||
"subnetPlaceholder": "서브넷",
|
||||
"addressDescription": "클라이언트의 내부 주소. 조직의 서브넷 내에 있어야 합니다.",
|
||||
"selectSites": "사이트 선택",
|
||||
"selectResources": "리소스 선택",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, other {# 자원}}",
|
||||
"selectLabels": "레이블 선택",
|
||||
"sitesDescription": "클라이언트는 선택한 사이트에 연결됩니다.",
|
||||
"clientInstallOlm": "Olm 설치",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "방법",
|
||||
"healthSelectScheme": "방법 선택",
|
||||
"healthCheckPortInvalid": "포트는 1에서 65535 사이여야 합니다",
|
||||
"healthCheckHostnameInvalid": "호스트 이름에는 공백이 포함될 수 없습니다",
|
||||
"healthCheckPath": "경로",
|
||||
"healthHostname": "IP / 호스트",
|
||||
"healthPort": "포트",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "시간은 초 단위입니다",
|
||||
"requireDeviceApproval": "장치 승인 요구",
|
||||
"requireDeviceApprovalDescription": "이 역할을 가진 사용자는 장치가 연결되기 전에 관리자의 승인이 필요합니다.",
|
||||
"sshSettings": "SSH 설정",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "AI 게이트웨이",
|
||||
"sshAccess": "SSH 접속",
|
||||
"rdpSettings": "RDP 설정",
|
||||
"vncSettings": "VNC 설정",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "SSH 서버",
|
||||
"rdpServer": "RDP 서버",
|
||||
"vncServer": "VNC 서버",
|
||||
@@ -2257,7 +2767,7 @@
|
||||
"vncServerDescription": "VNC 서버의 목적지 및 포트를 구성합니다",
|
||||
"sshServerMode": "모드",
|
||||
"sshServerModeStandard": "표준 SSH 서버",
|
||||
"sshServerModePangolin": "Pangolin SSH",
|
||||
"sshServerModePangolin": "판골린 SSH",
|
||||
"sshServerModeStandardDescription": "네트워크를 통해 OpenSSH와 같은 SSH 서버로 명령을 전달합니다.",
|
||||
"sshServerModeNative": "네이티브 SSH 서버",
|
||||
"sshServerModeNativeDescription": "사이트 커넥터를 통해 호스트에서 직접 명령을 실행합니다. 네트워크 구성이 필요 없습니다.",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "AI 게이트웨이",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "스킴",
|
||||
"editInternalResourceDialogEnableSsl": "TLS 활성화",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "ICMP 핑 허용",
|
||||
"privateResourceNetworkAccess": "네트워크 접근",
|
||||
"privateResourceNetworkAccessDescription": "이 리소스에 대한 TCP/UDP 포트 접근과 ICMP 핑이 허용되는지 여부를 제어합니다.",
|
||||
"hostSettings": "호스트 설정",
|
||||
"cidrSettings": "CIDR 설정",
|
||||
"hostSettings": "호스트",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "리소스 속성",
|
||||
"createInternalResourceDialogName": "이름",
|
||||
"createInternalResourceDialogSite": "사이트",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "AI 게이트웨이",
|
||||
"scheme": "스킴",
|
||||
"createInternalResourceDialogScheme": "스킴",
|
||||
"createInternalResourceDialogEnableSsl": "TLS 활성화",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "CIDR 범위 추가 (예: 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "서브넷 로드 실패",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "우선순위 레이블",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "이 레이블이 있는 사이트는 이 원격 출구 노드를 통해 연결됩니다.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "이 레이블이 있는 사이트는 이 원격 종료 노드를 통해 연결하는 것을 선호합니다.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "레이블 선택...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "레이블 검색...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "레이블 로드 실패",
|
||||
@@ -2861,7 +3373,7 @@
|
||||
"manageMachineClientsDescription": "서버와 시스템이 리소스에 개인적으로 연결하는 데 사용하는 클라이언트를 생성하고 관리하십시오",
|
||||
"machineClientsBannerTitle": "서버 및 자동 시스템",
|
||||
"machineClientsBannerDescription": "머신 클라이언트는 특정 사용자와 연결되지 않은 서버 및 자동화된 시스템을 위한 것입니다. 이들은 ID와 비밀을 통해 인증하며, Pangolin CLI, Olm CLI, 또는 Olm 컨테이너로 실행될 수 있습니다.",
|
||||
"machineClientsBannerPangolinCLI": "Pangolin CLI",
|
||||
"machineClientsBannerPangolinCLI": "판골린 CLI",
|
||||
"machineClientsBannerOlmCLI": "Olm CLI",
|
||||
"machineClientsBannerOlmContainer": "Olm 컨테이너",
|
||||
"clientsTableUserClients": "사용자",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "우선순위",
|
||||
"priorityDescription": "우선 순위가 높은 경로가 먼저 평가됩니다. 우선 순위 = 100은 자동 정렬(시스템 결정)이 의미합니다. 수동 우선 순위를 적용하려면 다른 숫자를 사용하세요.",
|
||||
"instanceName": "인스턴스 이름",
|
||||
"clearInstanceName": "서버 연결 해제",
|
||||
"pathMatchModalTitle": "경로 매칭 설정",
|
||||
"pathMatchModalDescription": "경로별로 들어오는 요청을 어떻게 매칭할지 설정합니다.",
|
||||
"pathMatchType": "일치 유형",
|
||||
@@ -3051,6 +3564,7 @@
|
||||
"validPassword": "유효한 비밀번호",
|
||||
"validEmail": "유효한 이메일",
|
||||
"validSSO": "유효한 SSO",
|
||||
"validVirtualAPIKey": "유효한 가상 API 키",
|
||||
"view": "보기",
|
||||
"configManaged": "구성 관리됨",
|
||||
"connectedClient": "연결된 클라이언트",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "작업 로그",
|
||||
"logRetention": "로그 보관",
|
||||
"logRetentionDescription": "다양한 유형의 로그를 이 조직에 대해 얼마나 오래 보관할지 관리하거나 비활성화합니다",
|
||||
"logRetentionDisabledWarningTitle": "로그 보존 비활성화",
|
||||
"logRetentionDisabledWarningDescription": "{logType}이/가 이 조직에 대해 보존되지 않으므로 새로운 활동이 여기에 나타나지 않습니다. 보안을 설정해서 보존을 활성화하여 이러한 로그를 수집하기 시작하세요.",
|
||||
"logRetentionDisabledWarningButton": "보안 설정으로 이동",
|
||||
"requestLogsDescription": "이 조직의 자원에 대한 상세한 요청 로그를 봅니다",
|
||||
"aiSessionLogs": "AI 게이트웨이 세션 로그",
|
||||
"aiSessionLogsDescription": "이 조직의 AI 게이트웨이 요청에 대한 프롬프트 및 응답 대본을 봅니다",
|
||||
"sidebarLogsAi": "세션 로그",
|
||||
"commandLogsAi": "세션 로그",
|
||||
"sidebarLogsAiUsage": "사용 분석",
|
||||
"commandLogsAiUsage": "사용 분석",
|
||||
"provider": "제공자",
|
||||
"capability": "능력",
|
||||
"model": "모델",
|
||||
"virtualApiKey": "가상 API 키",
|
||||
"noVirtualApiKey": "가상 API 키가 없습니다",
|
||||
"stream": "스트림",
|
||||
"streaming": "스트리밍",
|
||||
"nonStreaming": "비스트리밍",
|
||||
"statusCode": "상태 코드",
|
||||
"aiSessionId": "세션 ID",
|
||||
"aiSessionRequest": "요청",
|
||||
"aiSessionResponse": "응답",
|
||||
"aiSessionNoData": "캡처된 데이터 없음",
|
||||
"aiSessionCouldNotParse": "(원시, 대본을 구문 분석할 수 없음)",
|
||||
"aiSessionLogTruncated": "이 세션은 저장소 전송되기 전에 잘린 상태일 수 있습니다.",
|
||||
"aiSessionViewRaw": "원시 JSON 보기",
|
||||
"aiSessionViewChat": "채팅 보기",
|
||||
"cost": "비용",
|
||||
"estimated": "추정된",
|
||||
"tokenUsage": "토큰 사용량",
|
||||
"promptTokens": "프롬프트 토큰",
|
||||
"cacheReadTokens": "캐시 조회 토큰",
|
||||
"cacheWriteTokens": "캐시 쓰기 토큰",
|
||||
"completionTokens": "완료 토큰",
|
||||
"reasoningTokens": "추론 토큰",
|
||||
"totalTokens": "총 토큰수",
|
||||
"requestAnalyticsDescription": "이 조직의 리소스에 대한 자세한 요청 분석 보기",
|
||||
"logRetentionRequestLabel": "HTTP 요청 로그 보관",
|
||||
"logRetentionRequestDescription": "요청 로그를 얼마나 오래 보관할지",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "작업 로그를 얼마나 오래 보관할지",
|
||||
"logRetentionConnectionLabel": "연결 로그 보유 기간",
|
||||
"logRetentionConnectionDescription": "연결 로그를 얼마나 오래 보유할지",
|
||||
"logRetentionAISessionsLabel": "AI 게이트웨이 세션 로그 보관",
|
||||
"logRetentionAISessionsDescription": "AI 게이트웨이 프롬프트/응답 세션 로그를 보관할 기간",
|
||||
"logRetentionDisabled": "비활성화됨",
|
||||
"logRetention3Days": "3 일",
|
||||
"logRetention7Days": "7 일",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "소스 주소",
|
||||
"destinationAddress": "대상 주소",
|
||||
"duration": "지속 시간",
|
||||
"licenseRequiredToUse": "이 기능을 사용하려면 <enterpriseLicenseLink>엔터프라이즈 에디션</enterpriseLicenseLink> 라이선스가 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다. <bookADemoLink>데모 또는 POC 체험을 예약하세요</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "이 기능을 사용하려면 <enterpriseEditionLink>엔터프라이즈 에디션</enterpriseEditionLink>이 필요합니다. 이 기능은 <pangolinCloudLink>판골린 클라우드</pangolinCloudLink>에서도 사용할 수 있습니다. <bookADemoLink>데모 또는 POC 체험을 예약하세요</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Enterprise Edition 라이센스</enterpriseEditionLink>가 필요합니다. <bookADemoLink>데모 또는 평가판 예약</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Enterprise Edition 라이센스</enterpriseEditionLink>가 필요합니다. <bookADemoLink>데모 또는 평가판 예약</bookADemoLink>",
|
||||
"certResolver": "인증서 해결사",
|
||||
"certResolverDescription": "이 리소스에 사용할 인증서 해결사를 선택하세요.",
|
||||
"selectCertResolver": "인증서 해결사 선택",
|
||||
@@ -3242,6 +3793,7 @@
|
||||
"noData": "데이터 없음",
|
||||
"machineClients": "기계 클라이언트",
|
||||
"install": "설치",
|
||||
"downloadInstaller": "설치 프로그램 다운로드",
|
||||
"run": "실행",
|
||||
"envFile": "환경 파일",
|
||||
"serviceFile": "서비스 파일",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "다중 사이트를 선택하면 높은 가용성을 위해 회복력 있는 라우팅 및 페일오버가 가능해집니다.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "자세히 알아보기",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "특정 TCP/UDP 포트에 대한 접근을 제한하거나 모든 포트를 허용/차단하십시오.",
|
||||
"createInternalResourceDialogHttpConfiguration": "HTTP 구성",
|
||||
"createInternalResourceDialogHttpConfiguration": "도메인 구성",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "이 리소스에 HTTP 또는 HTTPS로 도달하기 위한 도메인을 선택하세요.",
|
||||
"editInternalResourceDialogHttpConfiguration": "HTTP 구성",
|
||||
"editInternalResourceDialogHttpConfiguration": "도메인 구성",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "이 리소스에 HTTP 또는 HTTPS로 도달하기 위한 도메인을 선택하세요.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "연결 해제됨",
|
||||
"approvalsEmptyStateTitle": "장치 승인 비활성화됨",
|
||||
"approvalsEmptyStateDescription": "사용자가 새 장치를 연결하기 전에 관리자의 승인을 필요로 하도록 역할에 대해 장치 승인을 활성화하세요.",
|
||||
"approvalsEmptyStateHowToTitle": "활성화 방법",
|
||||
"approvalsEmptyStateStep1Title": "역할로 이동",
|
||||
"approvalsEmptyStateStep1Description": "조직의 역할 설정으로 이동하여 장치 승인을 구성하십시오.",
|
||||
"approvalsEmptyStateStep2Title": "장치 승인 활성화",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "사이트 및 터널 연결 이벤트, 연결 및 연결 끊기를 포함합니다.",
|
||||
"httpDestRequestLogsTitle": "HTTP 요청 로그",
|
||||
"httpDestRequestLogsDescription": "프록시된 리소스에 대한 HTTP 요청 로그, 메서드, 경로 및 응답 코드를 포함합니다.",
|
||||
"httpDestAISessionLogsTitle": "AI 세션 로그",
|
||||
"httpDestAISessionLogsDescription": "AI 게이트웨이 요청 및 응답 세션, 프롬프트, 모델 응답 및 토큰 사용을 포함합니다.",
|
||||
"httpDestSaveChanges": "변경 사항 저장",
|
||||
"httpDestCreateDestination": "대상지 생성",
|
||||
"httpDestUpdatedSuccess": "대상지가 성공적으로 업데이트되었습니다",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "관리자로 보기",
|
||||
"resourceLauncherResourceDetailsDescription": "이 리소스에 대한 연결 정보 및 상태입니다.",
|
||||
"resourceLauncherResourceDetails": "리소스 세부 정보",
|
||||
"resourceLauncherSitesDescription": "리소스는 다음 사이트를 통해 액세스할 수 있습니다.",
|
||||
"resourceLauncherViewSiteAsAdmin": "관리자로 사이트 보기",
|
||||
"resourceLauncherFilterBySite": "사이트별 필터",
|
||||
"resourceLauncherSshCommand": "SSH 명령",
|
||||
"resourceLauncherSshCommandDescription": "Pangolin CLI를 사용하여 이 리소스에 대한 SSH 세션을 엽니다.",
|
||||
"resourceLauncherAuthMethodsDescription": "이 리소스에 활성화된 인증 방법입니다.",
|
||||
"resourceLauncherPrivateClientRequired": "이 리소스에 개인적으로 접근하려면 기기에서 클라이언트로 연결하십시오.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "클라이언트 연결 필요",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "레이블 없음",
|
||||
"resourceLauncherAiGateway": "AI 게이트웨이",
|
||||
"resourceLauncherNoSite": "사이트 없음",
|
||||
"resourceLauncherAvailableModels": "사용 가능한 모델",
|
||||
"resourceLauncherAvailableModelsDescription": "이 AI 게이트웨이에서 사용할 수 있는 모델입니다.",
|
||||
"resourceLauncherAvailableModelsEmpty": "이 리소스에 사용할 수 있는 모델이 없습니다.",
|
||||
"resourceLauncherAvailableModelsError": "사용 가능한 모델을 로드할 수 없습니다.",
|
||||
"resourceLauncherApiKeys": "API 키",
|
||||
"resourceLauncherApiKeysDescription": "신원 키나 속성 키를 사용하여 이 리소스에 인증하십시오.",
|
||||
"resourceLauncherApiKeysIdentity": "신원 키",
|
||||
"resourceLauncherApiKeysManual": "속성 키",
|
||||
"resourceLauncherApiKeysEmpty": "이 리소스에 사용할 수 있는 API 키가 없습니다.",
|
||||
"resourceLauncherApiKeysError": "API 키를 로드할 수 없습니다.",
|
||||
"resourceLauncherNoResourcesInGroup": "이 그룹에는 리소스가 없습니다",
|
||||
"resourceLauncherEmptyStateTitle": "사용 가능한 리소스 없음",
|
||||
"resourceLauncherEmptyStateDescription": "아직 리소스에 대한 액세스 권한이 없습니다. 액세스를 요청하려면 관리자에게 문의하세요.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "런처 뷰를 삭제할 수 없습니다. 다시 시도하세요.",
|
||||
"memberPortalPrevious": "이전",
|
||||
"memberPortalNext": "다음",
|
||||
"httpSettings": "HTTP 설정",
|
||||
"tcpSettings": "TCP 설정",
|
||||
"udpSettings": "UDP 설정",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "보안 연결 설정 중…",
|
||||
"sshConnecting": "연결 중…",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "유니코드 키보드 모드",
|
||||
"sessionToolbarShow": "툴바 보기",
|
||||
"sessionToolbarHide": "툴바 숨기기",
|
||||
"actionUpdateSiteApprovals": "사이트 승인 업데이트"
|
||||
"actionUpdateSiteApprovals": "사이트 승인 업데이트",
|
||||
"check": "확인"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"inviteLoginUser": "Lütfen doğru kullanıcı olarak oturum açtığınızdan emin olun.",
|
||||
"inviteErrorNoUser": "Üzgünüz, ancak erişmeye çalıştığınız davet, var olan bir kullanıcı için değil gibi görünüyor.",
|
||||
"inviteCreateUser": "Öncelikle bir hesap oluşturun.",
|
||||
"inviteErrorOidcNotAllowed": "Davetler yalnızca dahili hesaplar tarafından kabul edilebilir. Oturumunuzu kapatın ve bu e-postayla parolanızla giriş yapın.",
|
||||
"inviteLoginInternalOnly": "Davet, parolası olan bir dahili hesap gerektirir. Hesap oluşturun veya parolanızla giriş yapın.",
|
||||
"goHome": "Ana Sayfaya Dön",
|
||||
"inviteLogInOtherUser": "Başka bir kullanıcı olarak giriş yapın",
|
||||
"createAnAccount": "Bir Hesap Oluşturun",
|
||||
@@ -151,6 +153,7 @@
|
||||
"siteResourcesTargetsOnSite": "Bu sitedeki hedefler",
|
||||
"siteSetting": "{siteName} Ayarları",
|
||||
"siteNewtTunnel": "Newt Site (Önerilen)",
|
||||
"pangolinSite": "Pangolin Sitesi",
|
||||
"siteNewtTunnelDescription": "Ağınıza giriş noktası oluşturmanın en kolay yolu. Ekstra kurulum gerekmez.",
|
||||
"siteWg": "Temel WireGuard",
|
||||
"siteWgDescription": "Bir tünel oluşturmak için herhangi bir WireGuard istemcisi kullanın. Manuel NAT kurulumu gereklidir.",
|
||||
@@ -286,7 +289,21 @@
|
||||
"noCountryFound": "Ülke bulunamadı.",
|
||||
"siteSelectionDescription": "Bu site hedefe bağlantı sağlayacaktır.",
|
||||
"resourceType": "Kaynak Türü",
|
||||
"resourceTypeDescription": "Bu, kaynak protokolünü ve tarayıcıda nasıl görüntüleneceğini kontrol eder. Bu daha sonra değiştirilemez.",
|
||||
"resourceTypeDescription": "Bu, kaynak protokolünü kontrol eder ve daha sonra değiştirilemez.",
|
||||
"resourceTypeSearch": "Kaynak türlerini ara...",
|
||||
"resourceTypeNotFound": "Hiçbir kaynak türü bulunamadı",
|
||||
"resourceTypeHttpDescription": "Bir alan adı kullanarak HTTPS üzerinden web trafiğini proxy yapın",
|
||||
"resourceTypeInferenceDescription": "Bağlı sağlayıcılar üzerinden AI isteklerini yönlendirin",
|
||||
"resourceTypeSshDescription": "Tarayıcıdan bir SSH sunucusuna erişim",
|
||||
"resourceTypeRdpDescription": "Tarayıcıdan bir uzaktan masaüstüne erişim",
|
||||
"resourceTypeVncDescription": "Tarayıcıdan bir VNC masaüstüne erişim",
|
||||
"resourceTypeTcpDescription": "Bir bağlantı noktası numarası kullanarak ham TCP trafiğini proxy yapın",
|
||||
"resourceTypeUdpDescription": "Bir bağlantı noktası numarası kullanarak ham UDP trafiğini proxy yapın",
|
||||
"privateResourceTypeDescription": "Bu, müşterilerin kaynağa nasıl erişeceğini kontrol eder. Daha sonra değiştirilemez.",
|
||||
"privateResourceTypeHostDescription": "Bağlanan müşterilere site ağında tek bir ana makineyi açığa çıkartın",
|
||||
"privateResourceTypeCidrDescription": "Bağlanan müşterilere site ağında bir CIDR aralığını açığa çıkartın",
|
||||
"privateResourceTypeHttpDescription": "Bir alan adı üzerinden HTTP veya HTTPS hizmetine eriş",
|
||||
"privateResourceTypeSshDescription": "Bağlanan müşteriler için bir SSH sunucusuna eriş",
|
||||
"resourceDomainDescription": "Kaynak bu tam etki alanı adıyla sunulacak.",
|
||||
"resourceHTTPSSettings": "HTTPS Ayarları",
|
||||
"resourceHTTPSSettingsDescription": "Kaynağınıza HTTPS üzerinden erişimin nasıl sağlanacağını yapılandırın",
|
||||
@@ -449,6 +466,8 @@
|
||||
"apiKeysDelete": "API Anahtarını Sil",
|
||||
"apiKeysManage": "API Anahtarlarını Yönet",
|
||||
"apiKeysDescription": "API anahtarları entegrasyon API'sini doğrulamak için kullanılır",
|
||||
"orgsManage": "Örgütleri Yönet",
|
||||
"orgsDescription": "Bu örnekteki tüm örgütleri görüntüle ve yönet",
|
||||
"provisioningKeysTitle": "Tedarik Anahtarı",
|
||||
"provisioningKeysManage": "Tedarik Anahtarlarını Yönet",
|
||||
"provisioningKeysDescription": "Tedarik anahtarları, organizasyonunuz için otomatik site sağlama işlemini doğrulamak için kullanılır.",
|
||||
@@ -700,6 +719,7 @@
|
||||
"nameOptional": "İsim (İsteğe Bağlı)",
|
||||
"accessControls": "Erişim Kontrolleri",
|
||||
"userDescription2": "Bu kullanıcı üzerindeki ayarları yönetin",
|
||||
"userGeneralSettingsDescription": "Bu kullanıcının örgütteki rollerini ve ayarlarını yönetin",
|
||||
"accessRoleErrorAdd": "Kullanıcıyı role ekleme başarısız oldu",
|
||||
"accessRoleErrorAddDescription": "Kullanıcı role eklenirken bir hata oluştu.",
|
||||
"userSaved": "Kullanıcı kaydedildi",
|
||||
@@ -742,7 +762,7 @@
|
||||
"proxyUpdatedDescription": "Proxy ayarları başarıyla güncellendi",
|
||||
"proxyErrorUpdate": "Proxy ayarları güncellenemedi",
|
||||
"proxyErrorUpdateDescription": "Proxy ayarlarını güncellerken bir hata oluştu",
|
||||
"targetAddr": "Host",
|
||||
"targetAddr": "Sunucu",
|
||||
"targetPort": "Bağlantı Noktası",
|
||||
"targetProtocol": "Protokol",
|
||||
"targetTlsSettings": "HTTPS & TLS Settings",
|
||||
@@ -855,12 +875,16 @@
|
||||
"authMethodsSave": "Ayarları Kaydet",
|
||||
"policyAuthStackTitle": "Kimlik Doğrulama",
|
||||
"policyAuthStackDescription": "Bu kaynağa erişim için hangi kimlik doğrulama yöntemlerinin gerekli olduğuna karar verin",
|
||||
"policyAuthInferenceStackDescription": "Bu AI geçidine kimlerin ve hangi rollerin kimlik doğrulama yapabileceğini seçin",
|
||||
"policyAuthOrLogicTitle": "Birden fazla kimlik doğrulama yöntemi etkin",
|
||||
"policyAuthOrLogicBanner": "Ziyaretçiler aşağıdaki etkin yöntemlerden herhangi birini kullanarak kimlik doğrulaması yapabilirler. Hepsini tamamlamaları gerekmez.",
|
||||
"policyAuthMethodActive": "Etkin",
|
||||
"policyAuthMethodOff": "Kapalı",
|
||||
"policyAuthSsoTitle": "Platform SSO",
|
||||
"policyAuthSsoTitle": "Platform SSO'sunu Kullanın",
|
||||
"policyAuthSsoDescription": "Organizasyonunuzun kimlik sağlayıcısı üzerinden oturum açmayı zorunlu kılın",
|
||||
"policyAuthInferenceIdentityKeySignInUrl": "Giriş URL'si",
|
||||
"policyAuthInferenceIdentityKeyHelpNoUrl": "Her kullanıcının zaten bir kimlik API anahtarı vardır, bu nedenle yalnızca kullanıcı olmayan istemciler veya ortak erişim için sanal API anahtarları oluşturmanız gerekir. Kullanıcılar bu kaynağın URL'si üzerinden kimlik sağlayıcılarıyla oturum açarak anahtarlarını alabilirler, bu girişten sonra gösterilecektir.",
|
||||
"policyAuthInferenceIdentityKeyHelp": "Her kullanıcının zaten bir kimlik API anahtarı vardır, bu nedenle yalnızca kullanıcı olmayan istemciler veya ortak erişim için sanal API anahtarları oluşturmanız gerekir. Kullanıcılar burada kimlik sağlayıcılarıyla giriş yaptıktan sonra anahtarlarını alırlar.",
|
||||
"policyAuthSsoSummary": "{idp} · {users} kullanıcısı, {roles} rolü",
|
||||
"policyAuthSsoDefaultIdp": "Varsayılan sağlayıcı",
|
||||
"policyAuthAddDefaultIdentityProvider": "Varsayılan Kimlik Sağlayıcı Ekle",
|
||||
@@ -892,7 +916,7 @@
|
||||
"policyAccessRulesFallthroughOff": "Kurallar devre dışı bırakıldığında, tüm trafik kimlik doğrulamasına geçer.",
|
||||
"policyAccessRulesFallthroughOn": "Herhangi bir kural eşleşmediğinde trafik kimlik doğrulamasına geçer.",
|
||||
"rulesPlaceholderCidr": "10.0.0.0/8",
|
||||
"rulesPlaceholderPath": "/admin/*",
|
||||
"rulesPlaceholderPath": "/yönetici/*",
|
||||
"rulesPlaceholderGeo": "RU, KP",
|
||||
"rulesSave": "Kuralları Kaydet",
|
||||
"resourceErrorCreate": "Kaynak oluşturma hatası",
|
||||
@@ -1079,6 +1103,8 @@
|
||||
"accessRoleErrorNewRequired": "Yeni rol gerekli",
|
||||
"accessRoleErrorRemove": "Rol kaldırılamadı",
|
||||
"accessRoleErrorRemoveDescription": "Rol kaldırılırken bir hata oluştu.",
|
||||
"accessRoleInferenceBudget": "AI Bütçesi",
|
||||
"accessRoleInferenceBudgetDescription": "Bu role ait üyelerin harcama veya jeton sınırlarına dayalı AI kullanımını nasıl kısıtlayacaklarını yapılandırın",
|
||||
"accessRoleName": "Rol Adı",
|
||||
"accessRoleQuestionRemove": "{name} rolünü silmek üzeresiniz. Bu eylemi geri alamazsınız.",
|
||||
"accessRoleRemove": "Rolü Kaldır",
|
||||
@@ -1092,8 +1118,10 @@
|
||||
"sitesNotFound": "Site bulunamadı.",
|
||||
"pangolinServerAdmin": "Sunucu Yöneticisi - Pangolin",
|
||||
"licenseTierProfessional": "Profesyonel Lisans",
|
||||
"licenseTierEnterprise": "Kurumsal Lisans",
|
||||
"licenseTierPersonal": "Kişisel Lisans",
|
||||
"licenseTierEnterprise": "Kurumsal",
|
||||
"licenseTierPersonal": "Kişisel",
|
||||
"licenseTierTier1": "Başlangıç",
|
||||
"licenseTierTier2": "Ölçek",
|
||||
"licensed": "Lisanslı",
|
||||
"yes": "Evet",
|
||||
"no": "Hayır",
|
||||
@@ -1154,6 +1182,10 @@
|
||||
"idpJmespathAboutDescriptionLink": "JMESPath hakkında daha fazla bilgi edinin",
|
||||
"idpJmespathLabel": "Tanımlayıcı Yolu",
|
||||
"idpJmespathLabelDescription": "The JMESPath to the user identifier in the ID token",
|
||||
"idpIdentifierChangeTitle": "Tanımlayıcı Yol Değişikliği Uyarısı",
|
||||
"idpIdentifierChangeDescription": "Tanımlayıcı yolu değiştirmek üzeresiniz. Bu, mevcut kullanıcıların nasıl eşleneceğini etkileyecektir. Bu kimlik sağlayıcı üzerinden daha önce oturum açmış olan kullanıcılar artık aynı kullanıcılar olarak tanınmayabilir.",
|
||||
"idpIdentifierChangeConfirmMessage": "Onaylıyorum",
|
||||
"idpIdentifierChangeWarningText": "Bu, mevcut kullanıcıların nasıl eşleneceğini etkileyecek",
|
||||
"idpJmespathEmailPathOptional": "E-posta Yolu (İsteğe Bağlı)",
|
||||
"idpJmespathEmailPathOptionalDescription": "The JMESPath to the user's email in the ID token",
|
||||
"idpJmespathNamePathOptional": "Ad Yolu (İsteğe Bağlı)",
|
||||
@@ -1398,6 +1430,24 @@
|
||||
"logoutError": "Çıkış yaparken hata",
|
||||
"signingAs": "Olarak giriş yapıldı",
|
||||
"serverAdmin": "Sunucu Yöneticisi",
|
||||
"promoteServerAdmin": "Sunucu yöneticisine terfi et",
|
||||
"promoteServerAdminTitle": "Sunucu Yöneticisine Terfi",
|
||||
"promoteServerAdminQuestion": "{selectedUser} adresini sunucu yöneticisi olarak terfi etmek istediğinizden emin misiniz?",
|
||||
"promoteServerAdminMessage": "Sunucu yöneticileri en yüksek ayrıcalıklara sahiptir ve sunucuyu yönetebilir.",
|
||||
"promoteServerAdminWarning": "Bu, kullanıcıyı indirerek herhangi bir zamanda geri alınabilir.",
|
||||
"promoteServerAdminConfirm": "Sunucu Yöneticisine Terfi",
|
||||
"promoteServerAdminSuccess": "Kullanıcı Terfi Edildi",
|
||||
"promoteServerAdminSuccessDescription": "{selectedUser} artık bir sunucu yöneticisi.",
|
||||
"promoteServerAdminError": "Kullanıcıyı terfi ettirme başarısız oldu",
|
||||
"demoteServerAdmin": "Sunucu yöneticisinden negatif terfi et",
|
||||
"demoteServerAdminTitle": "Sunucu Yöneticisinden Negatif Terfi",
|
||||
"demoteServerAdminQuestion": "{selectedUser} adresini sunucu yöneticiliğinden indirmek istediğinizden emin misiniz?",
|
||||
"demoteServerAdminMessage": "{selectedUser} tüm sunucu yönetici ayrıcalıklarını kaybedecek.",
|
||||
"demoteServerAdminWarning": "Bu, kullanıcıyı terfi ettirerek herhangi bir zamanda geri alınabilir.",
|
||||
"demoteServerAdminConfirm": "Sunucu yöneticisinden negatif terfi et",
|
||||
"demoteServerAdminSuccess": "Kullanıcı indirildi",
|
||||
"demoteServerAdminSuccessDescription": "{selectedUser} artık bir sunucu yöneticisi değil.",
|
||||
"demoteServerAdminError": "Kullanıcıyı indirirken başarısız oldu",
|
||||
"managedSelfhosted": "Yönetilen Self-Hosted",
|
||||
"otpEnable": "İki faktörlü özelliğini etkinleştir",
|
||||
"otpDisable": "İki faktörlü özelliğini devre dışı bırak",
|
||||
@@ -1424,6 +1474,28 @@
|
||||
"actionDeleteSite": "Siteyi Sil",
|
||||
"actionGetSite": "Siteyi Al",
|
||||
"actionListSites": "Siteleri Listele",
|
||||
"actionCreateAiProvider": "AI Sağlayıcı Oluştur",
|
||||
"actionDeleteAiProvider": "AI Sağlayıcı Sil",
|
||||
"actionGetAiProvider": "AI Sağlayıcı Al",
|
||||
"actionListAiProviders": "AI Sağlayıcıları Listele",
|
||||
"actionUpdateAiProvider": "AI Sağlayıcı Güncelle",
|
||||
"actionCreateAiModel": "AI Modeli Oluştur",
|
||||
"actionDeleteAiModel": "AI Modeli Sil",
|
||||
"actionGetAiModel": "AI Modeli Al",
|
||||
"actionListAiModels": "AI Modellerini Listele",
|
||||
"actionUpdateAiModel": "AI Modelini Güncelle",
|
||||
"actionCreateAiBudget": "AI Bütçesi Oluştur",
|
||||
"actionDeleteAiBudget": "AI Bütçesini Sil",
|
||||
"actionGetAiBudget": "AI Bütçesi Al",
|
||||
"actionListAiBudgets": "AI Bütçelerini Listele",
|
||||
"actionUpdateAiBudget": "AI Bütçesi Güncelle",
|
||||
"actionListResourceAiModels": "Kaynak AI Modellerini Listele",
|
||||
"actionSetResourceAiModels": "Kaynak AI Modellerini Ayarla",
|
||||
"actionCreateVirtualApiKey": "Sanal API Anahtarı Oluştur",
|
||||
"actionDeleteVirtualApiKey": "Sanal API Anahtarını Sil",
|
||||
"actionGetVirtualApiKey": "Sanal API Anahtarı Al",
|
||||
"actionListVirtualApiKeys": "Sanal API Anahtarlarını Listele",
|
||||
"actionUpdateVirtualApiKey": "Sanal API Anahtarı Güncelle",
|
||||
"actionApplyBlueprint": "Planı Uygula",
|
||||
"actionListBlueprints": "Plan Listesini Görüntüle",
|
||||
"actionGetBlueprint": "Planı Elde Et",
|
||||
@@ -1449,8 +1521,11 @@
|
||||
"actionSetResourcePincode": "Kaynak PIN Kodunu Ayarla",
|
||||
"actionSetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesi Ayarla",
|
||||
"actionGetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesini Al",
|
||||
"actionListResourcePolicies": "Kaynak Politikalarını Listele",
|
||||
"actionCreateResourcePolicy": "Kaynak Politikası Oluştur",
|
||||
"actionGetResourcePolicy": "Kaynak Politikasını Al",
|
||||
"actionUpdateResourcePolicy": "Kaynak Politikasını Güncelle",
|
||||
"actionDeleteResourcePolicy": "Kaynak Politikasını Sil",
|
||||
"actionSetResourcePolicyUsers": "Kaynak Politika Kullanıcılarını Ayarla",
|
||||
"actionSetResourcePolicyRoles": "Kaynak Politika Rolleri Ayarla",
|
||||
"actionSetResourcePolicyPassword": "Kaynak Politika Şifresini Ayarla",
|
||||
@@ -1526,6 +1601,8 @@
|
||||
"search": "Ara…",
|
||||
"searchPlaceholder": "Ara...",
|
||||
"emptySearchOptions": "Seçenek bulunamadı",
|
||||
"ipFilterSearchPlaceholder": "Bir IP adresi girin…",
|
||||
"ipFilterEmptyMessage": "Filtrelemek için bir IP adresi girin",
|
||||
"create": "Oluştur",
|
||||
"orgs": "Organizasyonlar",
|
||||
"loginError": "Beklenmeyen bir hata oluştu. Lütfen tekrar deneyin.",
|
||||
@@ -1560,6 +1637,8 @@
|
||||
"commandPaletteCreateMachineClient": "Makine İstemcisi Oluştur",
|
||||
"commandPaletteCreateAlertRule": "Uyarı Kuralı Oluştur",
|
||||
"commandPaletteCreateIdentityProvider": "Kimlik Sağlayıcı Oluştur",
|
||||
"commandPaletteCreateAiProvider": "Yapay Zeka Sağlayıcısı Oluştur",
|
||||
"commandPaletteCreateVirtualApiKey": "Sanal API Anahtarı Oluştur",
|
||||
"commandPaletteToggleTheme": "Temayı Aç/Kapat",
|
||||
"commandPaletteChooseOrganization": "Organizasyon Seç",
|
||||
"commandPaletteShortcutMac": "⌘K",
|
||||
@@ -1622,7 +1701,425 @@
|
||||
"sidebarInvitations": "Davetiye",
|
||||
"sidebarRoles": "Roller",
|
||||
"sidebarShareableLinks": "Paylaşılabilir Bağlantılar",
|
||||
"sidebarAiGateway": "AI Geçidi",
|
||||
"sidebarAiProviders": "Sağlayıcılar",
|
||||
"commandAiProviders": "AI Sağlayıcıları",
|
||||
"sidebarVirtualApiKeys": "Sanal API Anahtarları",
|
||||
"sidebarMyApiKeys": "API Anahtarlarınız",
|
||||
"sidebarAccount": "Başlatıcı",
|
||||
"commandVirtualApiKeys": "Sanal API Anahtarları",
|
||||
"virtualApiKeysTitle": "Sanal API Anahtarlarını Yönet",
|
||||
"virtualApiKeysDescription": "AI Geçitlerine erişim için manuel API anahtarları oluşturun ve yönetin",
|
||||
"virtualApiKeysTabIdentity": "Kimlik Anahtarları",
|
||||
"virtualApiKeysTabVirtual": "Sanal Anahtarlar",
|
||||
"virtualApiKeysIdentitySplashTitle": "Her Kullanıcı İçin Kimlik Anahtarları",
|
||||
"virtualApiKeysIdentitySplashDescription": "Her kullanıcının bu organizasyon için zaten bir Pangolin kimlik anahtarı var. Hesaplarına özgüdür ve erişebilecekleri AI geçitlerine kimlik doğrulama sağlar.",
|
||||
"virtualApiKeysIdentitySplashExample": "Örnek",
|
||||
"virtualApiKeysIdentitySplashRetrieveTitle": "Kullanıcıların Anahtarlarını Nasıl Aldığı",
|
||||
"virtualApiKeysIdentitySplashRetrieveResource": "Tarayıcıda herkese açık bir AI geçit URL'sini ziyaret edin ve hesaplarıyla giriş yapın.",
|
||||
"virtualApiKeysIdentitySplashRetrievePage": "Veya giriş yaparken <url></url> adresine gidin.",
|
||||
"virtualApiKeysIdentitySplashManual": "Sanal Anahtarlar sekmesinde ek sanal API anahtarları oluşturabilirsiniz. Bu anahtarlar belirli herkese açık AI geçitlerine göre kapsamlandırılabilir ve isteğe bağlı olarak bir kullanıcıyla ilişkilendirilebilir. Bir anahtar oluşturmak seçilen kamu AI geçitlerine erişim sağlar.",
|
||||
"virtualApiKeysIdentitySplashGoToVirtual": "Anahtarı Manuel Olarak Oluştur",
|
||||
"virtualApiKeysEmailIdentity": "Kimlik Anahtarlarını E-posta Gönder",
|
||||
"virtualApiKeysEmailIdentityDescription": "Seçilen her kullanıcıya veya role Pangolin kimlik anahtarlarını gönderin.",
|
||||
"virtualApiKeysEmailIdentitySendAll": "Tüm kullanıcılara gönder",
|
||||
"virtualApiKeysEmailIdentitySendAllDescription": "Bir hesap e-posta adresi olan her organizasyon üyesine e-posta gönderin.",
|
||||
"virtualApiKeysEmailIdentitySelectUsers": "Kullanıcılar",
|
||||
"virtualApiKeysEmailIdentitySelectRoles": "Roller",
|
||||
"virtualApiKeysEmailIdentitySubmit": "E-postaları Gönder",
|
||||
"virtualApiKeysEmailIdentitySuccess": "Kimlik anahtarları e-posta ile gönderildi",
|
||||
"virtualApiKeysEmailIdentitySuccessDescription": "{sent} e-posta gönderildi.",
|
||||
"virtualApiKeysEmailIdentitySkipped": "E-posta adresine sahip olmayan {skipped} kullanıcılar atlandı.",
|
||||
"virtualApiKeysEmailIdentityRecipientsRequired": "En az bir kullanıcı veya rol seçin ya da tüm kullanıcılara gönderin.",
|
||||
"virtualApiKeysEmailIdentityError": "Kimlik anahtarlarını gönderme hatası",
|
||||
"virtualApiKeysEmailIdentityErrorDescription": "Kimlik anahtarları e-posta ile gönderilemiyor",
|
||||
"virtualApiKeysBannerTitle": "Her Kullanıcı İçin Kimlik Anahtarları",
|
||||
"virtualApiKeysBannerDescription": "Her kullanıcı {keysUrl}'de kullanılabilir bir kimlik anahtarına sahiptir. Burada genel AI geçitlerine doğrudan erişim sağlayan anahtarlar da oluşturabilirsiniz.",
|
||||
"virtualApiKeysBannerButtonText": "Kimlik Anahtarlarını Görüntüle",
|
||||
"virtualApiKeys": "Sanal API Anahtarları",
|
||||
"virtualApiKeysSearch": "Anahtarları ara...",
|
||||
"virtualApiKeysCreate": "Sanal API Anahtarı Oluştur",
|
||||
"virtualApiKeysCreateDescription": "Bu organizasyonda genel AI geçitlerini arayabilecek manuel bir anahtar mint edin",
|
||||
"virtualApiKeysCreateButton": "Anahtar Oluştur",
|
||||
"virtualApiKeysEmpty": "Henüz sanal API anahtarı yok",
|
||||
"virtualApiKeysName": "Ad",
|
||||
"virtualApiKeysDescriptionOptional": "Açıklama (isteğe bağlı)",
|
||||
"virtualApiKeysAssociateUserOptional": "Kullanıcıyla İlişkilendir (isteğe bağlı)",
|
||||
"virtualApiKeysAssociateUserDescription": "Bu anahtarı bir kullanıcıyla ilişkilendirerek kullanım takibini yapın. Oluşturulduktan hemen sonra bu anahtar seçilen kamu AI geçitlerine erişim sağlar. Anahtar, kullanıcının profilinde de gösterilebilir.",
|
||||
"virtualApiKeysAllResources": "Tüm kamu AI geçitleri",
|
||||
"virtualApiKeysAllResourcesDescription": "Bu anahtara organizasyondaki tüm genel AI geçitlerine erişim izni ver",
|
||||
"virtualApiKeysSelectResources": "Genel AI Geçitleri",
|
||||
"virtualApiKeysSelectResourcesPlaceholder": "Kaynakları seçin",
|
||||
"virtualApiKeysSelectResourcesDescription": "Bu anahtarın hangi kamu AI geçitlerine erişebileceğini seçin",
|
||||
"virtualApiKeysNoResources": "Kaynak yok",
|
||||
"virtualApiKeysSecret": "Anahtar",
|
||||
"virtualApiKeysCopyKey": "Bu anahtarı kopyalayın. Tablo veya düzenlemede tekrar görebilirsiniz.",
|
||||
"virtualApiKeysViewSecret": "Gizliyi Görüntüle",
|
||||
"virtualApiKeysViewSecretTitle": "Sanal API Anahtarı Gizli",
|
||||
"virtualApiKeysViewSecretDescription": "Bu gizli, bu anahtara atanmış kamu AI geçitlerine erişim sağlar",
|
||||
"virtualApiKeysEdit": "Sanal API Anahtarı Düzenle",
|
||||
"virtualApiKeysEditDescription": "Bu anahtar için ilişkili kullanıcı ve kamu AI geçidine erişimi güncelle",
|
||||
"virtualApiKeysSaveButton": "Değişiklikleri Kaydet",
|
||||
"virtualApiKeysSelectResourcesRequired": "En az bir kamu AI geçidi seçin veya tüm kamu AI geçitlerini etkinleştirin",
|
||||
"virtualApiKeysUpdated": "Sanal API anahtarı güncellendi",
|
||||
"virtualApiKeysUpdatedDescription": "Sanal API anahtarı güncellendi",
|
||||
"virtualApiKeysErrorUpdate": "Sanal API anahtarı güncellenirken hata",
|
||||
"virtualApiKeysErrorUpdateDescription": "Sanal API anahtarı güncellenemedi",
|
||||
"virtualApiKeysErrorCreate": "Sanal API anahtarı oluşturulurken hata",
|
||||
"virtualApiKeysErrorCreateDescription": "Sanal API anahtarı oluşturulamadı",
|
||||
"virtualApiKeysErrorDelete": "Sanal API anahtarı silinirken hata",
|
||||
"virtualApiKeysErrorDeleteMessage": "Sanal API anahtarı silinemedi",
|
||||
"virtualApiKeysDeleted": "Sanal API anahtarı silindi",
|
||||
"virtualApiKeysDeletedDescription": "Sanal API anahtarı silinmiştir",
|
||||
"virtualApiKeysDelete": "Sanal API Anahtarını Sil",
|
||||
"virtualApiKeysDeleteConfirm": "Anahtarı Sil",
|
||||
"virtualApiKeysQuestionRemove": "Bu sanal API anahtarını silmek istediğinizden emin misiniz?",
|
||||
"virtualApiKeysMessageRemove": "Bu anahtarı kullanan müşteriler hemen erişimlerini kaybedecektir.",
|
||||
"virtualApiKeysErrorFetchSecret": "Gizli yüklenirken hata",
|
||||
"virtualApiKeysErrorFetchSecretDescription": "Sanal API anahtarı gizli yüklenemedi",
|
||||
"virtualApiKeysFilterUnassigned": "Atanmamış",
|
||||
"virtualApiKeysInferenceBudget": "Bütçe",
|
||||
"virtualApiKeysInferenceBudgetDescription": "Bu anahtarın AI kullanımını harcama veya jeton sınırlarına göre nasıl kısıtladığını yapılandırın",
|
||||
"virtualApiKeysEmailOnGenerate": "Anahtar oluşturulduğunda e-posta gönder",
|
||||
"virtualApiKeysEmailThisKey": "Bu anahtarı e-posta gönder",
|
||||
"virtualApiKeysEmailOnGenerateDescription": "Anahtar oluşturulduktan sonra ilişkili kullanıcıya ve ek adreslere gönderin",
|
||||
"virtualApiKeysEmailThisKeyDescription": "Geçerli anahtarı ilişkili kullanıcıya ve ek adreslere gönderin",
|
||||
"virtualApiKeysEmailSmtpRequired": "Bu sunucuda e-posta yapılandırılmamış",
|
||||
"virtualApiKeysEmailSmtpRequiredDescription": "SMTP yapılandırmasını yaparak sanal API anahtarlarını e-posta gönderin.",
|
||||
"virtualApiKeysEmailSendToUser": "İlişkili kullanıcıya gönder",
|
||||
"virtualApiKeysEmailSendToUserDescription": "Anahtarı ilişkili kullanıcının hesabını alın ve e-posta gönderin",
|
||||
"virtualApiKeysEmailSendToUserDisabled": "İlişkili bir kullanıcı seçin ve o kullanıcıya anahtarı gönder",
|
||||
"virtualApiKeysEmailAdditional": "Ek e-postalar",
|
||||
"virtualApiKeysEmailAdditionalPlaceholder": "E-posta ekle ve Enter tuşuna basın",
|
||||
"virtualApiKeysEmailRecipientsRequired": "İlişkili kullanıcının hesabını seçin veya en az bir e-posta adresi ekleyin",
|
||||
"myVirtualApiKeysTitle": "API Anahtarlarınız",
|
||||
"myVirtualApiKeysDescription": "Bu organizasyonda size atanmış kimlik anahtarınızı ve sanal API anahtarlarınızı görüntüleyin",
|
||||
"myVirtualApiKeysResourceTitle": "{resourceName} için API Anahtarlarınız",
|
||||
"myVirtualApiKeysResourceDescription": "{resourceName} erişebileceğiniz kimlik anahtarınızı ve size atanmış sanal API anahtarlarını görüntüleyin",
|
||||
"myVirtualApiKeysIdentityTitle": "Kimlik Anahtarı",
|
||||
"myVirtualApiKeysIdentityHeadline": "Kişisel API Anahtarınız",
|
||||
"myVirtualApiKeysIdentityDescription": "Bu organizasyon için kişisel anahtarınız. Hesabınıza özeldir ve AI Gateway kaynaklarına çağrı yaptığınızda sizi tanımlar.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "{resourceName} için Kişisel API Anahtarınız",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Bu organizasyon için kişisel anahtarınız. {resourceName} 'e çağrı yaparken kullanın.",
|
||||
"myVirtualApiKeysManualTitle": "Atanmış Anahtarlar",
|
||||
"myVirtualApiKeysManualDescription": "Hesabınıza yönetici tarafından atanmış manuel sanal API anahtarları",
|
||||
"myVirtualApiKeysManualResourceDescription": "{resourceName} erişebilecek hesabınıza atanmış manuel sanal API anahtarları",
|
||||
"myVirtualApiKeysManualEmpty": "Henüz atanmış anahtar bulunmamaktadır",
|
||||
"myVirtualApiKeysKindUser": "Kimlik",
|
||||
"myVirtualApiKeysKindManual": "Manuel",
|
||||
"myVirtualApiKeysUnnamed": "İsimsiz anahtar",
|
||||
"myVirtualApiKeysRevealSecret": "Gizliyi Açıklayın",
|
||||
"myVirtualApiKeysViewSecretDescription": "Bu gizli, sizi AI Gateway kaynaklarına kimlik doğrular",
|
||||
"aiClientConfigTitle": "Kodlama Aracılarını Yapılandır",
|
||||
"aiClientConfigDescription": "Yaygın kodlama aracılarının yapılandırmalarını kopyalayın veya Pangolin CLI sizin için otomatik olarak kurmasını sağlayın.",
|
||||
"aiClientConfigDescriptionClaude": "Anthropic'in terminal için aracılık kodlama aracı.",
|
||||
"aiClientConfigDescriptionCodex": "OpenAI'nin terminal için aracılık kodlama aracı.",
|
||||
"aiClientConfigDescriptionOpencode": "Açık kaynak terminal kodlama aracı.",
|
||||
"aiClientConfigDescriptionGemini": "Google'un terminal için agentik kodlama aracı.",
|
||||
"aiClientConfigSetup": "Kurulum",
|
||||
"aiClientConfigTabCli": "Otomatik (CLI)",
|
||||
"aiClientConfigTabManual": "Manuel Yapılandırma",
|
||||
"aiClientConfigPreset": "Yapılandırma",
|
||||
"aiClientConfigStep": "Adım {number}",
|
||||
"aiClientConfigEndpointPlaceholder": "https://example.resource.url.com",
|
||||
"aiClientConfigPlaceholderWarning": "Bu kod parçası, model adı veya kaynak URL'si gibi örnek değerler içerir. Kullanmadan önce bunları kendi değerlerinizle değiştirin.",
|
||||
"aiClientConfigRevealError": "API anahtarınızı yükleyemedik.",
|
||||
"aiClientConfigRevealRetry": "Tekrar deneyin",
|
||||
"resourceGeneralAiClientConfigDescription": "Bu kaynak, Claude Code ve OpenCode gibi yaygın istemcilerden kullanılabilir. <configLink>Daha fazla bilgi edinin</configLink>",
|
||||
"aiProvidersTitle": "AI Sağlayıcıları",
|
||||
"aiProvidersDescription": "Bu organizasyonda AI iş yükleri için model sağlayıcılarla bağlantı kurun",
|
||||
"aiProvidersBannerTitle": "Model Sağlayıcılarla Bağlan",
|
||||
"aiProvidersBannerDescription": "Sağlayıcılar, Pangolin'in AI iş yükleri için kullandığı model arka uçlarıdır. Burada OpenAI, Anthropic ve diğer sağlayıcıları bağlayın, ardından bu sağlayıcıları genel AI geçitlerine ekleyin, böylece kullanıcılar modelleri kimlik farkındalığı ile erişebilir.",
|
||||
"aiProvidersAdd": "Sağlayıcı Ekle",
|
||||
"aiProvidersSearch": "Sağlayıcıları ara...",
|
||||
"aiProvidersEmpty": "Henüz AI sağlayıcısı yok",
|
||||
"aiProviderCreate": "AI Sağlayıcı Oluştur",
|
||||
"aiProviderCreateDescription": "Bu organizasyon için bir model sağlayıcı ekleyin",
|
||||
"aiProviderSeeAll": "Tüm Sağlayıcıları Gör",
|
||||
"aiProviderSetting": "{providerName} için Sağlayıcı Ayarları",
|
||||
"aiProviderSettingDescription": "Bu AI sağlayıcısını yapılandırın",
|
||||
"aiProviderGeneral": "Genel",
|
||||
"aiProviderGeneralDescription": "Bu sağlayıcı için temel ayarlar",
|
||||
"aiProviderConfiguration": "Yapılandırma",
|
||||
"aiProviderConfigurationDescription": "Bu sağlayıcının ağ yönlendirme ve kimlik doğrulaması",
|
||||
"aiProviderNetworkSettings": "Ağ Ayarları",
|
||||
"aiProviderNetworkSettingsDescription": "Trafiğin bu sağlayıcıya nasıl ulaşacağını seçin",
|
||||
"aiProviderAuthSettings": "Kimlik Doğrulama",
|
||||
"aiProviderAuthSettingsDescription": "Bu sağlayıcının URL'sine gelen isteklerde kimlik doğrulamalarını yapılandırın",
|
||||
"aiProviderBudgetSettings": "Bütçe",
|
||||
"aiProviderBudgetSettingsDescription": "Bu sağlayıcının harcama veya jeton sınırlamalarına dayalı olarak kullanımını nasıl kısıtlayacağını yapılandırın",
|
||||
"aiBudgetAdd": "Bütçe Ekle",
|
||||
"aiBudgetEmpty": "Henüz bütçe ayarlanmamış. Harcama veya jeton sınırını ayarlamak için Bütçe Ekle'ye tıklayın.",
|
||||
"aiBudgetUnit": "Harcama Türü",
|
||||
"aiBudgetPeriod": "Sıfırlama Dönemi",
|
||||
"aiBudgetAmount": "Maksimum Harcama",
|
||||
"aiBudgetAmountPlaceholder": "Maksimum harcama",
|
||||
"aiBudgetPeriodHourly": "Saatlik",
|
||||
"aiBudgetPeriodDaily": "Günlük",
|
||||
"aiBudgetPeriodWeekly": "Haftalık",
|
||||
"aiBudgetPeriodMonthly": "Aylık",
|
||||
"aiBudgetPeriodYearly": "Yıllık",
|
||||
"aiBudgetPeriodLifetime": "Ömür Boyu",
|
||||
"aiBudgetUnitUsd": "USD",
|
||||
"aiBudgetUnitTokens": "Jetonlar",
|
||||
"aiBudgetConflictError": "Bu sıfırlama dönemi ve harcama türü için zaten bir bütçe var",
|
||||
"aiBudgetInvalidAmountError": "0'dan büyük bir maksimum harcama girin",
|
||||
"aiBudgetUpdated": "Bütçeler Güncellendi",
|
||||
"aiBudgetErrorSave": "Bütçeleri güncelleme başarısız",
|
||||
"aiProviderType": "Sağlayıcı Türü",
|
||||
"aiProviderTypeSearch": "Sağlayıcıları ara...",
|
||||
"aiProviderTypeNotFound": "Hiçbir sağlayıcı türü bulunamadı",
|
||||
"aiProviderTypeOpenai": "OpenAI",
|
||||
"aiProviderTypeAnthropic": "Anthropic",
|
||||
"aiProviderTypeGoogleGemini": "Google Gemini",
|
||||
"aiProviderTypeVertexAi": "Vertex AI",
|
||||
"aiProviderTypeBedrock": "Amazon Bedrock",
|
||||
"aiProviderTypeMicrosoftFoundry": "Microsoft Foundry",
|
||||
"aiProviderTypeOpenRouter": "OpenRouter",
|
||||
"aiProviderTypeVercelAiGateway": "Vercel AI Geçidi",
|
||||
"aiProviderTypeCustom": "Özel",
|
||||
"aiProviderTypeOpenaiDescription": "OpenAI API",
|
||||
"aiProviderTypeAnthropicDescription": "Anthropic API",
|
||||
"aiProviderTypeGoogleGeminiDescription": "Google Gemini API",
|
||||
"aiProviderTypeVertexAiDescription": "Google Vertex AI API",
|
||||
"aiProviderTypeBedrockDescription": "Amazon Bedrock Runtime API",
|
||||
"aiProviderTypeMicrosoftFoundryDescription": "Microsoft Foundry API",
|
||||
"aiProviderTypeOpenRouterDescription": "OpenRouter API",
|
||||
"aiProviderTypeVercelAiGatewayDescription": "Vercel AI Geçidi API",
|
||||
"aiProviderTypeCustomDescription": "Kendi uç noktanızı getirin veya site hedefleri üzerinden yönlendirin",
|
||||
"aiProviderUpstreamUrl": "Yukarı Akış URL'si",
|
||||
"aiProviderUpstreamUrlDescription": "Sağlayıcı API için Temel URL",
|
||||
"aiProviderUpstreamUrlOptionalDescription": "Bu sağlayıcı için varsayılan yukarı akış URL'sini kullanmak için boş bırakın",
|
||||
"aiProviderEffectiveUpstreamUrl": "Etkili Yukarı Akış URL'si",
|
||||
"aiProviderApiKey": "API Anahtarı",
|
||||
"aiProviderApiKeyDescription": "Bu sağlayıcıya yapılan istekleri kimlik doğrulamak için kullanılan API anahtarı",
|
||||
"aiProviderCustomHeadersDescription": "Bu sağlayıcıya yapılan her istekte gönderilen başlıklar. Yeni satır ile ayrılmış: Başlık-Adı: değer",
|
||||
"aiProviderApiKeyLastChars": "API Anahtarı",
|
||||
"aiProviderAuthType": "Kimlik Doğrulama Türü",
|
||||
"aiProviderAuthTypeSearch": "Kimlik doğrulama türlerini ara...",
|
||||
"aiProviderAuthTypeNotFound": "Kimlik doğrulama türü bulunamadı",
|
||||
"aiProviderAuthTypeBearer": "Taşıyıcı",
|
||||
"aiProviderAuthTypeBearerDescription": "Yetkilendirme: Bearer anahtarı. OpenAI ve çoğu sağlayıcı tarafından kullanılır",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXApiKeyDescription": "x-api-key başlığı. Anthropic tarafından kullanılır",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKeyDescription": "x-goog-api-key başlığı. Google Gemini tarafından kullanılır",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeHecDescription": "Yetkilendirme: Splunk anahtarı. Splunk HTTP Olay Toplayıcı tarafından kullanılır",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI Geçidi",
|
||||
"aiProviderAuthTypeCfAigAuthorizationDescription": "cf-aig-yetkilendirme: Bearer anahtarı. Cloudflare AI Gateway tarafından kullanılır",
|
||||
"aiProviderAuthTypeNone": "Kimlik Doğrulama Yok",
|
||||
"aiProviderAuthTypePassthrough": "Geçir",
|
||||
"aiProviderAuthTypeDescription": "Yukarıdaki API, istekleri nasıl kimlik doğrular",
|
||||
"aiProviderAuthTypePassthroughDescription": "Arayanın API anahtarı başlıklarını yukarı akışa iletin",
|
||||
"aiProviderAuthTypeNoneDescription": "Yukarı akışa kimlik doğrulama başlıkları göndermeyin",
|
||||
"aiProviderRoutingMode": "Yönlendirme Modu",
|
||||
"aiProviderRoutingModeDescription": "Trafiği bir yukarı akış URL'sine veya sitenizdeki HTTP hedeflerine gönderin",
|
||||
"aiProviderRoutingModeUrl": "Yukarı Akış URL'si",
|
||||
"aiProviderRoutingModeUrlDescription": "Bir kamuya açık ya da özel API temel URL'si çağırın",
|
||||
"aiProviderRoutingModeTarget": "Site Hedefleri",
|
||||
"aiProviderRoutingModeTargetDescription": "Siteniz üzerindeki hedefler üzerinden yönlendirin",
|
||||
"aiProviderRoutingModeTargetNote": "Bu sağlayıcıyı oluşturduktan sonra, site hedeflerini Ağ Ayarları sekmesinde yapılandırın.",
|
||||
"aiProviderTargetNoOne": "Bu sağlayıcının herhangi bir hedefi yok. Sitemiz üzerinden istekleri yönlendirmek için bir hedef ekleyin.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Uzaktaki düğümlere bağlı siteler, AI Geçidi sağlayıcılarına yönlendirilemez durumda.",
|
||||
"aiProviderSkipTlsVerification": "TLS Doğrulamayı Atla",
|
||||
"aiProviderSkipTlsVerificationDescription": "Yukarı akış bağlantısı için TLS sertifika doğrulamasını devre dışı bırakın",
|
||||
"aiProviderBudget": "Bütçe",
|
||||
"aiProviderBudgetDescription": "Bu sağlayıcı için isteğe bağlı harcama veya jeton bütçesi",
|
||||
"aiProviderBudgetAmount": "Bütçe Miktarı",
|
||||
"aiProviderBudgetUnit": "Bütçe Birimi",
|
||||
"aiProviderBudgetUnitUsd": "USD",
|
||||
"aiProviderBudgetUnitTokens": "Jetonlar",
|
||||
"aiProviderEnabled": "Etkin",
|
||||
"aiProviderEnabledDescription": "Bu sağlayıcıyı tüm kaynaklarda tamamen devre dışı bırakın",
|
||||
"aiProviderErrorCreate": "AI sağlayıcı oluşturma başarısız",
|
||||
"aiProviderErrorUpdate": "AI sağlayıcı güncelleme başarısız",
|
||||
"aiProviderErrorDelete": "AI sağlayıcı silme başarısız",
|
||||
"aiProviderErrorLoad": "AI sağlayıcı yükleme başarısız",
|
||||
"aiProviderErrorUpstreamUrlInvalid": "Geçerli bir yukarı akış URL'si girin",
|
||||
"aiProviderErrorUpstreamUrlRequired": "Bu sağlayıcı için yukarı akış URL'si gereklidir",
|
||||
"aiProviderErrorAuthTypeRequired": "Kimlik doğrulama türü zorunludur",
|
||||
"aiProviderErrorApiKeyRequired": "API anahtarı zorunludur",
|
||||
"aiProviderErrorRoutingModeTarget": "Site hedefleri yönlendirme sadece özel sağlayıcılar için geçerlidir",
|
||||
"aiProviderErrorCapabilitiesRequired": "En az bir API yetenek seçin",
|
||||
"aiProviderCapabilities": "API Yetenekleri",
|
||||
"aiProviderCapabilitiesDescription": "Bu sağlayıcının hangi API formatlarını kullanabileceğini seçin. Bilinen sağlayıcılar, önerilen varsayılanlarla başlar.",
|
||||
"aiProviderCapabilitiesCustomDescription": "Bu özel sağlayıcının hangi API formatlarını işleyebileceğini seçin",
|
||||
"aiProviderCapabilitiesSelect": "Yetenekleri seçin",
|
||||
"aiProviderCapabilitiesEmpty": "Hiçbir yetenek bulunamadı",
|
||||
"aiProviderCapabilitiesSearch": "Yetenekleri ara...",
|
||||
"aiCapabilityOpenaiChat": "OpenAI Sohbet Tamamlama",
|
||||
"aiCapabilityOpenaiChatDescription": "/v1/chat/tamamlama desteği sağlar",
|
||||
"aiCapabilityOpenaiResponses": "OpenAI Yanıtları",
|
||||
"aiCapabilityOpenaiResponsesDescription": "/v1/yanıtlar desteği sağlar",
|
||||
"aiCapabilityAnthropicMessages": "Anthropic Mesajlar",
|
||||
"aiCapabilityAnthropicMessagesDescription": "/v1/mesajlar desteği sağlar",
|
||||
"aiCapabilityV1Models": "Modeller Listesi",
|
||||
"aiCapabilityV1ModelsDescription": "T /v1/models model keşfini destekler",
|
||||
"aiCapabilityGeminiGenerateContent": "Gemini İçerik Üret",
|
||||
"aiCapabilityGeminiGenerateContentDescription": "Doğrudan Gemini API desteği sağlar",
|
||||
"aiCapabilityBedrockModelInvoke": "Bedrock Modeli Çağır",
|
||||
"aiCapabilityBedrockModelInvokeDescription": "Amazon Bedrock InvokeModel desteği sağlar",
|
||||
"aiCapabilityGoogleGenerateContent": "Vertex İçerik Üret",
|
||||
"aiCapabilityGoogleGenerateContentDescription": "Vertex AI Gemini formatı desteği sağlar",
|
||||
"aiCapabilityGoogleRawPredict": "Vertex Ham Tahmin",
|
||||
"aiCapabilityGoogleRawPredictDescription": "Vertex AI HamTahmin desteği sağlar",
|
||||
"aiCapabilityBedrockConverse": "Bedrock Sohbet",
|
||||
"aiCapabilityBedrockConverseDescription": "Amazon Bedrock Sohbet API desteği sağlar",
|
||||
"aiProviderCreated": "AI sağlayıcı yaratıldı",
|
||||
"aiProviderUpdated": "AI sağlayıcı güncellendi",
|
||||
"aiProviderDeleted": "AI sağlayıcı silindi",
|
||||
"aiProviderDelete": "Sağlayıcıyı Sil",
|
||||
"aiProviderDeleteConfirm": "Sağlayıcıyı Sil",
|
||||
"aiProviderQuestionRemove": "Bu AI sağlayıcıyı silmek istediğinizden emin misiniz?",
|
||||
"aiProviderMessageRemove": "Bu, sağlayıcıyı ve modellerini ve hedeflerini kalıcı olarak silecektir. Geri alınamaz.",
|
||||
"aiProviderErrorNoUpdate": "AI sağlayıcı güncellenemiyor",
|
||||
"aiProviderModels": "Modeller",
|
||||
"aiProviderModelsDescription": "Bu sağlayıcı için izin ve engelleme listelerini tanımlayın. İstekler bir izin girişini karşılamalı ve bir engelleme girişini karşılamamalıdır.",
|
||||
"aiProviderCreateModelsDescription": "Bu sağlayıcının hizmet verebileceği modelleri seçin. Boş bir izin listesi tüm trafiği reddeder. Modelleri daha sonra ekleyebilir veya değiştirebilirsiniz.",
|
||||
"aiProviderModelsPlaceholder": "Modelleri arayın veya özel bir anahtar yazın",
|
||||
"aiProviderModelsAllow": "İzin Listesi",
|
||||
"aiProviderModelsAllowDescription": "Bu sağlayıcı aracılığıyla kullanılabilecek modeller. Boş, tümünü reddet anlamına gelir.",
|
||||
"aiProviderModelsAllowPlaceholder": "Model anahtarını girin",
|
||||
"aiProviderModelsAllowEmpty": "Henüz model eklenmedi.",
|
||||
"aiProviderModelsBlock": "Engelleme Listesi",
|
||||
"aiProviderModelsBlockDescription": "Bir izin girdisiyle eşleşseler bile reddedilecek modeller.",
|
||||
"aiProviderModelsBlockPlaceholder": "Model anahtarını girin",
|
||||
"aiProviderModelsBlockEmpty": "Henüz model eklenmedi.",
|
||||
"aiProviderModelsAdd": "Modelleri Ekle",
|
||||
"aiProviderModelsClearAll": "Hepsini Temizle",
|
||||
"aiProviderModelsAddCustom": "\"{key}\" ekle",
|
||||
"aiProviderModelsAddCustomHint": "Bu özel model anahtarını eklemek için Enter'a basın.",
|
||||
"aiProviderModelsAddBulk": "{count} özel anahtar ekle",
|
||||
"aiProviderModelsAddBulkHint": "{count} özel anahtarı eklemek için Enter'a basın.",
|
||||
"aiProviderModelsAddOne": "Şimdi {key} ekle",
|
||||
"aiProviderModelsAddSelected": "Seçileni Ekle",
|
||||
"aiProviderModelsSelectedCount": "{count} seçildi",
|
||||
"aiProviderModelsSelectAll": "Hepsini seç",
|
||||
"aiProviderModelsClearSelected": "Temizle",
|
||||
"aiProviderModelsBulkHint": "Bilinen modelleri seçin veya özel bir anahtar yazın.",
|
||||
"aiProviderModelsCatalogEmpty": "Eşleşen katalog modeli yok.",
|
||||
"aiProviderModelsCatalogHeading": "Bilinen Modeller",
|
||||
"aiProviderModelsAllLabel": "Tüm modeller",
|
||||
"aiProviderModelsAllPatternHint": "Genel arama deseni: *",
|
||||
"aiProviderModelsAddAllAllow": "Tüm modellere izin ver",
|
||||
"aiProviderModelsAddAllBlock": "Tüm modelleri engelle",
|
||||
"aiProviderModelsAddAllDescription": "* arama desenini kullanarak her model anahtarı eşleşir.",
|
||||
"aiProviderModelsViewMore": "Daha fazla göster ({count})",
|
||||
"aiProviderModelsViewLess": "Daha az göster",
|
||||
"aiProviderModelsRemove": "Modeli kaldır",
|
||||
"aiProviderModelsEditHint": "Model ayarlarını düzenlemek için tıklayın",
|
||||
"aiProviderModelsSourceCatalog": "Bilinen katalog modeli",
|
||||
"aiProviderModelsSourceCustom": "Özel model anahtarı",
|
||||
"aiProviderModelsSourcePattern": "Genel arama deseni",
|
||||
"aiProviderModelsSourceAll": "Her model anahtarı ile eşleşir",
|
||||
"aiProviderModelsBudgetConfigured": "Bütçe yapılandırılmış",
|
||||
"aiProviderModelsEditTitle": "Modeli düzenle",
|
||||
"aiProviderModelsEditDescription": "Model anahtarını güncelleyin veya bütçesini yapılandırın.",
|
||||
"aiProviderModelsBudgetTab": "Bütçe",
|
||||
"aiProviderModelsKeyLabel": "Model Anahtarı",
|
||||
"aiProviderModelsKeyRequired": "Bir model anahtarı girin",
|
||||
"aiProviderModelsKeyDuplicate": "Bu model anahtarı zaten bir listede var",
|
||||
"aiProviderModelsOverlapError": "Bu desenler her iki listede de olamaz: {keys}",
|
||||
"aiProviderModelsUpdated": "Modeller güncellendi",
|
||||
"aiProviderModelsErrorUpdate": "Modeller güncellenemedi",
|
||||
"aiResourceProviders": "Sağlayıcılar",
|
||||
"aiResourceProvidersDescription": "Bu AI ağ geçidi kullanabileceği AI sağlayıcılarını seçin",
|
||||
"aiResourceProvidersHelp": "Sağlayıcıları ekle ve miras almayı (her sağlayıcının listesini kullanmayı) ya da seçmeyi (bu kaynak için bir izin listesini seç) seçin. Birbirine zıt sağlayıcıların izin desenleri izin verilmez.",
|
||||
"aiResourceProvidersSelect": "Sağlayıcıları seçin",
|
||||
"aiResourceProvidersEmpty": "Hiçbir AI sağlayıcı bulunamadı",
|
||||
"aiResourceProvidersNoneAttached": "Henüz hiç sağlayıcı eklenmedi.",
|
||||
"aiResourceProvidersAdd": "Sağlayıcı ekle",
|
||||
"aiResourceProvidersRemove": "Sağlayıcıyı kaldır",
|
||||
"aiResourceProviderToggleEnabled": "Bu kaynaktaki sağlayıcıyı etkinleştir veya devre dışı bırak",
|
||||
"aiResourceProviderDisabled": "Devre Dışı",
|
||||
"aiResourceProvidersUpdated": "Sağlayıcılar güncellendi",
|
||||
"aiResourceProvidersErrorUpdate": "Sağlayıcılar güncellenemedi",
|
||||
"aiResourceProviderEditDescription": "Bu sağlayıcının modellerinin bu kaynakta nasıl göstereceğini seçin.",
|
||||
"viewProviderSettings": "Sağlayıcı Ayarlarını Görüntüle",
|
||||
"aiResourceProviderMode": "Erişim modu",
|
||||
"aiResourceProviderModeInherit": "Miras Al",
|
||||
"aiResourceProviderModeSelect": "Seç",
|
||||
"aiResourceProviderModeSelectSummary": "Seç · {count} modeller",
|
||||
"aiResourceProviderModeInheritHelp": "Bu sağlayıcıda yapılandırıldığı gibi izin ve engelleme listelerini kullanın.",
|
||||
"aiResourceProviderModeSelectHelp": "Bu kaynak için bu sağlayıcının izin listesi modellerinin bir alt kümesini seçin.",
|
||||
"aiResourceProviderAllowModels": "İzin listesi",
|
||||
"aiResourceProviderAllowModelsSelect": "Modelleri seçin",
|
||||
"aiResourceProviderAllowModelsSearch": "Modelleri ara...",
|
||||
"aiResourceProviderAllowModelsEmpty": "Hiçbir model bulunamadı",
|
||||
"aiResourceProviderAllowModelsHelp": "Sadece sağlayıcının izin listesindeki modeller seçilebilir.",
|
||||
"aiResourceAliasRequired": "AI ağ geçitleri için takma ad gereklidir",
|
||||
"aiResourceDomainConfiguration": "Alan Adı Yapılandırması",
|
||||
"aiResourceDomainConfigurationDescription": "Bu AI ağ geçidine ulaşmak için müşterilerin kullanacağı alan adını seçin.",
|
||||
"aiUsageAnalyticsTitle": "AI Kullanım Analizi",
|
||||
"aiUsageAnalyticsDescription": "AI ağ geçidi maliyetini, token kullanımını ve sağlayıcılar, kaynaklar, roller ve kullanıcılar genelindeki aktiviteyi analiz edin",
|
||||
"aiUsageTabOverview": "Genel Bakış",
|
||||
"aiUsageTabProviders": "Sağlayıcılar",
|
||||
"aiUsageTabResources": "Kaynaklar",
|
||||
"aiUsageFilterProvider": "Sağlayıcı",
|
||||
"aiUsageFilterModel": "Model",
|
||||
"aiUsageFilterResource": "Kaynak",
|
||||
"aiUsageFilterRole": "Rol",
|
||||
"aiUsageFilterUser": "Kullanıcı",
|
||||
"aiUsageFilterAllProviders": "Tüm Sağlayıcılar",
|
||||
"aiUsageFilterAllModels": "Tüm Modeller",
|
||||
"aiUsageFilterAllResources": "Tüm Kaynaklar",
|
||||
"aiUsageFilterAllRoles": "Tüm Roller",
|
||||
"aiUsageFilterAllUsers": "Tüm Kullanıcılar",
|
||||
"aiUsageFilterSearch": "Ara...",
|
||||
"aiUsageFilterNotFound": "Hiçbir seçenek bulunamadı",
|
||||
"aiUsageResetFilters": "Filtreleri Sıfırla",
|
||||
"aiUsageRefresh": "Yenile",
|
||||
"aiUsageTokenTypePrompt": "Yazım",
|
||||
"aiUsageTokenTypeCacheRead": "Önbellek okuma",
|
||||
"aiUsageTokenTypeCacheWrite": "Önbellek yazma",
|
||||
"aiUsageTokenTypeCompletion": "Tamamlama",
|
||||
"aiUsageTokenTypeReasoning": "Muhakeme",
|
||||
"aiUsageRequests": "İstekler",
|
||||
"aiUsageCost": "Maliyet",
|
||||
"aiUsageTokens": "Jetonlar",
|
||||
"aiUsageOther": "Diğer",
|
||||
"aiUsageTotalRequests": "Toplam İstekler",
|
||||
"aiUsageTotalTokens": "Toplam Jetonlar",
|
||||
"aiUsageTotalCost": "Toplam Maliyet",
|
||||
"aiUsageEstimated": "Tahmini",
|
||||
"aiUsageRequestVolume": "İstek Hacmi",
|
||||
"aiUsageTokenUsage": "Jeton Kullanımı",
|
||||
"aiUsageModelCost": "Model Maliyeti",
|
||||
"aiUsageModelTokens": "Model Jetonları",
|
||||
"aiUsageTopModels": "En İyi Modeller",
|
||||
"aiUsageTopProviders": "En İyi Sağlayıcılar",
|
||||
"aiUsageProviderCost": "Sağlayıcı Maliyeti",
|
||||
"aiUsageProviderTokenUsage": "Sağlayıcı Jeton Kullanımı",
|
||||
"aiUsageTopResources": "En İyi Kaynaklar",
|
||||
"aiUsageResourceCost": "Kaynak Maliyeti",
|
||||
"aiUsageResourceTokenUsage": "Kaynak Jeton Kullanımı",
|
||||
"aiUsageResourceTypePublic": "Kamusal Kaynak",
|
||||
"aiUsageResourceTypeSite": "Özel Kaynak",
|
||||
"aiUsageNoResource": "Kaynak yok",
|
||||
"aiUsageRolesTab": "Roller",
|
||||
"aiUsageUsersTab": "Kullanıcılar",
|
||||
"aiUsageTopRoles": "En İyi Roller",
|
||||
"aiUsageRoleCost": "Rol Maliyeti",
|
||||
"aiUsageRoleTokenUsage": "Rol Jeton Kullanımı",
|
||||
"aiUsageTopUsers": "En İyi Kullanıcılar",
|
||||
"aiUsageUserCost": "Kullanıcı Maliyeti",
|
||||
"aiUsageUserTokenUsage": "Kullanıcı Jeton Kullanımı",
|
||||
"aiUsageUnknownUser": "Bilinmeyen kullanıcı",
|
||||
"aiUsageVirtualApiKeysTab": "Sanal API Anahtarları",
|
||||
"aiUsageFilterVirtualApiKey": "Sanal API Anahtarı",
|
||||
"aiUsageFilterAllVirtualApiKeys": "Tüm Sanal API Anahtarları",
|
||||
"aiUsageTopVirtualApiKeys": "En İyi Sanal API Anahtarları",
|
||||
"aiUsageVirtualApiKeyCost": "Sanal API Anahtar Maliyeti",
|
||||
"aiUsageVirtualApiKeyTokenUsage": "Sanal API Anahtar Jeton Kullanımı",
|
||||
"aiUsageUnknownVirtualApiKey": "Sanal API anahtarı yok",
|
||||
"aiUsageUnnamedVirtualApiKey": "İsimsiz anahtar",
|
||||
"aiUsageLoading": "Yükleniyor...",
|
||||
"aiUsageNoData": "Veri yok",
|
||||
"resourceBudgetSettings": "Bütçe",
|
||||
"resourceBudgetSettingsDescription": "Bu AI ağ geçidi harcama veya jeton limitlerine göre kullanım nasıl sınırlayacağını yapılandırın",
|
||||
"sidebarApiKeys": "API Anahtarları",
|
||||
"sidebarOrgs": "Organizasyonlar",
|
||||
"sidebarProvisioning": "Tedarik",
|
||||
"sidebarSettings": "Ayarlar",
|
||||
"sidebarAllUsers": "Tüm Kullanıcılar",
|
||||
@@ -1695,6 +2192,8 @@
|
||||
"alertingRuleSaved": "Uyarı kuralı kaydedildi",
|
||||
"alertingRuleSavedCreatedDescription": "Yeni uyarı kuralınız oluşturuldu. Bu sayfada düzenlemeye devam edebilirsiniz.",
|
||||
"alertingRuleSavedUpdatedDescription": "Bu uyarı kuralındaki değişiklikleriniz kaydedildi.",
|
||||
"alertingTestAlertSent": "Test uyarısı gönderildi",
|
||||
"alertingTestAlertSentDescription": "Bu kurala yapılandırılmış eylemlere bir test uyarısı gönderildi.",
|
||||
"alertingEditRule": "Uyarı Kuralını Düzenle",
|
||||
"alertingCreateRule": "Uyarı Kuralı Oluştur",
|
||||
"alertingRuleCredenzaDescription": "Ne izlenecek, ne zaman tetiklenecek ve nasıl bildirilecek, bunları seçin",
|
||||
@@ -1804,6 +2303,12 @@
|
||||
"alertingRulesBannerDescription": "Her kural neyin izleneceğini (bir site, sağlık kontrolü veya kaynak), ne zaman tetikleneceğini (örneğin çevrimdışı veya sağlıksız) ve ekibinize e-posta, web kancaları veya entegrasyonlar aracılığıyla nasıl bildirileceğini bağlar. Bu listeyi kullanarak kuralları oluşturun, etkinleştirin ve yönetin.",
|
||||
"alertingHealthChecksBannerTitle": "Sağlık ve Kaynakları İzleyin",
|
||||
"alertingHealthChecksBannerDescription": "Sağlık kontrolleri bir kez tanımladığınız HTTP veya TCP monitörleridir. Ardından hedef sağlıklı veya sağlıksız olduğunda bildirilmeniz için onları uyarı kurallarında kaynak olarak kullanabilirsiniz. Kaynaklar üzerindeki sağlık kontrolleri de burada görünür.",
|
||||
"alertingTestRule": "Test Uyarı Kuralı",
|
||||
"alertingAddActionHeading": "Yeni Eylem Ekle",
|
||||
"alertingSelectActionType": "Bir Eylem Seçin",
|
||||
"alertingNoActionsTitle": "Eylem yapılandırılmamış",
|
||||
"alertingNoActionsSaveDescription": "Bu kuralın tetiklendiğinde birini bilgilendirebilmesi için en az bir eylem ekleyin.",
|
||||
"alertingNoActionsTestDescription": "Bu kuralı test etmeden önce en az bir eylem ekleyin.",
|
||||
"standaloneHcTableTitle": "Sağlık Kontrolleri",
|
||||
"standaloneHcSearchPlaceholder": "Sağlık kontrollerini ara…",
|
||||
"standaloneHcAddButton": "Sağlık Kontrolü Oluştur",
|
||||
@@ -2122,6 +2627,7 @@
|
||||
"createDomainType": "Tür:",
|
||||
"createDomainName": "Ad:",
|
||||
"createDomainValue": "Değer:",
|
||||
"multiSelectFilterCount": "{count} seçildi",
|
||||
"createDomainCnameRecords": "CNAME Kayıtları",
|
||||
"createDomainARecords": "A Kayıtları",
|
||||
"createDomainRecordNumber": "Kayıt {number}",
|
||||
@@ -2193,6 +2699,8 @@
|
||||
"subnetPlaceholder": "Alt ağ",
|
||||
"addressDescription": "İstemcinin dahili adresi. Organizasyon alt ağı içinde olmalıdır.",
|
||||
"selectSites": "Siteleri seçin",
|
||||
"selectResources": "Kaynakları seçin",
|
||||
"multiResourcesSelectorResourcesCount": "{count, plural, one {# kaynak} other {# kaynaklar}}",
|
||||
"selectLabels": "Etiketleri seçin",
|
||||
"sitesDescription": "Müşteri seçilen sitelere bağlantı kuracaktır",
|
||||
"clientInstallOlm": "Olm Yükle",
|
||||
@@ -2234,6 +2742,7 @@
|
||||
"healthScheme": "Yöntem",
|
||||
"healthSelectScheme": "Yöntem Seç",
|
||||
"healthCheckPortInvalid": "Bağlantı noktası 1 ile 65535 arasında olmalıdır",
|
||||
"healthCheckHostnameInvalid": "Ana bilgisayar adı boşluk içermemelidir",
|
||||
"healthCheckPath": "Yol",
|
||||
"healthHostname": "IP / Hostname",
|
||||
"healthPort": "Bağlantı Noktası",
|
||||
@@ -2245,10 +2754,11 @@
|
||||
"timeIsInSeconds": "Zaman saniye cinsindendir",
|
||||
"requireDeviceApproval": "Cihaz Onaylarını Gerektir",
|
||||
"requireDeviceApprovalDescription": "Bu role sahip kullanıcıların yeni cihazlarının bağlanabilmesi ve kaynaklara erişebilmesi için bir yönetici tarafından onaylanması gerekiyor.",
|
||||
"sshSettings": "SSH Ayarları",
|
||||
"sshSettings": "SSH",
|
||||
"inferenceSettings": "AI Ağ Geçidi",
|
||||
"sshAccess": "SSH Erişimi",
|
||||
"rdpSettings": "RDP Ayarları",
|
||||
"vncSettings": "VNC Ayarları",
|
||||
"rdpSettings": "RDP",
|
||||
"vncSettings": "VNC",
|
||||
"sshServer": "SSH Sunucusu",
|
||||
"rdpServer": "RDP Sunucusu",
|
||||
"vncServer": "VNC Sunucusu",
|
||||
@@ -2401,6 +2911,7 @@
|
||||
"editInternalResourceDialogModeCidr": "CIDR",
|
||||
"editInternalResourceDialogModeHttp": "HTTP",
|
||||
"editInternalResourceDialogModeHttps": "HTTPS",
|
||||
"editInternalResourceDialogModeInference": "AI Ağ Geçidi",
|
||||
"editInternalResourceDialogModeSsh": "SSH",
|
||||
"editInternalResourceDialogScheme": "Şema",
|
||||
"editInternalResourceDialogEnableSsl": "TLS'yi Etkinleştir",
|
||||
@@ -2421,8 +2932,8 @@
|
||||
"privateResourceAllowIcmpPing": "ICMP Ping İzne Ver",
|
||||
"privateResourceNetworkAccess": "Ağ Erişimi",
|
||||
"privateResourceNetworkAccessDescription": "Bu kaynak için TCP/UDP port erişimini kontrol edin ve ICMP ping'in izin verilip verilmediğini kontrol edin.",
|
||||
"hostSettings": "Sunucu Ayarları",
|
||||
"cidrSettings": "CIDR Ayarları",
|
||||
"hostSettings": "Sunucu",
|
||||
"cidrSettings": "CIDR",
|
||||
"createInternalResourceDialogResourceProperties": "Kaynak Özellikleri",
|
||||
"createInternalResourceDialogName": "Ad",
|
||||
"createInternalResourceDialogSite": "Site",
|
||||
@@ -2460,6 +2971,7 @@
|
||||
"createInternalResourceDialogModeHttp": "HTTP",
|
||||
"createInternalResourceDialogModeHttps": "HTTPS",
|
||||
"createInternalResourceDialogModeSsh": "SSH",
|
||||
"createInternalResourceDialogModeInference": "AI Ağ Geçidi",
|
||||
"scheme": "Şema",
|
||||
"createInternalResourceDialogScheme": "Şema",
|
||||
"createInternalResourceDialogEnableSsl": "TLS'yi Etkinleştir",
|
||||
@@ -2514,7 +3026,7 @@
|
||||
"remoteExitNodeNetworkingSubnetsPlaceholder": "Bir CIDR aralığı ekle (örneğin, 10.0.0.0/8)",
|
||||
"remoteExitNodeNetworkingSubnetsLoadError": "Alt ağlar yüklenemedi",
|
||||
"remoteExitNodeNetworkingLabelsTitle": "Tercih Etiketleri",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Bu etiketlere sahip siteler, bu uzak çıkış düğümü üzerinden bağlantı kurmaya zorlanacaktır.",
|
||||
"remoteExitNodeNetworkingLabelsDescription": "Bu etiketlere sahip siteler, bağlantıyı bu uzak çıkış düğümü üzerinden tercih edecektir.",
|
||||
"remoteExitNodeNetworkingLabelsButtonText": "Etiketleri seç...",
|
||||
"remoteExitNodeNetworkingLabelsSearchPlaceholder": "Etiketleri ara...",
|
||||
"remoteExitNodeNetworkingLabelsLoadError": "Etiketler yüklenemedi",
|
||||
@@ -2991,6 +3503,7 @@
|
||||
"priority": "Öncelik",
|
||||
"priorityDescription": "Daha yüksek öncelikli rotalar önce değerlendirilir. Öncelik = 100, otomatik sıralama anlamına gelir (sistem karar verir). Manuel öncelik uygulamak için başka bir numara kullanın.",
|
||||
"instanceName": "Örnek İsmi",
|
||||
"clearInstanceName": "Sunucu Birliği Sıfırla",
|
||||
"pathMatchModalTitle": "Yol Eşleşmesini Yapılandır",
|
||||
"pathMatchModalDescription": "Gelen isteklerin yolu temel alarak nasıl eşleştirilmesi gerektiğini ayarlayın.",
|
||||
"pathMatchType": "Eşleşme Türü",
|
||||
@@ -3001,7 +3514,7 @@
|
||||
"clear": "Temizle",
|
||||
"saveChanges": "Değişiklikleri Kaydet",
|
||||
"pathMatchRegexPlaceholder": "^/api/.*",
|
||||
"pathMatchDefaultPlaceholder": "/path",
|
||||
"pathMatchDefaultPlaceholder": "/yol",
|
||||
"pathMatchPrefixHelp": "Örnek: /api, /api/users vb.'ni eşleştirir.",
|
||||
"pathMatchExactHelp": "Örnek: /api yalnızca /api'yi eşleştirir",
|
||||
"pathMatchRegexHelp": "Örnek: ^/api/.* her şeyi eşleştirir /api/anything",
|
||||
@@ -3051,6 +3564,7 @@
|
||||
"validPassword": "Geçerli Şifre",
|
||||
"validEmail": "Geçerli E-posta",
|
||||
"validSSO": "Geçerli SSO",
|
||||
"validVirtualAPIKey": "Geçerli Sanal API Anahtarı",
|
||||
"view": "Görüntüle",
|
||||
"configManaged": "Yapılandırma Yönetildi",
|
||||
"connectedClient": "Bağlı İstemci",
|
||||
@@ -3071,7 +3585,42 @@
|
||||
"sidebarLogsAction": "Eylem Günlükleri",
|
||||
"logRetention": "Kayıt Saklama",
|
||||
"logRetentionDescription": "Bu organizasyon için farklı türdeki günlüklerin ne kadar süre saklanacağını yönetin veya devre dışı bırakın",
|
||||
"logRetentionDisabledWarningTitle": "Günlük Saklama Devre Dışı Bırakıldı",
|
||||
"logRetentionDisabledWarningDescription": "{logType} bu organizasyon için saklanmıyor, bu nedenle yeni etkinlikler burada görünmeyecek. Bu günlükleri toplamak için güvenlik ayarlarında saklamayı etkinleştirin.",
|
||||
"logRetentionDisabledWarningButton": "Güvenlik Ayarlarına Git",
|
||||
"requestLogsDescription": "Bu organizasyondaki kaynaklar için ayrıntılı istek günlüklerini görüntüleyin",
|
||||
"aiSessionLogs": "AI Ağ Geçidi Oturum Günlükleri",
|
||||
"aiSessionLogsDescription": "Bu organizasyondaki AI ağ geçidi isteklerinin istem ve yanıt transkriptlerini görüntüleyin",
|
||||
"sidebarLogsAi": "Oturum Günlükleri",
|
||||
"commandLogsAi": "Oturum Günlükleri",
|
||||
"sidebarLogsAiUsage": "Kullanım Analizi",
|
||||
"commandLogsAiUsage": "Kullanım Analizi",
|
||||
"provider": "Sağlayıcı",
|
||||
"capability": "Yetenek",
|
||||
"model": "Model",
|
||||
"virtualApiKey": "Sanal API Anahtarı",
|
||||
"noVirtualApiKey": "Sanal API anahtarı yok",
|
||||
"stream": "Akış",
|
||||
"streaming": "Akış",
|
||||
"nonStreaming": "Akış Dışı",
|
||||
"statusCode": "Durum Kodu",
|
||||
"aiSessionId": "Oturum ID",
|
||||
"aiSessionRequest": "İstek",
|
||||
"aiSessionResponse": "Yanıt",
|
||||
"aiSessionNoData": "Hiçbir veri yakalanmadı",
|
||||
"aiSessionCouldNotParse": "(ham, döküm çözümlenemedi)",
|
||||
"aiSessionLogTruncated": "Bu oturum saklama öncesi kısaltıldı ve eksik olabilir.",
|
||||
"aiSessionViewRaw": "Ham JSON Görüntüle",
|
||||
"aiSessionViewChat": "Sohbet Görüntüle",
|
||||
"cost": "Maliyet",
|
||||
"estimated": "Tahmini",
|
||||
"tokenUsage": "Jeton Kullanımı",
|
||||
"promptTokens": "Yazım Jetonları",
|
||||
"cacheReadTokens": "Önbellek Okuma Jetonları",
|
||||
"cacheWriteTokens": "Önbellek Yazma Jetonları",
|
||||
"completionTokens": "Tamamlama Jetonları",
|
||||
"reasoningTokens": "Muhakeme Jetonları",
|
||||
"totalTokens": "Toplam Jetonlar",
|
||||
"requestAnalyticsDescription": "Bu organizasyondaki kaynaklar için ayrıntılı istek analizlerini görüntüleyin.",
|
||||
"logRetentionRequestLabel": "HTTP İstek Günlüğü Saklama",
|
||||
"logRetentionRequestDescription": "İstek günlüklerini ne kadar süre tutacağını belirle",
|
||||
@@ -3081,6 +3630,8 @@
|
||||
"logRetentionActionDescription": "Eylem günlüklerini ne kadar süre tutacağını belirle",
|
||||
"logRetentionConnectionLabel": "Bağlantı kayıtlarını ne kadar süre saklayacağınız",
|
||||
"logRetentionConnectionDescription": "Bağlantı kayıtlarını ne kadar süre saklayacağınız",
|
||||
"logRetentionAISessionsLabel": "AI Ağ Geçidi Oturum Günlüğü Saklama Süresi",
|
||||
"logRetentionAISessionsDescription": "AI ağ geçidi istem/yanıt oturum günlüklerinin ne kadar süreyle saklanacağını belirleyin",
|
||||
"logRetentionDisabled": "Devre Dışı",
|
||||
"logRetention3Days": "3 gün",
|
||||
"logRetention7Days": "7 gün",
|
||||
@@ -3098,8 +3649,8 @@
|
||||
"sourceAddress": "Kaynak Adresi",
|
||||
"destinationAddress": "Hedef Adresi",
|
||||
"duration": "Süre",
|
||||
"licenseRequiredToUse": "Bu özelliği kullanmak için bir <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> lisansı veya <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> gereklidir. <bookADemoLink>Tanıtım veya POC denemesi ayarlayın</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "Bu özelliği kullanmak için <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> gereklidir. Bu özellik ayrıca <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>'da da mevcuttur. <bookADemoLink>Tanıtım veya POC denemesi ayarlayın</bookADemoLink>",
|
||||
"licenseRequiredToUse": "<enterpriseEditionLink>Kurumsal Sürüm lisansı</enterpriseEditionLink> gereklidir. <bookADemoLink>Bir demo veya deneme rezervasyonu yapın</bookADemoLink>",
|
||||
"ossEnterpriseEditionRequired": "<enterpriseEditionLink>Kurumsal Sürüm lisansı</enterpriseEditionLink> gereklidir. <bookADemoLink>Bir demo veya deneme rezervasyonu yapın</bookADemoLink>",
|
||||
"certResolver": "Sertifika Çözücü",
|
||||
"certResolverDescription": "Bu kaynak için kullanılacak sertifika çözücüsünü seçin.",
|
||||
"selectCertResolver": "Sertifika Çözücü Seçin",
|
||||
@@ -3242,6 +3793,7 @@
|
||||
"noData": "Veri Yok",
|
||||
"machineClients": "Makine İstemcileri",
|
||||
"install": "Yükle",
|
||||
"downloadInstaller": "Yükleyiciyi İndir",
|
||||
"run": "Çalıştır",
|
||||
"envFile": "Ortam Dosyası",
|
||||
"serviceFile": "Servis Dosyası",
|
||||
@@ -3297,9 +3849,9 @@
|
||||
"internalResourceFormMultiSiteRoutingHelp": "Birden fazla site seçmek, yüksek kullanılabilirlik için dirençli yönlendirme ve yedeklik sağlar.",
|
||||
"internalResourceFormMultiSiteRoutingHelpLearnMore": "Daha fazla bilgi",
|
||||
"editInternalResourceDialogPortRestrictionsDescription": "Belirtilen TCP/UDP portlarına erişimi kısıtlayın veya tüm portlara izin/engelleme verin.",
|
||||
"createInternalResourceDialogHttpConfiguration": "HTTP yapılandırması",
|
||||
"createInternalResourceDialogHttpConfiguration": "Alan Adı Yapılandırması",
|
||||
"createInternalResourceDialogHttpConfigurationDescription": "HTTP veya HTTPS üzerinden bu kaynağa ulaşmak için istemcilerin kullanacağı alan adını seçin.",
|
||||
"editInternalResourceDialogHttpConfiguration": "HTTP yapılandırması",
|
||||
"editInternalResourceDialogHttpConfiguration": "Alan Adı Yapılandırması",
|
||||
"editInternalResourceDialogHttpConfigurationDescription": "HTTP veya HTTPS üzerinden bu kaynağa ulaşmak için istemcilerin kullanacağı alan adını seçin.",
|
||||
"editInternalResourceDialogTcp": "TCP",
|
||||
"editInternalResourceDialogUdp": "UDP",
|
||||
@@ -3449,6 +4001,7 @@
|
||||
"disconnected": "Bağlantı Kesildi",
|
||||
"approvalsEmptyStateTitle": "Cihaz Onayları Etkin Değil",
|
||||
"approvalsEmptyStateDescription": "Kullanıcıların yeni cihazlara bağlanabilmeleri için yönetici onayı gerektiren rol cihaz onaylarını etkinleştirin.",
|
||||
"approvalsEmptyStateHowToTitle": "Nasıl Etkinleştirilir",
|
||||
"approvalsEmptyStateStep1Title": "Rollere Git",
|
||||
"approvalsEmptyStateStep1Description": "Cihaz onaylarını yapılandırmak için kuruluşunuzun rol ayarlarına gidin.",
|
||||
"approvalsEmptyStateStep2Title": "Cihaz Onaylarını Etkinleştir",
|
||||
@@ -3570,6 +4123,8 @@
|
||||
"httpDestConnectionLogsDescription": "Site ve tünel bağlantı olayları, bağlantılar ve bağlantı kesilmeleri dahil.",
|
||||
"httpDestRequestLogsTitle": "HTTP İstek Günlükleri",
|
||||
"httpDestRequestLogsDescription": "Yönlendirilmiş kaynaklar için HTTP istek kayıtları, yöntem, yol ve yanıt kodu dahil.",
|
||||
"httpDestAISessionLogsTitle": "AI Oturum Günlükleri",
|
||||
"httpDestAISessionLogsDescription": "AI geçidi istek ve yanıt oturumları, istemler, model yanıtları ve token kullanımı dahil.",
|
||||
"httpDestSaveChanges": "Değişiklikleri Kaydet",
|
||||
"httpDestCreateDestination": "Hedef Oluştur",
|
||||
"httpDestUpdatedSuccess": "Hedef başarıyla güncellendi",
|
||||
@@ -3726,6 +4281,11 @@
|
||||
"resourceLauncherViewAsAdmin": "Yönetici Olarak Görüntüle",
|
||||
"resourceLauncherResourceDetailsDescription": "Bu kaynağın bağlantı bilgileri ve durumu.",
|
||||
"resourceLauncherResourceDetails": "Kaynak Detayları",
|
||||
"resourceLauncherSitesDescription": "Kaynak, aşağıdaki siteler üzerinden erişilebilir.",
|
||||
"resourceLauncherViewSiteAsAdmin": "Siteyi Yönetici Olarak Görüntüle",
|
||||
"resourceLauncherFilterBySite": "Siteye Göre Filtrele",
|
||||
"resourceLauncherSshCommand": "SSH Komutu",
|
||||
"resourceLauncherSshCommandDescription": "Pangolin CLI kullanarak bu kaynağa bir SSH oturumu açın.",
|
||||
"resourceLauncherAuthMethodsDescription": "Bu kaynak için etkin kimlik doğrulama yöntemleri.",
|
||||
"resourceLauncherPrivateClientRequired": "Bu kaynağa özel olarak erişmek için cihazınızda bir istemci ile bağlanın.",
|
||||
"resourceLauncherPrivateClientRequiredTitle": "İstemci Bağlantısı Gerekli",
|
||||
@@ -3735,7 +4295,18 @@
|
||||
"resourceLauncherTcp": "TCP",
|
||||
"resourceLauncherUdp": "UDP",
|
||||
"resourceLauncherUnlabeled": "Etiketsiz",
|
||||
"resourceLauncherAiGateway": "AI Ağ Geçidi",
|
||||
"resourceLauncherNoSite": "Site Yok",
|
||||
"resourceLauncherAvailableModels": "Mevcut Modeller",
|
||||
"resourceLauncherAvailableModelsDescription": "Bu AI ağ geçidiyle kullanabileceğiniz modeller.",
|
||||
"resourceLauncherAvailableModelsEmpty": "Bu kaynak için hiçbir model mevcut değil.",
|
||||
"resourceLauncherAvailableModelsError": "Mevcut modeller yüklenemedi.",
|
||||
"resourceLauncherApiKeys": "API Anahtarları",
|
||||
"resourceLauncherApiKeysDescription": "Bu kaynakla kimlik doğrulamak için kimlik anahtarınızı veya atanmış bir anahtarı kullanın.",
|
||||
"resourceLauncherApiKeysIdentity": "Kimlik Anahtarı",
|
||||
"resourceLauncherApiKeysManual": "Atanmış Anahtarlar",
|
||||
"resourceLauncherApiKeysEmpty": "Bu kaynak için hiçbir API anahtarı mevcut değil.",
|
||||
"resourceLauncherApiKeysError": "API anahtarları yüklenemedi.",
|
||||
"resourceLauncherNoResourcesInGroup": "Bu grupta kaynak yok",
|
||||
"resourceLauncherEmptyStateTitle": "Kullanılabilir Kaynak Yok",
|
||||
"resourceLauncherEmptyStateDescription": "Henüz hiçbir kaynağa erişiminiz yok. Erişim istemek için yöneticinizle iletişime geçin.",
|
||||
@@ -3762,9 +4333,9 @@
|
||||
"resourceLauncherViewDeleteFailedDescription": "Başlatıcı görünümü silinemedi. Lütfen tekrar deneyin.",
|
||||
"memberPortalPrevious": "Önceki",
|
||||
"memberPortalNext": "Sonraki",
|
||||
"httpSettings": "HTTP Ayarları",
|
||||
"tcpSettings": "TCP Ayarları",
|
||||
"udpSettings": "UDP Ayarları",
|
||||
"httpSettings": "HTTP",
|
||||
"tcpSettings": "TCP",
|
||||
"udpSettings": "UDP",
|
||||
"sshTitle": "SSH",
|
||||
"sshConnectingDescription": "Güvenli bir bağlantı kuruluyor…",
|
||||
"sshConnecting": "Bağlanılıyor…",
|
||||
@@ -3825,5 +4396,6 @@
|
||||
"rdpUnicodeKeyboardMode": "Unicode klavye modu",
|
||||
"sessionToolbarShow": "Araç çubuğunu göster",
|
||||
"sessionToolbarHide": "Araç çubuğunu gizle",
|
||||
"actionUpdateSiteApprovals": "Site Onaylarını Güncelle"
|
||||
"actionUpdateSiteApprovals": "Site Onaylarını Güncelle",
|
||||
"check": "Kontrol Et"
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ const nextConfig: NextConfig = {
|
||||
source: "/:orgId/settings/resources/client/:path*",
|
||||
destination: "/:orgId/settings/resources/private/:path*",
|
||||
permanent: true
|
||||
},
|
||||
{
|
||||
source: "/:orgId/settings/access/users/:userId/access-controls",
|
||||
destination: "/:orgId/settings/access/users/:userId/general",
|
||||
permanent: false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -32,49 +32,50 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "8.5.0",
|
||||
"@asteasolutions/zod-to-openapi": "9.1.0",
|
||||
"@aws-sdk/client-s3": "3.1132.0",
|
||||
"@devolutions/iron-remote-desktop": "https://static.pangolin.net/packages/devolutions-iron-remote-desktop-0.0.0.tgz",
|
||||
"@devolutions/iron-remote-desktop-rdp": "https://static.pangolin.net/packages/devolutions-iron-remote-desktop-rdp-0.0.1.tgz",
|
||||
"@aws-sdk/client-s3": "3.1056.0",
|
||||
"@headlessui/react": "2.2.10",
|
||||
"@hookform/resolvers": "5.4.0",
|
||||
"@hookform/resolvers": "5.9.1",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@node-rs/argon2": "2.0.2",
|
||||
"@node-rs/argon2": "2.2.1",
|
||||
"@novnc/novnc": "^1.7.0",
|
||||
"@oslojs/crypto": "1.0.1",
|
||||
"@oslojs/encoding": "1.1.0",
|
||||
"@radix-ui/react-avatar": "1.1.11",
|
||||
"@radix-ui/react-checkbox": "1.3.3",
|
||||
"@radix-ui/react-collapsible": "1.1.12",
|
||||
"@radix-ui/react-dialog": "1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.16",
|
||||
"@radix-ui/react-avatar": "1.2.6",
|
||||
"@radix-ui/react-checkbox": "1.3.11",
|
||||
"@radix-ui/react-collapsible": "1.1.20",
|
||||
"@radix-ui/react-dialog": "1.1.23",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.24",
|
||||
"@radix-ui/react-icons": "1.3.2",
|
||||
"@radix-ui/react-label": "2.1.8",
|
||||
"@radix-ui/react-popover": "1.1.15",
|
||||
"@radix-ui/react-progress": "1.1.8",
|
||||
"@radix-ui/react-radio-group": "1.3.8",
|
||||
"@radix-ui/react-scroll-area": "1.2.10",
|
||||
"@radix-ui/react-select": "2.2.6",
|
||||
"@radix-ui/react-separator": "1.1.8",
|
||||
"@radix-ui/react-slot": "1.2.4",
|
||||
"@radix-ui/react-switch": "1.2.6",
|
||||
"@radix-ui/react-tabs": "1.1.13",
|
||||
"@radix-ui/react-toast": "1.2.15",
|
||||
"@radix-ui/react-tooltip": "1.2.8",
|
||||
"@radix-ui/react-label": "2.1.15",
|
||||
"@radix-ui/react-popover": "1.1.23",
|
||||
"@radix-ui/react-progress": "1.1.16",
|
||||
"@radix-ui/react-radio-group": "1.4.7",
|
||||
"@radix-ui/react-scroll-area": "1.2.18",
|
||||
"@radix-ui/react-select": "2.3.7",
|
||||
"@radix-ui/react-separator": "1.1.15",
|
||||
"@radix-ui/react-slot": "1.3.3",
|
||||
"@radix-ui/react-switch": "1.3.7",
|
||||
"@radix-ui/react-tabs": "1.1.21",
|
||||
"@radix-ui/react-toast": "1.2.23",
|
||||
"@radix-ui/react-tooltip": "1.2.16",
|
||||
"@react-email/body": "0.3.0",
|
||||
"@react-email/components": "1.0.12",
|
||||
"@react-email/render": "2.0.8",
|
||||
"@react-email/render": "2.1.0",
|
||||
"@react-email/tailwind": "2.0.7",
|
||||
"@simplewebauthn/browser": "13.3.0",
|
||||
"@simplewebauthn/server": "13.3.1",
|
||||
"@simplewebauthn/browser": "14.0.0",
|
||||
"@simplewebauthn/server": "14.0.2",
|
||||
"@tailwindcss/forms": "0.5.11",
|
||||
"@tanstack/react-query": "5.100.14",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tanstack/react-query": "5.102.8",
|
||||
"@tanstack/react-table": "9.2.4",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"acme-client": "^5.4.0",
|
||||
"arctic": "3.7.0",
|
||||
"axios": "1.18.0",
|
||||
"axios": "1.20.0",
|
||||
"better-sqlite3": "11.9.1",
|
||||
"canvas-confetti": "1.9.4",
|
||||
"class-variance-authority": "0.7.1",
|
||||
@@ -84,103 +85,106 @@
|
||||
"cors": "2.8.6",
|
||||
"crypto-js": "4.2.0",
|
||||
"d3": "7.9.0",
|
||||
"dns-packet": "^5.6.1",
|
||||
"drizzle-orm": "0.45.2",
|
||||
"express": "5.2.1",
|
||||
"express-rate-limit": "8.5.2",
|
||||
"express-rate-limit": "8.7.0",
|
||||
"glob": "13.0.6",
|
||||
"helmet": "8.2.0",
|
||||
"gpt-tokenizer": "^4.0.0",
|
||||
"helmet": "8.3.0",
|
||||
"http-errors": "2.0.1",
|
||||
"input-otp": "1.4.2",
|
||||
"ioredis": "5.11.0",
|
||||
"input-otp": "1.5.0",
|
||||
"ioredis": "6.0.0",
|
||||
"jmespath": "0.16.0",
|
||||
"js-yaml": "4.3.0",
|
||||
"js-yaml": "5.4.2",
|
||||
"jsonwebtoken": "9.0.3",
|
||||
"lucide-react": "1.17.0",
|
||||
"maxmind": "5.0.6",
|
||||
"moment": "2.30.1",
|
||||
"next": "16.2.11",
|
||||
"next-intl": "4.13.0",
|
||||
"lru-cache": "11.5.2",
|
||||
"lucide-react": "1.46.0",
|
||||
"maxmind": "5.0.7",
|
||||
"moment": "2.31.0",
|
||||
"next": "16.3.5",
|
||||
"next-intl": "4.14.5",
|
||||
"next-themes": "0.4.6",
|
||||
"nextjs-toploader": "3.9.17",
|
||||
"node-cache": "5.1.2",
|
||||
"nodemailer": "9.0.1",
|
||||
"nodemailer": "10.0.10",
|
||||
"oslo": "1.2.1",
|
||||
"pg": "8.21.0",
|
||||
"posthog-node": "5.35.6",
|
||||
"pg": "8.23.0",
|
||||
"posthog-node": "5.52.2",
|
||||
"qrcode.react": "4.2.0",
|
||||
"react": "19.2.6",
|
||||
"react-day-picker": "9.14.0",
|
||||
"react-dom": "19.2.6",
|
||||
"react": "19.3.0",
|
||||
"react-day-picker": "10.0.1",
|
||||
"react-dom": "19.3.0",
|
||||
"react-easy-sort": "1.8.0",
|
||||
"react-hook-form": "7.76.1",
|
||||
"react-icons": "5.6.0",
|
||||
"recharts": "3.8.1",
|
||||
"react-hook-form": "7.88.0",
|
||||
"react-icons": "5.7.0",
|
||||
"recharts": "3.10.1",
|
||||
"reodotdev": "1.1.0",
|
||||
"semver": "7.8.1",
|
||||
"semver": "7.8.5",
|
||||
"sshpk": "1.18.0",
|
||||
"stripe": "22.2.0",
|
||||
"stripe": "22.6.2",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"tailwind-merge": "3.6.0",
|
||||
"tailwind-merge": "3.7.0",
|
||||
"topojson-client": "3.1.0",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"use-debounce": "10.1.1",
|
||||
"uuid": "14.0.0",
|
||||
"uuid": "14.0.2",
|
||||
"vaul": "1.1.2",
|
||||
"visionscarto-world-atlas": "1.0.0",
|
||||
"winston": "3.19.0",
|
||||
"winston-daily-rotate-file": "5.0.0",
|
||||
"ws": "8.21.0",
|
||||
"yaml": "2.9.0",
|
||||
"yargs": "18.0.0",
|
||||
"zod": "4.4.3",
|
||||
"ws": "8.21.3",
|
||||
"yaml": "2.9.1",
|
||||
"yargs": "18.1.0",
|
||||
"zod": "4.6.5",
|
||||
"zod-validation-error": "5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dotenvx/dotenvx": "1.69.1",
|
||||
"@dotenvx/dotenvx": "2.24.1",
|
||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||
"@react-email/ui": "^6.5.0",
|
||||
"@tailwindcss/postcss": "4.3.0",
|
||||
"@tanstack/react-query-devtools": "5.100.14",
|
||||
"@react-email/ui": "^6.9.5",
|
||||
"@tailwindcss/postcss": "4.3.3",
|
||||
"@tanstack/react-query-devtools": "5.102.8",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/cookie-parser": "1.4.10",
|
||||
"@types/cors": "2.8.19",
|
||||
"@types/crypto-js": "4.2.2",
|
||||
"@types/d3": "7.4.3",
|
||||
"@types/dns-packet": "^5.6.5",
|
||||
"@types/express": "5.0.6",
|
||||
"@types/express-session": "1.19.0",
|
||||
"@types/jmespath": "0.15.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsonwebtoken": "9.0.10",
|
||||
"@types/node": "25.9.1",
|
||||
"@types/nodemailer": "8.0.0",
|
||||
"@types/node": "26.5.1",
|
||||
"@types/nodemailer": "8.0.1",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@types/pg": "8.20.0",
|
||||
"@types/react": "19.2.15",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/semver": "7.7.1",
|
||||
"@types/sshpk": "1.17.4",
|
||||
"@types/pg": "8.23.1",
|
||||
"@types/react": "19.3.0",
|
||||
"@types/react-dom": "19.3.0",
|
||||
"@types/semver": "7.8.0",
|
||||
"@types/sshpk": "1.17.5",
|
||||
"@types/swagger-ui-express": "4.1.8",
|
||||
"@types/topojson-client": "3.1.5",
|
||||
"@types/ws": "8.18.1",
|
||||
"@types/yargs": "17.0.35",
|
||||
"babel-plugin-react-compiler": "1.0.0",
|
||||
"drizzle-kit": "0.31.10",
|
||||
"esbuild": "0.28.1",
|
||||
"esbuild-node-externals": "1.22.0",
|
||||
"eslint": "10.4.0",
|
||||
"eslint-config-next": "16.2.6",
|
||||
"postcss": "8.5.15",
|
||||
"prettier": "3.8.3",
|
||||
"react-email": "6.5.0",
|
||||
"tailwindcss": "4.3.0",
|
||||
"tsc-alias": "1.8.17",
|
||||
"tsx": "4.22.3",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.60.0"
|
||||
"esbuild": "0.28.2",
|
||||
"esbuild-node-externals": "2.0.0",
|
||||
"eslint": "10.10.0",
|
||||
"eslint-config-next": "16.3.5",
|
||||
"postcss": "8.5.28",
|
||||
"prettier": "3.9.6",
|
||||
"react-email": "6.9.5",
|
||||
"tailwindcss": "4.3.3",
|
||||
"tsc-alias": "1.9.5",
|
||||
"tsx": "4.23.13",
|
||||
"typescript": "7.0.2",
|
||||
"typescript-eslint": "8.70.0"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "0.28.1",
|
||||
"esbuild": "0.28.2",
|
||||
"dompurify": "3.4.0",
|
||||
"postcss": "8.5.15"
|
||||
"postcss": "8.5.28"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 790 KiB |
|
Before Width: | Height: | Size: 621 KiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 621 KiB After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 711 KiB |
|
Before Width: | Height: | Size: 556 KiB After Width: | Height: | Size: 713 KiB |
|
Before Width: | Height: | Size: 574 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 516 KiB After Width: | Height: | Size: 802 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="256" height="257" viewBox="0 0 256 257" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_29_2)">
|
||||
<path d="M50.2278 170.321L100.585 142.064L101.428 139.601L100.585 138.24H98.1225L89.6972 137.722L60.9215 136.944L35.9696 135.907L11.7954 134.611L5.70329 133.314L0 125.796L0.583291 122.037L5.70329 118.603L13.0268 119.251L29.2294 120.352L53.5332 122.037L71.1615 123.074L97.28 125.796H101.428L102.011 124.111L100.585 123.074L99.4835 122.037L74.3372 104.992L47.117 86.9752L32.8587 76.6056L25.1463 71.3559L21.2577 66.4304L19.5727 55.6719L26.5722 47.9595L35.9696 48.6076L38.3676 49.2557L47.8947 56.5792L68.2451 72.3281L94.8172 91.9008L98.7058 95.1413L100.261 94.0395L100.456 93.2618L98.7058 90.3453L84.2532 64.2268L68.8284 37.6547L61.9585 26.637L60.1438 20.0263C59.4957 17.3043 59.042 15.0359 59.042 12.2491L67.0137 1.42582L71.4208 0L82.0496 1.42582L86.5215 5.31443L93.1321 20.4152L103.826 44.2005L120.417 76.5408L125.278 86.1327L127.87 95.0116L128.843 97.7337H130.528V96.1782L131.889 77.9666L134.416 55.6071L136.879 26.8314L137.722 18.7301L141.74 9.00861L149.711 3.75899L155.933 6.74025L161.053 14.0638L160.34 18.7949L157.294 38.562L151.332 69.5413L147.443 90.2805H149.711L152.304 87.6881L162.803 73.7539L180.431 51.7185L188.209 42.9691L197.282 33.3124L203.115 28.7109H214.133L222.234 40.7656L218.605 53.2091L207.263 67.597L197.865 79.7813L184.385 97.9281L175.959 112.446L176.737 113.612L178.746 113.418L209.207 106.937L225.669 103.955L245.306 100.585L254.185 104.733L255.157 108.946L251.658 117.566L230.659 122.75L206.031 127.676L169.349 136.361L168.895 136.685L169.414 137.333L185.94 138.888L193.005 139.277H210.309L242.52 141.675L250.945 147.249L256 154.054L255.157 159.238L242.195 165.849L224.697 161.701L183.866 151.98L169.867 148.48H167.923V149.647L179.589 161.053L200.976 180.367L227.743 205.254L229.104 211.411L225.669 216.271L222.039 215.753L198.513 198.06L189.44 190.088L168.895 172.784H167.534V174.598L172.265 181.533L197.282 219.123L198.578 230.659L196.764 234.418L190.283 236.687L183.153 235.39L168.506 214.846L153.406 191.708L141.221 170.969L139.731 171.812L132.537 249.26L129.167 253.213L121.389 256.194L114.908 251.269L111.473 243.297L114.908 227.548L119.056 207.004L122.426 190.671L125.472 170.386L127.287 163.646L127.157 163.192L125.667 163.386L110.372 184.385L87.1048 215.818L68.6987 235.52L64.2916 237.27L56.6441 233.316L57.357 226.252L61.6344 219.966L87.1048 187.561L102.465 167.469L112.381 155.868L112.316 154.183H111.733L44.0709 198.125L32.0162 199.68L26.8314 194.819L27.4795 186.848L29.9423 184.255L50.2927 170.256L50.2278 170.321Z" fill="#0B0B0B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_29_2">
|
||||
<rect width="256" height="257" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="256" height="257" viewBox="0 0 256 257" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_29_2)">
|
||||
<path d="M50.2278 170.321L100.585 142.064L101.428 139.601L100.585 138.24H98.1225L89.6972 137.722L60.9215 136.944L35.9696 135.907L11.7954 134.611L5.70329 133.314L0 125.796L0.583291 122.037L5.70329 118.603L13.0268 119.251L29.2294 120.352L53.5332 122.037L71.1615 123.074L97.28 125.796H101.428L102.011 124.111L100.585 123.074L99.4835 122.037L74.3372 104.992L47.117 86.9752L32.8587 76.6056L25.1463 71.3559L21.2577 66.4304L19.5727 55.6719L26.5722 47.9595L35.9696 48.6076L38.3676 49.2557L47.8947 56.5792L68.2451 72.3281L94.8172 91.9008L98.7058 95.1413L100.261 94.0395L100.456 93.2618L98.7058 90.3453L84.2532 64.2268L68.8284 37.6547L61.9585 26.637L60.1438 20.0263C59.4957 17.3043 59.042 15.0359 59.042 12.2491L67.0137 1.42582L71.4208 0L82.0496 1.42582L86.5215 5.31443L93.1321 20.4152L103.826 44.2005L120.417 76.5408L125.278 86.1327L127.87 95.0116L128.843 97.7337H130.528V96.1782L131.889 77.9666L134.416 55.6071L136.879 26.8314L137.722 18.7301L141.74 9.00861L149.711 3.75899L155.933 6.74025L161.053 14.0638L160.34 18.7949L157.294 38.562L151.332 69.5413L147.443 90.2805H149.711L152.304 87.6881L162.803 73.7539L180.431 51.7185L188.209 42.9691L197.282 33.3124L203.115 28.7109H214.133L222.234 40.7656L218.605 53.2091L207.263 67.597L197.865 79.7813L184.385 97.9281L175.959 112.446L176.737 113.612L178.746 113.418L209.207 106.937L225.669 103.955L245.306 100.585L254.185 104.733L255.157 108.946L251.658 117.566L230.659 122.75L206.031 127.676L169.349 136.361L168.895 136.685L169.414 137.333L185.94 138.888L193.005 139.277H210.309L242.52 141.675L250.945 147.249L256 154.054L255.157 159.238L242.195 165.849L224.697 161.701L183.866 151.98L169.867 148.48H167.923V149.647L179.589 161.053L200.976 180.367L227.743 205.254L229.104 211.411L225.669 216.271L222.039 215.753L198.513 198.06L189.44 190.088L168.895 172.784H167.534V174.598L172.265 181.533L197.282 219.123L198.578 230.659L196.764 234.418L190.283 236.687L183.153 235.39L168.506 214.846L153.406 191.708L141.221 170.969L139.731 171.812L132.537 249.26L129.167 253.213L121.389 256.194L114.908 251.269L111.473 243.297L114.908 227.548L119.056 207.004L122.426 190.671L125.472 170.386L127.287 163.646L127.157 163.192L125.667 163.386L110.372 184.385L87.1048 215.818L68.6987 235.52L64.2916 237.27L56.6441 233.316L57.357 226.252L61.6344 219.966L87.1048 187.561L102.465 167.469L112.381 155.868L112.316 154.183H111.733L44.0709 198.125L32.0162 199.68L26.8314 194.819L27.4795 186.848L29.9423 184.255L50.2927 170.256L50.2278 170.321Z" fill="#F0EFEC"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_29_2">
|
||||
<rect width="256" height="257" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="716" height="716" viewBox="157 157 402 402" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M508.749 317.399C516.777 287.314 508.991 253.884 485.389 230.282C461.788 206.681 428.36 198.895 398.273 206.923C376.231 184.928 343.39 174.956 311.148 183.596C278.906 192.234 255.45 217.292 247.36 247.361C217.291 255.451 192.233 278.91 183.595 311.149C174.957 343.391 184.927 376.232 206.924 398.274C198.896 428.359 206.683 461.789 230.284 485.391C253.885 508.992 287.313 516.779 317.401 508.75C339.442 530.745 372.286 540.717 404.525 532.079C436.767 523.441 460.223 498.384 468.313 468.315C498.383 460.224 523.44 436.766 532.078 404.526C540.716 372.285 530.747 339.443 508.749 317.402V317.399ZM470.899 244.776C486.892 260.77 493.488 282.601 490.687 303.412L415.577 260.046C412.411 258.218 408.509 258.218 405.345 260.046L317.401 310.82V277.526C317.401 275.191 318.652 273.005 320.676 271.837L387.644 233.174C414.178 218.353 448.346 222.223 470.901 244.776H470.899ZM357.837 311.144L398.275 334.491V381.185L357.837 404.532L317.398 381.185V334.491L357.837 311.144ZM264.776 269.693C265.207 239.305 285.644 211.649 316.453 203.393C338.3 197.54 360.505 202.744 377.127 215.573L302.014 258.937C298.848 260.764 296.898 264.144 296.898 267.798V369.346L268.065 352.699C266.043 351.531 264.776 349.353 264.776 347.017V269.691V269.693ZM203.391 316.454C209.244 294.608 224.854 277.978 244.276 269.999V356.73C244.276 360.384 246.226 363.763 249.392 365.591L337.337 416.365L308.503 433.013C306.481 434.181 303.961 434.188 301.939 433.02L234.971 394.357C208.868 378.789 195.138 347.261 203.391 316.454ZM244.775 470.9C228.781 454.906 222.186 433.075 224.986 412.264L300.096 455.63C303.263 457.457 307.164 457.457 310.328 455.63L398.273 404.856V438.149C398.273 440.485 397.022 442.671 394.997 443.839L328.029 482.502C301.495 497.322 267.327 493.452 244.772 470.9H244.775ZM450.897 445.982C450.466 476.371 430.029 504.027 399.22 512.283C377.373 518.136 355.168 512.932 338.547 500.102L413.659 456.738C416.826 454.911 418.775 451.532 418.775 447.877V346.329L447.609 362.977C449.631 364.145 450.897 366.323 450.897 368.659V445.985V445.982ZM512.282 399.221C506.429 421.068 490.819 437.697 471.397 445.676V358.946C471.397 355.292 469.448 351.912 466.281 350.085L378.336 299.311L407.17 282.663C409.192 281.495 411.712 281.487 413.734 282.655L480.702 321.318C506.805 336.887 520.536 368.415 512.282 399.221Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="716" height="716" viewBox="157 157 402 402" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M508.749 317.399C516.777 287.314 508.991 253.884 485.389 230.282C461.788 206.681 428.36 198.895 398.273 206.923C376.231 184.928 343.39 174.956 311.148 183.596C278.906 192.234 255.45 217.292 247.36 247.361C217.291 255.451 192.233 278.91 183.595 311.149C174.957 343.391 184.927 376.232 206.924 398.274C198.896 428.359 206.683 461.789 230.284 485.391C253.885 508.992 287.313 516.779 317.401 508.75C339.442 530.745 372.286 540.717 404.525 532.079C436.767 523.441 460.223 498.384 468.313 468.315C498.383 460.224 523.44 436.766 532.078 404.526C540.716 372.285 530.747 339.443 508.749 317.402V317.399ZM470.899 244.776C486.892 260.77 493.488 282.601 490.687 303.412L415.577 260.046C412.411 258.218 408.509 258.218 405.345 260.046L317.401 310.82V277.526C317.401 275.191 318.652 273.005 320.676 271.837L387.644 233.174C414.178 218.353 448.346 222.223 470.901 244.776H470.899ZM357.837 311.144L398.275 334.491V381.185L357.837 404.532L317.398 381.185V334.491L357.837 311.144ZM264.776 269.693C265.207 239.305 285.644 211.649 316.453 203.393C338.3 197.54 360.505 202.744 377.127 215.573L302.014 258.937C298.848 260.764 296.898 264.144 296.898 267.798V369.346L268.065 352.699C266.043 351.531 264.776 349.353 264.776 347.017V269.691V269.693ZM203.391 316.454C209.244 294.608 224.854 277.978 244.276 269.999V356.73C244.276 360.384 246.226 363.763 249.392 365.591L337.337 416.365L308.503 433.013C306.481 434.181 303.961 434.188 301.939 433.02L234.971 394.357C208.868 378.789 195.138 347.261 203.391 316.454ZM244.775 470.9C228.781 454.906 222.186 433.075 224.986 412.264L300.096 455.63C303.263 457.457 307.164 457.457 310.328 455.63L398.273 404.856V438.149C398.273 440.485 397.022 442.671 394.997 443.839L328.029 482.502C301.495 497.322 267.327 493.452 244.772 470.9H244.775ZM450.897 445.982C450.466 476.371 430.029 504.027 399.22 512.283C377.373 518.136 355.168 512.932 338.547 500.102L413.659 456.738C416.826 454.911 418.775 451.532 418.775 447.877V346.329L447.609 362.977C449.631 364.145 450.897 366.323 450.897 368.659V445.985V445.982ZM512.282 399.221C506.429 421.068 490.819 437.697 471.397 445.676V358.946C471.397 355.292 469.448 351.912 466.281 350.085L378.336 299.311L407.17 282.663C409.192 281.495 411.712 281.487 413.734 282.655L480.702 321.318C506.805 336.887 520.536 368.415 512.282 399.221Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg width='240' height='300' viewBox='0 0 240 300' fill='none' xmlns='http://www.w3.org/2000/svg'><g clip-path='url(#clip0_1401_86274)'><mask id='mask0_1401_86274' style='mask-type:luminance' maskUnits='userSpaceOnUse' x='0' y='0' width='240' height='300'><path d='M240 0H0V300H240V0Z' fill='white'/></mask><g mask='url(#mask0_1401_86274)'><path d='M180 240H60V120H180V240Z' fill='#CFCECD'/><path d='M180 60H60V240H180V60ZM240 300H0V0H240V300Z' fill='#211E1E'/></g></g><defs><clipPath id='clip0_1401_86274'><rect width='240' height='300' fill='white'/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 577 B |
@@ -0,0 +1 @@
|
||||
<svg width='240' height='300' viewBox='0 0 240 300' fill='none' xmlns='http://www.w3.org/2000/svg'><g clip-path='url(#clip0_1401_86283)'><mask id='mask0_1401_86283' style='mask-type:luminance' maskUnits='userSpaceOnUse' x='0' y='0' width='240' height='300'><path d='M240 0H0V300H240V0Z' fill='white'/></mask><g mask='url(#mask0_1401_86283)'><path d='M180 240H60V120H180V240Z' fill='#4B4646'/><path d='M180 60H60V240H180V60ZM240 300H0V0H240V300Z' fill='#F1ECEC'/></g></g><defs><clipPath id='clip0_1401_86283'><rect width='240' height='300' fill='white'/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 577 B |
@@ -0,0 +1,41 @@
|
||||
import express from "express";
|
||||
import helmet from "helmet";
|
||||
import cors from "cors";
|
||||
import config from "@server/lib/config";
|
||||
import logger from "@server/logger";
|
||||
import {
|
||||
errorHandlerMiddleware,
|
||||
notFoundMiddleware
|
||||
} from "@server/middlewares";
|
||||
import { createAiGatewayRouter } from "@server/routers/aiGateway";
|
||||
|
||||
const aiGatewayPort = config.getRawConfig().server.ai_gateway_port;
|
||||
|
||||
export function createAiGatewayServer() {
|
||||
const aiGatewayServer = express();
|
||||
|
||||
const trustProxy = config.getRawConfig().server.trust_proxy;
|
||||
if (trustProxy) {
|
||||
aiGatewayServer.set("trust proxy", trustProxy);
|
||||
}
|
||||
|
||||
aiGatewayServer.use(helmet());
|
||||
aiGatewayServer.use(cors());
|
||||
// AI requests can carry large payloads (long conversation history, tool
|
||||
// results, embedded documents), well beyond express.json()'s 100kb default.
|
||||
aiGatewayServer.use(express.json({ limit: "50mb" }));
|
||||
|
||||
aiGatewayServer.use(createAiGatewayRouter());
|
||||
|
||||
aiGatewayServer.use(notFoundMiddleware);
|
||||
aiGatewayServer.use(errorHandlerMiddleware);
|
||||
|
||||
aiGatewayServer.listen(aiGatewayPort, (err?: any) => {
|
||||
if (err) throw err;
|
||||
logger.info(
|
||||
`AI gateway server is running on http://localhost:${aiGatewayPort}`
|
||||
);
|
||||
});
|
||||
|
||||
return aiGatewayServer;
|
||||
}
|
||||
@@ -50,6 +50,8 @@ export enum ActionsEnum {
|
||||
setResourceUsers = "setResourceUsers",
|
||||
setResourceRoles = "setResourceRoles",
|
||||
listResourceUsers = "listResourceUsers",
|
||||
listResourceAiModels = "listResourceAiModels",
|
||||
setResourceAiModels = "setResourceAiModels",
|
||||
// removeRoleSite = "removeRoleSite",
|
||||
// addRoleAction = "addRoleAction",
|
||||
// removeRoleAction = "removeRoleAction",
|
||||
@@ -151,6 +153,7 @@ export enum ActionsEnum {
|
||||
createAlertRule = "createAlertRule",
|
||||
updateAlertRule = "updateAlertRule",
|
||||
deleteAlertRule = "deleteAlertRule",
|
||||
testAlertRule = "testAlertRule",
|
||||
listAlertRules = "listAlertRules",
|
||||
listOrgLabels = "listOrgLabels",
|
||||
createOrgLabel = "createOrgLabel",
|
||||
@@ -182,7 +185,27 @@ export enum ActionsEnum {
|
||||
setResourcePolicyHeaderAuth = "setResourcePolicyHeaderAuth",
|
||||
setResourcePolicyWhitelist = "setResourcePolicyWhitelist",
|
||||
setResourcePolicyRules = "setResourcePolicyRules",
|
||||
createOrgWideLauncherView = "createOrgWideLauncherView"
|
||||
createOrgWideLauncherView = "createOrgWideLauncherView",
|
||||
createAiProvider = "createAiProvider",
|
||||
deleteAiProvider = "deleteAiProvider",
|
||||
getAiProvider = "getAiProvider",
|
||||
listAiProviders = "listAiProviders",
|
||||
updateAiProvider = "updateAiProvider",
|
||||
createAiModel = "createAiModel",
|
||||
deleteAiModel = "deleteAiModel",
|
||||
getAiModel = "getAiModel",
|
||||
listAiModels = "listAiModels",
|
||||
updateAiModel = "updateAiModel",
|
||||
createAiBudget = "createAiBudget",
|
||||
deleteAiBudget = "deleteAiBudget",
|
||||
getAiBudget = "getAiBudget",
|
||||
listAiBudgets = "listAiBudgets",
|
||||
updateAiBudget = "updateAiBudget",
|
||||
createVirtualApiKey = "createVirtualApiKey",
|
||||
deleteVirtualApiKey = "deleteVirtualApiKey",
|
||||
getVirtualApiKey = "getVirtualApiKey",
|
||||
listVirtualApiKeys = "listVirtualApiKeys",
|
||||
updateVirtualApiKey = "updateVirtualApiKey"
|
||||
}
|
||||
|
||||
export async function checkUserActionPermission(
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
import { canUserAccessResource } from "@server/auth/canUserAccessResource";
|
||||
import {
|
||||
db,
|
||||
users,
|
||||
virtualApiKeyResources,
|
||||
virtualApiKeys,
|
||||
type VirtualApiKey
|
||||
} from "@server/db";
|
||||
import config from "@server/lib/config";
|
||||
import {
|
||||
decryptVirtualApiKeyToken,
|
||||
VIRTUAL_API_KEY_PREFIX,
|
||||
looksLikeVirtualApiKeyCredential
|
||||
} from "@server/lib/virtualApiKey";
|
||||
import { getUserOrgRoles } from "@server/lib/userOrgRoles";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { isWithinExpirationDate } from "oslo";
|
||||
|
||||
export type VirtualApiKeyCredential = {
|
||||
virtualApiKeyId: string;
|
||||
secret: string;
|
||||
};
|
||||
|
||||
export type VirtualApiKeyUserData = {
|
||||
userId: string;
|
||||
username: string;
|
||||
email: string | null;
|
||||
name: string | null;
|
||||
role: string | null;
|
||||
};
|
||||
|
||||
function getHeader(
|
||||
headers: Record<string, string> | undefined,
|
||||
name: string
|
||||
): string | undefined {
|
||||
if (!headers) {
|
||||
return undefined;
|
||||
}
|
||||
if (headers[name] !== undefined) {
|
||||
return headers[name];
|
||||
}
|
||||
const lower = name.toLowerCase();
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
if (key.toLowerCase() === lower) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseVkCredential(
|
||||
raw: string | undefined
|
||||
): VirtualApiKeyCredential | null {
|
||||
if (!raw || !looksLikeVirtualApiKeyCredential(raw)) {
|
||||
return null;
|
||||
}
|
||||
const withoutPrefix = raw.trim().slice(VIRTUAL_API_KEY_PREFIX.length);
|
||||
const dot = withoutPrefix.indexOf(".");
|
||||
return {
|
||||
virtualApiKeyId: withoutPrefix.slice(0, dot),
|
||||
secret: withoutPrefix.slice(dot + 1)
|
||||
};
|
||||
}
|
||||
|
||||
export function extractVirtualApiKeyCredential(
|
||||
headers: Record<string, string> | undefined
|
||||
): VirtualApiKeyCredential | null {
|
||||
if (!headers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const authorization = getHeader(headers, "authorization");
|
||||
if (authorization) {
|
||||
const bearerMatch = authorization.match(/^Bearer\s+(.+)$/i);
|
||||
if (bearerMatch) {
|
||||
const credential = parseVkCredential(bearerMatch[1]);
|
||||
if (credential) {
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
const splunkMatch = authorization.match(/^Splunk\s+(.+)$/i);
|
||||
if (splunkMatch) {
|
||||
const credential = parseVkCredential(splunkMatch[1]);
|
||||
if (credential) {
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cfAig = getHeader(headers, "cf-aig-authorization");
|
||||
if (cfAig) {
|
||||
const bearerMatch = cfAig.match(/^Bearer\s+(.+)$/i);
|
||||
const credential = parseVkCredential(
|
||||
bearerMatch ? bearerMatch[1] : cfAig
|
||||
);
|
||||
if (credential) {
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of ["x-api-key", "x-goog-api-key"] as const) {
|
||||
const credential = parseVkCredential(getHeader(headers, name));
|
||||
if (credential) {
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function buildUserData(
|
||||
userId: string,
|
||||
orgId: string
|
||||
): Promise<VirtualApiKeyUserData | undefined> {
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.userId, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
config.getRawConfig().flags?.require_email_verification &&
|
||||
!user.emailVerified
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const userOrgRoles = await getUserOrgRoles(user.userId, orgId);
|
||||
if (userOrgRoles.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
userId: user.userId,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: userOrgRoles.map((r) => r.roleName).join(", ") || null
|
||||
};
|
||||
}
|
||||
|
||||
async function userHasResourceAccess(
|
||||
userId: string,
|
||||
resourceId: number,
|
||||
orgId: string
|
||||
): Promise<{ allowed: boolean; userData?: VirtualApiKeyUserData }> {
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.userId, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
if (
|
||||
config.getRawConfig().flags?.require_email_verification &&
|
||||
!user.emailVerified
|
||||
) {
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
const userOrgRoles = await getUserOrgRoles(user.userId, orgId);
|
||||
if (userOrgRoles.length === 0) {
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
const allowed = await canUserAccessResource({
|
||||
userId,
|
||||
resourceId,
|
||||
roleIds: userOrgRoles.map((r) => r.roleId)
|
||||
});
|
||||
|
||||
if (!allowed) {
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
return {
|
||||
allowed: true,
|
||||
userData: {
|
||||
userId: user.userId,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: userOrgRoles.map((r) => r.roleName).join(", ") || null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function manualKeyHasResourceAccess(
|
||||
key: VirtualApiKey,
|
||||
resourceId: number
|
||||
): Promise<boolean> {
|
||||
if (key.allResources) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const [row] = await db
|
||||
.select({ resourceId: virtualApiKeyResources.resourceId })
|
||||
.from(virtualApiKeyResources)
|
||||
.where(
|
||||
and(
|
||||
eq(virtualApiKeyResources.virtualApiKeyId, key.virtualApiKeyId),
|
||||
eq(virtualApiKeyResources.resourceId, resourceId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return Boolean(row);
|
||||
}
|
||||
|
||||
async function touchLastUsedAt(virtualApiKeyId: string): Promise<void> {
|
||||
try {
|
||||
await db
|
||||
.update(virtualApiKeys)
|
||||
.set({ lastUsedAt: Date.now() })
|
||||
.where(eq(virtualApiKeys.virtualApiKeyId, virtualApiKeyId));
|
||||
} catch {
|
||||
// Best-effort; do not fail auth on audit timestamp updates.
|
||||
}
|
||||
}
|
||||
|
||||
export async function verifyVirtualApiKey({
|
||||
credential,
|
||||
resourceId,
|
||||
orgId
|
||||
}: {
|
||||
credential: VirtualApiKeyCredential;
|
||||
resourceId: number;
|
||||
orgId: string;
|
||||
}): Promise<{
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
key?: VirtualApiKey;
|
||||
userData?: VirtualApiKeyUserData;
|
||||
}> {
|
||||
const [key] = await db
|
||||
.select()
|
||||
.from(virtualApiKeys)
|
||||
.where(eq(virtualApiKeys.virtualApiKeyId, credential.virtualApiKeyId))
|
||||
.limit(1);
|
||||
|
||||
if (!key) {
|
||||
return { valid: false, error: "Virtual API key not found" };
|
||||
}
|
||||
|
||||
if (key.orgId !== orgId) {
|
||||
return { valid: false, error: "Virtual API key org mismatch" };
|
||||
}
|
||||
|
||||
let plaintext: string;
|
||||
try {
|
||||
plaintext = decryptVirtualApiKeyToken(key.token);
|
||||
} catch {
|
||||
return { valid: false, error: "Virtual API key secret is invalid" };
|
||||
}
|
||||
|
||||
if (plaintext !== credential.secret) {
|
||||
return { valid: false, error: "Invalid virtual API key secret" };
|
||||
}
|
||||
|
||||
if (key.expiresAt && !isWithinExpirationDate(new Date(key.expiresAt))) {
|
||||
return { valid: false, error: "Virtual API key has expired" };
|
||||
}
|
||||
|
||||
if (key.kind === "manual") {
|
||||
const scoped = await manualKeyHasResourceAccess(key, resourceId);
|
||||
if (!scoped) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "Virtual API key is not scoped to this resource"
|
||||
};
|
||||
}
|
||||
|
||||
let userData: VirtualApiKeyUserData | undefined;
|
||||
if (key.userId) {
|
||||
userData = await buildUserData(key.userId, orgId);
|
||||
}
|
||||
|
||||
await touchLastUsedAt(key.virtualApiKeyId);
|
||||
return { valid: true, key, userData };
|
||||
}
|
||||
|
||||
if (key.kind === "user") {
|
||||
if (!key.userId) {
|
||||
return { valid: false, error: "User virtual API key has no user" };
|
||||
}
|
||||
|
||||
const access = await userHasResourceAccess(
|
||||
key.userId,
|
||||
resourceId,
|
||||
orgId
|
||||
);
|
||||
if (!access.allowed || !access.userData) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "User is not allowed to access this resource"
|
||||
};
|
||||
}
|
||||
|
||||
await touchLastUsedAt(key.virtualApiKeyId);
|
||||
return { valid: true, key, userData: access.userData };
|
||||
}
|
||||
|
||||
return { valid: false, error: "Unknown virtual API key kind" };
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export async function startCertificateManager() {
|
||||
// No-op: ACME certificate generation/management is only available in
|
||||
// builds that include the private/enterprise feature set.
|
||||
}
|
||||
|
||||
export async function stopCertificateManager() {
|
||||
// No-op counterpart to startCertificateManager.
|
||||
}
|
||||
@@ -3,12 +3,16 @@ import { flushConnectionLogToDb } from "#dynamic/routers/newt";
|
||||
import { flushSiteBandwidthToDb } from "@server/routers/gerbil/receiveBandwidth";
|
||||
import { stopPingAccumulator } from "@server/routers/newt/pingAccumulator";
|
||||
import { cleanup as wsCleanup } from "#dynamic/routers/ws";
|
||||
import { shutdownUsageRecorder } from "@server/lib/aiBudgetEnforcement";
|
||||
import { shutdownAiSessionLogger } from "@server/routers/aiGateway/logAiSession";
|
||||
|
||||
async function cleanup() {
|
||||
await stopPingAccumulator();
|
||||
await flushBandwidthToDb();
|
||||
await flushConnectionLogToDb();
|
||||
await flushSiteBandwidthToDb();
|
||||
await shutdownUsageRecorder();
|
||||
await shutdownAiSessionLogger();
|
||||
await wsCleanup();
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { join } from "path";
|
||||
import { readFileSync } from "fs";
|
||||
import {
|
||||
aiProviders,
|
||||
clients,
|
||||
db,
|
||||
resourcePolicies,
|
||||
@@ -113,6 +114,32 @@ export async function getUniqueResourceName(orgId: string): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUniqueProviderName(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 aiProviderCount = await db
|
||||
.select({
|
||||
niceId: aiProviders.niceId,
|
||||
orgId: aiProviders.orgId
|
||||
})
|
||||
.from(aiProviders)
|
||||
.where(
|
||||
and(eq(aiProviders.niceId, name), eq(aiProviders.orgId, orgId))
|
||||
);
|
||||
|
||||
if (aiProviderCount.length === 0) {
|
||||
return name;
|
||||
}
|
||||
loops++;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUniqueResourcePolicyName(
|
||||
orgId: string
|
||||
): Promise<string> {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
|
||||
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||
import { readEnvOrFile } from "@server/lib/getEnvOrYaml";
|
||||
import { withReplicas } from "drizzle-orm/pg-core";
|
||||
import { createPool } from "./poolConfig";
|
||||
|
||||
@@ -7,17 +8,20 @@ function createDb() {
|
||||
const config = readConfigFile();
|
||||
|
||||
// check the environment variables for postgres config first before the config file
|
||||
if (process.env.POSTGRES_CONNECTION_STRING) {
|
||||
const envConnectionString = readEnvOrFile("POSTGRES_CONNECTION_STRING");
|
||||
if (envConnectionString) {
|
||||
config.postgres = {
|
||||
connection_string: process.env.POSTGRES_CONNECTION_STRING
|
||||
connection_string: envConnectionString
|
||||
};
|
||||
if (process.env.POSTGRES_REPLICA_CONNECTION_STRINGS) {
|
||||
const replicas =
|
||||
process.env.POSTGRES_REPLICA_CONNECTION_STRINGS.split(",").map(
|
||||
(conn) => ({
|
||||
connection_string: conn.trim()
|
||||
})
|
||||
);
|
||||
const replicaConnectionStrings = readEnvOrFile(
|
||||
"POSTGRES_REPLICA_CONNECTION_STRINGS"
|
||||
);
|
||||
if (replicaConnectionStrings) {
|
||||
const replicas = replicaConnectionStrings
|
||||
.split(",")
|
||||
.map((conn) => ({
|
||||
connection_string: conn.trim()
|
||||
}));
|
||||
config.postgres.replicas = replicas;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
|
||||
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||
import { readEnvOrFile } from "@server/lib/getEnvOrYaml";
|
||||
import { withReplicas } from "drizzle-orm/pg-core";
|
||||
import { build } from "@server/build";
|
||||
import { db as mainDb } from "./driver";
|
||||
@@ -17,7 +18,7 @@ function createLogsDb() {
|
||||
const logsConfig = config.postgres_logs;
|
||||
|
||||
// Check environment variable first
|
||||
let connectionString = process.env.POSTGRES_LOGS_CONNECTION_STRING;
|
||||
let connectionString = readEnvOrFile("POSTGRES_LOGS_CONNECTION_STRING");
|
||||
let replicaConnections: Array<{ connection_string: string }> = [];
|
||||
|
||||
if (!connectionString && logsConfig) {
|
||||
@@ -26,13 +27,15 @@ function createLogsDb() {
|
||||
}
|
||||
|
||||
// If POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS is set, use it
|
||||
if (process.env.POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS) {
|
||||
replicaConnections =
|
||||
process.env.POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS.split(",").map(
|
||||
(conn) => ({
|
||||
connection_string: conn.trim()
|
||||
})
|
||||
);
|
||||
const replicaConnectionStrings = readEnvOrFile(
|
||||
"POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS"
|
||||
);
|
||||
if (replicaConnectionStrings) {
|
||||
replicaConnections = replicaConnectionStrings
|
||||
.split(",")
|
||||
.map((conn) => ({
|
||||
connection_string: conn.trim()
|
||||
}));
|
||||
}
|
||||
|
||||
// If no logs database is configured, fall back to main database
|
||||
|
||||
@@ -26,28 +26,11 @@ import {
|
||||
sites,
|
||||
clients,
|
||||
sessions,
|
||||
labels
|
||||
labels,
|
||||
aiProviders,
|
||||
virtualApiKeys
|
||||
} 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(),
|
||||
@@ -95,7 +78,8 @@ export const subscriptions = pgTable("subscriptions", {
|
||||
billingCycleAnchor: bigint("billingCycleAnchor", { mode: "number" }),
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }),
|
||||
trial: boolean("trial").default(false),
|
||||
type: varchar("type", { length: 50 }) // tier1, tier2, tier3, or license
|
||||
type: varchar("type", { length: 50 }), // tier1, tier2, tier3, or license
|
||||
override: boolean("override").default(false)
|
||||
});
|
||||
|
||||
export const subscriptionItems = pgTable("subscriptionItems", {
|
||||
@@ -486,6 +470,9 @@ export const eventStreamingDestinations = pgTable(
|
||||
sendRequestLogs: boolean("sendRequestLogs").notNull().default(false),
|
||||
sendActionLogs: boolean("sendActionLogs").notNull().default(false),
|
||||
sendAccessLogs: boolean("sendAccessLogs").notNull().default(false),
|
||||
sendAISessionLogs: boolean("sendAISessionLogs")
|
||||
.notNull()
|
||||
.default(false),
|
||||
type: varchar("type", { length: 50 }).notNull(), // e.g. "http", "kafka", etc.
|
||||
config: text("config").notNull(), // JSON string with the configuration for the destination
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
@@ -629,10 +616,90 @@ export const trialNotifications = pgTable("trialNotifications", {
|
||||
sentAt: bigint("sentAt", { mode: "number" }).notNull()
|
||||
});
|
||||
|
||||
// Logs the aggregated prompt + response for a single AI gateway request, for
|
||||
// session replay. One row per request (not per streaming chunk). `sessionId`
|
||||
// is a fresh random id per row for now - no cross-request correlation yet,
|
||||
// but the column exists so a future pass can link multiple rows into a real
|
||||
// multi-turn session.
|
||||
export const aiSessionLog = pgTable(
|
||||
"aiSessionLog",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
sessionId: varchar("sessionId").notNull(),
|
||||
orgId: varchar("orgId").references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
providerId: integer("providerId").references(
|
||||
() => aiProviders.providerId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
capability: varchar("capability").notNull(),
|
||||
resourceId: integer("resourceId").references(
|
||||
() => resources.resourceId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
siteResourceId: integer("siteResourceId").references(
|
||||
() => siteResources.siteResourceId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
userId: varchar("userId").references(() => users.userId, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
virtualApiKeyId: varchar("virtualApiKeyId").references(
|
||||
() => virtualApiKeys.virtualApiKeyId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
requestedModel: varchar("requestedModel"),
|
||||
isStream: boolean("isStream").notNull().default(false),
|
||||
requestBody: text("requestBody"),
|
||||
responseBody: text("responseBody"),
|
||||
// Capability-agnostic message transcript (JSON-encoded
|
||||
// NormalizedAiMessage[] from server/lib/aiMessageNormalization.ts),
|
||||
// computed at write time so search/display never need per-capability
|
||||
// parsing logic. Null when normalization couldn't recognize the
|
||||
// shape - callers fall back to requestBody/responseBody.
|
||||
normalizedRequest: text("normalizedRequest"),
|
||||
normalizedResponse: text("normalizedResponse"),
|
||||
// True if any of the request/response (raw or normalized) fields
|
||||
// were cut short at AI_SESSION_LOG_MAX_BODY_CHARS before storage.
|
||||
truncated: boolean("truncated").notNull().default(false),
|
||||
statusCode: integer("statusCode"),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull() // epoch seconds
|
||||
},
|
||||
(t) => [
|
||||
index("idx_ai_session_log_org_created").on(t.orgId, t.createdAt),
|
||||
index("idx_ai_session_log_org_provider_created").on(
|
||||
t.orgId,
|
||||
t.providerId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_resource_created").on(
|
||||
t.orgId,
|
||||
t.resourceId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_site_resource_created").on(
|
||||
t.orgId,
|
||||
t.siteResourceId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_user_created").on(
|
||||
t.orgId,
|
||||
t.userId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_virtual_api_key_created").on(
|
||||
t.orgId,
|
||||
t.virtualApiKeyId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_session").on(t.sessionId)
|
||||
]
|
||||
);
|
||||
|
||||
export type Approval = InferSelectModel<typeof approvals>;
|
||||
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>;
|
||||
@@ -676,3 +743,4 @@ export type AlertEmailRecipients = InferSelectModel<
|
||||
>;
|
||||
export type AlertWebhookActions = InferSelectModel<typeof alertWebhookActions>;
|
||||
export type TrialNotification = InferSelectModel<typeof trialNotifications>;
|
||||
export type AiSessionLog = InferSelectModel<typeof aiSessionLog>;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { InferSelectModel, sql } from "drizzle-orm";
|
||||
import {
|
||||
bigint,
|
||||
boolean,
|
||||
check,
|
||||
index,
|
||||
integer,
|
||||
pgTable,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
serial,
|
||||
text,
|
||||
unique,
|
||||
uniqueIndex,
|
||||
varchar
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
@@ -63,6 +65,11 @@ export const orgs = pgTable("orgs", {
|
||||
) // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||
.notNull()
|
||||
.default(0),
|
||||
settingsLogRetentionDaysAISessions: integer(
|
||||
"settingsLogRetentionDaysAISessions"
|
||||
) // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||
.notNull()
|
||||
.default(0),
|
||||
sshCaPrivateKey: text("sshCaPrivateKey"), // Encrypted SSH CA private key (PEM format)
|
||||
sshCaPublicKey: text("sshCaPublicKey"), // SSH CA public key (OpenSSH format)
|
||||
isBillingOrg: boolean("isBillingOrg"),
|
||||
@@ -98,7 +105,7 @@ export const sites = pgTable(
|
||||
}),
|
||||
name: varchar("name").notNull(),
|
||||
pubKey: varchar("pubKey"),
|
||||
subnet: varchar("subnet"),
|
||||
exitNodeSubnet: text("exitNodeSubnet"), // this is the subnet when connecting to an exit node and INCLUDES THE CIDR
|
||||
megabytesIn: real("bytesIn").default(0),
|
||||
megabytesOut: real("bytesOut").default(0),
|
||||
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
|
||||
@@ -194,7 +201,12 @@ export const resources = pgTable(
|
||||
postAuthPath: text("postAuthPath"),
|
||||
health: varchar("health").default("unknown"), // "healthy", "unhealthy", "unknown"
|
||||
wildcard: boolean("wildcard").notNull().default(false),
|
||||
mode: text("mode").default("http").notNull(), // rdp, ssh, http, vnc
|
||||
mode: text("mode")
|
||||
.default("http")
|
||||
.$type<
|
||||
"rdp" | "ssh" | "http" | "vnc" | "inference" | "tcp" | "udp"
|
||||
>()
|
||||
.notNull(),
|
||||
pamMode: varchar("pamMode", { length: 32 })
|
||||
.$type<"passthrough" | "push">()
|
||||
.default("passthrough"),
|
||||
@@ -215,16 +227,55 @@ export const resources = pgTable(
|
||||
]
|
||||
);
|
||||
|
||||
export const labels = pgTable("labels", {
|
||||
labelId: serial("labelId").primaryKey(),
|
||||
name: varchar("name").notNull(),
|
||||
color: varchar("color").notNull(),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull()
|
||||
});
|
||||
export const resourceAiProviders = pgTable(
|
||||
"resourceAiProviders",
|
||||
{
|
||||
resourceId: integer("resourceId")
|
||||
.notNull()
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
providerId: integer("providerId")
|
||||
.notNull()
|
||||
.references(() => aiProviders.providerId, { onDelete: "cascade" }),
|
||||
accessMode: varchar("accessMode")
|
||||
.$type<"inherit" | "select">()
|
||||
.notNull()
|
||||
.default("inherit"),
|
||||
enabled: boolean("enabled").notNull().default(true)
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.resourceId, t.providerId] })]
|
||||
);
|
||||
|
||||
export const resourceAiModels = pgTable(
|
||||
"resourceAiModels",
|
||||
{
|
||||
resourceId: integer("resourceId")
|
||||
.notNull()
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
modelId: integer("modelId")
|
||||
.notNull()
|
||||
.references(() => aiModels.modelId, { onDelete: "cascade" }),
|
||||
listType: varchar("listType")
|
||||
.$type<"allow" | "block">()
|
||||
.notNull()
|
||||
.default("allow")
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.resourceId, t.modelId] })]
|
||||
);
|
||||
|
||||
export const labels = pgTable(
|
||||
"labels",
|
||||
{
|
||||
labelId: serial("labelId").primaryKey(),
|
||||
name: varchar("name").notNull(),
|
||||
color: varchar("color").notNull(),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull()
|
||||
},
|
||||
(t) => [index("idx_labels_orgid").on(t.orgId)]
|
||||
);
|
||||
|
||||
export const launcherViews = pgTable("launcherViews", {
|
||||
viewId: serial("viewId").primaryKey(),
|
||||
@@ -317,11 +368,18 @@ export const targets = pgTable(
|
||||
"targets",
|
||||
{
|
||||
targetId: serial("targetId").primaryKey(),
|
||||
resourceId: integer("resourceId")
|
||||
.references(() => resources.resourceId, {
|
||||
resourceId: integer("resourceId").references(
|
||||
() => resources.resourceId,
|
||||
{
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
}
|
||||
),
|
||||
providerId: integer("providerId").references(
|
||||
() => aiProviders.providerId,
|
||||
{
|
||||
onDelete: "cascade"
|
||||
}
|
||||
),
|
||||
siteId: integer("siteId")
|
||||
.references(() => sites.siteId, {
|
||||
onDelete: "cascade"
|
||||
@@ -345,6 +403,7 @@ export const targets = pgTable(
|
||||
},
|
||||
(t) => [
|
||||
index("idx_targets_resourceid_siteid").on(t.resourceId, t.siteId),
|
||||
index("idx_targets_providerid_siteid").on(t.providerId, t.siteId),
|
||||
index("idx_targets_site_enabled_priority_target_resource")
|
||||
.on(t.siteId, t.priority.desc(), t.targetId, t.resourceId)
|
||||
.where(sql`${t.enabled} = true`)
|
||||
@@ -424,11 +483,14 @@ export const siteResources = pgTable(
|
||||
onDelete: "restrict"
|
||||
}
|
||||
),
|
||||
requiresExitNodeConnection: boolean("requiresExitNodeConnection")
|
||||
.notNull()
|
||||
.default(false),
|
||||
niceId: varchar("niceId").notNull(),
|
||||
name: varchar("name").notNull(),
|
||||
ssl: boolean("ssl").notNull().default(false),
|
||||
mode: varchar("mode")
|
||||
.$type<"host" | "cidr" | "http" | "ssh">()
|
||||
.$type<"host" | "cidr" | "http" | "ssh" | "inference">()
|
||||
.notNull(), // "host" | "cidr" | "http"
|
||||
scheme: varchar("scheme").$type<"http" | "https">(), // only for when we are doing https or http mode
|
||||
proxyPort: integer("proxyPort"), // only for port mode
|
||||
@@ -463,6 +525,45 @@ export const siteResources = pgTable(
|
||||
(t) => [index("idx_siteresources_orgid_niceid").on(t.orgId, t.niceId)]
|
||||
);
|
||||
|
||||
export const siteResourceAiProviders = pgTable(
|
||||
"siteResourceAiProviders",
|
||||
{
|
||||
siteResourceId: integer("siteResourceId")
|
||||
.notNull()
|
||||
.references(() => siteResources.siteResourceId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
providerId: integer("providerId")
|
||||
.notNull()
|
||||
.references(() => aiProviders.providerId, { onDelete: "cascade" }),
|
||||
accessMode: varchar("accessMode")
|
||||
.$type<"inherit" | "select">()
|
||||
.notNull()
|
||||
.default("inherit"),
|
||||
enabled: boolean("enabled").notNull().default(true)
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.siteResourceId, t.providerId] })]
|
||||
);
|
||||
|
||||
export const siteResourceAiModels = pgTable(
|
||||
"siteResourceAiModels",
|
||||
{
|
||||
siteResourceId: integer("siteResourceId")
|
||||
.notNull()
|
||||
.references(() => siteResources.siteResourceId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
modelId: integer("modelId")
|
||||
.notNull()
|
||||
.references(() => aiModels.modelId, { onDelete: "cascade" }),
|
||||
listType: varchar("listType")
|
||||
.$type<"allow" | "block">()
|
||||
.notNull()
|
||||
.default("allow")
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.siteResourceId, t.modelId] })]
|
||||
);
|
||||
|
||||
export const networks = pgTable(
|
||||
"networks",
|
||||
{
|
||||
@@ -581,6 +682,8 @@ export const newts = pgTable(
|
||||
secretHash: varchar("secretHash").notNull(),
|
||||
dateCreated: varchar("dateCreated").notNull(),
|
||||
version: varchar("version"),
|
||||
agent: varchar("agent"), // either newt or cli
|
||||
agentVersion: varchar("agentVersion"),
|
||||
siteId: integer("siteId").references(() => sites.siteId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
@@ -596,15 +699,19 @@ export const twoFactorBackupCodes = pgTable("twoFactorBackupCodes", {
|
||||
codeHash: varchar("codeHash").notNull()
|
||||
});
|
||||
|
||||
export const sessions = pgTable("session", {
|
||||
sessionId: varchar("id").primaryKey(),
|
||||
userId: varchar("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
|
||||
issuedAt: bigint("issuedAt", { mode: "number" }),
|
||||
deviceAuthUsed: boolean("deviceAuthUsed").notNull().default(false)
|
||||
});
|
||||
export const sessions = pgTable(
|
||||
"session",
|
||||
{
|
||||
sessionId: varchar("id").primaryKey(),
|
||||
userId: varchar("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
|
||||
issuedAt: bigint("issuedAt", { mode: "number" }),
|
||||
deviceAuthUsed: boolean("deviceAuthUsed").notNull().default(false)
|
||||
},
|
||||
(t) => [index("idx_sessions_userid").on(t.userId)]
|
||||
);
|
||||
|
||||
export const newtSessions = pgTable("newtSession", {
|
||||
sessionId: varchar("id").primaryKey(),
|
||||
@@ -614,19 +721,26 @@ export const newtSessions = pgTable("newtSession", {
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||
});
|
||||
|
||||
export const userOrgs = pgTable("userOrgs", {
|
||||
userId: varchar("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
isOwner: boolean("isOwner").notNull().default(false),
|
||||
autoProvisioned: boolean("autoProvisioned").default(false),
|
||||
pamUsername: varchar("pamUsername") // cleaned username for ssh and such
|
||||
});
|
||||
export const userOrgs = pgTable(
|
||||
"userOrgs",
|
||||
{
|
||||
userId: varchar("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
isOwner: boolean("isOwner").notNull().default(false),
|
||||
autoProvisioned: boolean("autoProvisioned").default(false),
|
||||
pamUsername: varchar("pamUsername") // cleaned username for ssh and such
|
||||
},
|
||||
(t) => [
|
||||
index("idx_userOrgs_userid").on(t.userId),
|
||||
index("idx_userOrgs_orgid").on(t.orgId)
|
||||
]
|
||||
);
|
||||
|
||||
export const emailVerificationCodes = pgTable("emailVerificationCodes", {
|
||||
codeId: serial("id").primaryKey(),
|
||||
@@ -654,22 +768,26 @@ export const actions = pgTable("actions", {
|
||||
description: varchar("description")
|
||||
});
|
||||
|
||||
export const roles = pgTable("roles", {
|
||||
roleId: serial("roleId").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
isAdmin: boolean("isAdmin"),
|
||||
name: varchar("name").notNull(),
|
||||
description: varchar("description"),
|
||||
requireDeviceApproval: boolean("requireDeviceApproval").default(false),
|
||||
sshSudoMode: varchar("sshSudoMode", { length: 32 }).default("none"), // "none" | "full" | "commands"
|
||||
sshSudoCommands: text("sshSudoCommands").default("[]"),
|
||||
sshCreateHomeDir: boolean("sshCreateHomeDir").default(true),
|
||||
sshUnixGroups: text("sshUnixGroups").default("[]")
|
||||
});
|
||||
export const roles = pgTable(
|
||||
"roles",
|
||||
{
|
||||
roleId: serial("roleId").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
isAdmin: boolean("isAdmin"),
|
||||
name: varchar("name").notNull(),
|
||||
description: varchar("description"),
|
||||
requireDeviceApproval: boolean("requireDeviceApproval").default(false),
|
||||
sshSudoMode: varchar("sshSudoMode", { length: 32 }).default("full"), // "none" | "full" | "commands"
|
||||
sshSudoCommands: text("sshSudoCommands").default("[]"),
|
||||
sshCreateHomeDir: boolean("sshCreateHomeDir").default(true),
|
||||
sshUnixGroups: text("sshUnixGroups").default("[]")
|
||||
},
|
||||
(t) => [index("idx_roles_orgid").on(t.orgId)]
|
||||
);
|
||||
|
||||
export const userOrgRoles = pgTable(
|
||||
"userOrgRoles",
|
||||
@@ -1150,6 +1268,52 @@ export const apiKeyOrg = pgTable("apiKeyOrg", {
|
||||
.notNull()
|
||||
});
|
||||
|
||||
export const virtualApiKeys = pgTable(
|
||||
"virtualApiKeys",
|
||||
{
|
||||
virtualApiKeyId: varchar("virtualApiKeyId").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
kind: varchar("kind").$type<"user" | "manual">().notNull(),
|
||||
userId: varchar("userId").references(() => users.userId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
name: varchar("name"),
|
||||
description: varchar("description"),
|
||||
token: varchar("token").notNull(),
|
||||
lastChars: varchar("lastChars").notNull(),
|
||||
allResources: boolean("allResources").notNull().default(false),
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }),
|
||||
lastUsedAt: bigint("lastUsedAt", { mode: "number" }),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||
createdByUserId: varchar("createdByUserId").references(
|
||||
() => users.userId,
|
||||
{ onDelete: "set null" }
|
||||
)
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex("virtual_api_key_user_identity_uniq")
|
||||
.on(t.orgId, t.userId)
|
||||
.where(sql`${t.kind} = 'user'`)
|
||||
]
|
||||
);
|
||||
|
||||
export const virtualApiKeyResources = pgTable(
|
||||
"virtualApiKeyResources",
|
||||
{
|
||||
virtualApiKeyId: varchar("virtualApiKeyId")
|
||||
.notNull()
|
||||
.references(() => virtualApiKeys.virtualApiKeyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
resourceId: integer("resourceId")
|
||||
.notNull()
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.virtualApiKeyId, t.resourceId] })]
|
||||
);
|
||||
|
||||
export const idpOrg = pgTable("idpOrg", {
|
||||
idpId: integer("idpId")
|
||||
.notNull()
|
||||
@@ -1182,6 +1346,7 @@ export const clients = pgTable(
|
||||
name: varchar("name").notNull(),
|
||||
pubKey: varchar("pubKey"),
|
||||
subnet: varchar("subnet").notNull(),
|
||||
exitNodeSubnet: varchar("exitNodeSubnet"), // INCLUDES THE CIDR
|
||||
megabytesIn: real("bytesIn"),
|
||||
megabytesOut: real("bytesOut"),
|
||||
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
|
||||
@@ -1265,7 +1430,10 @@ export const olms = pgTable(
|
||||
}),
|
||||
archived: boolean("archived").notNull().default(false)
|
||||
},
|
||||
(t) => [index("idx_olms_clientid").on(t.clientId)]
|
||||
(t) => [
|
||||
index("idx_olms_clientid").on(t.clientId),
|
||||
index("idx_olms_userid").on(t.userId)
|
||||
]
|
||||
);
|
||||
|
||||
export const currentFingerprint = pgTable("currentFingerprint", {
|
||||
@@ -1540,6 +1708,277 @@ export const statusHistory = pgTable(
|
||||
]
|
||||
);
|
||||
|
||||
export const aiProviders = pgTable(
|
||||
"aiProviders",
|
||||
{
|
||||
providerId: serial("providerId").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
name: varchar("name").notNull(),
|
||||
niceId: varchar("niceId").notNull(),
|
||||
type: varchar("type")
|
||||
.$type<
|
||||
| "openai"
|
||||
| "anthropic"
|
||||
| "googleGemini"
|
||||
| "vertexAi"
|
||||
| "bedrock"
|
||||
| "microsoftFoundry"
|
||||
| "openRouter"
|
||||
| "vercelAiGateway"
|
||||
| "custom"
|
||||
>()
|
||||
.notNull(),
|
||||
upstreamUrl: text("upstreamUrl"),
|
||||
apiKey: text("apiKey"),
|
||||
apiKeyLastChars: varchar("apiKeyLastChars"),
|
||||
authType: varchar("authType")
|
||||
.$type<
|
||||
| "bearer"
|
||||
| "x-api-key"
|
||||
| "x-goog-api-key"
|
||||
| "hec"
|
||||
| "cf-aig-authorization"
|
||||
| "none"
|
||||
| "passthrough"
|
||||
>()
|
||||
.notNull(),
|
||||
routingMode: varchar("routingMode")
|
||||
.$type<"url" | "target">()
|
||||
.notNull()
|
||||
.default("url"),
|
||||
capabilities: text("capabilities").notNull().default("[]"),
|
||||
headers: text("headers"), // JSON array of { name, value }
|
||||
skipTlsVerification: boolean("skipTlsVerification")
|
||||
.notNull()
|
||||
.default(false),
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
||||
},
|
||||
(t) => [index("idx_aiProviders_orgId_niceId").on(t.orgId, t.niceId)]
|
||||
);
|
||||
|
||||
export const aiModels = pgTable(
|
||||
"aiModels",
|
||||
{
|
||||
modelId: serial("modelId").primaryKey(),
|
||||
providerId: integer("providerId")
|
||||
.notNull()
|
||||
.references(() => aiProviders.providerId, { onDelete: "cascade" }),
|
||||
modelKey: varchar("modelKey").notNull(),
|
||||
name: varchar("name").notNull(),
|
||||
listType: varchar("listType")
|
||||
.$type<"allow" | "block">()
|
||||
.notNull()
|
||||
.default("allow"),
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
||||
},
|
||||
(t) => [unique("ai_model_provider_key_uniq").on(t.providerId, t.modelKey)]
|
||||
);
|
||||
|
||||
export const aiBudgets = pgTable(
|
||||
"aiBudgets",
|
||||
{
|
||||
budgetId: serial("budgetId").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
providerId: integer("providerId").references(
|
||||
() => aiProviders.providerId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
modelId: integer("modelId").references(() => aiModels.modelId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
resourceId: integer("resourceId").references(
|
||||
() => resources.resourceId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
siteResourceId: integer("siteResourceId").references(
|
||||
() => siteResources.siteResourceId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
roleId: integer("roleId").references(() => roles.roleId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
virtualApiKeyId: varchar("virtualApiKeyId").references(
|
||||
() => virtualApiKeys.virtualApiKeyId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
amount: real("amount").notNull(),
|
||||
unit: varchar("unit").$type<"usd" | "tokens">().notNull(),
|
||||
period: varchar("period")
|
||||
.$type<
|
||||
| "monthly"
|
||||
| "yearly"
|
||||
| "lifetime"
|
||||
| "daily"
|
||||
| "hourly"
|
||||
| "weekly"
|
||||
>()
|
||||
.notNull()
|
||||
.default("monthly"),
|
||||
enforcement: varchar("enforcement")
|
||||
.$type<"hard" | "soft">()
|
||||
.notNull()
|
||||
.default("hard"),
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
||||
},
|
||||
(t) => [
|
||||
unique("ai_budget_provider_uniq").on(t.providerId, t.unit, t.period),
|
||||
unique("ai_budget_model_uniq").on(t.modelId, t.unit, t.period),
|
||||
unique("ai_budget_resource_uniq").on(t.resourceId, t.unit, t.period),
|
||||
unique("ai_budget_site_resource_uniq").on(
|
||||
t.siteResourceId,
|
||||
t.unit,
|
||||
t.period
|
||||
),
|
||||
unique("ai_budget_role_uniq").on(t.roleId, t.unit, t.period),
|
||||
unique("ai_budget_virtual_api_key_uniq").on(
|
||||
t.virtualApiKeyId,
|
||||
t.unit,
|
||||
t.period
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
export const aiUsageRecords = pgTable(
|
||||
"aiUsageRecords",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
providerId: integer("providerId").references(
|
||||
() => aiProviders.providerId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
resourceId: integer("resourceId").references(
|
||||
() => resources.resourceId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
siteResourceId: integer("siteResourceId").references(
|
||||
() => siteResources.siteResourceId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
userId: varchar("userId").references(() => users.userId, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
virtualApiKeyId: varchar("virtualApiKeyId").references(
|
||||
() => virtualApiKeys.virtualApiKeyId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
// Links this usage record back to the aiSessionLog row for the same
|
||||
// request (aiSessionLog.sessionId), so token/cost usage can be shown
|
||||
// alongside the session transcript. Not a DB-level FK - aiSessionLog
|
||||
// lives in the separate logs database. Nullable because the session
|
||||
// log may be disabled (retention set to 0) while usage tracking
|
||||
// stays on.
|
||||
sessionId: varchar("sessionId"),
|
||||
requestedModel: varchar("requestedModel").notNull(),
|
||||
promptTokens: integer("promptTokens").notNull().default(0),
|
||||
cacheReadTokens: integer("cacheReadTokens").notNull().default(0),
|
||||
cacheWriteTokens: integer("cacheWriteTokens").notNull().default(0),
|
||||
completionTokens: integer("completionTokens").notNull().default(0),
|
||||
reasoningTokens: integer("reasoningTokens").notNull().default(0),
|
||||
totalTokens: integer("totalTokens").notNull().default(0),
|
||||
costUsd: real("costUsd"),
|
||||
estimated: boolean("estimated").notNull().default(false),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull()
|
||||
},
|
||||
(t) => [
|
||||
index("idx_ai_usage_records_org_provider_created").on(
|
||||
t.orgId,
|
||||
t.providerId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_usage_records_org_resource_created").on(
|
||||
t.orgId,
|
||||
t.resourceId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_usage_records_org_site_resource_created").on(
|
||||
t.orgId,
|
||||
t.siteResourceId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_usage_records_org_user_created").on(
|
||||
t.orgId,
|
||||
t.userId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_usage_records_org_virtual_api_key_created").on(
|
||||
t.orgId,
|
||||
t.virtualApiKeyId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_usage_records_session").on(t.sessionId)
|
||||
]
|
||||
);
|
||||
|
||||
export const aiBudgetBreachEvents = pgTable(
|
||||
"aiBudgetBreachEvents",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
orgId: varchar("orgId")
|
||||
.notNull()
|
||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||
budgetId: integer("budgetId")
|
||||
.notNull()
|
||||
.references(() => aiBudgets.budgetId, { onDelete: "cascade" }),
|
||||
enforcement: varchar("enforcement").$type<"hard" | "soft">().notNull(),
|
||||
unit: varchar("unit").$type<"usd" | "tokens">().notNull(),
|
||||
period: varchar("period")
|
||||
.$type<
|
||||
| "monthly"
|
||||
| "yearly"
|
||||
| "lifetime"
|
||||
| "daily"
|
||||
| "hourly"
|
||||
| "weekly"
|
||||
>()
|
||||
.notNull(),
|
||||
amount: real("amount").notNull(),
|
||||
usageAmount: real("usageAmount").notNull(),
|
||||
blocked: boolean("blocked").notNull(),
|
||||
requestUserId: varchar("requestUserId").references(() => users.userId, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
createdAt: bigint("createdAt", { mode: "number" }).notNull()
|
||||
},
|
||||
(t) => [
|
||||
index("idx_ai_budget_breach_events_budget_created").on(
|
||||
t.budgetId,
|
||||
t.createdAt
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
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 type Org = InferSelectModel<typeof orgs>;
|
||||
export type User = InferSelectModel<typeof users>;
|
||||
export type Site = InferSelectModel<typeof sites>;
|
||||
@@ -1595,6 +2034,10 @@ export type Idp = InferSelectModel<typeof idp>;
|
||||
export type ApiKey = InferSelectModel<typeof apiKeys>;
|
||||
export type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
|
||||
export type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;
|
||||
export type VirtualApiKey = InferSelectModel<typeof virtualApiKeys>;
|
||||
export type VirtualApiKeyResource = InferSelectModel<
|
||||
typeof virtualApiKeyResources
|
||||
>;
|
||||
export type Client = InferSelectModel<typeof clients>;
|
||||
export type ClientSite = InferSelectModel<typeof clientSitesAssociationsCache>;
|
||||
export type Olm = InferSelectModel<typeof olms>;
|
||||
@@ -1624,3 +2067,15 @@ export type ResourcePolicy = InferSelectModel<typeof resourcePolicies>;
|
||||
export type RolePolicy = InferSelectModel<typeof rolePolicies>;
|
||||
export type UserPolicy = InferSelectModel<typeof userPolicies>;
|
||||
export type ResourcePolicyRule = InferSelectModel<typeof resourcePolicyRules>;
|
||||
export type AiProvider = InferSelectModel<typeof aiProviders>;
|
||||
export type AiModel = InferSelectModel<typeof aiModels>;
|
||||
export type AiBudget = InferSelectModel<typeof aiBudgets>;
|
||||
export type AiUsageRecord = InferSelectModel<typeof aiUsageRecords>;
|
||||
export type AiBudgetBreachEvent = InferSelectModel<typeof aiBudgetBreachEvents>;
|
||||
export type ResourceAiProvider = InferSelectModel<typeof resourceAiProviders>;
|
||||
export type SiteResourceAiProvider = InferSelectModel<
|
||||
typeof siteResourceAiProviders
|
||||
>;
|
||||
export type ResourceAiModel = InferSelectModel<typeof resourceAiModels>;
|
||||
export type SiteResourceAiModel = InferSelectModel<typeof siteResourceAiModels>;
|
||||
export type Certificate = InferSelectModel<typeof certificates>;
|
||||
|
||||
@@ -33,7 +33,9 @@ import {
|
||||
resourcePolicyPassword,
|
||||
ResourcePolicyPassword,
|
||||
resourcePolicyHeaderAuth,
|
||||
ResourcePolicyHeaderAuth
|
||||
ResourcePolicyHeaderAuth,
|
||||
resourceWhitelist,
|
||||
resourcePolicyWhiteList
|
||||
} from "@server/db";
|
||||
import { alias } from "@server/db";
|
||||
import { and, eq, inArray, isNull, or, sql } from "drizzle-orm";
|
||||
@@ -448,6 +450,36 @@ export async function getResourceRules(
|
||||
return [...directRules, ...offsetPolicyRules] as ResourceRule[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whitelisted email associated with a resource session's whitelist
|
||||
* match (either a direct resource whitelist entry or a resource policy
|
||||
* whitelist entry).
|
||||
*/
|
||||
export async function getWhitelistEmail(
|
||||
whitelistId?: number | null,
|
||||
policyWhitelistId?: number | null
|
||||
): Promise<string | null> {
|
||||
if (whitelistId) {
|
||||
const [row] = await db
|
||||
.select({ email: resourceWhitelist.email })
|
||||
.from(resourceWhitelist)
|
||||
.where(eq(resourceWhitelist.whitelistId, whitelistId))
|
||||
.limit(1);
|
||||
return row?.email ?? null;
|
||||
}
|
||||
|
||||
if (policyWhitelistId) {
|
||||
const [row] = await db
|
||||
.select({ email: resourcePolicyWhiteList.email })
|
||||
.from(resourcePolicyWhiteList)
|
||||
.where(eq(resourcePolicyWhiteList.whitelistId, policyWhitelistId))
|
||||
.limit(1);
|
||||
return row?.email ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get organization login page
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,8 @@ import path from "path";
|
||||
import fs from "fs";
|
||||
import { APP_PATH } from "@server/lib/consts";
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
import logger from "@server/logger";
|
||||
import { readEnvOrFile } from "@server/lib/getEnvOrYaml";
|
||||
|
||||
export const location = path.join(APP_PATH, "db", "db.sqlite");
|
||||
export const exists = checkFileExists(location);
|
||||
@@ -12,9 +14,13 @@ export const exists = checkFileExists(location);
|
||||
bootstrapVolume();
|
||||
|
||||
function createDb() {
|
||||
const sqlite = new Database(location);
|
||||
const verbose =
|
||||
process.env.QUERY_LOGGING == "true"
|
||||
? (message: unknown) => logger.debug(String(message))
|
||||
: undefined;
|
||||
const sqlite = new Database(location, { verbose });
|
||||
|
||||
if (process.env.ENABLE_SQLITE_WAL_MODE == "true") {
|
||||
if (readEnvOrFile("ENABLE_SQLITE_WAL_MODE") == "true") {
|
||||
// Enable WAL mode — allows concurrent readers + single writer, preventing
|
||||
// contention across subsystems (verifySession, Traefik, audit, ping).
|
||||
// NOTE: journal_mode persists in the DB file once set; unsetting this
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
uniqueIndex
|
||||
} from "drizzle-orm/sqlite-core";
|
||||
import {
|
||||
aiProviders,
|
||||
clients,
|
||||
domains,
|
||||
exitNodes,
|
||||
@@ -20,28 +21,10 @@ import {
|
||||
siteResources,
|
||||
sites,
|
||||
targetHealthCheck,
|
||||
users
|
||||
users,
|
||||
virtualApiKeys
|
||||
} from "./schema";
|
||||
|
||||
export const certificates = sqliteTable("certificates", {
|
||||
certId: integer("certId").primaryKey({ autoIncrement: true }),
|
||||
domain: text("domain").notNull().unique(),
|
||||
domainId: text("domainId").references(() => domains.domainId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
wildcard: integer("wildcard", { mode: "boolean" }).default(false),
|
||||
status: text("status").notNull().default("pending"), // pending, requested, valid, expired, failed
|
||||
expiresAt: integer("expiresAt"),
|
||||
lastRenewalAttempt: integer("lastRenewalAttempt"),
|
||||
createdAt: integer("createdAt").notNull(),
|
||||
updatedAt: integer("updatedAt").notNull(),
|
||||
orderId: text("orderId"),
|
||||
errorMessage: text("errorMessage"),
|
||||
renewalCount: integer("renewalCount").default(0),
|
||||
certFile: text("certFile"),
|
||||
keyFile: text("keyFile")
|
||||
});
|
||||
|
||||
export const dnsChallenge = sqliteTable("dnsChallenges", {
|
||||
dnsChallengeId: integer("dnsChallengeId").primaryKey({
|
||||
autoIncrement: true
|
||||
@@ -89,7 +72,8 @@ export const subscriptions = sqliteTable("subscriptions", {
|
||||
expiresAt: integer("expiresAt"),
|
||||
trial: integer("trial", { mode: "boolean" }).default(false),
|
||||
billingCycleAnchor: integer("billingCycleAnchor"),
|
||||
type: text("type") // tier1, tier2, tier3, or license
|
||||
type: text("type"), // tier1, tier2, tier3, or license
|
||||
override: integer("override", { mode: "boolean" }).default(false)
|
||||
});
|
||||
|
||||
export const subscriptionItems = sqliteTable("subscriptionItems", {
|
||||
@@ -477,6 +461,9 @@ export const eventStreamingDestinations = sqliteTable(
|
||||
sendAccessLogs: integer("sendAccessLogs", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
sendAISessionLogs: integer("sendAISessionLogs", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
type: text("type").notNull(), // e.g. "http", "kafka", etc.
|
||||
config: text("config").notNull(), // JSON string with the configuration for the destination
|
||||
enabled: integer("enabled", { mode: "boolean" })
|
||||
@@ -624,10 +611,94 @@ export const trialNotifications = sqliteTable("trialNotifications", {
|
||||
sentAt: integer("sentAt").notNull()
|
||||
});
|
||||
|
||||
// Logs the aggregated prompt + response for a single AI gateway request, for
|
||||
// session replay. One row per request (not per streaming chunk). `sessionId`
|
||||
// is a fresh random id per row for now - no cross-request correlation yet,
|
||||
// but the column exists so a future pass can link multiple rows into a real
|
||||
// multi-turn session.
|
||||
export const aiSessionLog = sqliteTable(
|
||||
"aiSessionLog",
|
||||
{
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
sessionId: text("sessionId").notNull(),
|
||||
orgId: text("orgId").references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
providerId: integer("providerId").references(
|
||||
() => aiProviders.providerId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
capability: text("capability").notNull(),
|
||||
resourceId: integer("resourceId").references(
|
||||
() => resources.resourceId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
siteResourceId: integer("siteResourceId").references(
|
||||
() => siteResources.siteResourceId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
userId: text("userId").references(() => users.userId, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
virtualApiKeyId: text("virtualApiKeyId").references(
|
||||
() => virtualApiKeys.virtualApiKeyId,
|
||||
{ onDelete: "set null" }
|
||||
),
|
||||
requestedModel: text("requestedModel"),
|
||||
isStream: integer("isStream", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
requestBody: text("requestBody"),
|
||||
responseBody: text("responseBody"),
|
||||
// Capability-agnostic message transcript (JSON-encoded
|
||||
// NormalizedAiMessage[] from server/lib/aiMessageNormalization.ts),
|
||||
// computed at write time so search/display never need per-capability
|
||||
// parsing logic. Null when normalization couldn't recognize the
|
||||
// shape - callers fall back to requestBody/responseBody.
|
||||
normalizedRequest: text("normalizedRequest"),
|
||||
normalizedResponse: text("normalizedResponse"),
|
||||
// True if any of the request/response (raw or normalized) fields
|
||||
// were cut short at AI_SESSION_LOG_MAX_BODY_CHARS before storage.
|
||||
truncated: integer("truncated", { mode: "boolean" })
|
||||
.notNull()
|
||||
.default(false),
|
||||
statusCode: integer("statusCode"),
|
||||
createdAt: integer("createdAt").notNull() // epoch seconds
|
||||
},
|
||||
(t) => [
|
||||
index("idx_ai_session_log_org_created").on(t.orgId, t.createdAt),
|
||||
index("idx_ai_session_log_org_provider_created").on(
|
||||
t.orgId,
|
||||
t.providerId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_resource_created").on(
|
||||
t.orgId,
|
||||
t.resourceId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_site_resource_created").on(
|
||||
t.orgId,
|
||||
t.siteResourceId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_user_created").on(
|
||||
t.orgId,
|
||||
t.userId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_org_virtual_api_key_created").on(
|
||||
t.orgId,
|
||||
t.virtualApiKeyId,
|
||||
t.createdAt
|
||||
),
|
||||
index("idx_ai_session_log_session").on(t.sessionId)
|
||||
]
|
||||
);
|
||||
|
||||
export type Approval = InferSelectModel<typeof approvals>;
|
||||
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>;
|
||||
@@ -663,3 +734,4 @@ export type AlertEmailAction = InferSelectModel<typeof alertEmailActions>;
|
||||
export type AlertEmailRecipient = InferSelectModel<typeof alertEmailRecipients>;
|
||||
export type AlertWebhookAction = InferSelectModel<typeof alertWebhookActions>;
|
||||
export type TrialNotification = InferSelectModel<typeof trialNotifications>;
|
||||
export type AiSessionLog = InferSelectModel<typeof aiSessionLog>;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export async function startDnsServer() {
|
||||
// No-op: the authoritative DNS server is only available in builds
|
||||
// that include the private/enterprise feature set.
|
||||
}
|
||||
|
||||
export async function stopDnsServer() {
|
||||
// No-op counterpart to startDnsServer.
|
||||
}
|
||||
@@ -31,9 +31,24 @@ export type AlertNotificationProps = {
|
||||
orgId: string;
|
||||
data: Record<string, unknown>;
|
||||
dashboardLink: string;
|
||||
isTestAlert?: boolean;
|
||||
};
|
||||
|
||||
function getEventMeta(eventType: AlertEventType): {
|
||||
function getEventMeta(
|
||||
eventType: AlertEventType,
|
||||
isTestAlert: boolean = false
|
||||
): {
|
||||
heading: string;
|
||||
previewText: string;
|
||||
summary: string;
|
||||
statusLabel: string | null;
|
||||
statusColor: string | null;
|
||||
} {
|
||||
const meta = getBaseEventMeta(eventType);
|
||||
return isTestAlert ? { ...meta, heading: `[TEST] ${meta.heading}` } : meta;
|
||||
}
|
||||
|
||||
function getBaseEventMeta(eventType: AlertEventType): {
|
||||
heading: string;
|
||||
previewText: string;
|
||||
summary: string;
|
||||
@@ -180,8 +195,14 @@ function formatDataItems(
|
||||
}
|
||||
|
||||
export const AlertNotification = (props: AlertNotificationProps) => {
|
||||
const { eventType, orgId, data, dashboardLink } = props;
|
||||
const meta = getEventMeta(eventType);
|
||||
const {
|
||||
eventType,
|
||||
orgId,
|
||||
data,
|
||||
dashboardLink,
|
||||
isTestAlert = false
|
||||
} = props;
|
||||
const meta = getEventMeta(eventType, isTestAlert);
|
||||
const dataItems = formatDataItems(data);
|
||||
|
||||
const isToggle =
|
||||
@@ -242,6 +263,12 @@ export const AlertNotification = (props: AlertNotificationProps) => {
|
||||
Open your dashboard to view more details and manage
|
||||
your alert rules.
|
||||
</EmailText>
|
||||
{isTestAlert && (
|
||||
<EmailText>
|
||||
This is a test alert. No action is required,
|
||||
and no real event has occurred.
|
||||
</EmailText>
|
||||
)}
|
||||
|
||||
<EmailSection>
|
||||
<ButtonLink href={dashboardLink}>
|
||||
|
||||
@@ -93,9 +93,8 @@ export const EnterpriseEditionKeyGenerated = ({
|
||||
</EmailSection>
|
||||
|
||||
<EmailText>
|
||||
If you need to purchase additional license keys or
|
||||
modify your existing license, please reach out to
|
||||
our support team at{" "}
|
||||
For any questions or concerns regarding your license
|
||||
or billing, please reach out to support at{" "}
|
||||
<a
|
||||
href="mailto:support@pangolin.net"
|
||||
className="text-primary font-medium"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from "react";
|
||||
import { Body, Head, Html, Preview, Tailwind } from "@react-email/components";
|
||||
import { themeColors } from "./lib/theme";
|
||||
import {
|
||||
EmailContainer,
|
||||
EmailFooter,
|
||||
EmailGreeting,
|
||||
EmailHeading,
|
||||
EmailInfoSection,
|
||||
EmailLetterHead,
|
||||
EmailSection,
|
||||
EmailSignature,
|
||||
EmailText
|
||||
} from "./components/Email";
|
||||
|
||||
type IdentityApiKeyGeneratedProps = {
|
||||
orgName: string;
|
||||
accountLabel?: string | null;
|
||||
credential: string;
|
||||
resourceUrls: string[];
|
||||
hasMoreResources: boolean;
|
||||
};
|
||||
|
||||
export const IdentityApiKeyGenerated = ({
|
||||
orgName,
|
||||
accountLabel,
|
||||
credential,
|
||||
resourceUrls,
|
||||
hasMoreResources
|
||||
}: IdentityApiKeyGeneratedProps) => {
|
||||
const previewText = `Your personal identity key for ${orgName}`;
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind config={themeColors}>
|
||||
<Body className="font-sans bg-gray-50">
|
||||
<EmailContainer>
|
||||
<EmailLetterHead />
|
||||
|
||||
<EmailGreeting>Hi there,</EmailGreeting>
|
||||
|
||||
<EmailText>
|
||||
This is your personal identity key for{" "}
|
||||
<strong>{orgName}</strong>. It belongs to your
|
||||
account and identifies you when you use public AI
|
||||
gateways.
|
||||
</EmailText>
|
||||
|
||||
<EmailText>
|
||||
Use it with resources your administrator has granted
|
||||
you, or that your role has access to. Treat this key
|
||||
like a password and do not share it.
|
||||
</EmailText>
|
||||
|
||||
<EmailSection>
|
||||
<EmailText>Your identity key:</EmailText>
|
||||
<div className="inline-block max-w-full">
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 mx-auto text-left">
|
||||
<span className="text-sm font-mono text-gray-900 break-all">
|
||||
{credential}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</EmailSection>
|
||||
|
||||
<EmailFooter>
|
||||
<EmailSignature />
|
||||
</EmailFooter>
|
||||
</EmailContainer>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default IdentityApiKeyGenerated;
|
||||
@@ -0,0 +1,119 @@
|
||||
import React from "react";
|
||||
import { Body, Head, Html, Preview, Tailwind } from "@react-email/components";
|
||||
import { themeColors } from "./lib/theme";
|
||||
import {
|
||||
EmailContainer,
|
||||
EmailFooter,
|
||||
EmailGreeting,
|
||||
EmailInfoSection,
|
||||
EmailLetterHead,
|
||||
EmailSection,
|
||||
EmailSignature,
|
||||
EmailText
|
||||
} from "./components/Email";
|
||||
|
||||
type VirtualApiKeyGeneratedProps = {
|
||||
orgName: string;
|
||||
keyName: string | null;
|
||||
credential: string;
|
||||
resourceUrls: string[];
|
||||
hasMoreResources: boolean;
|
||||
};
|
||||
|
||||
export const VirtualApiKeyGenerated = ({
|
||||
orgName,
|
||||
keyName,
|
||||
credential,
|
||||
resourceUrls,
|
||||
hasMoreResources
|
||||
}: VirtualApiKeyGeneratedProps) => {
|
||||
const previewText = `A virtual API key for ${orgName} has been shared with you`;
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind config={themeColors}>
|
||||
<Body className="font-sans bg-gray-50">
|
||||
<EmailContainer>
|
||||
<EmailLetterHead />
|
||||
|
||||
<EmailGreeting>Hi there,</EmailGreeting>
|
||||
|
||||
<EmailText>
|
||||
A virtual API key for <strong>{orgName}</strong> has
|
||||
been shared with you. This key grants access to the
|
||||
public AI gateways it was created for. Treat this
|
||||
key like a password and do not share it.
|
||||
</EmailText>
|
||||
|
||||
<EmailSection>
|
||||
<EmailText>Your virtual API key:</EmailText>
|
||||
<div className="inline-block max-w-full">
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 mx-auto text-left">
|
||||
<span className="text-sm font-mono text-gray-900 break-all">
|
||||
{credential}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</EmailSection>
|
||||
|
||||
<EmailInfoSection
|
||||
title="Key details"
|
||||
items={[
|
||||
{
|
||||
label: "Organization",
|
||||
value: orgName
|
||||
},
|
||||
...(keyName
|
||||
? [
|
||||
{
|
||||
label: "Name",
|
||||
value: keyName
|
||||
}
|
||||
]
|
||||
: [])
|
||||
]}
|
||||
/>
|
||||
|
||||
{resourceUrls.length > 0 && (
|
||||
<>
|
||||
<EmailText>
|
||||
This key can be used to authenticate to the
|
||||
following AI gateway resources:
|
||||
</EmailText>
|
||||
<div className="px-6 pb-2">
|
||||
{resourceUrls.map((url) => (
|
||||
<p
|
||||
key={url}
|
||||
className="text-base text-gray-700 leading-relaxed"
|
||||
>
|
||||
<a
|
||||
href={url}
|
||||
className="text-primary font-medium break-all"
|
||||
>
|
||||
{url}
|
||||
</a>
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
{hasMoreResources && (
|
||||
<EmailText>
|
||||
Contact your administrator to get the
|
||||
full list.
|
||||
</EmailText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<EmailFooter>
|
||||
<EmailSignature />
|
||||
</EmailFooter>
|
||||
</EmailContainer>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default VirtualApiKeyGenerated;
|
||||
@@ -18,7 +18,7 @@ export function EmailLetterHead() {
|
||||
<Img
|
||||
src="https://fossorial-public-assets.s3.us-east-1.amazonaws.com/word_mark_black.png"
|
||||
alt="Pangolin Logo"
|
||||
width="180"
|
||||
width="135"
|
||||
height="auto"
|
||||
className="mx-auto"
|
||||
/>
|
||||
|
||||
@@ -5,26 +5,31 @@ import { runSetupFunctions } from "./setup";
|
||||
import { createApiServer } from "./apiServer";
|
||||
import { createNextServer } from "./nextServer";
|
||||
import { createInternalServer } from "./internalServer";
|
||||
import { createAiGatewayServer } from "./aiGatewayServer";
|
||||
import { createIntegrationApiServer } from "./integrationApiServer";
|
||||
import {
|
||||
ApiKey,
|
||||
ApiKeyOrg,
|
||||
AiBudget,
|
||||
AiModel,
|
||||
AiProvider,
|
||||
RemoteExitNode,
|
||||
Session,
|
||||
SiteResource,
|
||||
User,
|
||||
UserOrg
|
||||
UserOrg,
|
||||
VirtualApiKey
|
||||
} from "@server/db";
|
||||
import config from "@server/lib/config";
|
||||
import { setHostMeta } from "@server/lib/hostMeta";
|
||||
import { initTelemetryClient } from "@server/lib/telemetry";
|
||||
import { TraefikConfigManager } from "@server/lib/traefik/TraefikConfigManager";
|
||||
import { initCleanup } from "#dynamic/cleanup";
|
||||
import { startSchedulers } from "#dynamic/startSchedulers";
|
||||
import { startDnsServer } from "#dynamic/dns";
|
||||
import { startCertificateManager } from "#dynamic/certificates";
|
||||
import license from "#dynamic/license/license";
|
||||
import { initLogCleanupInterval } from "@server/lib/cleanupLogs";
|
||||
import { initAcmeCertSync } from "#dynamic/lib/acmeCertSync";
|
||||
import { fetchServerIp } from "@server/lib/serverIpService";
|
||||
import { startRebuildQueueProcessor } from "@server/lib/rebuildClientAssociations";
|
||||
import { initAiModelCatalog } from "@server/lib/aiModelCatalog";
|
||||
|
||||
async function startServers() {
|
||||
await setHostMeta();
|
||||
@@ -38,15 +43,18 @@ async function startServers() {
|
||||
|
||||
await fetchServerIp();
|
||||
|
||||
initTelemetryClient();
|
||||
await initAiModelCatalog();
|
||||
|
||||
initLogCleanupInterval();
|
||||
initAcmeCertSync();
|
||||
startRebuildQueueProcessor();
|
||||
startSchedulers();
|
||||
|
||||
await startDnsServer();
|
||||
|
||||
await startCertificateManager();
|
||||
|
||||
// Start all servers
|
||||
const apiServer = createApiServer();
|
||||
const internalServer = createInternalServer();
|
||||
const aiGatewayServer = createAiGatewayServer();
|
||||
|
||||
const nextServer = await createNextServer();
|
||||
if (config.getRawConfig().traefik.file_mode) {
|
||||
@@ -65,6 +73,7 @@ async function startServers() {
|
||||
apiServer,
|
||||
nextServer,
|
||||
internalServer,
|
||||
aiGatewayServer,
|
||||
integrationServer
|
||||
};
|
||||
}
|
||||
@@ -83,6 +92,10 @@ declare global {
|
||||
userOrgIds?: string[];
|
||||
remoteExitNode?: RemoteExitNode;
|
||||
siteResource?: SiteResource;
|
||||
aiProvider?: AiProvider;
|
||||
aiModel?: AiModel;
|
||||
aiBudget?: AiBudget;
|
||||
virtualApiKey?: VirtualApiKey;
|
||||
orgPolicyAllowed?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { registry, openApiTags } from "./openApi";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { APP_PATH } from "./lib/consts";
|
||||
import yaml from "js-yaml";
|
||||
import * as yaml from "js-yaml";
|
||||
import { z } from "zod";
|
||||
|
||||
const dev = process.env.ENVIRONMENT !== "prod";
|
||||
|
||||
@@ -1,3 +1,866 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import crypto from "crypto";
|
||||
import {
|
||||
certificates,
|
||||
clients,
|
||||
clientSiteResourcesAssociationsCache,
|
||||
db,
|
||||
domains,
|
||||
newts,
|
||||
siteNetworks,
|
||||
SiteResource,
|
||||
siteResources
|
||||
} from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { encrypt, decrypt } from "@server/lib/crypto";
|
||||
import logger from "@server/logger";
|
||||
import config from "@server/lib/config";
|
||||
import {
|
||||
generateSubnetProxyTargetV2,
|
||||
SubnetProxyTargetV2
|
||||
} from "@server/lib/ip";
|
||||
import { updateTargets } from "@server/routers/client/targets";
|
||||
import cache from "#dynamic/lib/cache";
|
||||
import { build } from "@server/build";
|
||||
|
||||
interface AcmeCert {
|
||||
domain: { main: string; sans?: string[] };
|
||||
certificate: string;
|
||||
key: string;
|
||||
Store: string;
|
||||
}
|
||||
|
||||
interface AcmeJson {
|
||||
[resolver: string]: {
|
||||
Certificates: AcmeCert[];
|
||||
};
|
||||
}
|
||||
|
||||
export async function pushCertUpdateToAffectedNewts(
|
||||
domain: string,
|
||||
domainId: string | null,
|
||||
oldCertPem: string | null,
|
||||
oldKeyPem: string | null
|
||||
): Promise<void> {
|
||||
// Find all SSL-enabled HTTP site resources that use this cert's domain
|
||||
let affectedResources: SiteResource[] = [];
|
||||
|
||||
if (domainId) {
|
||||
affectedResources = await db
|
||||
.select()
|
||||
.from(siteResources)
|
||||
.where(
|
||||
and(
|
||||
eq(siteResources.domainId, domainId),
|
||||
eq(siteResources.ssl, true)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Fallback: match by exact fullDomain when no domainId is available
|
||||
affectedResources = await db
|
||||
.select()
|
||||
.from(siteResources)
|
||||
.where(
|
||||
and(
|
||||
eq(siteResources.fullDomain, domain),
|
||||
eq(siteResources.ssl, true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (affectedResources.length === 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no affected site resources for cert domain "${domain}"`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`acmeCertSync: pushing cert update to ${affectedResources.length} affected site resource(s) for domain "${domain}"`
|
||||
);
|
||||
|
||||
for (const resource of affectedResources) {
|
||||
try {
|
||||
// Get all sites for this resource via siteNetworks
|
||||
const resourceSiteRows = resource.networkId
|
||||
? await db
|
||||
.select({ siteId: siteNetworks.siteId })
|
||||
.from(siteNetworks)
|
||||
.where(eq(siteNetworks.networkId, resource.networkId))
|
||||
: [];
|
||||
|
||||
if (resourceSiteRows.length === 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no sites for resource ${resource.siteResourceId}, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all clients with access to this resource
|
||||
const resourceClients = await db
|
||||
.select({
|
||||
clientId: clients.clientId,
|
||||
pubKey: clients.pubKey,
|
||||
subnet: clients.subnet
|
||||
})
|
||||
.from(clients)
|
||||
.innerJoin(
|
||||
clientSiteResourcesAssociationsCache,
|
||||
eq(
|
||||
clients.clientId,
|
||||
clientSiteResourcesAssociationsCache.clientId
|
||||
)
|
||||
)
|
||||
.where(
|
||||
eq(
|
||||
clientSiteResourcesAssociationsCache.siteResourceId,
|
||||
resource.siteResourceId
|
||||
)
|
||||
);
|
||||
|
||||
if (resourceClients.length === 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no clients for resource ${resource.siteResourceId}, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Invalidate the cert cache so generateSubnetProxyTargetV2 fetches fresh data
|
||||
if (resource.fullDomain) {
|
||||
await cache.del(`cert:${resource.fullDomain}`);
|
||||
}
|
||||
|
||||
// Generate target once - same cert applies to all sites for this resource
|
||||
const newTargets = await generateSubnetProxyTargetV2(
|
||||
resource,
|
||||
resourceClients
|
||||
);
|
||||
|
||||
if (!newTargets) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not generate target for resource ${resource.siteResourceId}, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Construct the old targets - same routing shape but with the previous cert/key.
|
||||
// The newt only uses destPrefix/sourcePrefixes for removal, but we keep the
|
||||
// semantics correct so the update message accurately reflects what changed.
|
||||
const oldTargets: SubnetProxyTargetV2[] = newTargets.map((t) => ({
|
||||
...t,
|
||||
tlsCert: oldCertPem ?? undefined,
|
||||
tlsKey: oldKeyPem ?? undefined
|
||||
}));
|
||||
|
||||
// Push update to each site's newt
|
||||
for (const { siteId } of resourceSiteRows) {
|
||||
const [newt] = await db
|
||||
.select()
|
||||
.from(newts)
|
||||
.where(eq(newts.siteId, siteId))
|
||||
.limit(1);
|
||||
|
||||
if (!newt) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no newt found for site ${siteId}, skipping resource ${resource.siteResourceId}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
await updateTargets(
|
||||
newt.newtId,
|
||||
{ oldTargets: oldTargets, newTargets: newTargets },
|
||||
newt.version
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
`acmeCertSync: pushed cert update to newt for site ${siteId}, resource ${resource.siteResourceId}`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`acmeCertSync: error pushing cert update for resource ${resource?.siteResourceId}: ${err}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function findDomainId(certDomain: string): Promise<string | null> {
|
||||
// Strip wildcard prefix before lookup (*.example.com -> example.com)
|
||||
const lookupDomain = certDomain.startsWith("*.")
|
||||
? certDomain.slice(2)
|
||||
: certDomain;
|
||||
|
||||
// 1. Exact baseDomain match (any domain type)
|
||||
const exactMatch = await db
|
||||
.select({ domainId: domains.domainId })
|
||||
.from(domains)
|
||||
.where(eq(domains.baseDomain, lookupDomain))
|
||||
.limit(1);
|
||||
|
||||
if (exactMatch.length > 0) {
|
||||
return exactMatch[0].domainId;
|
||||
}
|
||||
|
||||
// 2. Walk up the domain hierarchy looking for a wildcard-type domain whose
|
||||
// baseDomain is a suffix of the cert domain. e.g. cert "sub.example.com"
|
||||
// matches a wildcard domain with baseDomain "example.com".
|
||||
const parts = lookupDomain.split(".");
|
||||
for (let i = 1; i < parts.length; i++) {
|
||||
const candidate = parts.slice(i).join(".");
|
||||
if (!candidate) continue;
|
||||
|
||||
const wildcardMatch = await db
|
||||
.select({ domainId: domains.domainId })
|
||||
.from(domains)
|
||||
.where(
|
||||
and(
|
||||
eq(domains.baseDomain, candidate),
|
||||
eq(domains.type, "wildcard")
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (wildcardMatch.length > 0) {
|
||||
return wildcardMatch[0].domainId;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractFirstCert(pemBundle: string): string | null {
|
||||
const match = pemBundle.match(
|
||||
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/
|
||||
);
|
||||
return match ? match[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an ACME cert entry represents a wildcard cert by checking
|
||||
* both the primary domain (`main`) and the SANs. Some ACME clients (notably
|
||||
* Traefik) store the bare apex in `main` and only put the wildcard form in
|
||||
* `sans` (e.g. main="access.example.com", sans=["*.access.example.com"]).
|
||||
*/
|
||||
function detectWildcard(
|
||||
main: string,
|
||||
sans: string[] | undefined
|
||||
): { wildcard: boolean; wildcardSan: string | null } {
|
||||
if (main.startsWith("*.")) {
|
||||
return { wildcard: true, wildcardSan: null };
|
||||
}
|
||||
if (Array.isArray(sans)) {
|
||||
for (const san of sans) {
|
||||
if (typeof san !== "string") continue;
|
||||
if (san === `*.${main}` || san.startsWith("*.")) {
|
||||
return { wildcard: true, wildcardSan: san };
|
||||
}
|
||||
}
|
||||
}
|
||||
return { wildcard: false, wildcardSan: null };
|
||||
}
|
||||
|
||||
interface HttpCert {
|
||||
wildcard: boolean;
|
||||
altName: string;
|
||||
certName: string;
|
||||
commonName: string;
|
||||
certFile: string;
|
||||
keyFile: string;
|
||||
}
|
||||
|
||||
async function syncAcmeCertsFromHttp(endpoint: string): Promise<void> {
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(endpoint);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not reach HTTP endpoint ${endpoint}: ${err}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
logger.debug(
|
||||
`acmeCertSync: HTTP endpoint returned status ${response.status}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let httpCerts: HttpCert[];
|
||||
try {
|
||||
httpCerts = await response.json();
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not parse JSON from HTTP endpoint: ${err}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(httpCerts) || httpCerts.length === 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no certificates returned from HTTP endpoint`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const cert of httpCerts) {
|
||||
const domain = cert?.certName;
|
||||
|
||||
if (!domain || typeof domain !== "string") {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping HTTP cert with missing certName`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const certPem = cert.certFile;
|
||||
const keyPem = cert.keyFile;
|
||||
|
||||
if (!certPem?.trim() || !keyPem?.trim()) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping HTTP cert for ${domain} - empty certFile or keyFile`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const firstCertPemForValidation = extractFirstCert(certPem);
|
||||
if (!firstCertPemForValidation) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping HTTP cert for ${domain} - no PEM certificate block found`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let validatedX509: crypto.X509Certificate;
|
||||
try {
|
||||
validatedX509 = new crypto.X509Certificate(
|
||||
firstCertPemForValidation
|
||||
);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping HTTP cert for ${domain} - invalid X.509 certificate: ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
crypto.createPrivateKey(keyPem);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping HTTP cert for ${domain} - invalid private key: ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const wildcard = cert.wildcard ?? false;
|
||||
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(certificates)
|
||||
.where(eq(certificates.domain, domain))
|
||||
.limit(1);
|
||||
|
||||
let oldCertPem: string | null = null;
|
||||
let oldKeyPem: string | null = null;
|
||||
|
||||
if (existing.length > 0 && existing[0].certFile) {
|
||||
try {
|
||||
const storedCertPem = decrypt(
|
||||
existing[0].certFile,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
const wildcardUnchanged = existing[0].wildcard === wildcard;
|
||||
if (storedCertPem === certPem && wildcardUnchanged) {
|
||||
continue;
|
||||
}
|
||||
oldCertPem = storedCertPem;
|
||||
if (existing[0].keyFile) {
|
||||
try {
|
||||
oldKeyPem = decrypt(
|
||||
existing[0].keyFile,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
} catch (keyErr) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not decrypt stored key for ${domain}: ${keyErr}`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not decrypt stored cert for ${domain}, will update: ${err}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let expiresAt: number | null = null;
|
||||
try {
|
||||
expiresAt = Math.floor(
|
||||
new Date(validatedX509.validTo).getTime() / 1000
|
||||
);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not parse cert expiry for ${domain}: ${err}`
|
||||
);
|
||||
}
|
||||
|
||||
const encryptedCert = encrypt(
|
||||
certPem,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
const encryptedKey = encrypt(
|
||||
keyPem,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
const domainId = await findDomainId(domain);
|
||||
if (domainId) {
|
||||
logger.debug(
|
||||
`acmeCertSync: resolved domainId "${domainId}" for HTTP cert domain "${domain}"`
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`acmeCertSync: no matching domain record found for HTTP cert domain "${domain}"`
|
||||
);
|
||||
}
|
||||
|
||||
if (existing.length > 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: updating existing certificate (HTTP) for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
|
||||
);
|
||||
await db
|
||||
.update(certificates)
|
||||
.set({
|
||||
certFile: encryptedCert,
|
||||
keyFile: encryptedKey,
|
||||
status: "valid",
|
||||
expiresAt,
|
||||
updatedAt: now,
|
||||
wildcard,
|
||||
...(domainId !== null && { domainId })
|
||||
})
|
||||
.where(eq(certificates.domain, domain));
|
||||
|
||||
await pushCertUpdateToAffectedNewts(
|
||||
domain,
|
||||
domainId,
|
||||
oldCertPem,
|
||||
oldKeyPem
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`acmeCertSync: inserting new certificate (HTTP) for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
|
||||
);
|
||||
await db.insert(certificates).values({
|
||||
domain,
|
||||
domainId,
|
||||
certFile: encryptedCert,
|
||||
keyFile: encryptedKey,
|
||||
status: "valid",
|
||||
expiresAt,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
wildcard
|
||||
});
|
||||
|
||||
await pushCertUpdateToAffectedNewts(domain, domainId, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function storeCertForDomain(
|
||||
domain: string,
|
||||
certPem: string,
|
||||
keyPem: string,
|
||||
validatedX509: crypto.X509Certificate
|
||||
): Promise<void> {
|
||||
const wildcard = domain.startsWith("*.");
|
||||
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(certificates)
|
||||
.where(eq(certificates.domain, domain))
|
||||
.limit(1);
|
||||
|
||||
let oldCertPem: string | null = null;
|
||||
let oldKeyPem: string | null = null;
|
||||
|
||||
if (existing.length > 0 && existing[0].certFile) {
|
||||
try {
|
||||
const storedCertPem = decrypt(
|
||||
existing[0].certFile,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
const wildcardUnchanged = existing[0].wildcard === wildcard;
|
||||
if (storedCertPem === certPem && wildcardUnchanged) {
|
||||
return;
|
||||
}
|
||||
oldCertPem = storedCertPem;
|
||||
if (existing[0].keyFile) {
|
||||
try {
|
||||
oldKeyPem = decrypt(
|
||||
existing[0].keyFile,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
} catch (keyErr) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not decrypt stored key for ${domain}: ${keyErr}`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not decrypt stored cert for ${domain}, will update: ${err}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let expiresAt: number | null = null;
|
||||
try {
|
||||
expiresAt = Math.floor(
|
||||
new Date(validatedX509.validTo).getTime() / 1000
|
||||
);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: could not parse cert expiry for ${domain}: ${err}`
|
||||
);
|
||||
}
|
||||
|
||||
const encryptedCert = encrypt(
|
||||
certPem,
|
||||
config.getRawConfig().server.secret!
|
||||
);
|
||||
const encryptedKey = encrypt(keyPem, config.getRawConfig().server.secret!);
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
const domainId = await findDomainId(domain);
|
||||
if (domainId) {
|
||||
logger.debug(
|
||||
`acmeCertSync: resolved domainId "${domainId}" for cert domain "${domain}"`
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`acmeCertSync: no matching domain record found for cert domain "${domain}"`
|
||||
);
|
||||
}
|
||||
|
||||
if (existing.length > 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: updating existing certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
|
||||
);
|
||||
await db
|
||||
.update(certificates)
|
||||
.set({
|
||||
certFile: encryptedCert,
|
||||
keyFile: encryptedKey,
|
||||
status: "valid",
|
||||
expiresAt,
|
||||
updatedAt: now,
|
||||
wildcard,
|
||||
...(domainId !== null && { domainId })
|
||||
})
|
||||
.where(eq(certificates.domain, domain));
|
||||
|
||||
logger.debug(
|
||||
`acmeCertSync: updated certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
|
||||
);
|
||||
|
||||
await pushCertUpdateToAffectedNewts(
|
||||
domain,
|
||||
domainId,
|
||||
oldCertPem,
|
||||
oldKeyPem
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`acmeCertSync: inserting new certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
|
||||
);
|
||||
await db.insert(certificates).values({
|
||||
domain,
|
||||
domainId,
|
||||
certFile: encryptedCert,
|
||||
keyFile: encryptedKey,
|
||||
status: "valid",
|
||||
expiresAt,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
wildcard
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`acmeCertSync: inserted new certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
|
||||
);
|
||||
|
||||
await pushCertUpdateToAffectedNewts(domain, domainId, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
function findAcmeJsonFiles(dirPath: string): string[] {
|
||||
const results: string[] = [];
|
||||
let entries: fs.Dirent[];
|
||||
try {
|
||||
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
`acmeCertSync: could not read directory "${dirPath}": ${err}`
|
||||
);
|
||||
return results;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dirPath, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
results.push(...findAcmeJsonFiles(fullPath));
|
||||
} else if (entry.isFile()) {
|
||||
// check if it is a json file
|
||||
if (entry.name.endsWith(".json")) {
|
||||
let raw: string;
|
||||
try {
|
||||
raw = fs.readFileSync(fullPath, "utf8");
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
`acmeCertSync: could not read file "${fullPath}": ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let parsed: any;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
`acmeCertSync: could not parse "${fullPath}" as JSON: ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async function syncAcmeCerts(acmeJsonPath: string): Promise<void> {
|
||||
let raw: string;
|
||||
try {
|
||||
raw = fs.readFileSync(acmeJsonPath, "utf8");
|
||||
} catch (err) {
|
||||
logger.warn(`acmeCertSync: could not read "${acmeJsonPath}": ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let acmeJson: AcmeJson;
|
||||
try {
|
||||
acmeJson = JSON.parse(raw);
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
`acmeCertSync: could not parse "${acmeJsonPath}" as JSON: ${err}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvers = Object.keys(acmeJson || {});
|
||||
if (resolvers.length === 0) {
|
||||
logger.debug(`acmeCertSync: no resolvers found in acme.json`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect certificates from every resolver. If the same domain appears in
|
||||
// multiple resolvers, the last one wins (resolvers iterated in object order).
|
||||
const allCerts: AcmeCert[] = [];
|
||||
for (const resolver of resolvers) {
|
||||
const resolverData = acmeJson[resolver];
|
||||
if (!resolverData || !Array.isArray(resolverData.Certificates)) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no certificates found for resolver "${resolver}"`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// logger.debug(
|
||||
// `acmeCertSync: found ${resolverData.Certificates.length} certificate(s) for resolver "${resolver}"`
|
||||
// );
|
||||
for (const cert of resolverData.Certificates) {
|
||||
allCerts.push(cert);
|
||||
}
|
||||
}
|
||||
|
||||
for (const cert of allCerts) {
|
||||
const mainDomain = cert?.domain?.main;
|
||||
|
||||
if (!mainDomain || typeof mainDomain !== "string") {
|
||||
logger.debug(`acmeCertSync: skipping cert with missing domain`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cert.certificate || !cert.key) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping cert for ${mainDomain} - empty certificate or key field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let certPem: string;
|
||||
let keyPem: string;
|
||||
try {
|
||||
certPem = Buffer.from(cert.certificate, "base64").toString("utf8");
|
||||
keyPem = Buffer.from(cert.key, "base64").toString("utf8");
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping cert for ${mainDomain} - failed to base64-decode cert/key: ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!certPem.trim() || !keyPem.trim()) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping cert for ${mainDomain} - blank PEM after base64 decode`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate that the decoded data actually parses as a real X.509 cert
|
||||
// before we touch the database. This prevents importing partially-written
|
||||
// or corrupted entries from acme.json.
|
||||
const firstCertPemForValidation = extractFirstCert(certPem);
|
||||
if (!firstCertPemForValidation) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping cert for ${mainDomain} - no PEM certificate block found`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let validatedX509: crypto.X509Certificate;
|
||||
try {
|
||||
validatedX509 = new crypto.X509Certificate(
|
||||
firstCertPemForValidation
|
||||
);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping cert for ${mainDomain} - invalid X.509 certificate: ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity-check the private key parses too
|
||||
try {
|
||||
crypto.createPrivateKey(keyPem);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`acmeCertSync: skipping cert for ${mainDomain} - invalid private key: ${err}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect all domains covered by this cert: main + every SAN.
|
||||
// Each domain gets its own row in the certificates table so that
|
||||
// lookups by any hostname on the cert succeed independently.
|
||||
const allDomains = new Set<string>([mainDomain]);
|
||||
if (Array.isArray(cert.domain?.sans)) {
|
||||
for (const san of cert.domain.sans) {
|
||||
if (typeof san === "string" && san.trim()) {
|
||||
allDomains.add(san.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logger.debug(
|
||||
// `acmeCertSync: cert for ${mainDomain} covers ${allDomains.size} domain(s): ${[...allDomains].join(", ")}`
|
||||
// );
|
||||
|
||||
for (const domain of allDomains) {
|
||||
try {
|
||||
await storeCertForDomain(
|
||||
domain,
|
||||
certPem,
|
||||
keyPem,
|
||||
validatedX509
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`acmeCertSync: error storing cert for domain "${domain}": ${err}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function initAcmeCertSync(): void {
|
||||
// stub
|
||||
}
|
||||
if (build == "saas") {
|
||||
logger.debug(`acmeCertSync: skipping ACME cert sync in SaaS build`);
|
||||
return;
|
||||
}
|
||||
|
||||
const configData = config.getRawConfig();
|
||||
|
||||
if (!configData.flags?.enable_acme_cert_sync) {
|
||||
logger.debug(
|
||||
`acmeCertSync: ACME cert sync is disabled by config flag, skipping`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const acmeJsonPath =
|
||||
configData.acme?.acme_json_path ?? "config/letsencrypt/acme.json";
|
||||
const intervalMs = configData.acme?.sync_interval_ms ?? 5000;
|
||||
const httpEndpoint = configData.acme?.acme_http_endpoint;
|
||||
|
||||
logger.debug(
|
||||
`acmeCertSync: starting ACME cert sync from "${acmeJsonPath}" across all resolvers every ${intervalMs}ms`
|
||||
);
|
||||
if (httpEndpoint) {
|
||||
logger.debug(
|
||||
`acmeCertSync: also syncing from HTTP endpoint "${httpEndpoint}" every ${intervalMs}ms`
|
||||
);
|
||||
}
|
||||
|
||||
const runSync = () => {
|
||||
if (httpEndpoint) {
|
||||
syncAcmeCertsFromHttp(httpEndpoint).catch((err) => {
|
||||
logger.error(`acmeCertSync: error during HTTP sync: ${err}`);
|
||||
});
|
||||
} else {
|
||||
// only run the file-based sync if the HTTP endpoint is not configured, to avoid doubling up
|
||||
let stat: fs.Stats | null = null;
|
||||
try {
|
||||
stat = fs.statSync(acmeJsonPath);
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
`acmeCertSync: cannot stat path "${acmeJsonPath}": ${err}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const files = findAcmeJsonFiles(acmeJsonPath);
|
||||
if (files.length === 0) {
|
||||
logger.debug(
|
||||
`acmeCertSync: no acme.json files found in directory "${acmeJsonPath}"`
|
||||
);
|
||||
return;
|
||||
}
|
||||
// logger.debug(
|
||||
// `acmeCertSync: found ${files.length} acme.json file(s) in directory "${acmeJsonPath}"`
|
||||
// );
|
||||
for (const file of files) {
|
||||
syncAcmeCerts(file).catch((err) => {
|
||||
logger.error(
|
||||
`acmeCertSync: error during sync of "${file}": ${err}`
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
syncAcmeCerts(acmeJsonPath).catch((err) => {
|
||||
logger.error(`acmeCertSync: error during sync: ${err}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Run immediately on init, then on the configured interval
|
||||
runSync();
|
||||
|
||||
setInterval(runSync, intervalMs);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
import {
|
||||
and,
|
||||
eq,
|
||||
gte,
|
||||
inArray,
|
||||
isNull,
|
||||
or,
|
||||
sql,
|
||||
SQL,
|
||||
type InferInsertModel
|
||||
} from "drizzle-orm";
|
||||
import {
|
||||
AiBudget,
|
||||
aiBudgetBreachEvents,
|
||||
aiBudgets,
|
||||
aiModels,
|
||||
aiUsageRecords,
|
||||
db,
|
||||
logsDb,
|
||||
userOrgRoles
|
||||
} from "@server/db";
|
||||
import { modelKeyMatches } from "@server/lib/aiModelKeyMatch";
|
||||
import type { AiUsage } from "@server/lib/aiUsageExtraction";
|
||||
import { regionalCache as cache } from "#dynamic/lib/cache";
|
||||
import logger from "@server/logger";
|
||||
|
||||
type BudgetPeriod = AiBudget["period"];
|
||||
|
||||
const PERIOD_DURATIONS_MS: Record<Exclude<BudgetPeriod, "lifetime">, number> = {
|
||||
hourly: 60 * 60 * 1000,
|
||||
daily: 24 * 60 * 60 * 1000,
|
||||
weekly: 7 * 24 * 60 * 60 * 1000,
|
||||
monthly: 30 * 24 * 60 * 60 * 1000,
|
||||
yearly: 365 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
|
||||
// Budgets are cheap to be a little stale about (enforcement is already
|
||||
// check-then-act, not transactional). Re-derive each budget's usage sum
|
||||
// from aiUsageRecords at most this often; in between, completed requests
|
||||
// just add their own contribution onto the cached sum instead of
|
||||
// re-querying/re-aggregating from scratch.
|
||||
const BUDGET_CACHE_REFRESH_MS = 8_000;
|
||||
// Redis-level TTL is only a safety net for eviction if a budget stops
|
||||
// seeing traffic - the actual staleness check is the computedAt timestamp
|
||||
// stored in the cached value, compared against BUDGET_CACHE_REFRESH_MS.
|
||||
const BUDGET_CACHE_SAFETY_TTL_SEC = 60;
|
||||
|
||||
function applicableBudgetsCacheKey(ctx: BudgetScopeContext): string {
|
||||
const roleKey = [...ctx.roleIds].sort((a, b) => a - b).join(",");
|
||||
return [
|
||||
"aiBudget:applicable",
|
||||
ctx.orgId,
|
||||
ctx.providerId,
|
||||
ctx.requestedModel,
|
||||
ctx.resourceId ?? "",
|
||||
ctx.siteResourceId ?? "",
|
||||
roleKey,
|
||||
ctx.virtualApiKeyId ?? ""
|
||||
].join(":");
|
||||
}
|
||||
|
||||
function budgetUsageCacheKey(budgetId: number): string {
|
||||
return `aiBudget:usage:${budgetId}`;
|
||||
}
|
||||
|
||||
type CachedBudgetUsage = {
|
||||
sum: number;
|
||||
computedAt: number;
|
||||
};
|
||||
|
||||
// Budget periods are trailing windows from "now", not calendar-aligned
|
||||
// (e.g. "daily" = last 24h). "lifetime" has no lower bound.
|
||||
function windowStart(period: BudgetPeriod, now: number): number {
|
||||
if (period === "lifetime") {
|
||||
return 0;
|
||||
}
|
||||
return now - PERIOD_DURATIONS_MS[period];
|
||||
}
|
||||
|
||||
export type BudgetScopeContext = {
|
||||
orgId: string;
|
||||
providerId: number;
|
||||
requestedModel: string;
|
||||
resourceId: number | null;
|
||||
siteResourceId: number | null;
|
||||
roleIds: number[];
|
||||
requestUserId: string | null;
|
||||
virtualApiKeyId: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Every budget that could apply to this request: the provider itself, any
|
||||
* model on that provider whose (possibly wildcarded) modelKey matches the
|
||||
* requested model, the target resource/site-resource, and any role the
|
||||
* requesting user holds in the org. Cached for BUDGET_CACHE_REFRESH_MS since
|
||||
* budget/model config changes are rare and a request-scoped org/provider/
|
||||
* model/resource/role combination repeats constantly under real traffic.
|
||||
*/
|
||||
export async function resolveApplicableBudgets(
|
||||
ctx: BudgetScopeContext
|
||||
): Promise<AiBudget[]> {
|
||||
const cacheKey = applicableBudgetsCacheKey(ctx);
|
||||
const cached = await cache.get<AiBudget[]>(cacheKey);
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const budgets = await fetchApplicableBudgets(ctx);
|
||||
await cache.set(cacheKey, budgets, BUDGET_CACHE_REFRESH_MS / 1000);
|
||||
return budgets;
|
||||
}
|
||||
|
||||
async function fetchApplicableBudgets(
|
||||
ctx: BudgetScopeContext
|
||||
): Promise<AiBudget[]> {
|
||||
const providerModels = await db
|
||||
.select({ modelId: aiModels.modelId, modelKey: aiModels.modelKey })
|
||||
.from(aiModels)
|
||||
.where(
|
||||
and(
|
||||
eq(aiModels.providerId, ctx.providerId),
|
||||
eq(aiModels.enabled, true)
|
||||
)
|
||||
);
|
||||
|
||||
const matchingModelIds = providerModels
|
||||
.filter((m) => modelKeyMatches(m.modelKey, ctx.requestedModel))
|
||||
.map((m) => m.modelId);
|
||||
|
||||
const scopeConditions: SQL[] = [
|
||||
and(
|
||||
eq(aiBudgets.providerId, ctx.providerId),
|
||||
isNull(aiBudgets.modelId)
|
||||
)!
|
||||
];
|
||||
if (matchingModelIds.length > 0) {
|
||||
scopeConditions.push(inArray(aiBudgets.modelId, matchingModelIds));
|
||||
}
|
||||
if (ctx.resourceId != null) {
|
||||
scopeConditions.push(eq(aiBudgets.resourceId, ctx.resourceId));
|
||||
}
|
||||
if (ctx.siteResourceId != null) {
|
||||
scopeConditions.push(eq(aiBudgets.siteResourceId, ctx.siteResourceId));
|
||||
}
|
||||
if (ctx.roleIds.length > 0) {
|
||||
scopeConditions.push(inArray(aiBudgets.roleId, ctx.roleIds));
|
||||
}
|
||||
if (ctx.virtualApiKeyId != null) {
|
||||
scopeConditions.push(
|
||||
eq(aiBudgets.virtualApiKeyId, ctx.virtualApiKeyId)
|
||||
);
|
||||
}
|
||||
|
||||
return db
|
||||
.select()
|
||||
.from(aiBudgets)
|
||||
.where(
|
||||
and(
|
||||
eq(aiBudgets.orgId, ctx.orgId),
|
||||
eq(aiBudgets.enabled, true),
|
||||
or(...scopeConditions)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function sumUsageAmount(
|
||||
where: SQL,
|
||||
unit: AiBudget["unit"]
|
||||
): Promise<number> {
|
||||
const column =
|
||||
unit === "usd" ? aiUsageRecords.costUsd : aiUsageRecords.totalTokens;
|
||||
const [row] = await logsDb
|
||||
.select({ total: sql<number>`coalesce(sum(${column}), 0)` })
|
||||
.from(aiUsageRecords)
|
||||
.where(where);
|
||||
return Number(row?.total ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums recorded usage for a single budget's scope + rolling window. Model
|
||||
* budgets can't be pushed down to SQL because the model's key may itself be
|
||||
* a glob, so those rows are fetched for the provider+window and matched in
|
||||
* JS the same way access-control matching does.
|
||||
*/
|
||||
export async function sumUsageForBudget(
|
||||
budget: AiBudget,
|
||||
ctx: BudgetScopeContext,
|
||||
now: number
|
||||
): Promise<number> {
|
||||
const start = windowStart(budget.period, now);
|
||||
|
||||
if (budget.modelId != null) {
|
||||
const [model] = await db
|
||||
.select({
|
||||
providerId: aiModels.providerId,
|
||||
modelKey: aiModels.modelKey
|
||||
})
|
||||
.from(aiModels)
|
||||
.where(eq(aiModels.modelId, budget.modelId))
|
||||
.limit(1);
|
||||
if (!model) {
|
||||
return 0;
|
||||
}
|
||||
const rows = await logsDb
|
||||
.select({
|
||||
requestedModel: aiUsageRecords.requestedModel,
|
||||
costUsd: aiUsageRecords.costUsd,
|
||||
totalTokens: aiUsageRecords.totalTokens
|
||||
})
|
||||
.from(aiUsageRecords)
|
||||
.where(
|
||||
and(
|
||||
eq(aiUsageRecords.orgId, ctx.orgId),
|
||||
eq(aiUsageRecords.providerId, model.providerId),
|
||||
gte(aiUsageRecords.createdAt, start)
|
||||
)
|
||||
);
|
||||
return rows
|
||||
.filter((r) => modelKeyMatches(model.modelKey, r.requestedModel))
|
||||
.reduce(
|
||||
(sum, r) =>
|
||||
sum +
|
||||
(budget.unit === "usd" ? (r.costUsd ?? 0) : r.totalTokens),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (budget.providerId != null) {
|
||||
return sumUsageAmount(
|
||||
and(
|
||||
eq(aiUsageRecords.orgId, ctx.orgId),
|
||||
eq(aiUsageRecords.providerId, budget.providerId),
|
||||
gte(aiUsageRecords.createdAt, start)
|
||||
)!,
|
||||
budget.unit
|
||||
);
|
||||
}
|
||||
|
||||
if (budget.resourceId != null) {
|
||||
return sumUsageAmount(
|
||||
and(
|
||||
eq(aiUsageRecords.orgId, ctx.orgId),
|
||||
eq(aiUsageRecords.resourceId, budget.resourceId),
|
||||
gte(aiUsageRecords.createdAt, start)
|
||||
)!,
|
||||
budget.unit
|
||||
);
|
||||
}
|
||||
|
||||
if (budget.siteResourceId != null) {
|
||||
return sumUsageAmount(
|
||||
and(
|
||||
eq(aiUsageRecords.orgId, ctx.orgId),
|
||||
eq(aiUsageRecords.siteResourceId, budget.siteResourceId),
|
||||
gte(aiUsageRecords.createdAt, start)
|
||||
)!,
|
||||
budget.unit
|
||||
);
|
||||
}
|
||||
|
||||
if (budget.roleId != null) {
|
||||
const members = await db
|
||||
.select({ userId: userOrgRoles.userId })
|
||||
.from(userOrgRoles)
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgRoles.roleId, budget.roleId),
|
||||
eq(userOrgRoles.orgId, ctx.orgId)
|
||||
)
|
||||
);
|
||||
const userIds = members.map((m) => m.userId);
|
||||
if (userIds.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return sumUsageAmount(
|
||||
and(
|
||||
eq(aiUsageRecords.orgId, ctx.orgId),
|
||||
inArray(aiUsageRecords.userId, userIds),
|
||||
gte(aiUsageRecords.createdAt, start)
|
||||
)!,
|
||||
budget.unit
|
||||
);
|
||||
}
|
||||
|
||||
if (budget.virtualApiKeyId != null) {
|
||||
return sumUsageAmount(
|
||||
and(
|
||||
eq(aiUsageRecords.orgId, ctx.orgId),
|
||||
eq(aiUsageRecords.virtualApiKeyId, budget.virtualApiKeyId),
|
||||
gte(aiUsageRecords.createdAt, start)
|
||||
)!,
|
||||
budget.unit
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached wrapper around sumUsageForBudget. Reuses a per-budget cached sum
|
||||
* for up to BUDGET_CACHE_REFRESH_MS, and otherwise falls through to the DB
|
||||
* aggregation and reseeds the cache. Completed requests within that window
|
||||
* top the cached sum up via applyUsageToBudgetCache below rather than
|
||||
* forcing a re-aggregation on every request.
|
||||
*/
|
||||
async function getBudgetUsage(
|
||||
budget: AiBudget,
|
||||
ctx: BudgetScopeContext,
|
||||
now: number
|
||||
): Promise<number> {
|
||||
const cacheKey = budgetUsageCacheKey(budget.budgetId);
|
||||
const cached = await cache.get<CachedBudgetUsage>(cacheKey);
|
||||
if (cached && now - cached.computedAt < BUDGET_CACHE_REFRESH_MS) {
|
||||
return cached.sum;
|
||||
}
|
||||
|
||||
const sum = await sumUsageForBudget(budget, ctx, now);
|
||||
await cache.set(
|
||||
cacheKey,
|
||||
{ sum, computedAt: now } satisfies CachedBudgetUsage,
|
||||
BUDGET_CACHE_SAFETY_TTL_SEC
|
||||
);
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once a request's actual usage is known, for every budget that was
|
||||
* resolved as applicable to it (i.e. checkBudgets' returned `budgets`).
|
||||
* Adds this request's contribution directly onto each budget's cached sum
|
||||
* so the next request in the same refresh window doesn't need to re-query
|
||||
* or re-aggregate. If there's no warm cache entry, or it's already due for
|
||||
* a refresh, this is a no-op - the next reader re-derives from the DB,
|
||||
* which by then already includes this request's row via recordUsage.
|
||||
*/
|
||||
export async function applyUsageToBudgetCache(
|
||||
budgets: AiBudget[],
|
||||
usage: { usd: number; tokens: number }
|
||||
): Promise<void> {
|
||||
await Promise.all(
|
||||
budgets.map(async (budget) => {
|
||||
const delta = budget.unit === "usd" ? usage.usd : usage.tokens;
|
||||
if (!delta) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheKey = budgetUsageCacheKey(budget.budgetId);
|
||||
const cached = await cache.get<CachedBudgetUsage>(cacheKey);
|
||||
if (
|
||||
!cached ||
|
||||
Date.now() - cached.computedAt >= BUDGET_CACHE_REFRESH_MS
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
await cache.set(
|
||||
cacheKey,
|
||||
{
|
||||
sum: cached.sum + delta,
|
||||
computedAt: cached.computedAt
|
||||
} satisfies CachedBudgetUsage,
|
||||
BUDGET_CACHE_SAFETY_TTL_SEC
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Throttled to one durable event per budget per breach window, so a soft
|
||||
// budget being exceeded doesn't write a row on every subsequent request
|
||||
// while it stays over.
|
||||
async function recordBreachEventIfNew(
|
||||
budget: AiBudget,
|
||||
ctx: BudgetScopeContext,
|
||||
usageAmount: number,
|
||||
now: number
|
||||
): Promise<void> {
|
||||
try {
|
||||
const start = windowStart(budget.period, now);
|
||||
const [existing] = await db
|
||||
.select({ id: aiBudgetBreachEvents.id })
|
||||
.from(aiBudgetBreachEvents)
|
||||
.where(
|
||||
and(
|
||||
eq(aiBudgetBreachEvents.budgetId, budget.budgetId),
|
||||
gte(aiBudgetBreachEvents.createdAt, start)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
if (existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.insert(aiBudgetBreachEvents).values({
|
||||
orgId: ctx.orgId,
|
||||
budgetId: budget.budgetId,
|
||||
enforcement: budget.enforcement,
|
||||
unit: budget.unit,
|
||||
period: budget.period,
|
||||
amount: budget.amount,
|
||||
usageAmount,
|
||||
blocked: budget.enforcement === "hard",
|
||||
requestUserId: ctx.requestUserId,
|
||||
createdAt: now
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Failed to record AI budget breach event", {
|
||||
error,
|
||||
budgetId: budget.budgetId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export type BudgetCheckResult = {
|
||||
blocked: boolean;
|
||||
blockingBudget?: AiBudget;
|
||||
// Every budget resolved as applicable to this request, regardless of
|
||||
// whether it was breached - pass to applyUsageToBudgetCache once this
|
||||
// request's actual usage is known.
|
||||
budgets: AiBudget[];
|
||||
};
|
||||
|
||||
export async function checkBudgets(
|
||||
ctx: BudgetScopeContext
|
||||
): Promise<BudgetCheckResult> {
|
||||
const budgets = await resolveApplicableBudgets(ctx);
|
||||
if (budgets.length === 0) {
|
||||
return { blocked: false, budgets: [] };
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
let blockingBudget: AiBudget | undefined;
|
||||
|
||||
for (const budget of budgets) {
|
||||
const usage = await getBudgetUsage(budget, ctx, now);
|
||||
if (usage < budget.amount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await recordBreachEventIfNew(budget, ctx, usage, now);
|
||||
|
||||
if (budget.enforcement === "hard" && !blockingBudget) {
|
||||
blockingBudget = budget;
|
||||
}
|
||||
}
|
||||
|
||||
return blockingBudget
|
||||
? { blocked: true, blockingBudget, budgets }
|
||||
: { blocked: false, budgets };
|
||||
}
|
||||
|
||||
export type UsageRecordInput = {
|
||||
orgId: string;
|
||||
providerId: number;
|
||||
resourceId: number | null;
|
||||
siteResourceId: number | null;
|
||||
userId: string | null;
|
||||
virtualApiKeyId: string | null;
|
||||
requestedModel: string;
|
||||
usage: AiUsage;
|
||||
costUsd: number | null;
|
||||
createdAt?: number;
|
||||
// Same id as the aiSessionLog row logged for this request, so the two
|
||||
// can be joined to show token/cost usage alongside the session
|
||||
// transcript. Undefined when the session wasn't logged (e.g. session
|
||||
// log retention disabled for the org).
|
||||
sessionId?: string;
|
||||
};
|
||||
|
||||
type AiUsageRecordInsert = InferInsertModel<typeof aiUsageRecords>;
|
||||
|
||||
// In-memory buffer for batching AI usage record inserts, mirroring the
|
||||
// approach in server/routers/badger/logRequestAudit.ts. Usage rows are read
|
||||
// back on every budget-cache miss (see getBudgetUsage above), which happens
|
||||
// at least every BUDGET_CACHE_REFRESH_MS, so this buffer is flushed much
|
||||
// more aggressively than the request audit log to keep the table from
|
||||
// lagging behind what budget enforcement needs. Unlike the audit log, there
|
||||
// is no retention/cleanup job for this table - usage history is kept
|
||||
// indefinitely for billing and historical reporting.
|
||||
const usageRecordBuffer: AiUsageRecordInsert[] = [];
|
||||
|
||||
const USAGE_BATCH_SIZE = 20; // Write to DB every 20 records
|
||||
const USAGE_BATCH_INTERVAL_MS = 1000; // Or every 1 second, whichever comes first
|
||||
const USAGE_MAX_BUFFER_SIZE = 5000; // Prevent unbounded memory growth
|
||||
let usageFlushTimer: NodeJS.Timeout | null = null;
|
||||
let isUsageFlushInProgress = false;
|
||||
|
||||
async function flushUsageRecords() {
|
||||
if (usageRecordBuffer.length === 0 || isUsageFlushInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
isUsageFlushInProgress = true;
|
||||
|
||||
const recordsToWrite = usageRecordBuffer.splice(
|
||||
0,
|
||||
usageRecordBuffer.length
|
||||
);
|
||||
|
||||
try {
|
||||
// Use a transaction to ensure all inserts succeed or fail together
|
||||
await logsDb.transaction(async (tx) => {
|
||||
// Batch insert in groups to avoid overwhelming the database
|
||||
const DB_BATCH_SIZE = 25;
|
||||
for (let i = 0; i < recordsToWrite.length; i += DB_BATCH_SIZE) {
|
||||
const batch = recordsToWrite.slice(i, i + DB_BATCH_SIZE);
|
||||
await tx.insert(aiUsageRecords).values(batch);
|
||||
}
|
||||
});
|
||||
logger.debug(
|
||||
`Flushed ${recordsToWrite.length} AI usage records to database`
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error("Error flushing AI usage records:", error);
|
||||
// On transaction error, put records back at the front of the buffer
|
||||
// to retry, but only if the buffer isn't too large
|
||||
if (
|
||||
usageRecordBuffer.length <
|
||||
USAGE_MAX_BUFFER_SIZE - recordsToWrite.length
|
||||
) {
|
||||
usageRecordBuffer.unshift(...recordsToWrite);
|
||||
logger.info(
|
||||
`Re-queued ${recordsToWrite.length} AI usage records for retry`
|
||||
);
|
||||
} else {
|
||||
logger.error(
|
||||
`Buffer full, dropped ${recordsToWrite.length} AI usage records`
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
isUsageFlushInProgress = false;
|
||||
// If buffer filled up while we were flushing, flush again
|
||||
if (usageRecordBuffer.length >= USAGE_BATCH_SIZE) {
|
||||
flushUsageRecords().catch((err) =>
|
||||
logger.error("Error in follow-up AI usage flush:", err)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleUsageFlush() {
|
||||
if (usageFlushTimer === null) {
|
||||
usageFlushTimer = setTimeout(() => {
|
||||
usageFlushTimer = null;
|
||||
flushUsageRecords().catch((err) =>
|
||||
logger.error("Error in scheduled AI usage flush:", err)
|
||||
);
|
||||
}, USAGE_BATCH_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully flush all pending AI usage records (call this on shutdown).
|
||||
*/
|
||||
export async function shutdownUsageRecorder() {
|
||||
if (usageFlushTimer) {
|
||||
clearTimeout(usageFlushTimer);
|
||||
usageFlushTimer = null;
|
||||
}
|
||||
// Force flush even if one is in progress by waiting and retrying
|
||||
while (isUsageFlushInProgress) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
await flushUsageRecords();
|
||||
}
|
||||
|
||||
export async function recordUsage(input: UsageRecordInput): Promise<void> {
|
||||
try {
|
||||
const { usage } = input;
|
||||
const totalTokens =
|
||||
usage.promptTokens +
|
||||
usage.cacheReadTokens +
|
||||
usage.cacheWriteTokens +
|
||||
usage.completionTokens +
|
||||
usage.reasoningTokens;
|
||||
|
||||
// Prevent unbounded buffer growth - drop oldest entries if buffer is too large
|
||||
if (usageRecordBuffer.length >= USAGE_MAX_BUFFER_SIZE) {
|
||||
const dropped = usageRecordBuffer.splice(0, USAGE_BATCH_SIZE);
|
||||
logger.warn(
|
||||
`AI usage record buffer exceeded max size (${USAGE_MAX_BUFFER_SIZE}), dropped ${dropped.length} oldest entries`
|
||||
);
|
||||
}
|
||||
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
usageRecordBuffer.push({
|
||||
orgId: input.orgId,
|
||||
providerId: input.providerId,
|
||||
resourceId: input.resourceId,
|
||||
siteResourceId: input.siteResourceId,
|
||||
userId: input.userId,
|
||||
virtualApiKeyId: input.virtualApiKeyId,
|
||||
sessionId: input.sessionId,
|
||||
requestedModel: input.requestedModel,
|
||||
promptTokens: usage.promptTokens,
|
||||
cacheReadTokens: usage.cacheReadTokens,
|
||||
cacheWriteTokens: usage.cacheWriteTokens,
|
||||
completionTokens: usage.completionTokens,
|
||||
reasoningTokens: usage.reasoningTokens,
|
||||
totalTokens,
|
||||
costUsd: input.costUsd,
|
||||
estimated: usage.estimated,
|
||||
createdAt: input.createdAt ?? timestamp
|
||||
});
|
||||
|
||||
// Flush immediately if buffer is full, otherwise schedule a flush
|
||||
if (usageRecordBuffer.length >= USAGE_BATCH_SIZE) {
|
||||
flushUsageRecords().catch((err) =>
|
||||
logger.error("Error flushing AI usage records:", err)
|
||||
);
|
||||
} else {
|
||||
scheduleUsageFlush();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to record AI usage", { error });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
import type { Request } from "express";
|
||||
import { AI_CAPABILITIES, type AiCapability } from "@app/lib/aiCapabilities";
|
||||
|
||||
export { AI_CAPABILITIES, type AiCapability };
|
||||
|
||||
export type AiCapabilityRoute = {
|
||||
method: "GET" | "POST";
|
||||
path: string;
|
||||
};
|
||||
|
||||
export type AiProtocolFamily = "openai" | "anthropic" | "google" | "bedrock";
|
||||
|
||||
export type AiCapabilityDefinition = {
|
||||
id: AiCapability;
|
||||
protocolFamily: AiProtocolFamily;
|
||||
routes: AiCapabilityRoute[];
|
||||
extractModel: (req: Request) => string | undefined;
|
||||
resolveUpstreamUrl: (
|
||||
baseUrl: string,
|
||||
req: Request,
|
||||
model: string
|
||||
) => string;
|
||||
isStreaming: (req: Request, contentType: string) => boolean;
|
||||
};
|
||||
|
||||
function bodyModel(req: Request): string | undefined {
|
||||
return typeof req.body?.model === "string" ? req.body.model : undefined;
|
||||
}
|
||||
|
||||
function paramModel(req: Request): string | undefined {
|
||||
const model = req.params?.model;
|
||||
return typeof model === "string" && model.length > 0 ? model : undefined;
|
||||
}
|
||||
|
||||
export function joinUpstreamUrl(baseUrl: string, path: string): string {
|
||||
const base = baseUrl.replace(/\/+$/, "");
|
||||
let suffix = path.startsWith("/") ? path : `/${path}`;
|
||||
|
||||
let basePathname = "/";
|
||||
try {
|
||||
basePathname = new URL(base).pathname.replace(/\/+$/, "") || "/";
|
||||
} catch {
|
||||
// Fall through with "/" non-absolute bases are not expected in
|
||||
// production, but keep joining usable for malformed input.
|
||||
}
|
||||
|
||||
if (basePathname !== "/") {
|
||||
const baseSegs = basePathname.split("/").filter(Boolean);
|
||||
const pathSegs = suffix.split("/").filter(Boolean);
|
||||
const max = Math.min(baseSegs.length, pathSegs.length);
|
||||
let overlap = 0;
|
||||
for (let n = max; n >= 1; n--) {
|
||||
const baseSuffix = baseSegs.slice(-n);
|
||||
const pathPrefix = pathSegs.slice(0, n);
|
||||
if (baseSuffix.every((seg, i) => seg === pathPrefix[i])) {
|
||||
overlap = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (overlap > 0) {
|
||||
const remaining = pathSegs.slice(overlap);
|
||||
suffix = remaining.length > 0 ? `/${remaining.join("/")}` : "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (suffix === "/") {
|
||||
return base;
|
||||
}
|
||||
|
||||
return `${base}${suffix}`;
|
||||
}
|
||||
|
||||
function pathFromRequest(req: Request): string {
|
||||
const raw = req.originalUrl || req.url || req.path;
|
||||
return raw.startsWith("/") ? raw : `/${raw}`;
|
||||
}
|
||||
|
||||
function bodyRequestsStream(req: Request): boolean {
|
||||
return req.body?.stream === true;
|
||||
}
|
||||
|
||||
function contentTypeIsSse(contentType: string): boolean {
|
||||
return contentType.includes("text/event-stream");
|
||||
}
|
||||
|
||||
function contentTypeIsAmazonEventStream(contentType: string): boolean {
|
||||
return contentType.includes("application/vnd.amazon.eventstream");
|
||||
}
|
||||
|
||||
function pathIncludes(req: Request, fragment: string): boolean {
|
||||
return pathFromRequest(req).includes(fragment);
|
||||
}
|
||||
|
||||
function isBodyOrSseStreaming(req: Request, contentType: string): boolean {
|
||||
return bodyRequestsStream(req) || contentTypeIsSse(contentType);
|
||||
}
|
||||
|
||||
function isGeminiStyleStreaming(req: Request, contentType: string): boolean {
|
||||
return (
|
||||
pathIncludes(req, "streamGenerateContent") ||
|
||||
pathIncludes(req, "alt=sse") ||
|
||||
contentTypeIsSse(contentType)
|
||||
);
|
||||
}
|
||||
|
||||
export const AI_CAPABILITY_DEFS: Record<AiCapability, AiCapabilityDefinition> =
|
||||
{
|
||||
openai_chat: {
|
||||
id: "openai_chat",
|
||||
protocolFamily: "openai",
|
||||
routes: [
|
||||
{ method: "POST", path: "/v1/chat/completions" },
|
||||
{ method: "POST", path: "/chat/completions" }
|
||||
],
|
||||
extractModel: bodyModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: isBodyOrSseStreaming
|
||||
},
|
||||
openai_responses: {
|
||||
id: "openai_responses",
|
||||
protocolFamily: "openai",
|
||||
routes: [{ method: "POST", path: "/v1/responses" }],
|
||||
extractModel: bodyModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: isBodyOrSseStreaming
|
||||
},
|
||||
anthropic_messages: {
|
||||
id: "anthropic_messages",
|
||||
protocolFamily: "anthropic",
|
||||
routes: [{ method: "POST", path: "/v1/messages" }],
|
||||
extractModel: bodyModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: isBodyOrSseStreaming
|
||||
},
|
||||
v1_models: {
|
||||
id: "v1_models",
|
||||
protocolFamily: "anthropic",
|
||||
routes: [
|
||||
{ method: "GET", path: "/v1/models" },
|
||||
{ method: "GET", path: "/v1/models/:model" }
|
||||
],
|
||||
extractModel: paramModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
// Model listings are answered from the gateway's own view of the
|
||||
// provider allow/block lists rather than proxied upstream, so
|
||||
// there is never a stream to detect.
|
||||
isStreaming: () => false
|
||||
},
|
||||
gemini_generate_content: {
|
||||
id: "gemini_generate_content",
|
||||
protocolFamily: "google",
|
||||
routes: [
|
||||
{
|
||||
method: "POST",
|
||||
path: "/v1beta/models/:model\\:generateContent"
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
path: "/v1beta/models/:model\\:streamGenerateContent"
|
||||
}
|
||||
],
|
||||
extractModel: paramModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: isGeminiStyleStreaming
|
||||
},
|
||||
google_generate_content: {
|
||||
id: "google_generate_content",
|
||||
protocolFamily: "google",
|
||||
routes: [
|
||||
{
|
||||
method: "POST",
|
||||
// Vertex publisher model generateContent
|
||||
path: "/v1/projects/:project/locations/:location/publishers/:publisher/models/:model\\:generateContent"
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
path: "/v1/projects/:project/locations/:location/publishers/:publisher/models/:model\\:streamGenerateContent"
|
||||
}
|
||||
],
|
||||
extractModel: paramModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: isGeminiStyleStreaming
|
||||
},
|
||||
google_raw_predict: {
|
||||
id: "google_raw_predict",
|
||||
protocolFamily: "google",
|
||||
routes: [
|
||||
{
|
||||
method: "POST",
|
||||
path: "/v1/projects/:project/locations/:location/publishers/:publisher/models/:model\\:rawPredict"
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
path: "/v1/projects/:project/locations/:location/publishers/:publisher/models/:model\\:streamRawPredict"
|
||||
}
|
||||
],
|
||||
extractModel: paramModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: (req, contentType) =>
|
||||
pathIncludes(req, "streamRawPredict") ||
|
||||
pathIncludes(req, "alt=sse") ||
|
||||
contentTypeIsSse(contentType)
|
||||
},
|
||||
bedrock_model_invoke: {
|
||||
id: "bedrock_model_invoke",
|
||||
protocolFamily: "bedrock",
|
||||
routes: [
|
||||
{ method: "POST", path: "/model/:model/invoke" },
|
||||
{
|
||||
method: "POST",
|
||||
path: "/model/:model/invoke-with-response-stream"
|
||||
}
|
||||
],
|
||||
extractModel: paramModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: (req, contentType) =>
|
||||
pathIncludes(req, "invoke-with-response-stream") ||
|
||||
contentTypeIsAmazonEventStream(contentType) ||
|
||||
contentTypeIsSse(contentType)
|
||||
},
|
||||
bedrock_converse: {
|
||||
id: "bedrock_converse",
|
||||
protocolFamily: "bedrock",
|
||||
routes: [
|
||||
{ method: "POST", path: "/model/:model/converse" },
|
||||
{ method: "POST", path: "/model/:model/converse-stream" }
|
||||
],
|
||||
extractModel: paramModel,
|
||||
resolveUpstreamUrl: (base, req) =>
|
||||
joinUpstreamUrl(base, pathFromRequest(req)),
|
||||
isStreaming: (req, contentType) =>
|
||||
pathIncludes(req, "converse-stream") ||
|
||||
contentTypeIsAmazonEventStream(contentType) ||
|
||||
contentTypeIsSse(contentType)
|
||||
}
|
||||
};
|
||||
|
||||
export function isAiCapability(value: unknown): value is AiCapability {
|
||||
return (
|
||||
typeof value === "string" &&
|
||||
(AI_CAPABILITIES as readonly string[]).includes(value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Express-style route path from AI_CAPABILITY_DEFS into a RegExp.
|
||||
* Handles `:param` segments and escaped literal colons (`\:`).
|
||||
*/
|
||||
export function routePatternToRegExp(routePath: string): RegExp {
|
||||
let pattern = "";
|
||||
for (let i = 0; i < routePath.length; i++) {
|
||||
const ch = routePath[i];
|
||||
if (
|
||||
ch === "\\" &&
|
||||
i + 1 < routePath.length &&
|
||||
routePath[i + 1] === ":"
|
||||
) {
|
||||
pattern += ":";
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (ch === ":") {
|
||||
// Named param: consume until next / or end
|
||||
i++;
|
||||
while (
|
||||
i < routePath.length &&
|
||||
routePath[i] !== "/" &&
|
||||
!(routePath[i] === "\\" && routePath[i + 1] === ":")
|
||||
) {
|
||||
i++;
|
||||
}
|
||||
i--; // loop will ++
|
||||
pattern += "[^/]+";
|
||||
continue;
|
||||
}
|
||||
// Escape regex special chars
|
||||
if (/[.*+?^${}()|[\]\\]/.test(ch)) {
|
||||
pattern += "\\" + ch;
|
||||
} else {
|
||||
pattern += ch;
|
||||
}
|
||||
}
|
||||
return new RegExp(`^${pattern}$`);
|
||||
}
|
||||
|
||||
export function resolveAiCapabilityFromPath(path: string): AiCapability | null {
|
||||
const pathname = path.split("?")[0] || "/";
|
||||
const normalized = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
||||
|
||||
for (const def of Object.values(AI_CAPABILITY_DEFS)) {
|
||||
for (const route of def.routes) {
|
||||
if (routePatternToRegExp(route.path).test(normalized)) {
|
||||
return def.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function parseCapabilities(raw: unknown): AiCapability[] {
|
||||
if (raw == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let parsed: unknown = raw;
|
||||
if (typeof raw === "string") {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
parsed = JSON.parse(trimmed);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(parsed)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const out: AiCapability[] = [];
|
||||
const seen = new Set<AiCapability>();
|
||||
for (const item of parsed) {
|
||||
if (isAiCapability(item) && !seen.has(item)) {
|
||||
seen.add(item);
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function serializeCapabilities(capabilities: AiCapability[]): string {
|
||||
return JSON.stringify(capabilities);
|
||||
}
|
||||
|
||||
export function providerHasCapability(
|
||||
capabilities: AiCapability[] | string | null | undefined,
|
||||
capability: AiCapability
|
||||
): boolean {
|
||||
const list =
|
||||
typeof capabilities === "string" || capabilities == null
|
||||
? parseCapabilities(capabilities)
|
||||
: capabilities;
|
||||
return list.includes(capability);
|
||||
}
|
||||